1/* GLib testing framework examples and tests
2 * Copyright (C) 2009 Red Hat, Inc.
3 * Authors: Alexander Larsson <alexl@redhat.com>
4 *
5 * This work is provided "as is"; redistribution and modification
6 * in whole or in part, in any medium, physical or electronic is
7 * permitted without restriction.
8 *
9 * This work is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * In no event shall the authors or contributors be liable for any
14 * direct, indirect, incidental, special, exemplary, or consequential
15 * damages (including, but not limited to, procurement of substitute
16 * goods or services; loss of use, data, or profits; or business
17 * interruption) however caused and on any theory of liability, whether
18 * in contract, strict liability, or tort (including negligence or
19 * otherwise) arising in any way out of the use of this software, even
20 * if advised of the possibility of such damage.
21 */
22
23#include <glib/glib.h>
24#include <gio/gio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#define G_TYPE_EXPANDER_CONVERTER (g_expander_converter_get_type ())
29#define G_EXPANDER_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_EXPANDER_CONVERTER, GExpanderConverter))
30#define G_EXPANDER_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_EXPANDER_CONVERTER, GExpanderConverterClass))
31#define G_IS_EXPANDER_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_EXPANDER_CONVERTER))
32#define G_IS_EXPANDER_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_EXPANDER_CONVERTER))
33#define G_EXPANDER_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_EXPANDER_CONVERTER, GExpanderConverterClass))
34
35typedef struct _GExpanderConverter GExpanderConverter;
36typedef struct _GExpanderConverterClass GExpanderConverterClass;
37
38struct _GExpanderConverterClass
39{
40 GObjectClass parent_class;
41};
42
43GType g_expander_converter_get_type (void) G_GNUC_CONST;
44GConverter *g_expander_converter_new (void);
45
46
47
48static void g_expander_converter_iface_init (GConverterIface *iface);
49
50struct _GExpanderConverter
51{
52 GObject parent_instance;
53};
54
55G_DEFINE_TYPE_WITH_CODE (GExpanderConverter, g_expander_converter, G_TYPE_OBJECT,
56 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
57 g_expander_converter_iface_init))
58
59static void
60g_expander_converter_class_init (GExpanderConverterClass *klass)
61{
62}
63
64static void
65g_expander_converter_init (GExpanderConverter *local)
66{
67}
68
69GConverter *
70g_expander_converter_new (void)
71{
72 GConverter *conv;
73
74 conv = g_object_new (G_TYPE_EXPANDER_CONVERTER, NULL);
75
76 return conv;
77}
78
79static void
80g_expander_converter_reset (GConverter *converter)
81{
82}
83
84static GConverterResult
85g_expander_converter_convert (GConverter *converter,
86 const void *inbuf,
87 gsize inbuf_size,
88 void *outbuf,
89 gsize outbuf_size,
90 GConverterFlags flags,
91 gsize *bytes_read,
92 gsize *bytes_written,
93 GError **error)
94{
95 const guint8 *in, *in_end;
96 guint8 v, *out;
97 int i;
98 gsize block_size;
99
100 in = inbuf;
101 out = outbuf;
102 in_end = in + inbuf_size;
103
104 while (in < in_end)
105 {
106 v = *in;
107
108 if (v == 0)
109 block_size = 10;
110 else
111 block_size = v * 1000;
112
113 if (outbuf_size < block_size)
114 {
115 if (*bytes_read > 0)
116 return G_CONVERTER_CONVERTED;
117
118 g_set_error_literal (err: error, G_IO_ERROR,
119 code: G_IO_ERROR_NO_SPACE,
120 message: "No space in dest");
121 return G_CONVERTER_ERROR;
122 }
123
124 in++;
125 *bytes_read += 1;
126 *bytes_written += block_size;
127 outbuf_size -= block_size;
128 for (i = 0; i < block_size; i++)
129 *out++ = v;
130 }
131
132 if (in == in_end && (flags & G_CONVERTER_INPUT_AT_END))
133 return G_CONVERTER_FINISHED;
134 return G_CONVERTER_CONVERTED;
135}
136
137static void
138g_expander_converter_iface_init (GConverterIface *iface)
139{
140 iface->convert = g_expander_converter_convert;
141 iface->reset = g_expander_converter_reset;
142}
143
144#define G_TYPE_COMPRESSOR_CONVERTER (g_compressor_converter_get_type ())
145#define G_COMPRESSOR_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverter))
146#define G_COMPRESSOR_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverterClass))
147#define G_IS_COMPRESSOR_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_COMPRESSOR_CONVERTER))
148#define G_IS_COMPRESSOR_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_COMPRESSOR_CONVERTER))
149#define G_COMPRESSOR_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverterClass))
150
151typedef struct _GCompressorConverter GCompressorConverter;
152typedef struct _GCompressorConverterClass GCompressorConverterClass;
153
154struct _GCompressorConverterClass
155{
156 GObjectClass parent_class;
157};
158
159GType g_compressor_converter_get_type (void) G_GNUC_CONST;
160GConverter *g_compressor_converter_new (void);
161
162
163
164static void g_compressor_converter_iface_init (GConverterIface *iface);
165
166struct _GCompressorConverter
167{
168 GObject parent_instance;
169};
170
171G_DEFINE_TYPE_WITH_CODE (GCompressorConverter, g_compressor_converter, G_TYPE_OBJECT,
172 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
173 g_compressor_converter_iface_init))
174
175static void
176g_compressor_converter_class_init (GCompressorConverterClass *klass)
177{
178}
179
180static void
181g_compressor_converter_init (GCompressorConverter *local)
182{
183}
184
185GConverter *
186g_compressor_converter_new (void)
187{
188 GConverter *conv;
189
190 conv = g_object_new (G_TYPE_COMPRESSOR_CONVERTER, NULL);
191
192 return conv;
193}
194
195static void
196g_compressor_converter_reset (GConverter *converter)
197{
198}
199
200static GConverterResult
201g_compressor_converter_convert (GConverter *converter,
202 const void *inbuf,
203 gsize inbuf_size,
204 void *outbuf,
205 gsize outbuf_size,
206 GConverterFlags flags,
207 gsize *bytes_read,
208 gsize *bytes_written,
209 GError **error)
210{
211 const guint8 *in, *in_end;
212 guint8 v, *out;
213 int i;
214 gsize block_size;
215
216 in = inbuf;
217 out = outbuf;
218 in_end = in + inbuf_size;
219
220 while (in < in_end)
221 {
222 v = *in;
223
224 if (v == 0)
225 {
226 block_size = 0;
227 while (in+block_size < in_end && *(in+block_size) == 0)
228 block_size ++;
229 }
230 else
231 block_size = v * 1000;
232
233 /* Not enough data */
234 if (in_end - in < block_size)
235 {
236 if (*bytes_read > 0)
237 break;
238 g_set_error_literal (err: error, G_IO_ERROR,
239 code: G_IO_ERROR_PARTIAL_INPUT,
240 message: "Need more data");
241 return G_CONVERTER_ERROR;
242 }
243
244 for (i = 0; i < block_size; i++)
245 {
246 if (*(in + i) != v)
247 {
248 if (*bytes_read > 0)
249 break;
250 g_set_error_literal (err: error, G_IO_ERROR,
251 code: G_IO_ERROR_INVALID_DATA,
252 message: "invalid data");
253 return G_CONVERTER_ERROR;
254 }
255 }
256
257 if (v == 0 && in_end - in == block_size && (flags & G_CONVERTER_INPUT_AT_END) == 0)
258 {
259 if (*bytes_read > 0)
260 break;
261 g_set_error_literal (err: error, G_IO_ERROR,
262 code: G_IO_ERROR_PARTIAL_INPUT,
263 message: "Need more data");
264 return G_CONVERTER_ERROR;
265 }
266
267 in += block_size;
268 *out++ = v;
269 *bytes_read += block_size;
270 *bytes_written += 1;
271 }
272
273 if (in == in_end && (flags & G_CONVERTER_INPUT_AT_END))
274 return G_CONVERTER_FINISHED;
275 return G_CONVERTER_CONVERTED;
276}
277
278static void
279g_compressor_converter_iface_init (GConverterIface *iface)
280{
281 iface->convert = g_compressor_converter_convert;
282 iface->reset = g_compressor_converter_reset;
283}
284
285guint8 unexpanded_data[] = { 0,1,3,4,5,6,7,3,12,0,0};
286
287static void
288test_expander (void)
289{
290 guint8 *converted1, *converted2, *ptr;
291 gsize n_read, n_written;
292 gsize total_read;
293 gssize res;
294 GConverterResult cres;
295 GInputStream *mem, *cstream;
296 GOutputStream *mem_out, *cstream_out;
297 GConverter *expander;
298 GConverter *converter;
299 GError *error;
300 int i;
301
302 expander = g_expander_converter_new ();
303
304 converted1 = g_malloc (n_bytes: 100*1000); /* Large enough */
305 converted2 = g_malloc (n_bytes: 100*1000); /* Large enough */
306
307 cres = g_converter_convert (converter: expander,
308 inbuf: unexpanded_data, inbuf_size: sizeof(unexpanded_data),
309 outbuf: converted1, outbuf_size: 100*1000,
310 flags: G_CONVERTER_INPUT_AT_END,
311 bytes_read: &n_read, bytes_written: &n_written, NULL);
312
313 g_assert (cres == G_CONVERTER_FINISHED);
314 g_assert (n_read == 11);
315 g_assert (n_written == 41030);
316
317 g_converter_reset (converter: expander);
318
319 mem = g_memory_input_stream_new_from_data (data: unexpanded_data,
320 len: sizeof (unexpanded_data),
321 NULL);
322 cstream = g_converter_input_stream_new (base_stream: mem, converter: expander);
323 g_assert (g_converter_input_stream_get_converter (G_CONVERTER_INPUT_STREAM (cstream)) == expander);
324 g_object_get (object: cstream, first_property_name: "converter", &converter, NULL);
325 g_assert (converter == expander);
326 g_object_unref (object: converter);
327 g_object_unref (object: mem);
328
329 total_read = 0;
330 ptr = converted2;
331 while (TRUE)
332 {
333 error = NULL;
334 res = g_input_stream_read (stream: cstream,
335 buffer: ptr, count: 1,
336 NULL, error: &error);
337 g_assert (res != -1);
338 if (res == 0)
339 break;
340 ptr += res;
341 total_read += res;
342 }
343
344 g_assert_cmpmem (converted1, n_written, converted2, total_read);
345
346 g_converter_reset (converter: expander);
347
348 mem_out = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
349 cstream_out = g_converter_output_stream_new (base_stream: mem_out, converter: expander);
350 g_assert (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (cstream_out)) == expander);
351 g_object_get (object: cstream_out, first_property_name: "converter", &converter, NULL);
352 g_assert (converter == expander);
353 g_object_unref (object: converter);
354 g_object_unref (object: mem_out);
355
356 for (i = 0; i < sizeof(unexpanded_data); i++)
357 {
358 error = NULL;
359 res = g_output_stream_write (stream: cstream_out,
360 buffer: unexpanded_data + i, count: 1,
361 NULL, error: &error);
362 g_assert (res != -1);
363 if (res == 0)
364 {
365 g_assert (i == sizeof(unexpanded_data) -1);
366 break;
367 }
368 g_assert (res == 1);
369 }
370
371 g_output_stream_close (stream: cstream_out, NULL, NULL);
372
373 g_assert_cmpmem (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
374 g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)),
375 converted1, n_written);
376
377 g_free (mem: converted1);
378 g_free (mem: converted2);
379 g_object_unref (object: cstream);
380 g_object_unref (object: cstream_out);
381 g_object_unref (object: expander);
382}
383
384static void
385test_compressor (void)
386{
387 guint8 *converted, *expanded, *ptr;
388 gsize n_read, expanded_size;
389 gsize total_read;
390 gssize res;
391 GConverterResult cres;
392 GInputStream *mem, *cstream;
393 GOutputStream *mem_out, *cstream_out;
394 GConverter *expander, *compressor;
395 GError *error;
396 int i;
397
398 expander = g_expander_converter_new ();
399 expanded = g_malloc (n_bytes: 100*1000); /* Large enough */
400 cres = g_converter_convert (converter: expander,
401 inbuf: unexpanded_data, inbuf_size: sizeof(unexpanded_data),
402 outbuf: expanded, outbuf_size: 100*1000,
403 flags: G_CONVERTER_INPUT_AT_END,
404 bytes_read: &n_read, bytes_written: &expanded_size, NULL);
405 g_assert (cres == G_CONVERTER_FINISHED);
406 g_assert (n_read == 11);
407 g_assert (expanded_size == 41030);
408
409 compressor = g_compressor_converter_new ();
410
411 converted = g_malloc (n_bytes: 100*1000); /* Large enough */
412
413 mem = g_memory_input_stream_new_from_data (data: expanded,
414 len: expanded_size,
415 NULL);
416 cstream = g_converter_input_stream_new (base_stream: mem, converter: compressor);
417 g_object_unref (object: mem);
418
419 total_read = 0;
420 ptr = converted;
421 while (TRUE)
422 {
423 error = NULL;
424 res = g_input_stream_read (stream: cstream,
425 buffer: ptr, count: 1,
426 NULL, error: &error);
427 g_assert (res != -1);
428 if (res == 0)
429 break;
430 ptr += res;
431 total_read += res;
432 }
433
434 /* "n_read - 1" because last 2 zeros are combined */
435 g_assert_cmpmem (unexpanded_data, n_read - 1, converted, total_read);
436
437 g_object_unref (object: cstream);
438
439 g_converter_reset (converter: compressor);
440
441 mem_out = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
442 cstream_out = g_converter_output_stream_new (base_stream: mem_out, converter: compressor);
443 g_object_unref (object: mem_out);
444
445 for (i = 0; i < expanded_size; i++)
446 {
447 error = NULL;
448 res = g_output_stream_write (stream: cstream_out,
449 buffer: expanded + i, count: 1,
450 NULL, error: &error);
451 g_assert (res != -1);
452 if (res == 0)
453 {
454 g_assert (i == expanded_size -1);
455 break;
456 }
457 g_assert (res == 1);
458 }
459
460 g_output_stream_close (stream: cstream_out, NULL, NULL);
461
462 /* "n_read - 1" because last 2 zeros are combined */
463 g_assert_cmpmem (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
464 g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)),
465 unexpanded_data,
466 n_read - 1);
467
468 g_object_unref (object: cstream_out);
469
470 g_converter_reset (converter: compressor);
471
472 memset (s: expanded, c: 5, n: 5*1000*2);
473
474 mem = g_memory_input_stream_new_from_data (data: expanded,
475 len: 5*1000,
476 NULL);
477 cstream = g_converter_input_stream_new (base_stream: mem, converter: compressor);
478 g_object_unref (object: mem);
479
480 total_read = 0;
481 ptr = converted;
482 while (TRUE)
483 {
484 error = NULL;
485 res = g_input_stream_read (stream: cstream,
486 buffer: ptr, count: 1,
487 NULL, error: &error);
488 g_assert (res != -1);
489 if (res == 0)
490 break;
491 ptr += res;
492 total_read += res;
493 }
494
495 g_assert (total_read == 1);
496 g_assert (*converted == 5);
497
498 g_object_unref (object: cstream);
499
500 mem = g_memory_input_stream_new_from_data (data: expanded,
501 len: 5*1000 * 2,
502 NULL);
503 cstream = g_converter_input_stream_new (base_stream: mem, converter: compressor);
504 g_object_unref (object: mem);
505
506 total_read = 0;
507 ptr = converted;
508 while (TRUE)
509 {
510 error = NULL;
511 res = g_input_stream_read (stream: cstream,
512 buffer: ptr, count: 1,
513 NULL, error: &error);
514 g_assert (res != -1);
515 if (res == 0)
516 break;
517 ptr += res;
518 total_read += res;
519 }
520
521 g_assert (total_read == 2);
522 g_assert (converted[0] == 5);
523 g_assert (converted[1] == 5);
524
525 g_object_unref (object: cstream);
526
527 g_converter_reset (converter: compressor);
528
529 mem = g_memory_input_stream_new_from_data (data: expanded,
530 len: 5*1000 * 2 - 1,
531 NULL);
532 cstream = g_converter_input_stream_new (base_stream: mem, converter: compressor);
533 g_object_unref (object: mem);
534
535 total_read = 0;
536 ptr = converted;
537 while (TRUE)
538 {
539 error = NULL;
540 res = g_input_stream_read (stream: cstream,
541 buffer: ptr, count: 1,
542 NULL, error: &error);
543 if (res == -1)
544 {
545 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT);
546 g_error_free (error);
547 break;
548 }
549
550 g_assert (res != 0);
551 ptr += res;
552 total_read += res;
553 }
554
555 g_assert (total_read == 1);
556 g_assert (converted[0] == 5);
557
558 g_object_unref (object: cstream);
559
560 g_free (mem: expanded);
561 g_free (mem: converted);
562 g_object_unref (object: expander);
563 g_object_unref (object: compressor);
564}
565
566#define LEFTOVER_SHORT_READ_SIZE 512
567
568#define G_TYPE_LEFTOVER_CONVERTER (g_leftover_converter_get_type ())
569#define G_LEFTOVER_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LEFTOVER_CONVERTER, GLeftoverConverter))
570#define G_LEFTOVER_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LEFTOVER_CONVERTER, GLeftoverConverterClass))
571#define G_IS_LEFTOVER_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LEFTOVER_CONVERTER))
572#define G_IS_LEFTOVER_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LEFTOVER_CONVERTER))
573#define G_LEFTOVER_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LEFTOVER_CONVERTER, GLeftoverConverterClass))
574
575typedef struct _GLeftoverConverter GLeftoverConverter;
576typedef struct _GLeftoverConverterClass GLeftoverConverterClass;
577
578struct _GLeftoverConverterClass
579{
580 GObjectClass parent_class;
581};
582
583GType g_leftover_converter_get_type (void) G_GNUC_CONST;
584GConverter *g_leftover_converter_new (void);
585
586
587
588static void g_leftover_converter_iface_init (GConverterIface *iface);
589
590struct _GLeftoverConverter
591{
592 GObject parent_instance;
593};
594
595G_DEFINE_TYPE_WITH_CODE (GLeftoverConverter, g_leftover_converter, G_TYPE_OBJECT,
596 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
597 g_leftover_converter_iface_init))
598
599static void
600g_leftover_converter_class_init (GLeftoverConverterClass *klass)
601{
602}
603
604static void
605g_leftover_converter_init (GLeftoverConverter *local)
606{
607}
608
609GConverter *
610g_leftover_converter_new (void)
611{
612 GConverter *conv;
613
614 conv = g_object_new (G_TYPE_LEFTOVER_CONVERTER, NULL);
615
616 return conv;
617}
618
619static void
620g_leftover_converter_reset (GConverter *converter)
621{
622}
623
624static GConverterResult
625g_leftover_converter_convert (GConverter *converter,
626 const void *inbuf,
627 gsize inbuf_size,
628 void *outbuf,
629 gsize outbuf_size,
630 GConverterFlags flags,
631 gsize *bytes_read,
632 gsize *bytes_written,
633 GError **error)
634{
635 if (outbuf_size == LEFTOVER_SHORT_READ_SIZE)
636 {
637 g_set_error_literal (err: error,
638 G_IO_ERROR,
639 code: G_IO_ERROR_PARTIAL_INPUT,
640 message: "partial input");
641 return G_CONVERTER_ERROR;
642 }
643
644 if (inbuf_size < 100)
645 *bytes_read = *bytes_written = MIN (inbuf_size, outbuf_size);
646 else
647 *bytes_read = *bytes_written = MIN (inbuf_size - 10, outbuf_size);
648 memcpy (dest: outbuf, src: inbuf, n: *bytes_written);
649
650 if (*bytes_read == inbuf_size && (flags & G_CONVERTER_INPUT_AT_END))
651 return G_CONVERTER_FINISHED;
652 return G_CONVERTER_CONVERTED;
653}
654
655static void
656g_leftover_converter_iface_init (GConverterIface *iface)
657{
658 iface->convert = g_leftover_converter_convert;
659 iface->reset = g_leftover_converter_reset;
660}
661
662#define LEFTOVER_BUFSIZE 8192
663#define INTERNAL_BUFSIZE 4096
664
665static void
666test_converter_leftover (void)
667{
668 gchar *orig, *converted;
669 gsize total_read;
670 gssize res;
671 goffset offset;
672 GInputStream *mem, *cstream;
673 GConverter *converter;
674 GError *error;
675 int i;
676
677 converter = g_leftover_converter_new ();
678
679 orig = g_malloc (LEFTOVER_BUFSIZE);
680 converted = g_malloc (LEFTOVER_BUFSIZE);
681 for (i = 0; i < LEFTOVER_BUFSIZE; i++)
682 orig[i] = i % 64 + 32;
683
684 mem = g_memory_input_stream_new_from_data (data: orig, LEFTOVER_BUFSIZE, NULL);
685 cstream = g_converter_input_stream_new (base_stream: mem, G_CONVERTER (converter));
686 g_object_unref (object: mem);
687
688 total_read = 0;
689
690 error = NULL;
691 res = g_input_stream_read (stream: cstream,
692 buffer: converted, LEFTOVER_SHORT_READ_SIZE,
693 NULL, error: &error);
694 g_assert_cmpint (res, ==, LEFTOVER_SHORT_READ_SIZE);
695 total_read += res;
696
697 offset = g_seekable_tell (G_SEEKABLE (mem));
698 g_assert_cmpint (offset, >, LEFTOVER_SHORT_READ_SIZE);
699 g_assert_cmpint (offset, <, LEFTOVER_BUFSIZE);
700
701 /* At this point, @cstream has both a non-empty input_buffer
702 * and a non-empty converted_buffer, which is the case
703 * we want to test.
704 */
705
706 while (TRUE)
707 {
708 error = NULL;
709 res = g_input_stream_read (stream: cstream,
710 buffer: converted + total_read,
711 LEFTOVER_BUFSIZE - total_read,
712 NULL, error: &error);
713 g_assert (res >= 0);
714 if (res == 0)
715 break;
716 total_read += res;
717 }
718
719 g_assert_cmpmem (orig, LEFTOVER_BUFSIZE, converted, total_read);
720
721 g_object_unref (object: cstream);
722 g_free (mem: orig);
723 g_free (mem: converted);
724 g_object_unref (object: converter);
725}
726
727#define DATA_LENGTH 1000000
728
729typedef struct {
730 const gchar *path;
731 GZlibCompressorFormat format;
732 gint level;
733} CompressorTest;
734
735static void
736test_roundtrip (gconstpointer data)
737{
738 const CompressorTest *test = data;
739 GError *error = NULL;
740 guint32 *data0, *data1;
741 gsize data1_size;
742 gint i;
743 GInputStream *istream0, *istream1, *cistream1;
744 GOutputStream *ostream1, *ostream2, *costream1;
745 GConverter *compressor, *decompressor;
746 GZlibCompressorFormat fmt;
747 gint lvl;
748 GFileInfo *info;
749 GFileInfo *info2;
750
751 g_test_bug (bug_uri_snippet: "619945");
752
753 data0 = g_malloc (DATA_LENGTH * sizeof (guint32));
754 for (i = 0; i < DATA_LENGTH; i++)
755 data0[i] = g_random_int ();
756
757 istream0 = g_memory_input_stream_new_from_data (data: data0,
758 DATA_LENGTH * sizeof (guint32), NULL);
759
760 ostream1 = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
761 compressor = G_CONVERTER (g_zlib_compressor_new (test->format, test->level));
762 info = g_file_info_new ();
763 g_file_info_set_name (info, name: "foo");
764 g_object_set (object: compressor, first_property_name: "file-info", info, NULL);
765 info2 = g_zlib_compressor_get_file_info (G_ZLIB_COMPRESSOR (compressor));
766 g_assert (info == info2);
767 g_object_unref (object: info);
768 costream1 = g_converter_output_stream_new (base_stream: ostream1, converter: compressor);
769 g_assert (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
770
771 g_output_stream_splice (stream: costream1, source: istream0, flags: 0, NULL, error: &error);
772 g_assert_no_error (error);
773
774 g_object_unref (object: costream1);
775
776 g_converter_reset (converter: compressor);
777 g_object_get (object: compressor, first_property_name: "format", &fmt, "level", &lvl, NULL);
778 g_assert_cmpint (fmt, ==, test->format);
779 g_assert_cmpint (lvl, ==, test->level);
780 g_object_unref (object: compressor);
781 data1 = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream1));
782 data1_size = g_memory_output_stream_get_data_size (
783 G_MEMORY_OUTPUT_STREAM (ostream1));
784 g_object_unref (object: ostream1);
785 g_object_unref (object: istream0);
786
787 istream1 = g_memory_input_stream_new_from_data (data: data1, len: data1_size, destroy: g_free);
788 decompressor = G_CONVERTER (g_zlib_decompressor_new (test->format));
789 cistream1 = g_converter_input_stream_new (base_stream: istream1, converter: decompressor);
790
791 ostream2 = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
792
793 g_output_stream_splice (stream: ostream2, source: cistream1, flags: 0, NULL, error: &error);
794 g_assert_no_error (error);
795
796 g_assert_cmpmem (data0, DATA_LENGTH * sizeof (guint32),
797 g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (ostream2)),
798 g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (ostream2)));
799 g_object_unref (object: istream1);
800 g_converter_reset (converter: decompressor);
801 g_object_get (object: decompressor, first_property_name: "format", &fmt, NULL);
802 g_assert_cmpint (fmt, ==, test->format);
803 g_object_unref (object: decompressor);
804 g_object_unref (object: cistream1);
805 g_object_unref (object: ostream2);
806 g_free (mem: data0);
807}
808
809typedef struct {
810 const gchar *path;
811 const gchar *charset_in;
812 const gchar *text_in;
813 const gchar *charset_out;
814 const gchar *text_out;
815 gint n_fallbacks;
816} CharsetTest;
817
818static void
819test_charset (gconstpointer data)
820{
821 const CharsetTest *test = data;
822 GInputStream *in, *in2;
823 GConverter *conv;
824 gchar *buffer;
825 gsize count;
826 gsize bytes_read;
827 GError *error;
828 gboolean fallback;
829
830 conv = (GConverter *)g_charset_converter_new (to_charset: test->charset_out, from_charset: test->charset_in, NULL);
831 g_object_get (object: conv, first_property_name: "use-fallback", &fallback, NULL);
832 g_assert (!fallback);
833
834 in = g_memory_input_stream_new_from_data (data: test->text_in, len: -1, NULL);
835 in2 = g_converter_input_stream_new (base_stream: in, converter: conv);
836
837 count = 2 * strlen (s: test->text_out);
838 buffer = g_malloc0 (n_bytes: count);
839 error = NULL;
840 g_input_stream_read_all (stream: in2, buffer, count, bytes_read: &bytes_read, NULL, error: &error);
841 if (test->n_fallbacks == 0)
842 {
843 g_assert_no_error (error);
844 g_assert_cmpint (bytes_read, ==, strlen (test->text_out));
845 g_assert_cmpstr (buffer, ==, test->text_out);
846 }
847 else
848 {
849 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA);
850 g_error_free (error);
851 }
852
853 g_free (mem: buffer);
854 g_object_unref (object: in2);
855 g_object_unref (object: in);
856
857 if (test->n_fallbacks == 0)
858 {
859 g_object_unref (object: conv);
860 return;
861 }
862
863 g_converter_reset (converter: conv);
864
865 g_assert (!g_charset_converter_get_use_fallback (G_CHARSET_CONVERTER (conv)));
866 g_charset_converter_set_use_fallback (G_CHARSET_CONVERTER (conv), TRUE);
867
868 in = g_memory_input_stream_new_from_data (data: test->text_in, len: -1, NULL);
869 in2 = g_converter_input_stream_new (base_stream: in, converter: conv);
870
871 count = 2 * strlen (s: test->text_out);
872 buffer = g_malloc0 (n_bytes: count);
873 error = NULL;
874 g_input_stream_read_all (stream: in2, buffer, count, bytes_read: &bytes_read, NULL, error: &error);
875 g_assert_no_error (error);
876 g_assert_cmpstr (buffer, ==, test->text_out);
877 g_assert_cmpint (bytes_read, ==, strlen (test->text_out));
878 g_assert_cmpint (test->n_fallbacks, ==, g_charset_converter_get_num_fallbacks (G_CHARSET_CONVERTER (conv)));
879
880 g_free (mem: buffer);
881 g_object_unref (object: in2);
882 g_object_unref (object: in);
883
884 g_object_unref (object: conv);
885}
886
887
888static void
889client_connected (GObject *source,
890 GAsyncResult *result,
891 gpointer user_data)
892{
893 GSocketClient *client = G_SOCKET_CLIENT (source);
894 GSocketConnection **conn = user_data;
895 GError *error = NULL;
896
897 *conn = g_socket_client_connect_finish (client, result, error: &error);
898 g_assert_no_error (error);
899}
900
901static void
902server_connected (GObject *source,
903 GAsyncResult *result,
904 gpointer user_data)
905{
906 GSocketListener *listener = G_SOCKET_LISTENER (source);
907 GSocketConnection **conn = user_data;
908 GError *error = NULL;
909
910 *conn = g_socket_listener_accept_finish (listener, result, NULL, error: &error);
911 g_assert_no_error (error);
912}
913
914static void
915make_socketpair (GIOStream **left,
916 GIOStream **right)
917{
918 GInetAddress *iaddr;
919 GSocketAddress *saddr, *effective_address;
920 GSocketListener *listener;
921 GSocketClient *client;
922 GError *error = NULL;
923 GSocketConnection *client_conn = NULL, *server_conn = NULL;
924
925 iaddr = g_inet_address_new_loopback (family: G_SOCKET_FAMILY_IPV4);
926 saddr = g_inet_socket_address_new (address: iaddr, port: 0);
927 g_object_unref (object: iaddr);
928
929 listener = g_socket_listener_new ();
930 g_socket_listener_add_address (listener, address: saddr,
931 type: G_SOCKET_TYPE_STREAM,
932 protocol: G_SOCKET_PROTOCOL_TCP,
933 NULL,
934 effective_address: &effective_address,
935 error: &error);
936 g_assert_no_error (error);
937 g_object_unref (object: saddr);
938
939 client = g_socket_client_new ();
940
941 g_socket_client_connect_async (client,
942 G_SOCKET_CONNECTABLE (effective_address),
943 NULL, callback: client_connected, user_data: &client_conn);
944 g_socket_listener_accept_async (listener, NULL,
945 callback: server_connected, user_data: &server_conn);
946
947 while (!client_conn || !server_conn)
948 g_main_context_iteration (NULL, TRUE);
949
950 g_object_unref (object: client);
951 g_object_unref (object: listener);
952 g_object_unref (object: effective_address);
953
954 *left = G_IO_STREAM (client_conn);
955 *right = G_IO_STREAM (server_conn);
956}
957
958static void
959test_converter_pollable (void)
960{
961 GIOStream *left, *right;
962 guint8 *converted, *inptr;
963 guint8 *expanded, *outptr, *expanded_end;
964 gsize n_read, expanded_size;
965 gsize total_read;
966 gssize res;
967 gboolean is_readable;
968 GConverterResult cres;
969 GInputStream *cstream;
970 GPollableInputStream *pollable_in;
971 GOutputStream *socket_out, *mem_out, *cstream_out;
972 GPollableOutputStream *pollable_out;
973 GConverter *expander, *compressor;
974 GError *error;
975 int i;
976
977 expander = g_expander_converter_new ();
978 expanded = g_malloc (n_bytes: 100*1000); /* Large enough */
979 cres = g_converter_convert (converter: expander,
980 inbuf: unexpanded_data, inbuf_size: sizeof(unexpanded_data),
981 outbuf: expanded, outbuf_size: 100*1000,
982 flags: G_CONVERTER_INPUT_AT_END,
983 bytes_read: &n_read, bytes_written: &expanded_size, NULL);
984 g_assert (cres == G_CONVERTER_FINISHED);
985 g_assert (n_read == 11);
986 g_assert (expanded_size == 41030);
987 expanded_end = expanded + expanded_size;
988
989 make_socketpair (left: &left, right: &right);
990
991 compressor = g_compressor_converter_new ();
992
993 converted = g_malloc (n_bytes: 100*1000); /* Large enough */
994
995 cstream = g_converter_input_stream_new (base_stream: g_io_stream_get_input_stream (stream: left),
996 converter: compressor);
997 pollable_in = G_POLLABLE_INPUT_STREAM (cstream);
998 g_assert (g_pollable_input_stream_can_poll (pollable_in));
999
1000 socket_out = g_io_stream_get_output_stream (stream: right);
1001
1002 total_read = 0;
1003 outptr = expanded;
1004 inptr = converted;
1005 while (TRUE)
1006 {
1007 error = NULL;
1008
1009 if (outptr < expanded_end)
1010 {
1011 res = g_output_stream_write (stream: socket_out,
1012 buffer: outptr,
1013 MIN (1000, (expanded_end - outptr)),
1014 NULL, error: &error);
1015 g_assert_cmpint (res, >, 0);
1016 outptr += res;
1017 }
1018 else if (socket_out)
1019 {
1020 g_object_unref (object: right);
1021 socket_out = NULL;
1022 }
1023
1024 /* Wait a few ticks to check for the pipe to propagate the
1025 * write. Finesses the race condition in the following test,
1026 * where is_readable fails because the write hasn't propagated,
1027 * but the read then succeeds because it has. */
1028 g_usleep (microseconds: 80L);
1029
1030 is_readable = g_pollable_input_stream_is_readable (stream: pollable_in);
1031 res = g_pollable_input_stream_read_nonblocking (stream: pollable_in,
1032 buffer: inptr, count: 1,
1033 NULL, error: &error);
1034
1035 /* is_readable can be a false positive, but not a false negative */
1036 if (!is_readable)
1037 g_assert_cmpint (res, ==, -1);
1038
1039 /* After closing the write end, we can't get WOULD_BLOCK any more */
1040 if (!socket_out)
1041 g_assert_cmpint (res, !=, -1);
1042
1043 if (res == -1)
1044 {
1045 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK);
1046 g_error_free (error);
1047
1048 continue;
1049 }
1050
1051 if (res == 0)
1052 break;
1053 inptr += res;
1054 total_read += res;
1055 }
1056
1057 /* "n_read - 1" because last 2 zeros are combined */
1058 g_assert_cmpmem (unexpanded_data, n_read - 1, converted, total_read);
1059
1060 g_object_unref (object: cstream);
1061 g_object_unref (object: left);
1062
1063 g_converter_reset (converter: compressor);
1064
1065 /* This doesn't actually test the behavior on
1066 * G_IO_ERROR_WOULD_BLOCK; to do that we'd need to implement a
1067 * custom GOutputStream that we could control blocking on.
1068 */
1069
1070 mem_out = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
1071 cstream_out = g_converter_output_stream_new (base_stream: mem_out, converter: compressor);
1072 g_object_unref (object: mem_out);
1073 pollable_out = G_POLLABLE_OUTPUT_STREAM (cstream_out);
1074 g_assert (g_pollable_output_stream_can_poll (pollable_out));
1075 g_assert (g_pollable_output_stream_is_writable (pollable_out));
1076
1077 for (i = 0; i < expanded_size; i++)
1078 {
1079 error = NULL;
1080 res = g_pollable_output_stream_write_nonblocking (stream: pollable_out,
1081 buffer: expanded + i, count: 1,
1082 NULL, error: &error);
1083 g_assert (res != -1);
1084 if (res == 0)
1085 {
1086 g_assert (i == expanded_size -1);
1087 break;
1088 }
1089 g_assert (res == 1);
1090 }
1091
1092 g_output_stream_close (stream: cstream_out, NULL, NULL);
1093
1094 /* "n_read - 1" because last 2 zeros are combined */
1095 g_assert_cmpmem (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
1096 g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)),
1097 unexpanded_data,
1098 n_read - 1);
1099
1100 g_object_unref (object: cstream_out);
1101
1102 g_free (mem: expanded);
1103 g_free (mem: converted);
1104 g_object_unref (object: expander);
1105 g_object_unref (object: compressor);
1106}
1107
1108static void
1109test_truncation (gconstpointer data)
1110{
1111 const CompressorTest *test = data;
1112 GError *error = NULL;
1113 guint32 *data0, *data1;
1114 gsize data1_size;
1115 gint i;
1116 GInputStream *istream0, *istream1, *cistream1;
1117 GOutputStream *ostream1, *ostream2, *costream1;
1118 GConverter *compressor, *decompressor;
1119
1120 data0 = g_malloc (DATA_LENGTH * sizeof (guint32));
1121 for (i = 0; i < DATA_LENGTH; i++)
1122 data0[i] = g_random_int ();
1123
1124 istream0 = g_memory_input_stream_new_from_data (data: data0,
1125 DATA_LENGTH * sizeof (guint32), NULL);
1126
1127 ostream1 = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
1128 compressor = G_CONVERTER (g_zlib_compressor_new (test->format, -1));
1129 costream1 = g_converter_output_stream_new (base_stream: ostream1, converter: compressor);
1130 g_assert (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
1131
1132 g_output_stream_splice (stream: costream1, source: istream0, flags: 0, NULL, error: &error);
1133 g_assert_no_error (error);
1134
1135 g_object_unref (object: costream1);
1136 g_object_unref (object: compressor);
1137
1138 data1 = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream1));
1139 data1_size = g_memory_output_stream_get_data_size (
1140 G_MEMORY_OUTPUT_STREAM (ostream1));
1141 g_object_unref (object: ostream1);
1142 g_object_unref (object: istream0);
1143
1144 /* truncate */
1145 data1_size /= 2;
1146
1147 istream1 = g_memory_input_stream_new_from_data (data: data1, len: data1_size, destroy: g_free);
1148 decompressor = G_CONVERTER (g_zlib_decompressor_new (test->format));
1149 cistream1 = g_converter_input_stream_new (base_stream: istream1, converter: decompressor);
1150
1151 ostream2 = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free);
1152
1153 g_output_stream_splice (stream: ostream2, source: cistream1, flags: 0, NULL, error: &error);
1154 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT);
1155 g_error_free (error);
1156
1157 g_object_unref (object: istream1);
1158 g_object_unref (object: decompressor);
1159 g_object_unref (object: cistream1);
1160 g_object_unref (object: ostream2);
1161 g_free (mem: data0);
1162}
1163
1164static void
1165test_converter_basics (void)
1166{
1167 GConverter *converter;
1168 GError *error = NULL;
1169 gchar *to;
1170 gchar *from;
1171
1172 converter = (GConverter *)g_charset_converter_new (to_charset: "utf-8", from_charset: "latin1", error: &error);
1173 g_assert_no_error (error);
1174 g_object_get (object: converter,
1175 first_property_name: "to-charset", &to,
1176 "from-charset", &from,
1177 NULL);
1178
1179 g_assert_cmpstr (to, ==, "utf-8");
1180 g_assert_cmpstr (from, ==, "latin1");
1181
1182 g_free (mem: to);
1183 g_free (mem: from);
1184 g_object_unref (object: converter);
1185}
1186
1187int
1188main (int argc,
1189 char *argv[])
1190{
1191 CompressorTest compressor_tests[] = {
1192 { "/converter-output-stream/roundtrip/zlib-0", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 0 },
1193 { "/converter-output-stream/roundtrip/zlib-9", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 9 },
1194 { "/converter-output-stream/roundtrip/gzip-0", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 0 },
1195 { "/converter-output-stream/roundtrip/gzip-9", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 9 },
1196 { "/converter-output-stream/roundtrip/raw-0", G_ZLIB_COMPRESSOR_FORMAT_RAW, 0 },
1197 { "/converter-output-stream/roundtrip/raw-9", G_ZLIB_COMPRESSOR_FORMAT_RAW, 9 },
1198 };
1199 CompressorTest truncation_tests[] = {
1200 { "/converter-input-stream/truncation/zlib", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 0 },
1201 { "/converter-input-stream/truncation/gzip", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 0 },
1202 { "/converter-input-stream/truncation/raw", G_ZLIB_COMPRESSOR_FORMAT_RAW, 0 },
1203 };
1204 CharsetTest charset_tests[] = {
1205 { "/converter-input-stream/charset/utf8->latin1", "UTF-8", "\303\205rr Sant\303\251", "ISO-8859-1", "\305rr Sant\351", 0 },
1206 { "/converter-input-stream/charset/latin1->utf8", "ISO-8859-1", "\305rr Sant\351", "UTF-8", "\303\205rr Sant\303\251", 0 },
1207 { "/converter-input-stream/charset/fallbacks", "UTF-8", "Some characters just don't fit into latin1: πא", "ISO-8859-1", "Some characters just don't fit into latin1: \\CF\\80\\D7\\90", 4 },
1208 };
1209
1210 gint i;
1211
1212 g_test_init (argc: &argc, argv: &argv, NULL);
1213
1214 g_test_bug_base (uri_pattern: "http://bugzilla.gnome.org/");
1215
1216 g_test_add_func (testpath: "/converter/basics", test_func: test_converter_basics);
1217 g_test_add_func (testpath: "/converter-input-stream/expander", test_func: test_expander);
1218 g_test_add_func (testpath: "/converter-input-stream/compressor", test_func: test_compressor);
1219
1220 for (i = 0; i < G_N_ELEMENTS (compressor_tests); i++)
1221 g_test_add_data_func (testpath: compressor_tests[i].path, test_data: &compressor_tests[i], test_func: test_roundtrip);
1222
1223 for (i = 0; i < G_N_ELEMENTS (truncation_tests); i++)
1224 g_test_add_data_func (testpath: truncation_tests[i].path, test_data: &truncation_tests[i], test_func: test_truncation);
1225
1226 for (i = 0; i < G_N_ELEMENTS (charset_tests); i++)
1227 g_test_add_data_func (testpath: charset_tests[i].path, test_data: &charset_tests[i], test_func: test_charset);
1228
1229 g_test_add_func (testpath: "/converter-stream/pollable", test_func: test_converter_pollable);
1230 g_test_add_func (testpath: "/converter-stream/leftover", test_func: test_converter_leftover);
1231
1232 return g_test_run();
1233}
1234

source code of gtk/subprojects/glib/gio/tests/converter-stream.c