• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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