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