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 int len;
293 fgets(line, LINE_SIZE, channel);
294 len = strlen(line);
295 if (len == LINE_SIZE-1)
296 die("LINE_SIZE=%d too small at line %d!\n", LINE_SIZE, line_no);
297
298 line_no++;
299
300 if (line[0] == '#' || line[0] == '\n')
301 continue;
302
303 free (code_points);
304 free (expected_levels);
305 free (expected_ltor);
306 free (bracket_types);
307 free (types);
308 free (levels);
309 free (ltor);
310
311 parse_test_line (line,
312 line_no,
313 &code_points, /* Field 0 */
314 &code_points_len,
315 ¶graph_dir, /* Field 1 */
316 &resolved_paragraph_embedding_level, /* Field 2 */
317 &expected_levels, /* Field 3 */
318 &expected_ltor, /* Field 4 */
319 &expected_ltor_len
320 );
321
322 /* Test it */
323 bracket_types = malloc ( sizeof(FriBidiBracketType) * code_points_len);
324 types = malloc ( sizeof(FriBidiCharType) * code_points_len);
325 levels = malloc (sizeof (FriBidiLevel) * code_points_len);
326 ltor = malloc (sizeof (FriBidiStrIndex) * code_points_len);
327
328
329 {
330 FriBidiParType base_dir;
331 int i, j;
332 int matches;
333 int types_len = code_points_len;
334 int levels_len = types_len;
335 FriBidiBracketType NoBracket = FRIBIDI_NO_BRACKET;
336
337 for (i=0; i<code_points_len; i++)
338 {
339 types[i] = fribidi_get_bidi_type(code_points[i]);
340
341 /* Note the optimization that a bracket is always
342 of type neutral */
343 if (types[i] == FRIBIDI_TYPE_ON)
344 bracket_types[i] = fribidi_get_bracket(code_points[i]);
345 else
346 bracket_types[i] = NoBracket;
347 }
348
349 switch (paragraph_dir)
350 {
351 case 0: base_dir = FRIBIDI_PAR_LTR; break;
352 case 1: base_dir = FRIBIDI_PAR_RTL; break;
353 case 2: base_dir = FRIBIDI_PAR_ON; break;
354 }
355
356 if (fribidi_get_par_embedding_levels_ex (types,
357 bracket_types,
358 types_len,
359 &base_dir,
360 levels))
361 {}
362
363 for (i = 0; i < types_len; i++)
364 ltor[i] = i;
365
366 if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/,
367 types, types_len,
368 0, base_dir,
369 levels,
370 NULL,
371 ltor))
372 {}
373
374 j = 0;
375 for (i = 0; i < types_len; i++)
376 if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]]))
377 ltor[j++] = ltor[i];
378 ltor_len = j;
379
380 /* Compare */
381 matches = TRUE;
382 if (matches)
383 for (i = 0; i < code_points_len; i++)
384 if (levels[i] != expected_levels[i] &&
385 expected_levels[i] != (FriBidiLevel) -1) {
386 matches = FALSE;
387 break;
388 }
389
390 if (ltor_len != expected_ltor_len)
391 matches = FALSE;
392 if (matches)
393 for (i = 0; i < ltor_len; i++)
394 if (ltor[i] != expected_ltor[i]) {
395 matches = FALSE;
396 break;
397 }
398
399 if (!matches)
400 {
401 numerrs++;
402
403 fprintf (stderr, "failure on line %d\n", line_no);
404 fprintf (stderr, "input is: %s\n", line);
405 fprintf (stderr, "base dir: %s\n", paragraph_dir==0 ? "LTR"
406 : paragraph_dir==1 ? "RTL" : "AUTO");
407
408 fprintf (stderr, "expected levels:");
409 for (i = 0; i < code_points_len; i++)
410 if (expected_levels[i] == (FriBidiLevel) -1)
411 fprintf (stderr, " x");
412 else
413 fprintf (stderr, " %d", expected_levels[i]);
414 fprintf (stderr, "\n");
415 fprintf (stderr, "returned levels:");
416 for (i = 0; i < levels_len; i++)
417 fprintf (stderr, " %d", levels[i]);
418 fprintf (stderr, "\n");
419
420 fprintf (stderr, "expected order:");
421 for (i = 0; i < expected_ltor_len; i++)
422 fprintf (stderr, " %d", expected_ltor[i]);
423 fprintf (stderr, "\n");
424 fprintf (stderr, "returned order:");
425 for (i = 0; i < ltor_len; i++)
426 fprintf (stderr, " %d", ltor[i]);
427 fprintf (stderr, "\n");
428
429 if (debug)
430 {
431 fribidi_set_debug (1);
432
433 if (fribidi_get_par_embedding_levels_ex (types,
434 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 if (numerrs)
449 fprintf (stderr, "%d errors\n", numerrs);
450 else
451 printf("No errors found! :-)\n");
452
453 free (code_points);
454 free (expected_levels);
455 free (expected_ltor);
456 free (bracket_types);
457 free (types);
458 free (levels);
459 free (ltor);
460
461 return numerrs;
462 }