Imported Upstream version 1.0.2
[platform/upstream/fribidi.git] / test / unicode-conformance / BidiTest.c
1 /*
2  * Copyright (C) 2009 Behdad Esfahbod
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library, in a file named COPYING; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA
18  */
19
20 #include <fribidi.h>
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <errno.h>
27 #include <ctype.h>
28
29 #define TRUE 1
30 #define FALSE 0
31
32 /* Glib array types */
33 typedef struct {
34   int capacity;
35   int len;
36   int *data;
37 } int_array_t;
38
39 typedef struct {
40   int capacity;
41   int len;
42   char *data;
43 } char_array_t;
44
45 #define LINE_SIZE 2048 /* Size of largest example line in BidiTest */
46 #define ARRAY_CHUNK_SIZE 32
47 int_array_t *new_int_array()
48 {
49   int_array_t *arr = (int_array_t*)malloc(sizeof(int_array_t));
50   arr->len = 0;
51   arr->capacity = ARRAY_CHUNK_SIZE;
52   arr->data = (int*)malloc(arr->capacity * sizeof(int));
53
54   return arr;
55 }
56
57 void int_array_add(int_array_t *arr, int val)
58 {
59   if (arr->len == arr->capacity)
60     {
61       arr->capacity += ARRAY_CHUNK_SIZE;
62       arr->data = (int*)realloc(arr->data, arr->capacity*sizeof(int));
63     }
64   arr->data[arr->len++] = val;
65 }
66
67 int *int_array_free(int_array_t *arr, int free_data)
68 {
69   int *data = arr->data;
70   if (free_data) {
71     data = NULL;
72     free(arr->data);
73   }
74   free(arr);
75   return data;
76 }
77
78 char_array_t *new_char_array()
79 {
80   char_array_t *arr = (char_array_t*)malloc(sizeof(char_array_t));
81   arr->len = 0;
82   arr->capacity = ARRAY_CHUNK_SIZE;
83   arr->data = (char*)malloc(arr->capacity);
84
85   return arr;
86 }
87
88 void char_array_add(char_array_t *arr, char val)
89 {
90   if (arr->len == arr->capacity)
91     {
92       arr->capacity += ARRAY_CHUNK_SIZE;
93       arr->data = (char*)realloc(arr->data, arr->capacity * sizeof(char));
94     }
95   arr->data[arr->len++] = val;
96 }
97
98 char *char_array_free(char_array_t *arr, int free_data)
99 {
100   char *data = arr->data;
101   if (free_data) {
102     data = NULL;
103     free(arr->data);
104   }
105   free(arr);
106   return data;
107 }
108
109 static void die(const char *fmt, ...)
110 {
111     va_list ap;
112     va_start(ap,fmt); 
113     
114     vfprintf(stderr, fmt, ap);
115     exit(-1);
116 }
117
118 static FriBidiCharType
119 parse_char_type (const char *s, int len)
120 {
121 #define MATCH(name, value) \
122     if (!strncmp (name, s, len) && name[len] == '\0') return value;
123
124     MATCH ("L",   FRIBIDI_TYPE_LTR);
125     MATCH ("R",   FRIBIDI_TYPE_RTL);
126     MATCH ("AL",  FRIBIDI_TYPE_AL);
127     MATCH ("EN",  FRIBIDI_TYPE_EN);
128     MATCH ("AN",  FRIBIDI_TYPE_AN);
129     MATCH ("ES",  FRIBIDI_TYPE_ES);
130     MATCH ("ET",  FRIBIDI_TYPE_ET);
131     MATCH ("CS",  FRIBIDI_TYPE_CS);
132     MATCH ("NSM", FRIBIDI_TYPE_NSM);
133     MATCH ("BN",  FRIBIDI_TYPE_BN);
134     MATCH ("B",   FRIBIDI_TYPE_BS);
135     MATCH ("S",   FRIBIDI_TYPE_SS);
136     MATCH ("WS",  FRIBIDI_TYPE_WS);
137     MATCH ("ON",  FRIBIDI_TYPE_ON);
138     MATCH ("LRE", FRIBIDI_TYPE_LRE);
139     MATCH ("RLE", FRIBIDI_TYPE_RLE);
140     MATCH ("LRO", FRIBIDI_TYPE_LRO);
141     MATCH ("RLO", FRIBIDI_TYPE_RLO);
142     MATCH ("PDF", FRIBIDI_TYPE_PDF);
143     MATCH ("LRI", FRIBIDI_TYPE_LRI);
144     MATCH ("RLI", FRIBIDI_TYPE_RLI);
145     MATCH ("FSI", FRIBIDI_TYPE_FSI);
146     MATCH ("PDI", FRIBIDI_TYPE_PDI);
147
148     die("Oops. I shouldn't reach here!\n");
149     return -1;
150 }
151
152 static FriBidiLevel *
153 parse_levels_line (const char *line,
154                    FriBidiLevel *len)
155 {
156     char_array_t *levels;
157
158     if (!strncmp (line, "@Levels:", 8))
159         line += 8;
160
161     levels = new_char_array ();
162
163     while (*line)
164     {
165         FriBidiLevel l;
166         char *end;
167
168         errno = 0;
169         l = strtol (line, &end, 10);
170         if (errno != EINVAL && line != end)
171         {
172           char_array_add (levels, l);
173           line = end;
174           continue;
175         }
176
177         while (isspace (*line))
178           line++;
179
180         if (*line == 'x')
181         {
182           char_array_add (levels, -1);
183           line++;
184           continue;
185         }
186
187         if (!*line)
188           break;
189
190         die("Oops. I shouldn't be here!\n");
191     }
192
193     *len = levels->len;
194     return (FriBidiLevel *) char_array_free(levels, FALSE);
195 }
196
197 static FriBidiStrIndex *
198 parse_reorder_line (const char *line,
199                     FriBidiStrIndex *len)
200 {
201     int_array_t *map;
202     FriBidiStrIndex l;
203     char *end;
204
205     if (!strncmp (line, "@Reorder:", 9))
206         line += 9;
207
208     map = new_int_array ();
209
210     for(; errno = 0, l = strtol (line, &end, 10), line != end && errno != EINVAL; line = end) {
211         int_array_add (map, l);
212     }
213
214     *len = map->len;
215     return (FriBidiStrIndex *) int_array_free (map, FALSE);
216 }
217
218 static FriBidiCharType *
219 parse_test_line (const char *line,
220                  FriBidiStrIndex *len,
221                  int *base_dir_flags)
222 {
223     int_array_t *types;
224     FriBidiCharType c;
225     const char *end;
226
227     types = new_int_array();
228
229     for(;;) {
230         while (isspace (*line))
231             line++;
232         end = line;
233         while (isalpha (*end))
234             end++;
235         if (line == end)
236             break;
237
238         c = parse_char_type (line, end - line);
239         int_array_add (types, c);
240
241         line = end;
242     }
243
244     if (*line == ';')
245         line++;
246     *base_dir_flags = strtol (line, NULL, 10);
247
248     *len = types->len;
249     return (FriBidiCharType *) int_array_free (types, FALSE);
250 }
251
252 int
253 main (int argc, char **argv)
254 {
255     FILE *channel;
256     char line[LINE_SIZE];
257     FriBidiStrIndex *expected_ltor = NULL;
258     FriBidiStrIndex expected_ltor_len = 0;
259     FriBidiStrIndex *ltor = NULL;
260     FriBidiStrIndex ltor_len = 0;
261     FriBidiCharType *types = NULL;
262     FriBidiStrIndex types_len = 0;
263     FriBidiLevel *expected_levels = NULL;
264     FriBidiLevel expected_levels_len = 0;
265     FriBidiLevel *levels = NULL;
266     FriBidiStrIndex levels_len = 0;
267     int base_dir_flags, base_dir_mode;
268     int numerrs = 0;
269     int numtests = 0;
270     int line_no = 0;
271     int debug = FALSE;
272     const char *filename;
273     int next_arg;
274
275     if (argc < 2) 
276         die ("usage: %s [--debug] test-file-name\n", argv[0]);
277
278     next_arg = 1;
279     if (!strcmp (argv[next_arg], "--debug")) {
280         debug = TRUE;
281         next_arg++;
282     }
283
284     filename = argv[next_arg++];
285
286     channel = fopen(filename, "r");
287     if (!channel) 
288         die ("Failed opening %s\n", filename);
289
290     while (!feof(channel)) {
291         fgets(line, LINE_SIZE, channel);
292         int len = strlen(line);
293         if (len == LINE_SIZE-1)
294           die("LINE_SIZE too small at line %d!\n", line_no);
295
296         line_no++;
297
298         if (line[0] == '#')
299             continue;
300
301         if (line[0] == '@')
302         {
303             if (!strncmp (line, "@Reorder:", 9)) {
304                 free (expected_ltor);
305                 expected_ltor = parse_reorder_line (line, &expected_ltor_len);
306                 continue;
307             }
308             if (!strncmp (line, "@Levels:", 8)) {
309                 free (expected_levels);
310                 expected_levels = parse_levels_line (line, &expected_levels_len);
311                 continue;
312             }
313             continue;
314         }
315
316         /* Test line */
317         free (types);
318         types = parse_test_line (line, &types_len, &base_dir_flags);
319
320         free (levels);
321         levels = malloc (sizeof (FriBidiLevel) * types_len);
322         levels_len = types_len;
323
324         free (ltor);
325         ltor = malloc (sizeof (FriBidiStrIndex) * types_len);
326
327         /* Test it */
328         for (base_dir_mode = 0; base_dir_mode < 3; base_dir_mode++) {
329             FriBidiParType base_dir;
330             int i, j;
331             int matches;
332
333             if ((base_dir_flags & (1<<base_dir_mode)) == 0)
334                 continue;
335
336             numtests++;
337
338             switch (base_dir_mode) {
339             case 0: base_dir = FRIBIDI_PAR_ON;  break;
340             case 1: base_dir = FRIBIDI_PAR_LTR; break;
341             case 2: base_dir = FRIBIDI_PAR_RTL; break;
342             }
343
344             if (fribidi_get_par_embedding_levels_ex (types,
345                                                      NULL, /* Brackets are not used in the BidiTest.txt file */
346                                                      types_len,
347                                                      &base_dir,
348                                                      levels))
349             {}
350
351             for (i = 0; i < types_len; i++)
352                 ltor[i] = i;
353
354             if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/,
355                                       types, types_len,
356                                       0, base_dir,
357                                       levels,
358                                       NULL,
359                                       ltor))
360             {}
361
362             j = 0;
363             for (i = 0; i < types_len; i++)
364                 if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]]))
365                     ltor[j++] = ltor[i];
366             ltor_len = j;
367
368             /* Compare */
369             matches = TRUE;
370             if (levels_len != expected_levels_len)
371                 matches = FALSE;
372             if (matches)
373                 for (i = 0; i < levels_len; i++)
374                     if (levels[i] != expected_levels[i] &&
375                         expected_levels[i] != (FriBidiLevel) -1) {
376                         matches = FALSE;
377                         break;
378                     }
379
380             if (ltor_len != expected_ltor_len)
381                 matches = FALSE;
382             if (matches)
383                 for (i = 0; i < ltor_len; i++)
384                     if (ltor[i] != expected_ltor[i]) {
385                         matches = FALSE;
386                         break;
387                     }
388
389             if (!matches)
390             {
391                 numerrs++;
392
393                 fprintf (stderr, "failure on line %d\n", line_no);
394                 fprintf (stderr, "input is: %s\n", line);
395                 fprintf (stderr, "base dir: %s\n",
396                          base_dir_mode==0 ? "auto"
397                          : base_dir_mode==1 ? "LTR" : "RTL");
398
399                 fprintf (stderr, "expected levels:");
400                 for (i = 0; i < expected_levels_len; i++)
401                     if (expected_levels[i] == (FriBidiLevel) -1)
402                         fprintf (stderr," x");
403                     else
404                         fprintf (stderr, " %d", expected_levels[i]);
405                 fprintf (stderr, "\n");
406                 fprintf (stderr, "returned levels:");
407                 for (i = 0; i < levels_len; i++)
408                     fprintf (stderr, " %d", levels[i]);
409                 fprintf (stderr, "\n");
410
411                 fprintf (stderr, "expected order:");
412                 for (i = 0; i < expected_ltor_len; i++)
413                     fprintf (stderr, " %d", expected_ltor[i]);
414                 fprintf (stderr, "\n");
415                 fprintf (stderr, "returned order:");
416                 for (i = 0; i < ltor_len; i++)
417                     fprintf (stderr, " %d", ltor[i]);
418                 fprintf (stderr, "\n");
419
420                 if (debug)
421                   {
422                     FriBidiParType base_dir;
423
424                     fribidi_set_debug (1);
425
426                     switch (base_dir_mode) {
427                     case 0: base_dir = FRIBIDI_PAR_ON;  break;
428                     case 1: base_dir = FRIBIDI_PAR_LTR; break;
429                     case 2: base_dir = FRIBIDI_PAR_RTL; break;
430                     }
431
432                     if (fribidi_get_par_embedding_levels_ex (types,
433                                                              NULL, /* No bracket types */
434                                                              types_len,
435                                                              &base_dir,
436                                                              levels))
437                     {}
438
439                     fribidi_set_debug (0);
440                 }
441
442                 fprintf (stderr, "\n");
443             }
444         }
445     }
446
447     free (ltor);
448     free (levels);
449     free (expected_ltor);
450     free (expected_levels);
451     free (types);
452     fclose(channel);
453
454     if (numerrs)
455         fprintf (stderr, "%d errors out of %d total tests\n", numerrs, numtests);
456     else
457         printf("No errors found! :-)\n");
458
459     return numerrs;
460 }