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