Tests for 'striconveha' module.
[gnulib.git] / tests / test-striconveha.c
1 /* Test of character set conversion with error handling and autodetection.
2    Copyright (C) 2007 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program 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.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include "striconveha.h"
25
26 #if HAVE_ICONV
27 # include <iconv.h>
28 #endif
29
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #define SIZEOF(array) (sizeof (array) / sizeof (array[0]))
35 #define ASSERT(expr) if (!(expr)) abort ();
36
37 /* Magic number for detecting bounds violations.  */
38 #define MAGIC 0x1983EFF1
39
40 static size_t *
41 new_offsets (size_t n)
42 {
43   size_t *offsets = (size_t *) malloc ((n + 1) * sizeof (size_t));
44   offsets[n] = MAGIC;
45   return offsets;
46 }
47
48 int
49 main ()
50 {
51   static enum iconv_ilseq_handler handlers[] =
52     { iconveh_error, iconveh_question_mark, iconveh_escape_sequence };
53   size_t h;
54   size_t o;
55   size_t i;
56
57 #if HAVE_ICONV
58   /* Assume that iconv() supports at least the encodings ASCII, ISO-8859-1,
59      ISO-8859-2, and UTF-8.  */
60
61   /* ------------------------- Test mem_iconveha() ------------------------- */
62
63   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
64   for (h = 0; h < SIZEOF (handlers); h++)
65     {
66       enum iconv_ilseq_handler handler = handlers[h];
67       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
68       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
69       for (o = 0; o < 2; o++)
70         {
71           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
72           char *result = NULL;
73           size_t length = 0;
74           int retval = mem_iconveha (input, strlen (input),
75                                      "ISO-8859-2", "ISO-8859-1",
76                                      false, handler,
77                                      offsets,
78                                      &result, &length);
79           ASSERT (retval == 0);
80           ASSERT (length == strlen (expected));
81           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
82           if (o)
83             {
84               for (i = 0; i < 37; i++)
85                 ASSERT (offsets[i] == i);
86               ASSERT (offsets[37] == MAGIC);
87               free (offsets);
88             }
89           free (result);
90         }
91     }
92
93   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
94   for (h = 0; h < SIZEOF (handlers); h++)
95     {
96       enum iconv_ilseq_handler handler = handlers[h];
97       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
98       for (o = 0; o < 2; o++)
99         {
100           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
101           char *result = NULL;
102           size_t length = 0;
103           int retval = mem_iconveha (input, strlen (input),
104                                      "ISO-8859-2", "ISO-8859-1",
105                                      false, handler,
106                                      offsets,
107                                      &result, &length);
108           switch (handler)
109             {
110             case iconveh_error:
111               ASSERT (retval == -1 && errno == EILSEQ);
112               ASSERT (result == NULL);
113               if (o)
114                 free (offsets);
115               break;
116             case iconveh_question_mark:
117               {
118                 static const char expected[] = "Rafa? Maszkowski";
119                 ASSERT (retval == 0);
120                 ASSERT (length == strlen (expected));
121                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
122                 if (o)
123                   {
124                     for (i = 0; i < 16; i++)
125                       ASSERT (offsets[i] == i);
126                     ASSERT (offsets[16] == MAGIC);
127                     free (offsets);
128                   }
129                 free (result);
130               }
131               break;
132             case iconveh_escape_sequence:
133               {
134                 static const char expected[] = "Rafa\\u0142 Maszkowski";
135                 ASSERT (retval == 0);
136                 ASSERT (length == strlen (expected));
137                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
138                 if (o)
139                   {
140                     for (i = 0; i < 16; i++)
141                       ASSERT (offsets[i] == (i < 5 ? i :
142                                              i + 5));
143                     ASSERT (offsets[16] == MAGIC);
144                     free (offsets);
145                   }
146                 free (result);
147               }
148               break;
149             }
150         }
151     }
152
153   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
154   for (h = 0; h < SIZEOF (handlers); h++)
155     {
156       enum iconv_ilseq_handler handler = handlers[h];
157       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
158       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
159       for (o = 0; o < 2; o++)
160         {
161           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
162           char *result = NULL;
163           size_t length = 0;
164           int retval = mem_iconveha (input, strlen (input),
165                                      "ISO-8859-1", "UTF-8",
166                                      false, handler,
167                                      offsets,
168                                      &result, &length);
169           ASSERT (retval == 0);
170           ASSERT (length == strlen (expected));
171           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
172           if (o)
173             {
174               for (i = 0; i < 37; i++)
175                 ASSERT (offsets[i] == (i < 1 ? i :
176                                        i < 12 ? i + 1 :
177                                        i < 18 ? i + 2 :
178                                        i + 3));
179               ASSERT (offsets[37] == MAGIC);
180               free (offsets);
181             }
182           free (result);
183         }
184     }
185
186   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
187   for (h = 0; h < SIZEOF (handlers); h++)
188     {
189       enum iconv_ilseq_handler handler = handlers[h];
190       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
191       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
192       for (o = 0; o < 2; o++)
193         {
194           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
195           char *result = NULL;
196           size_t length = 0;
197           int retval = mem_iconveha (input, strlen (input),
198                                      "UTF-8", "ISO-8859-1",
199                                      false, handler,
200                                      offsets,
201                                      &result, &length);
202           ASSERT (retval == 0);
203           ASSERT (length == strlen (expected));
204           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
205           if (o)
206             {
207               for (i = 0; i < 41; i++)
208                 ASSERT (offsets[i] == (i < 1 ? i :
209                                        i == 1 ? (size_t)(-1) :
210                                        i < 13 ? i - 1 :
211                                        i == 13 ? (size_t)(-1) :
212                                        i < 20 ? i - 2 :
213                                        i == 20 ? (size_t)(-1) :
214                                        i < 40 ? i - 3 :
215                                        (size_t)(-1)));
216               ASSERT (offsets[41] == MAGIC);
217               free (offsets);
218             }
219           free (result);
220         }
221     }
222
223   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
224   for (h = 0; h < SIZEOF (handlers); h++)
225     {
226       enum iconv_ilseq_handler handler = handlers[h];
227       static const char input[] = "Rafa\305\202 Maszkowski"; /* Rafał Maszkowski */
228       for (o = 0; o < 2; o++)
229         {
230           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
231           char *result = NULL;
232           size_t length = 0;
233           int retval = mem_iconveha (input, strlen (input),
234                                      "UTF-8", "ISO-8859-1",
235                                      false, handler,
236                                      offsets,
237                                      &result, &length);
238           switch (handler)
239             {
240             case iconveh_error:
241               ASSERT (retval == -1 && errno == EILSEQ);
242               ASSERT (result == NULL);
243               if (o)
244                 free (offsets);
245               break;
246             case iconveh_question_mark:
247               {
248                 static const char expected[] = "Rafa? Maszkowski";
249                 ASSERT (retval == 0);
250                 ASSERT (length == strlen (expected));
251                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
252                 if (o)
253                   {
254                     for (i = 0; i < 17; i++)
255                       ASSERT (offsets[i] == (i < 5 ? i :
256                                              i == 5 ? (size_t)(-1) :
257                                              i - 1));
258                     ASSERT (offsets[17] == MAGIC);
259                     free (offsets);
260                   }
261                 free (result);
262               }
263               break;
264             case iconveh_escape_sequence:
265               {
266                 static const char expected[] = "Rafa\\u0142 Maszkowski";
267                 ASSERT (retval == 0);
268                 ASSERT (length == strlen (expected));
269                 ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
270                 if (o)
271                   {
272                     for (i = 0; i < 17; i++)
273                       ASSERT (offsets[i] == (i < 5 ? i :
274                                              i == 5 ? (size_t)(-1) :
275                                              i + 4));
276                     ASSERT (offsets[17] == MAGIC);
277                     free (offsets);
278                   }
279                 free (result);
280               }
281               break;
282             }
283         }
284     }
285
286   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
287   for (h = 0; h < SIZEOF (handlers); h++)
288     {
289       enum iconv_ilseq_handler handler = handlers[h];
290       static const char input[] = "\342";
291       for (o = 0; o < 2; o++)
292         {
293           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
294           char *result = NULL;
295           size_t length = 0;
296           int retval = mem_iconveha (input, strlen (input),
297                                      "UTF-8", "ISO-8859-1",
298                                      false, handler,
299                                      offsets,
300                                      &result, &length);
301           ASSERT (retval == 0);
302           ASSERT (length == 0);
303           if (o)
304             {
305               ASSERT (offsets[0] == 0);
306               ASSERT (offsets[1] == MAGIC);
307               free (offsets);
308             }
309           if (result != NULL)
310             free (result);
311         }
312     }
313
314   /* Test conversions from autodetect_jp to UTF-8.  */
315   for (h = 0; h < SIZEOF (handlers); h++)
316     {
317       enum iconv_ilseq_handler handler = handlers[h];
318       static const char input[] = "\244\263\244\363\244\313\244\301\244\317"; /* こんにちは in EUC-JP */
319       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
320       for (o = 0; o < 2; o++)
321         {
322           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
323           char *result = NULL;
324           size_t length = 0;
325           int retval = mem_iconveha (input, strlen (input),
326                                      "autodetect_jp", "UTF-8",
327                                      false, handler,
328                                      offsets,
329                                      &result, &length);
330           ASSERT (retval == 0);
331           ASSERT (length == strlen (expected));
332           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
333           if (o)
334             {
335               for (i = 0; i < 10; i++)
336                 ASSERT (offsets[i] == ((i % 2) == 0 ? (i / 2) * 3 : (size_t)(-1)));
337               ASSERT (offsets[10] == MAGIC);
338               free (offsets);
339             }
340           free (result);
341         }
342     }
343   for (h = 0; h < SIZEOF (handlers); h++)
344     {
345       enum iconv_ilseq_handler handler = handlers[h];
346       static const char input[] = "\202\261\202\361\202\311\202\277\202\315"; /* こんにちは in Shift_JIS */
347       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
348       for (o = 0; o < 2; o++)
349         {
350           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
351           char *result = NULL;
352           size_t length = 0;
353           int retval = mem_iconveha (input, strlen (input),
354                                      "autodetect_jp", "UTF-8",
355                                      false, handler,
356                                      offsets,
357                                      &result, &length);
358           ASSERT (retval == 0);
359           ASSERT (length == strlen (expected));
360           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
361           if (o)
362             {
363               for (i = 0; i < 10; i++)
364                 ASSERT (offsets[i] == ((i % 2) == 0 ? (i / 2) * 3 : (size_t)(-1)));
365               ASSERT (offsets[10] == MAGIC);
366               free (offsets);
367             }
368           free (result);
369         }
370     }
371   for (h = 0; h < SIZEOF (handlers); h++)
372     {
373       enum iconv_ilseq_handler handler = handlers[h];
374       static const char input[] = "\033$B$3$s$K$A$O\033(B"; /* こんにちは in ISO-2022-JP-2 */
375       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
376       for (o = 0; o < 2; o++)
377         {
378           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
379           char *result = NULL;
380           size_t length = 0;
381           int retval = mem_iconveha (input, strlen (input),
382                                      "autodetect_jp", "UTF-8",
383                                      false, handler,
384                                      offsets,
385                                      &result, &length);
386           ASSERT (retval == 0);
387           ASSERT (length == strlen (expected));
388           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
389           if (o)
390             {
391               for (i = 0; i < 16; i++)
392                 ASSERT (offsets[i] == (i == 0 ? 0 :
393                                        i == 5 ? 3 :
394                                        i == 7 ? 6 :
395                                        i == 9 ? 9 :
396                                        i == 11 ? 12 :
397                                        i == 13 ? 15 :
398                                        (size_t)(-1)));
399               ASSERT (offsets[16] == MAGIC);
400               free (offsets);
401             }
402           free (result);
403         }
404     }
405
406 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
407   /* Test conversion from UTF-8 to ISO-8859-1 with transliteration.  */
408   for (h = 0; h < SIZEOF (handlers); h++)
409     {
410       enum iconv_ilseq_handler handler = handlers[h];
411       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
412       static const char expected[] = "Costs: 27 EUR";
413       for (o = 0; o < 2; o++)
414         {
415           size_t *offsets = (o ? new_offsets (strlen (input)) : NULL);
416           char *result = NULL;
417           size_t length = 0;
418           int retval = mem_iconveha (input, strlen (input),
419                                      "UTF-8", "ISO-8859-1",
420                                      true, handler,
421                                      offsets,
422                                      &result, &length);
423           ASSERT (retval == 0);
424           ASSERT (length == strlen (expected));
425           ASSERT (result != NULL && memcmp (result, expected, strlen (expected)) == 0);
426           if (o)
427             {
428               for (i = 0; i < 13; i++)
429                 ASSERT (offsets[i] == (i < 11 ? i : (size_t)(-1)));
430               ASSERT (offsets[13] == MAGIC);
431               free (offsets);
432             }
433           free (result);
434         }
435     }
436 # endif
437
438   /* ------------------------- Test str_iconveha() ------------------------- */
439
440   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
441   for (h = 0; h < SIZEOF (handlers); h++)
442     {
443       enum iconv_ilseq_handler handler = handlers[h];
444       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
445       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
446       char *result = str_iconveha (input, "ISO-8859-2", "ISO-8859-1", false, handler);
447       ASSERT (result != NULL);
448       ASSERT (strcmp (result, expected) == 0);
449       free (result);
450     }
451
452   /* Test conversion from ISO-8859-2 to ISO-8859-1 with EILSEQ.  */
453   for (h = 0; h < SIZEOF (handlers); h++)
454     {
455       enum iconv_ilseq_handler handler = handlers[h];
456       static const char input[] = "Rafa\263 Maszkowski"; /* Rafał Maszkowski */
457       char *result = str_iconveha (input, "ISO-8859-2", "ISO-8859-1", false, handler);
458       switch (handler)
459         {
460         case iconveh_error:
461           ASSERT (result == NULL && errno == EILSEQ);
462           break;
463         case iconveh_question_mark:
464           {
465             static const char expected[] = "Rafa? Maszkowski";
466             ASSERT (result != NULL);
467             ASSERT (strcmp (result, expected) == 0);
468             free (result);
469           }
470           break;
471         case iconveh_escape_sequence:
472           {
473             static const char expected[] = "Rafa\\u0142 Maszkowski";
474             ASSERT (result != NULL);
475             ASSERT (strcmp (result, expected) == 0);
476             free (result);
477           }
478           break;
479         }
480     }
481
482   /* Test conversion from ISO-8859-1 to UTF-8 with no errors.  */
483   for (h = 0; h < SIZEOF (handlers); h++)
484     {
485       enum iconv_ilseq_handler handler = handlers[h];
486       static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
487       static const char expected[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
488       char *result = str_iconveha (input, "ISO-8859-1", "UTF-8", false, handler);
489       ASSERT (result != NULL);
490       ASSERT (strcmp (result, expected) == 0);
491       free (result);
492     }
493
494   /* Test conversion from UTF-8 to ISO-8859-1 with no errors.  */
495   for (h = 0; h < SIZEOF (handlers); h++)
496     {
497       enum iconv_ilseq_handler handler = handlers[h];
498       static const char input[] = "\303\204rger mit b\303\266sen B\303\274bchen ohne Augenma\303\237";
499       static const char expected[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
500       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
501       ASSERT (result != NULL);
502       ASSERT (strcmp (result, expected) == 0);
503       free (result);
504     }
505
506   /* Test conversion from UTF-8 to ISO-8859-1 with EILSEQ.  */
507   for (h = 0; h < SIZEOF (handlers); h++)
508     {
509       enum iconv_ilseq_handler handler = handlers[h];
510       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
511       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
512       switch (handler)
513         {
514         case iconveh_error:
515           ASSERT (result == NULL && errno == EILSEQ);
516           break;
517         case iconveh_question_mark:
518           {
519             static const char expected[] = "Costs: 27 ?";
520             ASSERT (result != NULL);
521             ASSERT (strcmp (result, expected) == 0);
522             free (result);
523           }
524           break;
525         case iconveh_escape_sequence:
526           {
527             static const char expected[] = "Costs: 27 \\u20AC";
528             ASSERT (result != NULL);
529             ASSERT (strcmp (result, expected) == 0);
530             free (result);
531           }
532           break;
533         }
534     }
535
536   /* Test conversion from UTF-8 to ISO-8859-1 with EINVAL.  */
537   for (h = 0; h < SIZEOF (handlers); h++)
538     {
539       enum iconv_ilseq_handler handler = handlers[h];
540       static const char input[] = "\342";
541       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", false, handler);
542       ASSERT (result != NULL);
543       ASSERT (strcmp (result, "") == 0);
544       free (result);
545     }
546
547   /* Test conversions from autodetect_jp to UTF-8.  */
548   for (h = 0; h < SIZEOF (handlers); h++)
549     {
550       enum iconv_ilseq_handler handler = handlers[h];
551       static const char input[] = "\244\263\244\363\244\313\244\301\244\317"; /* こんにちは in EUC-JP */
552       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
553       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
554       ASSERT (result != NULL);
555       ASSERT (strcmp (result, expected) == 0);
556       free (result);
557     }
558   for (h = 0; h < SIZEOF (handlers); h++)
559     {
560       enum iconv_ilseq_handler handler = handlers[h];
561       static const char input[] = "\202\261\202\361\202\311\202\277\202\315"; /* こんにちは in Shift_JIS */
562       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
563       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
564       ASSERT (result != NULL);
565       ASSERT (strcmp (result, expected) == 0);
566       free (result);
567     }
568   for (h = 0; h < SIZEOF (handlers); h++)
569     {
570       enum iconv_ilseq_handler handler = handlers[h];
571       static const char input[] = "\033$B$3$s$K$A$O\033(B"; /* こんにちは in ISO-2022-JP-2 */
572       static const char expected[] = "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257"; /* こんにちは */
573       char *result = str_iconveha (input, "autodetect_jp", "UTF-8", false, handler);
574       ASSERT (result != NULL);
575       ASSERT (strcmp (result, expected) == 0);
576       free (result);
577     }
578
579 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
580   /* Test conversion from UTF-8 to ISO-8859-1 with transliteration.  */
581   for (h = 0; h < SIZEOF (handlers); h++)
582     {
583       enum iconv_ilseq_handler handler = handlers[h];
584       static const char input[] = "Costs: 27 \342\202\254"; /* EURO SIGN */
585       static const char expected[] = "Costs: 27 EUR";
586       char *result = str_iconveha (input, "UTF-8", "ISO-8859-1", true, handler);
587       ASSERT (result != NULL);
588       ASSERT (strcmp (result, expected) == 0);
589       free (result);
590     }
591 # endif
592
593 #endif
594
595   return 0;
596 }