Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2009 Codethink Limited
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * See the included COPYING file for more information.
12 : : *
13 : : * Author: Ryan Lortie <desrt@desrt.ca>
14 : : */
15 : :
16 : : #include <gio/gio.h>
17 : : #include <string.h>
18 : :
19 : : #define MAX_PIECE_SIZE 100
20 : : #define MAX_PIECES 60
21 : :
22 : : static gchar *
23 : 50 : cook_piece (void)
24 : : {
25 : : char buffer[MAX_PIECE_SIZE * 2];
26 : 50 : gint symbols, i = 0;
27 : :
28 : 50 : symbols = g_test_rand_int_range (1, MAX_PIECE_SIZE + 1);
29 : :
30 : 2586 : while (symbols--)
31 : : {
32 : 2536 : gint c = g_test_rand_int_range (0, 30);
33 : :
34 : 2536 : switch (c)
35 : : {
36 : 54 : case 26:
37 : 54 : buffer[i++] = '\n';
38 : : G_GNUC_FALLTHROUGH;
39 : 130 : case 27:
40 : 130 : buffer[i++] = '\r';
41 : 130 : break;
42 : :
43 : 88 : case 28:
44 : 88 : buffer[i++] = '\r';
45 : : G_GNUC_FALLTHROUGH;
46 : 168 : case 29:
47 : 168 : buffer[i++] = '\n';
48 : 168 : break;
49 : :
50 : 2238 : default:
51 : 2238 : buffer[i++] = c + 'a';
52 : 2238 : break;
53 : : }
54 : :
55 : 2536 : g_assert_cmpint (i, <=, sizeof buffer);
56 : : }
57 : :
58 : 50 : return g_strndup (buffer, i);
59 : : }
60 : :
61 : : static gchar **
62 : 2 : cook_pieces (void)
63 : : {
64 : : gchar **array;
65 : : gint pieces;
66 : :
67 : 2 : pieces = g_test_rand_int_range (0, MAX_PIECES + 1);
68 : 2 : array = g_new (char *, pieces + 1);
69 : 2 : array[pieces] = NULL;
70 : :
71 : 52 : while (pieces--)
72 : 50 : array[pieces] = cook_piece ();
73 : :
74 : 2 : return array;
75 : : }
76 : :
77 : : typedef struct
78 : : {
79 : : GInputStream parent_instance;
80 : :
81 : : gboolean built_to_fail;
82 : : gchar **pieces;
83 : : gint index;
84 : :
85 : : const gchar *current;
86 : : } SleepyStream;
87 : :
88 : : typedef GInputStreamClass SleepyStreamClass;
89 : :
90 : : GType sleepy_stream_get_type (void);
91 : :
92 : 4 : G_DEFINE_TYPE (SleepyStream, sleepy_stream, G_TYPE_INPUT_STREAM)
93 : :
94 : : static gssize
95 : 54 : sleepy_stream_read (GInputStream *stream,
96 : : void *buffer,
97 : : gsize length,
98 : : GCancellable *cancellable,
99 : : GError **error)
100 : : {
101 : 54 : SleepyStream *sleepy = (SleepyStream *) stream;
102 : :
103 : 54 : if (sleepy->pieces[sleepy->index] == NULL)
104 : : {
105 : 4 : if (sleepy->built_to_fail)
106 : : {
107 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "fail");
108 : 0 : return -1;
109 : : }
110 : : else
111 : 4 : return 0;
112 : : }
113 : : else
114 : : {
115 : 50 : if (!sleepy->current)
116 : 50 : sleepy->current = sleepy->pieces[sleepy->index++];
117 : :
118 : 50 : length = MIN (strlen (sleepy->current), length);
119 : 50 : memcpy (buffer, sleepy->current, length);
120 : :
121 : 50 : sleepy->current += length;
122 : 50 : if (*sleepy->current == '\0')
123 : 50 : sleepy->current = NULL;
124 : :
125 : 50 : return length;
126 : : }
127 : : }
128 : :
129 : : static void
130 : 2 : sleepy_stream_init (SleepyStream *sleepy)
131 : : {
132 : 2 : sleepy->pieces = cook_pieces ();
133 : 2 : sleepy->built_to_fail = FALSE;
134 : 2 : sleepy->index = 0;
135 : 2 : }
136 : :
137 : : static void
138 : 2 : sleepy_stream_finalize (GObject *object)
139 : : {
140 : 2 : SleepyStream *sleepy = (SleepyStream *) object;
141 : :
142 : 2 : g_strfreev (sleepy->pieces);
143 : 2 : G_OBJECT_CLASS (sleepy_stream_parent_class)
144 : 2 : ->finalize (object);
145 : 2 : }
146 : :
147 : : static void
148 : 1 : sleepy_stream_class_init (SleepyStreamClass *class)
149 : : {
150 : 1 : G_OBJECT_CLASS (class)->finalize = sleepy_stream_finalize;
151 : 1 : class->read_fn = sleepy_stream_read;
152 : :
153 : : /* no read_async implementation.
154 : : * main thread will sleep while read runs in a worker.
155 : : */
156 : 1 : }
157 : :
158 : : static SleepyStream *
159 : 2 : sleepy_stream_new (void)
160 : : {
161 : 2 : return g_object_new (sleepy_stream_get_type (), NULL);
162 : : }
163 : :
164 : : static gboolean
165 : 113 : read_line (GDataInputStream *stream,
166 : : GString *string,
167 : : const gchar *eol,
168 : : GError **error)
169 : : {
170 : : gsize length;
171 : : char *str;
172 : :
173 : 113 : str = g_data_input_stream_read_line (stream, &length, NULL, error);
174 : :
175 : 113 : if (str == NULL)
176 : 1 : return FALSE;
177 : :
178 : 112 : g_assert (strstr (str, eol) == NULL);
179 : 112 : g_assert (strlen (str) == length);
180 : :
181 : : g_string_append (string, str);
182 : : g_string_append (string, eol);
183 : 112 : g_free (str);
184 : :
185 : 112 : return TRUE;
186 : : }
187 : :
188 : : static void
189 : 2 : build_comparison (GString *str,
190 : : SleepyStream *stream)
191 : : {
192 : : /* build this for comparison */
193 : : gint i;
194 : :
195 : 52 : for (i = 0; stream->pieces[i]; i++)
196 : 50 : g_string_append (str, stream->pieces[i]);
197 : :
198 : 2 : if (str->len && str->str[str->len - 1] != '\n')
199 : : g_string_append_c (str, '\n');
200 : 2 : }
201 : :
202 : :
203 : : static void
204 : 1 : test (void)
205 : : {
206 : 1 : SleepyStream *stream = sleepy_stream_new ();
207 : : GDataInputStream *data;
208 : 1 : GError *error = NULL;
209 : : GString *one;
210 : : GString *two;
211 : :
212 : 1 : one = g_string_new (NULL);
213 : 1 : two = g_string_new (NULL);
214 : :
215 : 1 : data = g_data_input_stream_new (G_INPUT_STREAM (stream));
216 : 1 : g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_LF);
217 : 1 : build_comparison (one, stream);
218 : :
219 : 113 : while (read_line (data, two, "\n", &error));
220 : :
221 : 1 : g_assert_cmpstr (one->str, ==, two->str);
222 : 1 : g_string_free (one, TRUE);
223 : 1 : g_string_free (two, TRUE);
224 : 1 : g_object_unref (stream);
225 : 1 : g_object_unref (data);
226 : 1 : }
227 : :
228 : : static GDataInputStream *data;
229 : : static GString *one, *two;
230 : : static GMainLoop *loop;
231 : : static const gchar *eol;
232 : :
233 : : static void
234 : 113 : asynch_ready (GObject *object,
235 : : GAsyncResult *result,
236 : : gpointer user_data)
237 : : {
238 : 113 : GError *error = NULL;
239 : : gsize length;
240 : : gchar *str;
241 : :
242 : 113 : g_assert (data == G_DATA_INPUT_STREAM (object));
243 : :
244 : 113 : str = g_data_input_stream_read_line_finish (data, result, &length, &error);
245 : :
246 : 113 : if (str == NULL)
247 : : {
248 : 1 : g_main_loop_quit (loop);
249 : 1 : if (error)
250 : 0 : g_error_free (error);
251 : : }
252 : : else
253 : : {
254 : 112 : g_assert (length == strlen (str));
255 : 112 : g_string_append (two, str);
256 : 112 : g_string_append (two, eol);
257 : 112 : g_free (str);
258 : :
259 : : /* MOAR!! */
260 : 112 : g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
261 : : }
262 : 113 : }
263 : :
264 : :
265 : : static void
266 : 1 : asynch (void)
267 : : {
268 : 1 : SleepyStream *sleepy = sleepy_stream_new ();
269 : :
270 : 1 : data = g_data_input_stream_new (G_INPUT_STREAM (sleepy));
271 : 1 : one = g_string_new (NULL);
272 : 1 : two = g_string_new (NULL);
273 : 1 : eol = "\n";
274 : :
275 : 1 : build_comparison (one, sleepy);
276 : 1 : g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
277 : 1 : g_main_loop_run (loop = g_main_loop_new (NULL, FALSE));
278 : :
279 : 1 : g_assert_cmpstr (one->str, ==, two->str);
280 : 1 : g_string_free (one, TRUE);
281 : 1 : g_string_free (two, TRUE);
282 : 1 : g_object_unref (sleepy);
283 : 1 : g_object_unref (data);
284 : 1 : }
285 : :
286 : : int
287 : 1 : main (int argc, char **argv)
288 : : {
289 : 1 : g_test_init (&argc, &argv, NULL);
290 : :
291 : 1 : g_test_add_func ("/filter-stream/input", test);
292 : 1 : g_test_add_func ("/filter-stream/async", asynch);
293 : :
294 : 1 : return g_test_run();
295 : : }
|