Imported Upstream version 1.0.8
[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         int len;
292         fgets(line, LINE_SIZE, channel);
293         len = strlen(line);
294         if (len == LINE_SIZE-1)
295           die("LINE_SIZE too small at line %d!\n", line_no);
296
297         line_no++;
298
299         if (line[0] == '#')
300             continue;
301
302         if (line[0] == '@')
303         {
304             if (!strncmp (line, "@Reorder:", 9)) {
305                 free (expected_ltor);
306                 expected_ltor = parse_reorder_line (line, &expected_ltor_len);
307                 continue;
308             }
309             if (!strncmp (line, "@Levels:", 8)) {
310                 free (expected_levels);
311                 expected_levels = parse_levels_line (line, &expected_levels_len);
312                 continue;
313             }
314             continue;
315         }
316
317         /* Test line */
318         free (types);
319         types = parse_test_line (line, &types_len, &base_dir_flags);
320
321         free (levels);
322         levels = malloc (sizeof (FriBidiLevel) * types_len);
323         levels_len = types_len;
324
325         free (ltor);
326         ltor = malloc (sizeof (FriBidiStrIndex) * types_len);
327
328         /* Test it */
329         for (base_dir_mode = 0; base_dir_mode < 3; base_dir_mode++) {
330             FriBidiParType base_dir;
331             int i, j;
332             int matches;
333
334             if ((base_dir_flags & (1<<base_dir_mode)) == 0)
335                 continue;
336
337             numtests++;
338
339             switch (base_dir_mode) {
340             case 0: base_dir = FRIBIDI_PAR_ON;  break;
341             case 1: base_dir = FRIBIDI_PAR_LTR; break;
342             case 2: base_dir = FRIBIDI_PAR_RTL; break;
343             }
344
345             if (fribidi_get_par_embedding_levels_ex (types,
346                                                      NULL, /* Brackets are not used in the BidiTest.txt file */
347                                                      types_len,
348                                                      &base_dir,
349                                                      levels))
350             {}
351
352             for (i = 0; i < types_len; i++)
353                 ltor[i] = i;
354
355             if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/,
356                                       types, types_len,
357                                       0, base_dir,
358                                       levels,
359                                       NULL,
360                                       ltor))
361             {}
362
363             j = 0;
364             for (i = 0; i < types_len; i++)
365                 if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]]))
366                     ltor[j++] = ltor[i];
367             ltor_len = j;
368
369             /* Compare */
370             matches = TRUE;
371             if (levels_len != expected_levels_len)
372                 matches = FALSE;
373             if (matches)
374                 for (i = 0; i < levels_len; i++)
375                     if (levels[i] != expected_levels[i] &&
376                         expected_levels[i] != (FriBidiLevel) -1) {
377                         matches = FALSE;
378                         break;
379                     }
380
381             if (ltor_len != expected_ltor_len)
382                 matches = FALSE;
383             if (matches)
384                 for (i = 0; i < ltor_len; i++)
385                     if (ltor[i] != expected_ltor[i]) {
386                         matches = FALSE;
387                         break;
388                     }
389
390             if (!matches)
391             {
392                 numerrs++;
393
394                 fprintf (stderr, "failure on line %d\n", line_no);
395                 fprintf (stderr, "input is: %s\n", line);
396                 fprintf (stderr, "base dir: %s\n",
397                          base_dir_mode==0 ? "auto"
398                          : base_dir_mode==1 ? "LTR" : "RTL");
399
400                 fprintf (stderr, "expected levels:");
401                 for (i = 0; i < expected_levels_len; i++)
402                     if (expected_levels[i] == (FriBidiLevel) -1)
403                         fprintf (stderr," x");
404                     else
405                         fprintf (stderr, " %d", expected_levels[i]);
406                 fprintf (stderr, "\n");
407                 fprintf (stderr, "returned levels:");
408                 for (i = 0; i < levels_len; i++)
409                     fprintf (stderr, " %d", levels[i]);
410                 fprintf (stderr, "\n");
411
412                 fprintf (stderr, "expected order:");
413                 for (i = 0; i < expected_ltor_len; i++)
414                     fprintf (stderr, " %d", expected_ltor[i]);
415                 fprintf (stderr, "\n");
416                 fprintf (stderr, "returned order:");
417                 for (i = 0; i < ltor_len; i++)
418                     fprintf (stderr, " %d", ltor[i]);
419                 fprintf (stderr, "\n");
420
421                 if (debug)
422                   {
423                     FriBidiParType base_dir;
424
425                     fribidi_set_debug (1);
426
427                     switch (base_dir_mode) {
428                     case 0: base_dir = FRIBIDI_PAR_ON;  break;
429                     case 1: base_dir = FRIBIDI_PAR_LTR; break;
430                     case 2: base_dir = FRIBIDI_PAR_RTL; break;
431                     }
432
433                     if (fribidi_get_par_embedding_levels_ex (types,
434                                                              NULL, /* No bracket types */
435                                                              types_len,
436                                                              &base_dir,
437                                                              levels))
438                     {}
439
440                     fribidi_set_debug (0);
441                 }
442
443                 fprintf (stderr, "\n");
444             }
445         }
446     }
447
448     free (ltor);
449     free (levels);
450     free (expected_ltor);
451     free (expected_levels);
452     free (types);
453     fclose(channel);
454
455     if (numerrs)
456         fprintf (stderr, "%d errors out of %d total tests\n", numerrs, numtests);
457     else
458         printf("No errors found! :-)\n");
459
460     return numerrs;
461 }