version up to 1.0.2
[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 base_dir_mode = 0, paragraph_dir;
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         if ((paragraph_dir & (1<<base_dir_mode)) == 0)
349           continue;
350
351         switch (paragraph_dir)
352           {
353           case 0: base_dir = FRIBIDI_PAR_LTR; break;
354           case 1: base_dir = FRIBIDI_PAR_RTL; break;
355           case 2: base_dir = FRIBIDI_PAR_ON;  break;
356           }
357
358         if (fribidi_get_par_embedding_levels_ex (types,
359                                                  bracket_types,
360                                                  types_len,
361                                                  &base_dir,
362                                                  levels))
363         {}
364
365         for (i = 0; i < types_len; i++)
366           ltor[i] = i;
367
368         if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/,
369                                   types, types_len,
370                                   0, base_dir,
371                                   levels,
372                                   NULL,
373                                   ltor))
374         {}
375
376         j = 0;
377         for (i = 0; i < types_len; i++)
378           if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]]))
379             ltor[j++] = ltor[i];
380         ltor_len = j;
381
382         /* Compare */
383         matches = TRUE;
384         if (matches)
385           for (i = 0; i < code_points_len; i++)
386             if (levels[i] != expected_levels[i] &&
387                 expected_levels[i] != (FriBidiLevel) -1) {
388               matches = FALSE;
389               break;
390             }
391
392         if (ltor_len != expected_ltor_len)
393           matches = FALSE;
394         if (matches)
395           for (i = 0; i < ltor_len; i++)
396             if (ltor[i] != expected_ltor[i]) {
397               matches = FALSE;
398               break;
399             }
400
401         if (!matches)
402           {
403             numerrs++;
404
405             fprintf (stderr, "failure on line %d\n", line_no);
406             fprintf (stderr, "input is: %s\n", line);
407             fprintf (stderr, "base dir: %s\n", paragraph_dir==0 ? "LTR"
408                         : paragraph_dir==1 ? "RTL" : "AUTO");
409
410             fprintf (stderr, "expected levels:");
411             for (i = 0; i < code_points_len; i++)
412               if (expected_levels[i] == (FriBidiLevel) -1)
413                 fprintf (stderr, " x");
414               else
415                 fprintf (stderr, " %d", expected_levels[i]);
416             fprintf (stderr, "\n");
417             fprintf (stderr, "returned levels:");
418             for (i = 0; i < levels_len; i++)
419               fprintf (stderr, " %d", levels[i]);
420             fprintf (stderr, "\n");
421
422             fprintf (stderr, "expected order:");
423             for (i = 0; i < expected_ltor_len; i++)
424               fprintf (stderr, " %d", expected_ltor[i]);
425             fprintf (stderr, "\n");
426             fprintf (stderr, "returned order:");
427             for (i = 0; i < ltor_len; i++)
428               fprintf (stderr, " %d", ltor[i]);
429             fprintf (stderr, "\n");
430
431             if (debug)
432               {
433                 FriBidiParType base_dir;
434
435                 fribidi_set_debug (1);
436
437                 switch (base_dir_mode)
438                   {
439                   case 0: base_dir = FRIBIDI_PAR_ON;  break;
440                   case 1: base_dir = FRIBIDI_PAR_LTR; break;
441                   case 2: base_dir = FRIBIDI_PAR_RTL; break;
442                   }
443
444                 if (fribidi_get_par_embedding_levels_ex (types,
445                                                          bracket_types,
446                                                          types_len,
447                                                          &base_dir,
448                                                          levels))
449                 {}
450
451                 fribidi_set_debug (0);
452               }
453
454             fprintf (stderr, "\n");
455           }
456       }
457     }
458
459   if (numerrs)
460     fprintf (stderr, "%d errors\n", numerrs);
461   else
462     printf("No errors found! :-)\n");
463
464   free (code_points);
465   free (expected_levels);
466   free (expected_ltor);
467   free (bracket_types);
468   free (types);
469   free (levels);
470   free (ltor);
471
472   return numerrs;
473 }