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