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 20static gchar * 21cook_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 57static gchar ** 58cook_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 73typedef 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 84typedef GInputStreamClass SleepyStreamClass; 85 86G_DEFINE_TYPE (SleepyStream, sleepy_stream, G_TYPE_INPUT_STREAM) 87 88static gssize 89sleepy_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 123static void 124sleepy_stream_init (SleepyStream *sleepy) 125{ 126 sleepy->pieces = cook_pieces (); 127 sleepy->built_to_fail = FALSE; 128 sleepy->index = 0; 129} 130 131static void 132sleepy_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 141static void 142sleepy_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 152SleepyStream * 153sleepy_stream_new (void) 154{ 155 return g_object_new (sleepy_stream_get_type (), NULL); 156} 157 158static gboolean 159read_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 185static void 186build_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 200void 201test (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 225static GDataInputStream *data; 226static GString *one, *two; 227static GMainLoop *loop; 228static const gchar *eol; 229 230static void 231asynch_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 262void 263asynch (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 283int 284main (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