cf3f6766748e42f1e11c75f9463ca1b778b514e4
[platform/upstream/fribidi.git] / test / unicode-conformance / BidiCharacterTest.c
1 /*
2  * Copyright (C) 2015, 2017 Dov Grobgeld
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 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27
28 #define FALSE 0
29 #define TRUE 1
30 #define LINE_SIZE 2048 /* Size of biggest line in test file */
31
32 /* Glib like arrays */
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 ARRAY_CHUNK_SIZE 32
46 int_array_t *new_int_array()
47 {
48   int_array_t *arr = (int_array_t*)malloc(sizeof(int_array_t));
49   arr->len = 0;
50   arr->capacity = ARRAY_CHUNK_SIZE;
51   arr->data = (int*)malloc(arr->capacity * sizeof(int));
52
53   return arr;
54 }
55
56 void int_array_add(int_array_t *arr, int val)
57 {
58   if (arr->len == arr->capacity)
59     {
60       arr->capacity += ARRAY_CHUNK_SIZE;
61       arr->data = (int*)realloc(arr->data, arr->capacity*sizeof(int));
62     }
63   arr->data[arr->len++] = val;
64 }
65
66 int *int_array_free(int_array_t *arr, int free_data)
67 {
68   int *data = arr->data;
69   if (free_data) {
70     data = NULL;
71     free(arr->data);
72   }
73   free(arr);
74   return data;
75 }
76
77 char_array_t *new_char_array()
78 {
79   char_array_t *arr = (char_array_t*)malloc(sizeof(char_array_t));
80   arr->len = 0;
81   arr->capacity = ARRAY_CHUNK_SIZE;
82   arr->data = (char*)malloc(arr->capacity);
83
84   return arr;
85 }
86
87 void char_array_add(char_array_t *arr, char val)
88 {
89   if (arr->len == arr->capacity)
90     {
91       arr->capacity += ARRAY_CHUNK_SIZE;
92       arr->data = (char*)realloc(arr->data, arr->capacity * sizeof(char));
93     }
94   arr->data[arr->len++] = val;
95 }
96
97 char *char_array_free(char_array_t *arr, int free_data)
98 {
99   char *data = arr->data;
100   if (free_data) {
101     data = NULL;
102     free(arr->data);
103   }
104   free(arr);
105   return data;
106 }
107
108 static void die(const char *fmt, ...)
109 {
110   va_list ap;
111   va_start(ap,fmt);
112
113   vfprintf(stderr, fmt, ap);
114   exit(-1);
115 }
116
117 static
118 FriBidiChar parse_uni_char(const char *start, int len)
119 {
120   return strtoul(start, NULL, 16);
121 }
122
123 static
124 void parse_test_line (char *line,
125                       int line_no,
126                       FriBidiChar **code_points,      /* Field 0 */
127                       int *code_points_len,
128                       int *paragraph_dir,    /* Field 1 */
129                       int *resolved_paragraph_embedding_level,   /* Field 2 */
130                       FriBidiLevel **resolved_levels,   /* Field 3 */
131                       int **visual_ordering,    /* Field 4 */
132                       int *visual_ordering_len
133                       )
134 {
135   int_array_t *code_points_array, *visual_ordering_array;
136   char_array_t *levels_array;
137   char *end;
138   int level;
139
140   code_points_array = new_int_array ();
141   levels_array = new_char_array ();
142
143   /* Field 0. Code points */
144   for(;;)
145     {
146       FriBidiChar c;
147       while (isspace (*line))
148         line++;
149       end = line;
150       while (isxdigit (*end))
151         end++;
152       if (line == end)
153         break;
154
155       c = parse_uni_char (line, end - line);
156       int_array_add(code_points_array, c);
157
158       line = end;
159     }
160
161   *code_points_len = code_points_array->len;
162   *code_points = (FriBidiChar *) int_array_free (code_points_array, FALSE);
163
164   if (*line == ';')
165     line++;
166   else
167     die("Oops! Didn't find expected ;\n");
168
169   /* Field 1. Paragraph direction */
170   end = line;
171   while (isdigit (*end))
172     end++;
173   *paragraph_dir = atoi(line);
174   line = end;
175
176   if (*line == ';')
177     line++;
178   else
179     die("Oops! Didn't find expected ;\n");
180
181   /* Field 2. resolved paragraph_dir */
182   end = line;
183   while (isdigit (*end))
184     end++;
185   *resolved_paragraph_embedding_level = atoi(line);
186   line = end;
187
188   if (*line == ';')
189     line++;
190   else
191     die("Oops! Didn't find expected ; at line %d\n", line_no);
192
193   while (*line)
194     {
195       FriBidiLevel level;
196       char *end;
197
198       errno = 0;
199       level = strtol (line, &end, 10);
200       if (errno != EINVAL && line != end)
201         {
202           char_array_add (levels_array, level);
203           line = end;
204           continue;
205         }
206
207       while (isspace (*line))
208         line++;
209
210       if (*line == 'x')
211         {
212           level = (FriBidiLevel) -1;
213           char_array_add (levels_array, level);
214           line++;
215           continue;
216         }
217
218       if (*line == ';')
219         break;
220
221       die("Oops! I shouldn't be here!\n");
222     }
223
224   if (levels_array->len != *code_points_len)
225     die("Oops! Different lengths for levels and codepoints at line %d!\n", line_no);
226
227   *resolved_levels = (FriBidiLevel*)char_array_free (levels_array, FALSE);
228
229   if (*line == ';')
230     line++;
231   else
232     die("Oops! Didn't find expected ; at line %d\n", line_no);
233
234   /* Field 4 - resulting visual ordering */
235   visual_ordering_array = new_int_array ();
236   for(; errno = 0, level = strtol (line, &end, 10), line != end && errno != EINVAL; line = end) {
237     int_array_add (visual_ordering_array, level);
238   }
239
240   *visual_ordering_len = visual_ordering_array->len;
241   *visual_ordering = (int*)int_array_free (visual_ordering_array, FALSE);
242 }
243
244 int
245 main (int argc, char **argv)
246 {
247   int next_arg;
248   FILE *channel;
249   const char *filename;
250   char line[LINE_SIZE];
251   int numerrs = 0;
252   int line_no = 0;
253   FriBidiChar *code_points = NULL;
254   int code_points_len = 0;
255   int expected_ltor_len = 0;
256   int paragraph_dir = 0;
257   FriBidiLevel *expected_levels = NULL;
258   int *expected_ltor = NULL;
259   int resolved_paragraph_embedding_level;
260   FriBidiLevel *levels = NULL;
261   FriBidiCharType *types = NULL;
262   FriBidiBracketType *bracket_types = NULL;
263   FriBidiStrIndex *ltor = NULL;
264   int ltor_len;
265   int debug = FALSE;
266
267   if (argc < 2)
268     {
269       fprintf (stderr, "usage: %s [--debug] test-file-name\n", argv[0]);
270       exit (1);
271     }
272
273   next_arg = 1;
274   while(next_arg < argc && argv[next_arg][0]=='-')
275     {
276       const char *arg = argv[next_arg++];
277       if (strcmp(arg, "--debug")==0)
278         {
279           debug=TRUE;
280           continue;
281         }
282       die("Unknown option %s!\n", arg);
283     }
284
285     filename = argv[next_arg++];
286
287     channel = fopen(filename, "r");
288     if (!channel) 
289         die ("Failed opening %s\n", filename);
290
291     while (!feof(channel)) {
292       fgets(line, LINE_SIZE, channel);
293       int len = strlen(line);
294       if (len == LINE_SIZE-1)
295         die("LINE_SIZE=%d too small at line %d!\n", LINE_SIZE, line_no);
296
297       line_no++;
298
299       if (line[0] == '#' || line[0] == '\n')
300         continue;
301
302       free (code_points);
303       free (expected_levels);
304       free (expected_ltor);
305       free (bracket_types);
306       free (types);
307       free (levels);
308       free (ltor);
309
310       parse_test_line (line,
311                        line_no,
312                        &code_points,      /* Field 0 */
313                        &code_points_len,
314                        &paragraph_dir,    /* Field 1 */
315                        &resolved_paragraph_embedding_level,   /* Field 2 */
316                        &expected_levels,   /* Field 3 */
317                        &expected_ltor,    /* Field 4 */
318                        &expected_ltor_len
319                        );
320
321       /* Test it */
322       bracket_types = malloc ( sizeof(FriBidiBracketType) * code_points_len);
323       types = malloc ( sizeof(FriBidiCharType) * code_points_len);
324       levels = malloc (sizeof (FriBidiLevel) * code_points_len);
325       ltor = malloc (sizeof (FriBidiStrIndex) * code_points_len);
326
327
328       {
329         FriBidiParType base_dir;
330         int i, j;
331         int matches;
332         int types_len = code_points_len;
333         int levels_len = types_len;
334         FriBidiBracketType NoBracket = FRIBIDI_NO_BRACKET;
335
336         for (i=0; i<code_points_len; i++)
337           {
338             types[i] = fribidi_get_bidi_type(code_points[i]);
339
340             /* Note the optimization that a bracket is always
341                of type neutral */
342             if (types[i] == FRIBIDI_TYPE_ON)
343                 bracket_types[i] = fribidi_get_bracket(code_points[i]);
344             else
345                 bracket_types[i] = NoBracket;
346           }
347
348         switch (paragraph_dir)
349           {
350           case 0: base_dir = FRIBIDI_PAR_LTR; break;
351           case 1: base_dir = FRIBIDI_PAR_RTL; break;
352           case 2: base_dir = FRIBIDI_PAR_ON;  break;
353           }
354
355         if (fribidi_get_par_embedding_levels_ex (types,
356                                                  bracket_types,
357                                                  types_len,
358                                                  &base_dir,
359                                                  levels))
360         {}
361
362         for (i = 0; i < types_len; i++)
363           ltor[i] = i;
364
365         if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/,
366                                   types, types_len,
367                                   0, base_dir,
368                                   levels,
369                                   NULL,
370                                   ltor))
371         {}
372
373         j = 0;
374         for (i = 0; i < types_len; i++)
375           if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]]))
376             ltor[j++] = ltor[i];
377         ltor_len = j;
378
379         /* Compare */
380         matches = TRUE;
381         if (matches)
382           for (i = 0; i < code_points_len; i++)
383             if (levels[i] != expected_levels[i] &&
384                 expected_levels[i] != (FriBidiLevel) -1) {
385               matches = FALSE;
386               break;
387             }
388
389         if (ltor_len != expected_ltor_len)
390           matches = FALSE;
391         if (matches)
392           for (i = 0; i < ltor_len; i++)
393             if (ltor[i] != expected_ltor[i]) {
394               matches = FALSE;
395               break;
396             }
397
398         if (!matches)
399           {
400             numerrs++;
401
402             fprintf (stderr, "failure on line %d\n", line_no);
403             fprintf (stderr, "input is: %s\n", line);
404             fprintf (stderr, "base dir: %s\n", paragraph_dir==0 ? "LTR"
405                         : paragraph_dir==1 ? "RTL" : "AUTO");
406
407             fprintf (stderr, "expected levels:");
408             for (i = 0; i < code_points_len; i++)
409               if (expected_levels[i] == (FriBidiLevel) -1)
410                 fprintf (stderr, " x");
411               else
412                 fprintf (stderr, " %d", expected_levels[i]);
413             fprintf (stderr, "\n");
414             fprintf (stderr, "returned levels:");
415             for (i = 0; i < levels_len; i++)
416               fprintf (stderr, " %d", levels[i]);
417             fprintf (stderr, "\n");
418
419             fprintf (stderr, "expected order:");
420             for (i = 0; i < expected_ltor_len; i++)
421               fprintf (stderr, " %d", expected_ltor[i]);
422             fprintf (stderr, "\n");
423             fprintf (stderr, "returned order:");
424             for (i = 0; i < ltor_len; i++)
425               fprintf (stderr, " %d", ltor[i]);
426             fprintf (stderr, "\n");
427
428             if (debug)
429               {
430                 fribidi_set_debug (1);
431
432                 if (fribidi_get_par_embedding_levels_ex (types,
433                                                          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   if (numerrs)
448     fprintf (stderr, "%d errors\n", numerrs);
449   else
450     printf("No errors found! :-)\n");
451
452   free (code_points);
453   free (expected_levels);
454   free (expected_ltor);
455   free (bracket_types);
456   free (types);
457   free (levels);
458   free (ltor);
459
460   return numerrs;
461 }