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