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
20static gchar *
21cook_piece (void)
22{
23 char buffer[MAX_PIECE_SIZE * 2];
24 gint symbols, i = 0;
25
26 symbols = g_test_rand_int_range (begin: 1, MAX_PIECE_SIZE + 1);
27
28 while (symbols--)
29 {
30 gint c = g_test_rand_int_range (begin: 0, end: 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 (str: buffer, n: i);
57}
58
59static gchar **
60cook_pieces (void)
61{
62 gchar **array;
63 gint pieces;
64
65 pieces = g_test_rand_int_range (begin: 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
75typedef 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
86typedef GInputStreamClass SleepyStreamClass;
87
88GType sleepy_stream_get_type (void);
89
90G_DEFINE_TYPE (SleepyStream, sleepy_stream, G_TYPE_INPUT_STREAM)
91
92static gssize
93sleepy_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 (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED, format: "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 (dest: buffer, src: sleepy->current, n: length);
118
119 sleepy->current += length;
120 if (*sleepy->current == '\0')
121 sleepy->current = NULL;
122
123 return length;
124 }
125}
126
127static void
128sleepy_stream_init (SleepyStream *sleepy)
129{
130 sleepy->pieces = cook_pieces ();
131 sleepy->built_to_fail = FALSE;
132 sleepy->index = 0;
133}
134
135static void
136sleepy_stream_finalize (GObject *object)
137{
138 SleepyStream *sleepy = (SleepyStream *) object;
139
140 g_strfreev (str_array: sleepy->pieces);
141 G_OBJECT_CLASS (sleepy_stream_parent_class)
142 ->finalize (object);
143}
144
145static void
146sleepy_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
156static SleepyStream *
157sleepy_stream_new (void)
158{
159 return g_object_new (object_type: sleepy_stream_get_type (), NULL);
160}
161
162static gboolean
163read_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: &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, val: str);
180 g_string_append (string, val: eol);
181 g_free (mem: str);
182
183 return TRUE;
184}
185
186static void
187build_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 (string: str, val: stream->pieces[i]);
195
196 if (str->len && str->str[str->len - 1] != '\n')
197 g_string_append_c (str, '\n');
198}
199
200
201static void
202test (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 (stream: data, type: G_DATA_STREAM_NEWLINE_TYPE_LF);
215 build_comparison (str: one, stream);
216
217 while (read_line (stream: data, string: two, eol: "\n", error: &error));
218
219 g_assert_cmpstr (one->str, ==, two->str);
220 g_string_free (string: one, TRUE);
221 g_string_free (string: two, TRUE);
222 g_object_unref (object: stream);
223 g_object_unref (object: data);
224}
225
226static GDataInputStream *data;
227static GString *one, *two;
228static GMainLoop *loop;
229static const gchar *eol;
230
231static void
232asynch_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 (stream: data, result, length: &length, error: &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 (string: two, val: str);
254 g_string_append (string: two, val: eol);
255 g_free (mem: str);
256
257 /* MOAR!! */
258 g_data_input_stream_read_line_async (stream: data, io_priority: 0, NULL, callback: asynch_ready, NULL);
259 }
260}
261
262
263static void
264asynch (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 (str: one, stream: sleepy);
274 g_data_input_stream_read_line_async (stream: data, io_priority: 0, NULL, callback: asynch_ready, NULL);
275 g_main_loop_run (loop: loop = g_main_loop_new (NULL, FALSE));
276
277 g_assert_cmpstr (one->str, ==, two->str);
278 g_string_free (string: one, TRUE);
279 g_string_free (string: two, TRUE);
280 g_object_unref (object: sleepy);
281 g_object_unref (object: data);
282}
283
284int
285main (int argc, char **argv)
286{
287 g_test_init (argc: &argc, argv: &argv, NULL);
288 g_test_bug_base (uri_pattern: "http://bugzilla.gnome.org/");
289
290 g_test_add_func (testpath: "/filter-stream/input", test_func: test);
291 g_test_add_func (testpath: "/filter-stream/async", test_func: asynch);
292
293 return g_test_run();
294}
295

source code of gtk/subprojects/glib/gio/tests/sleepy-stream.c