Merge remote-tracking 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   char *str;
168
169   str = g_data_input_stream_read_line (stream, &length, NULL, error);
170
171   if (str == NULL)
172     return FALSE;
173
174   g_assert (strstr (str, eol) == NULL);
175   g_assert (strlen (str) == length);
176
177   g_string_append (string, str);
178   g_string_append (string, eol);
179   g_free (str);
180
181   return TRUE;
182 }
183
184 static void
185 build_comparison (GString      *str,
186                   SleepyStream *stream)
187 {
188   /* build this for comparison */
189   gint i;
190
191   for (i = 0; stream->pieces[i]; i++)
192     g_string_append (str, stream->pieces[i]);
193
194   if (str->len && str->str[str->len - 1] != '\n')
195     g_string_append_c (str, '\n');
196 }
197
198
199 static void
200 test (void)
201 {
202   SleepyStream *stream = sleepy_stream_new ();
203   GDataInputStream *data;
204   GError *error = NULL;
205   GString *one;
206   GString *two;
207
208   one = g_string_new (NULL);
209   two = g_string_new (NULL);
210
211   data = g_data_input_stream_new (G_INPUT_STREAM (stream));
212   g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_LF);
213   build_comparison (one, stream);
214
215   while (read_line (data, two, "\n", &error));
216
217   g_assert_cmpstr (one->str, ==, two->str);
218   g_string_free (one, TRUE);
219   g_string_free (two, TRUE);
220   g_object_unref (stream);
221   g_object_unref (data);
222 }
223
224 static GDataInputStream *data;
225 static GString *one, *two;
226 static GMainLoop *loop;
227 static const gchar *eol;
228
229 static void
230 asynch_ready (GObject      *object,
231               GAsyncResult *result,
232               gpointer      user_data)
233 {
234   GError *error = NULL;
235   gsize length;
236   gchar *str;
237
238   g_assert (data == G_DATA_INPUT_STREAM (object));
239
240   str = g_data_input_stream_read_line_finish (data, result, &length, &error);
241
242   if (str == NULL)
243     {
244       g_main_loop_quit (loop);
245       if (error)
246         g_error_free (error);
247     }
248   else
249     {
250       g_assert (length == strlen (str));
251       g_string_append (two, str);
252       g_string_append (two, eol);
253       g_free (str);
254
255       /* MOAR!! */
256       g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
257     }
258 }
259
260
261 static void
262 asynch (void)
263 {
264   SleepyStream *sleepy = sleepy_stream_new ();
265
266   data = g_data_input_stream_new (G_INPUT_STREAM (sleepy));
267   one = g_string_new (NULL);
268   two = g_string_new (NULL);
269   eol = "\n";
270
271   build_comparison (one, sleepy);
272   g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
273   g_main_loop_run (loop = g_main_loop_new (NULL, FALSE));
274
275   g_assert_cmpstr (one->str, ==, two->str);
276   g_string_free (one, TRUE);
277   g_string_free (two, TRUE);
278   g_object_unref (sleepy);
279   g_object_unref (data);
280 }
281
282 int
283 main (int argc, char **argv)
284 {
285   g_test_init (&argc, &argv, NULL);
286   g_test_bug_base ("http://bugzilla.gnome.org/");
287
288   g_type_init ();
289   g_test_add_func ("/filter-stream/input", test);
290   g_test_add_func ("/filter-stream/async", asynch);
291
292   return g_test_run();
293 }