Merge remote branch 'gvdb/master'
[platform/upstream/glib.git] / gio / tests / sleepy-stream.c
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 *
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          case 27:
37           buffer[i++] = '\r';
38           break;
39
40          case 28:
41           buffer[i++] = '\r';
42          case 29:
43           buffer[i++] = '\n';
44           break;
45
46          default:
47           buffer[i++] = c + 'a';
48           break;
49         }
50
51       g_assert_cmpint (i, <=, sizeof buffer);
52     }
53
54   return g_strndup (buffer, i);
55 }
56
57 static gchar **
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
86 GType sleepy_stream_get_type (void);
87
88 G_DEFINE_TYPE (SleepyStream, sleepy_stream, G_TYPE_INPUT_STREAM)
89
90 static gssize
91 sleepy_stream_read (GInputStream  *stream,
92                     void          *buffer,
93                     gsize          length,
94                     GCancellable  *cancellable,
95                     GError       **error)
96 {
97   SleepyStream *sleepy = (SleepyStream *) stream;
98
99   if (sleepy->pieces[sleepy->index] == NULL)
100     {
101       if (sleepy->built_to_fail)
102         {
103           g_set_error (error, 0, 0, "fail");
104           return -1;
105         }
106       else
107         return 0;
108     }
109   else
110     {
111       if (!sleepy->current)
112         sleepy->current = sleepy->pieces[sleepy->index++];
113
114       length = MIN (strlen (sleepy->current), length);
115       memcpy (buffer, sleepy->current, length);
116
117       sleepy->current += length;
118       if (*sleepy->current == '\0')
119         sleepy->current = NULL;
120
121       return length;
122     }
123 }
124
125 static void
126 sleepy_stream_init (SleepyStream *sleepy)
127 {
128   sleepy->pieces = cook_pieces ();
129   sleepy->built_to_fail = FALSE;
130   sleepy->index = 0;
131 }
132
133 static void
134 sleepy_stream_finalize (GObject *object)
135 {
136   SleepyStream *sleepy = (SleepyStream *) object;
137
138   g_strfreev (sleepy->pieces);
139   G_OBJECT_CLASS (sleepy_stream_parent_class)
140     ->finalize (object);
141 }
142
143 static void
144 sleepy_stream_class_init (SleepyStreamClass *class)
145 {
146   G_OBJECT_CLASS (class)->finalize = sleepy_stream_finalize;
147   class->read_fn = sleepy_stream_read;
148
149   /* no read_async implementation.
150    * main thread will sleep while read runs in a worker.
151    */
152 }
153
154 static SleepyStream *
155 sleepy_stream_new (void)
156 {
157   return g_object_new (sleepy_stream_get_type (), NULL);
158 }
159
160 static gboolean
161 read_line (GDataInputStream  *stream,
162            GString           *string,
163            const gchar       *eol,
164            GError           **error)
165 {
166   gsize length;
167   int eol_len;
168   char *str;
169
170   eol_len = 1 + (eol[1] != '\0');
171
172   str = g_data_input_stream_read_line (stream, &length, NULL, error);
173
174   if (str == NULL)
175     return FALSE;
176
177   g_assert (strstr (str, eol) == NULL);
178   g_assert (strlen (str) == length);
179
180   g_string_append (string, str);
181   g_string_append (string, eol);
182   g_free (str);
183
184   return TRUE;
185 }
186
187 static void
188 build_comparison (GString      *str,
189                   SleepyStream *stream)
190 {
191   /* build this for comparison */
192   gint i;
193
194   for (i = 0; stream->pieces[i]; i++)
195     g_string_append (str, stream->pieces[i]);
196
197   if (str->len && str->str[str->len - 1] != '\n')
198     g_string_append_c (str, '\n');
199 }
200
201
202 static void
203 test (void)
204 {
205   SleepyStream *stream = sleepy_stream_new ();
206   GDataInputStream *data;
207   GError *error = NULL;
208   GString *one;
209   GString *two;
210
211   one = g_string_new (NULL);
212   two = g_string_new (NULL);
213
214   data = g_data_input_stream_new (G_INPUT_STREAM (stream));
215   g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_LF);
216   build_comparison (one, stream);
217
218   while (read_line (data, two, "\n", &error));
219
220   g_assert_cmpstr (one->str, ==, two->str);
221   g_string_free (one, TRUE);
222   g_string_free (two, TRUE);
223   g_object_unref (stream);
224   g_object_unref (data);
225 }
226
227 static GDataInputStream *data;
228 static GString *one, *two;
229 static GMainLoop *loop;
230 static const gchar *eol;
231
232 static void
233 asynch_ready (GObject      *object,
234               GAsyncResult *result,
235               gpointer      user_data)
236 {
237   GError *error = NULL;
238   gsize length;
239   gchar *str;
240
241   g_assert (data == G_DATA_INPUT_STREAM (object));
242
243   str = g_data_input_stream_read_line_finish (data, result, &length, &error);
244
245   if (str == NULL)
246     {
247       g_main_loop_quit (loop);
248       if (error)
249         g_error_free (error);
250     }
251   else
252     {
253       g_assert (length == strlen (str));
254       g_string_append (two, str);
255       g_string_append (two, eol);
256       g_free (str);
257
258       /* MOAR!! */
259       g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
260     }
261 }
262
263
264 static void
265 asynch (void)
266 {
267   SleepyStream *sleepy = sleepy_stream_new ();
268
269   data = g_data_input_stream_new (G_INPUT_STREAM (sleepy));
270   one = g_string_new (NULL);
271   two = g_string_new (NULL);
272   eol = "\n";
273
274   build_comparison (one, sleepy);
275   g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
276   g_main_loop_run (loop = g_main_loop_new (NULL, FALSE));
277
278   g_assert_cmpstr (one->str, ==, two->str);
279   g_string_free (one, TRUE);
280   g_string_free (two, TRUE);
281   g_object_unref (sleepy);
282   g_object_unref (data);
283 }
284
285 int
286 main (int argc, char **argv)
287 {
288   g_test_init (&argc, &argv, NULL);
289   g_test_bug_base ("http://bugzilla.gnome.org/");
290
291   g_type_init ();
292   g_test_add_func ("/filter-stream/input", test);
293   g_test_add_func ("/filter-stream/async", asynch);
294
295   return g_test_run();
296 }