Add some CharsetConverter tests
[platform/upstream/glib.git] / gio / tests / converter-stream.c
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
35 typedef struct _GExpanderConverter       GExpanderConverter;
36 typedef struct _GExpanderConverterClass  GExpanderConverterClass;
37
38 struct _GExpanderConverterClass
39 {
40   GObjectClass parent_class;
41 };
42
43 GType       g_expander_converter_get_type (void) G_GNUC_CONST;
44 GConverter *g_expander_converter_new      (void);
45
46
47
48 static void g_expander_converter_iface_init          (GConverterIface *iface);
49
50 struct _GExpanderConverter
51 {
52   GObject parent_instance;
53 };
54
55 G_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
59 static void
60 g_expander_converter_class_init (GExpanderConverterClass *klass)
61 {
62 }
63
64 static void
65 g_expander_converter_init (GExpanderConverter *local)
66 {
67 }
68
69 GConverter *
70 g_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
79 static void
80 g_expander_converter_reset (GConverter *converter)
81 {
82 }
83
84 static GConverterResult
85 g_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   GExpanderConverter  *conv;
96   const guint8 *in, *in_end;
97   guint8 v, *out;
98   int i;
99   gsize block_size;
100
101   conv = G_EXPANDER_CONVERTER (converter);
102
103   in = inbuf;
104   out = outbuf;
105   in_end = in + inbuf_size;
106
107   while (in < in_end)
108     {
109       v = *in;
110
111       if (v == 0)
112         block_size = 10;
113       else
114         block_size = v * 1000;
115
116       if (outbuf_size < block_size)
117         {
118           if (*bytes_read > 0)
119             return G_CONVERTER_CONVERTED;
120
121           g_set_error_literal (error, G_IO_ERROR,
122                                G_IO_ERROR_NO_SPACE,
123                                "No space in dest");
124           return G_CONVERTER_ERROR;
125         }
126
127       in++;
128       *bytes_read += 1;
129       *bytes_written += block_size;
130       outbuf_size -= block_size;
131       for (i = 0; i < block_size; i++)
132         *out++ = v;
133     }
134
135   if (in == in_end && (flags & G_CONVERTER_INPUT_AT_END))
136     return G_CONVERTER_FINISHED;
137   return G_CONVERTER_CONVERTED;
138 }
139
140 static void
141 g_expander_converter_iface_init (GConverterIface *iface)
142 {
143   iface->convert = g_expander_converter_convert;
144   iface->reset = g_expander_converter_reset;
145 }
146
147 #define G_TYPE_COMPRESSOR_CONVERTER         (g_compressor_converter_get_type ())
148 #define G_COMPRESSOR_CONVERTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverter))
149 #define G_COMPRESSOR_CONVERTER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverterClass))
150 #define G_IS_COMPRESSOR_CONVERTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_COMPRESSOR_CONVERTER))
151 #define G_IS_COMPRESSOR_CONVERTER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_COMPRESSOR_CONVERTER))
152 #define G_COMPRESSOR_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverterClass))
153
154 typedef struct _GCompressorConverter       GCompressorConverter;
155 typedef struct _GCompressorConverterClass  GCompressorConverterClass;
156
157 struct _GCompressorConverterClass
158 {
159   GObjectClass parent_class;
160 };
161
162 GType       g_compressor_converter_get_type (void) G_GNUC_CONST;
163 GConverter *g_compressor_converter_new      (void);
164
165
166
167 static void g_compressor_converter_iface_init          (GConverterIface *iface);
168
169 struct _GCompressorConverter
170 {
171   GObject parent_instance;
172 };
173
174 G_DEFINE_TYPE_WITH_CODE (GCompressorConverter, g_compressor_converter, G_TYPE_OBJECT,
175                          G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
176                                                 g_compressor_converter_iface_init))
177
178 static void
179 g_compressor_converter_class_init (GCompressorConverterClass *klass)
180 {
181 }
182
183 static void
184 g_compressor_converter_init (GCompressorConverter *local)
185 {
186 }
187
188 GConverter *
189 g_compressor_converter_new (void)
190 {
191   GConverter *conv;
192
193   conv = g_object_new (G_TYPE_COMPRESSOR_CONVERTER, NULL);
194
195   return conv;
196 }
197
198 static void
199 g_compressor_converter_reset (GConverter *converter)
200 {
201 }
202
203 static GConverterResult
204 g_compressor_converter_convert (GConverter *converter,
205                                 const void *inbuf,
206                                 gsize       inbuf_size,
207                                 void       *outbuf,
208                                 gsize       outbuf_size,
209                                 GConverterFlags flags,
210                                 gsize      *bytes_read,
211                                 gsize      *bytes_written,
212                                 GError    **error)
213 {
214   GCompressorConverter  *conv;
215   const guint8 *in, *in_end;
216   guint8 v, *out;
217   int i;
218   gsize block_size;
219
220   conv = G_COMPRESSOR_CONVERTER (converter);
221
222   in = inbuf;
223   out = outbuf;
224   in_end = in + inbuf_size;
225
226   while (in < in_end)
227     {
228       v = *in;
229
230       if (v == 0)
231         {
232           block_size = 0;
233           while (in+block_size < in_end && *(in+block_size) == 0)
234             block_size ++;
235         }
236       else
237         block_size = v * 1000;
238
239       /* Not enough data */
240       if (in_end - in < block_size)
241         {
242           if (*bytes_read > 0)
243             break;
244           g_set_error_literal (error, G_IO_ERROR,
245                                G_IO_ERROR_PARTIAL_INPUT,
246                                "Need more data");
247           return G_CONVERTER_ERROR;
248         }
249
250       for (i = 0; i < block_size; i++)
251         {
252           if (*(in + i) != v)
253             {
254               if (*bytes_read > 0)
255                 break;
256               g_set_error_literal (error, G_IO_ERROR,
257                                    G_IO_ERROR_INVALID_DATA,
258                                    "invalid data");
259               return G_CONVERTER_ERROR;
260             }
261         }
262
263       if (v == 0 && in_end - in == block_size && (flags & G_CONVERTER_INPUT_AT_END) == 0)
264         {
265           if (*bytes_read > 0)
266             break;
267           g_set_error_literal (error, G_IO_ERROR,
268                                G_IO_ERROR_PARTIAL_INPUT,
269                                "Need more data");
270           return G_CONVERTER_ERROR;
271         }
272
273       in += block_size;
274       *out++ = v;
275       *bytes_read += block_size;
276       *bytes_written += 1;
277     }
278
279   if (in == in_end && (flags & G_CONVERTER_INPUT_AT_END))
280     return G_CONVERTER_FINISHED;
281   return G_CONVERTER_CONVERTED;
282 }
283
284 static void
285 g_compressor_converter_iface_init (GConverterIface *iface)
286 {
287   iface->convert = g_compressor_converter_convert;
288   iface->reset = g_compressor_converter_reset;
289 }
290
291 guint8 unexpanded_data[] = { 0,1,3,4,5,6,7,3,12,0,0};
292
293 static void
294 test_expander (void)
295 {
296   guint8 *converted1, *converted2, *ptr;
297   gsize n_read, n_written;
298   gsize total_read;
299   gssize res;
300   GConverterResult cres;
301   GInputStream *mem, *cstream;
302   GOutputStream *mem_out, *cstream_out;
303   GConverter *expander;
304   GError *error;
305   int i;
306
307   expander = g_expander_converter_new ();
308
309   converted1 = g_malloc (100*1000); /* Large enough */
310   converted2 = g_malloc (100*1000); /* Large enough */
311
312   cres = g_converter_convert (expander,
313                               unexpanded_data, sizeof(unexpanded_data),
314                               converted1, 100*1000,
315                               G_CONVERTER_INPUT_AT_END,
316                               &n_read, &n_written, NULL);
317
318   g_assert (cres == G_CONVERTER_FINISHED);
319   g_assert (n_read == 11);
320   g_assert (n_written == 41030);
321
322   g_converter_reset (expander);
323
324   mem = g_memory_input_stream_new_from_data (unexpanded_data,
325                                              sizeof (unexpanded_data),
326                                              NULL);
327   cstream = g_converter_input_stream_new (mem, expander);
328   g_object_unref (mem);
329
330   total_read = 0;
331   ptr = converted2;
332   while (TRUE)
333     {
334       error = NULL;
335       res = g_input_stream_read (cstream,
336                                  ptr, 1,
337                                  NULL, &error);
338       g_assert (res != -1);
339       if (res == 0)
340         break;
341       ptr += res;
342       total_read += res;
343     }
344
345   g_assert (total_read == n_written);
346   g_assert (memcmp (converted1, converted2, n_written)  == 0);
347
348   g_converter_reset (expander);
349
350   mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
351   cstream_out = g_converter_output_stream_new (mem_out, expander);
352   g_object_unref (mem_out);
353
354   for (i = 0; i < sizeof(unexpanded_data); i++)
355     {
356       error = NULL;
357       res = g_output_stream_write (cstream_out,
358                                    unexpanded_data + i, 1,
359                                    NULL, &error);
360       g_assert (res != -1);
361       if (res == 0)
362         {
363           g_assert (i == sizeof(unexpanded_data) -1);
364           break;
365         }
366       g_assert (res == 1);
367     }
368
369   g_output_stream_close (cstream_out, NULL, NULL);
370
371   g_assert (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)) == n_written);
372   g_assert (memcmp (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
373                     converted1,
374                     n_written)  == 0);
375
376   g_free (converted1);
377   g_free (converted2);
378   g_object_unref (cstream);
379   g_object_unref (cstream_out);
380   g_object_unref (expander);
381 }
382
383 static void
384 test_compressor (void)
385 {
386   guint8 *converted, *expanded, *ptr;
387   gsize n_read, expanded_size;
388   gsize total_read;
389   gssize res;
390   GConverterResult cres;
391   GInputStream *mem, *cstream;
392   GOutputStream *mem_out, *cstream_out;
393   GConverter *expander, *compressor;
394   GError *error;
395   int i;
396
397   expander = g_expander_converter_new ();
398   expanded = g_malloc (100*1000); /* Large enough */
399   cres = g_converter_convert (expander,
400                               unexpanded_data, sizeof(unexpanded_data),
401                               expanded, 100*1000,
402                               G_CONVERTER_INPUT_AT_END,
403                               &n_read, &expanded_size, NULL);
404   g_assert (cres == G_CONVERTER_FINISHED);
405   g_assert (n_read == 11);
406   g_assert (expanded_size == 41030);
407
408   compressor = g_compressor_converter_new ();
409
410   converted = g_malloc (100*1000); /* Large enough */
411
412   mem = g_memory_input_stream_new_from_data (expanded,
413                                              expanded_size,
414                                              NULL);
415   cstream = g_converter_input_stream_new (mem, compressor);
416   g_object_unref (mem);
417
418   total_read = 0;
419   ptr = converted;
420   while (TRUE)
421     {
422       error = NULL;
423       res = g_input_stream_read (cstream,
424                                  ptr, 1,
425                                  NULL, &error);
426       g_assert (res != -1);
427       if (res == 0)
428         break;
429       ptr += res;
430       total_read += res;
431     }
432
433   g_assert (total_read == n_read - 1); /* Last 2 zeros are combined */
434   g_assert (memcmp (converted, unexpanded_data, total_read)  == 0);
435
436   g_object_unref (cstream);
437
438   g_converter_reset (compressor);
439
440   mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
441   cstream_out = g_converter_output_stream_new (mem_out, compressor);
442   g_object_unref (mem_out);
443
444   for (i = 0; i < expanded_size; i++)
445     {
446       error = NULL;
447       res = g_output_stream_write (cstream_out,
448                                    expanded + i, 1,
449                                    NULL, &error);
450       g_assert (res != -1);
451       if (res == 0)
452         {
453           g_assert (i == expanded_size -1);
454           break;
455         }
456       g_assert (res == 1);
457     }
458
459   g_output_stream_close (cstream_out, NULL, NULL);
460
461   g_assert (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)) == n_read - 1); /* Last 2 zeros are combined */
462   g_assert (memcmp (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
463                     unexpanded_data,
464                     g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)))  == 0);
465
466   g_object_unref (cstream_out);
467
468   g_converter_reset (compressor);
469
470   memset (expanded, 5, 5*1000*2);
471
472   mem = g_memory_input_stream_new_from_data (expanded,
473                                              5*1000,
474                                              NULL);
475   cstream = g_converter_input_stream_new (mem, compressor);
476   g_object_unref (mem);
477
478   total_read = 0;
479   ptr = converted;
480   while (TRUE)
481     {
482       error = NULL;
483       res = g_input_stream_read (cstream,
484                                  ptr, 1,
485                                  NULL, &error);
486       g_assert (res != -1);
487       if (res == 0)
488         break;
489       ptr += res;
490       total_read += res;
491     }
492
493   g_assert (total_read == 1);
494   g_assert (*converted == 5);
495
496   mem = g_memory_input_stream_new_from_data (expanded,
497                                              5*1000 * 2,
498                                              NULL);
499   cstream = g_converter_input_stream_new (mem, compressor);
500   g_object_unref (mem);
501
502   total_read = 0;
503   ptr = converted;
504   while (TRUE)
505     {
506       error = NULL;
507       res = g_input_stream_read (cstream,
508                                  ptr, 1,
509                                  NULL, &error);
510       g_assert (res != -1);
511       if (res == 0)
512         break;
513       ptr += res;
514       total_read += res;
515     }
516
517   g_assert (total_read == 2);
518   g_assert (converted[0] == 5);
519   g_assert (converted[1] == 5);
520
521   g_object_unref (cstream);
522
523   g_converter_reset (compressor);
524
525   mem = g_memory_input_stream_new_from_data (expanded,
526                                              5*1000 * 2 - 1,
527                                              NULL);
528   cstream = g_converter_input_stream_new (mem, compressor);
529   g_object_unref (mem);
530
531   total_read = 0;
532   ptr = converted;
533   while (TRUE)
534     {
535       error = NULL;
536       res = g_input_stream_read (cstream,
537                                  ptr, 1,
538                                  NULL, &error);
539       if (res == -1)
540         {
541           g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT);
542           break;
543         }
544
545       g_assert (res != 0);
546       ptr += res;
547       total_read += res;
548     }
549
550   g_assert (total_read == 1);
551   g_assert (converted[0] == 5);
552
553   g_object_unref (cstream);
554
555   g_free (expanded);
556   g_free (converted);
557   g_object_unref (expander);
558   g_object_unref (compressor);
559 }
560
561 #define DATA_LENGTH 1000000
562
563 static void
564 test_corruption (GZlibCompressorFormat format, gint level)
565 {
566   GError *error = NULL;
567   guint32 *data0, *data1;
568   gsize data1_size;
569   gint i;
570   GInputStream *istream0, *istream1, *cistream1;
571   GOutputStream *ostream1, *ostream2, *costream1;
572   GConverter *compressor, *decompressor;
573
574   data0 = g_malloc (DATA_LENGTH * sizeof (guint32));
575   for (i = 0; i < DATA_LENGTH; i++)
576     data0[i] = g_random_int ();
577
578   istream0 = g_memory_input_stream_new_from_data (data0,
579     DATA_LENGTH * sizeof (guint32), NULL);
580
581   ostream1 = g_memory_output_stream_new (NULL, 0, g_realloc, NULL);
582   compressor = G_CONVERTER (g_zlib_compressor_new (format, level));
583   costream1 = g_converter_output_stream_new (ostream1, compressor);
584
585   g_output_stream_splice (costream1, istream0, 0, NULL, &error);
586   g_assert_no_error (error);
587
588   g_object_unref (costream1);
589   g_object_unref (compressor);
590   data1 = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (ostream1));
591   data1_size = g_memory_output_stream_get_data_size (
592     G_MEMORY_OUTPUT_STREAM (ostream1));
593   g_object_unref (ostream1);
594   g_object_unref (istream0);
595
596   istream1 = g_memory_input_stream_new_from_data (data1, data1_size, NULL);
597   decompressor = G_CONVERTER (g_zlib_decompressor_new (format));
598   cistream1 = g_converter_input_stream_new (istream1, decompressor);
599
600   ostream2 = g_memory_output_stream_new (NULL, 0, g_realloc, NULL);
601
602   g_output_stream_splice (ostream2, cistream1, 0, NULL, &error);
603   g_assert_no_error (error);
604
605   g_assert_cmpuint (DATA_LENGTH * sizeof (guint32), ==,
606     g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (ostream2)));
607   g_assert (memcmp (data0, g_memory_output_stream_get_data (
608     G_MEMORY_OUTPUT_STREAM (ostream2)), DATA_LENGTH * sizeof (guint32)) == 0);
609 }
610
611 typedef struct {
612   const gchar *path;
613   GZlibCompressorFormat format;
614   gint level;
615 } CompressorTest;
616
617 static void
618 test_roundtrip (gconstpointer data)
619 {
620   const CompressorTest *test = data;
621
622   test_corruption (test->format, test->level);
623 }
624
625 typedef struct {
626   const gchar *path;
627   const gchar *charset_in;
628   const gchar *text_in;
629   const gchar *charset_out;
630   const gchar *text_out;
631 } CharsetTest;
632
633 static void
634 test_charset (gconstpointer data)
635 {
636   const CharsetTest *test = data;
637   GInputStream *in, *in2;
638   GConverter *conv;
639   gchar *buffer;
640   gsize count;
641   gsize bytes_read;
642   GError *error;
643
644   in = g_memory_input_stream_new_from_data (test->text_in, -1, NULL);
645   conv = (GConverter *)g_charset_converter_new (test->charset_out, test->charset_in, NULL);
646   in2 = g_converter_input_stream_new (in, conv);
647   g_object_unref (in);
648   g_object_unref (conv);
649
650   count = 2 * strlen (test->text_out);
651   buffer = g_malloc (count);
652   error = NULL;
653   g_input_stream_read_all (in2, buffer, count, &bytes_read, NULL, &error);
654   g_assert_no_error (error);
655   g_assert_cmpint (bytes_read, ==, strlen (test->text_out));
656   g_assert_cmpstr (buffer, ==, test->text_out);
657
658   g_free (buffer);
659   g_object_unref (in2);
660 }
661
662 int
663 main (int   argc,
664       char *argv[])
665 {
666   CompressorTest compressor_tests[] = {
667     { "/converter-output-stream/corruption/zlib-0", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 0 },
668     { "/converter-output-stream/corruption/zlib-9", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 9 },
669     { "/converter-output-stream/corruption/gzip-0", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 0 },
670     { "/converter-output-stream/corruption/gzip-9", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 9 },
671     { "/converter-output-stream/corruption/raw-0", G_ZLIB_COMPRESSOR_FORMAT_RAW, 0 },
672     { "/converter-output-stream/corruption/raw-9", G_ZLIB_COMPRESSOR_FORMAT_RAW, 9 },
673   };
674   CharsetTest charset_tests[] = {
675     { "/converter-input-stream/charset/1", "UTF-8", "\303\205rr Sant\303\251", "ISO-8859-1", "\305rr Sant\351" },
676     { "/converter-input-stream/charset/2", "ISO-8859-1", "\305rr Sant\351", "UTF-8", "\303\205rr Sant\303\251" },
677   };
678
679   gint i;
680
681   g_type_init ();
682   g_test_init (&argc, &argv, NULL);
683
684   g_test_add_func ("/converter-input-stream/expander", test_expander);
685   g_test_add_func ("/converter-input-stream/compressor", test_compressor);
686
687   for (i = 0; i < G_N_ELEMENTS (compressor_tests); i++)
688     g_test_add_data_func (compressor_tests[i].path, &compressor_tests[i], test_roundtrip);
689
690   for (i = 0; i < G_N_ELEMENTS (charset_tests); i++)
691     g_test_add_data_func (charset_tests[i].path, &charset_tests[i], test_charset);
692
693
694   return g_test_run();
695 }