1 /*
2 NAME
3   stream-getdelim - test the mu_stream_getdelim function.
4 
5 DESCRIPTION
6   This implements a simple memory-based stream and tests the
7   mu_stream_getdelim function on a predefined stream content with
8   various combinations of buffering type and buffer size settings.
9 
10   On success, returns 0.  On error, prints diagnostics on stderr and
11   exits with a non-0 code or aborts.
12 
13   Before running each test, its short description is printed on stdout.
14 
15   For obvious reasons, libc functions are used for output.
16 
17 LICENSE
18   This file is part of GNU mailutils.
19   Copyright (C) 2020-2021 Free Software Foundation, Inc.
20 
21   This program is free software; you can redistribute it and/or modify
22   it under the terms of the GNU General Public License as published by
23   the Free Software Foundation; either version 3, or (at your option)
24   any later version.
25 
26   This program is distributed in the hope that it will be useful,
27   but WITHOUT ANY WARRANTY; without even the implied warranty of
28   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29   GNU General Public License for more details.
30 
31   You should have received a copy of the GNU General Public License
32   along with this program.  If not, see <http://www.gnu.org/licenses/>.
33 */
34 
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <assert.h>
42 #include <errno.h>
43 #include <mailutils/types.h>
44 #include <mailutils/debug.h>
45 #include <mailutils/errno.h>
46 #include <mailutils/stream.h>
47 #include <mailutils/sys/stream.h>
48 
49 char content[] =
50   "AB\n"
51   "\n"
52   "\n"
53   "CDEFG\n"
54   "H\n"
55   "IJ\n"
56   "KLMNOPQRST\n"
57   "UVWXYZ";
58 
59 struct test_stream
60 {
61   struct _mu_stream stream;
62   char *ptr;
63   size_t size;
64   mu_off_t offset;
65 };
66 
67 static int
ts_open(mu_stream_t stream)68 ts_open (mu_stream_t stream)
69 {
70   struct test_stream *ts = (struct test_stream *) stream;
71   ts->ptr = content;
72   ts->size = strlen (content);
73   ts->offset = 0;
74   return 0;
75 }
76 
77 static int
ts_read(mu_stream_t stream,char * optr,size_t osize,size_t * nbytes)78 ts_read (mu_stream_t stream, char *optr, size_t osize, size_t *nbytes)
79 {
80   struct test_stream *ts = (struct test_stream *) stream;
81   size_t n = 0;
82   if (ts->ptr != NULL && ((size_t)ts->offset <= ts->size))
83     {
84       n = ts->size - ts->offset;
85       if (n > osize)
86 	n = osize;
87       memcpy (optr, ts->ptr + ts->offset, n);
88       ts->offset += n;
89     }
90   if (nbytes)
91     *nbytes = n;
92   return 0;
93 }
94 
95 static int
ts_seek(mu_stream_t stream,mu_off_t off,mu_off_t * presult)96 ts_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
97 {
98   struct test_stream *ts = (struct test_stream *) stream;
99 
100   if (off < 0)
101     return ESPIPE;
102   ts->offset = off;
103   *presult = off;
104   return 0;
105 }
106 
107 static int
ts_size(mu_stream_t stream,mu_off_t * psize)108 ts_size (mu_stream_t stream, mu_off_t *psize)
109 {
110   struct test_stream *ts = (struct test_stream *) stream;
111   *psize = ts->size;
112   return 0;
113 }
114 
115 int
test_stream_create(mu_stream_t * pstream)116 test_stream_create (mu_stream_t *pstream)
117 {
118   int rc;
119   mu_stream_t stream;
120 
121   stream = _mu_stream_create (sizeof (struct test_stream), MU_STREAM_READ|MU_STREAM_SEEK);
122   assert (stream != NULL);
123 
124   stream->open = ts_open;
125   stream->read = ts_read;
126   stream->size = ts_size;
127   stream->seek = ts_seek;
128 
129   rc = mu_stream_open (stream);
130   if (rc)
131     mu_stream_destroy (&stream);
132   else
133     *pstream = stream;
134 
135   return rc;
136 }
137 
138 void
runtest(mu_stream_t str)139 runtest (mu_stream_t str)
140 {
141   char *buf = NULL;
142   size_t size = 0;
143   mu_off_t off;
144   size_t n;
145   int rc;
146   unsigned long start = 0;
147 
148   MU_ASSERT (mu_stream_seek (str, 0, MU_SEEK_SET, NULL));
149 
150   while ((rc = mu_stream_getdelim (str, &buf, &size, '\n', &n)) == 0
151 	 && n > 0)
152     {
153       size_t blen = strlen (buf);
154       size_t len = strcspn (content + start, "\n");
155       if (content[start+len] == '\n')
156 	len++;
157       if (len != blen || memcmp (content + start, buf, len))
158 	{
159 	  fprintf (stderr, "at %lu: expected:\n", start);
160 	  fwrite (content + start, blen, 1, stderr);
161 	  fprintf (stderr, "\nfound:\n");
162 	  fwrite (buf, blen, 1, stderr);
163 	  fputc ('\n', stderr);
164 	  exit (1);
165 	}
166       start += len;
167 
168       MU_ASSERT (mu_stream_seek (str, 0, MU_SEEK_CUR, &off));
169       if (off != start)
170 	{
171 	  fprintf (stderr, "wrong offset reported (%lu, expected %lu\n",
172 		   (unsigned long)off, (unsigned long)start);
173 	  exit (2);
174 	}
175     }
176   if (rc)
177     {
178       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_getline", NULL, rc);
179       exit (1);
180     }
181   free (buf);
182 }
183 
184 #define TESTBUFSIZE 512
185 
186 static struct test
187 {
188   char *name;
189   enum mu_buffer_type type;
190   size_t size;
191 } testtab[] = {
192   { "No buffering", mu_buffer_none, 0 },
193   { "Linear buffering", mu_buffer_line, TESTBUFSIZE },
194   { "Linear buffering (small buffer)", mu_buffer_line, 1 },
195   { "Full buffering (big buffer)",  mu_buffer_full, TESTBUFSIZE },
196   { "Full buffering (moderate buffer)", mu_buffer_full, sizeof(content)/2 },
197   { "Full buffering (small buffer)", mu_buffer_full, 1 },
198   { NULL }
199 };
200 
201 int
main(int argc,char ** argv)202 main (int argc, char **argv)
203 {
204   mu_stream_t str;
205   int i;
206 
207   MU_ASSERT (test_stream_create (&str));
208 
209   for (i = 0; testtab[i].name; i++)
210     {
211       printf ("%d: %s\n", i, testtab[i].name);
212       MU_ASSERT (mu_stream_set_buffer (str, testtab[i].type, testtab[i].size));
213       runtest (str);
214     }
215 
216   mu_stream_destroy (&str);
217   return 0;
218 }
219