diff options
Diffstat (limited to 'test/unicode-conformance/BidiTest.c')
-rw-r--r-- | test/unicode-conformance/BidiTest.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/test/unicode-conformance/BidiTest.c b/test/unicode-conformance/BidiTest.c new file mode 100644 index 0000000..5f931f1 --- /dev/null +++ b/test/unicode-conformance/BidiTest.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2009 Behdad Esfahbod + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library, in a file named COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include <fribidi.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <ctype.h> + +#define TRUE 1 +#define FALSE 0 + +/* Glib array types */ +typedef struct { + int capacity; + int len; + int *data; +} int_array_t; + +typedef struct { + int capacity; + int len; + char *data; +} char_array_t; + +#define LINE_SIZE 2048 /* Size of largest example line in BidiTest */ +#define ARRAY_CHUNK_SIZE 32 +int_array_t *new_int_array() +{ + int_array_t *arr = (int_array_t*)malloc(sizeof(int_array_t)); + arr->len = 0; + arr->capacity = ARRAY_CHUNK_SIZE; + arr->data = (int*)malloc(arr->capacity * sizeof(int)); + + return arr; +} + +void int_array_add(int_array_t *arr, int val) +{ + if (arr->len == arr->capacity) + { + arr->capacity += ARRAY_CHUNK_SIZE; + arr->data = (int*)realloc(arr->data, arr->capacity*sizeof(int)); + } + arr->data[arr->len++] = val; +} + +int *int_array_free(int_array_t *arr, int free_data) +{ + int *data = arr->data; + if (free_data) { + data = NULL; + free(arr->data); + } + free(arr); + return data; +} + +char_array_t *new_char_array() +{ + char_array_t *arr = (char_array_t*)malloc(sizeof(char_array_t)); + arr->len = 0; + arr->capacity = ARRAY_CHUNK_SIZE; + arr->data = (char*)malloc(arr->capacity); + + return arr; +} + +void char_array_add(char_array_t *arr, char val) +{ + if (arr->len == arr->capacity) + { + arr->capacity += ARRAY_CHUNK_SIZE; + arr->data = (char*)realloc(arr->data, arr->capacity * sizeof(char)); + } + arr->data[arr->len++] = val; +} + +char *char_array_free(char_array_t *arr, int free_data) +{ + char *data = arr->data; + if (free_data) { + data = NULL; + free(arr->data); + } + free(arr); + return data; +} + +static void die(const char *fmt, ...) +{ + va_list ap; + va_start(ap,fmt); + + vfprintf(stderr, fmt, ap); + exit(-1); +} + +static FriBidiCharType +parse_char_type (const char *s, int len) +{ +#define MATCH(name, value) \ + if (!strncmp (name, s, len) && name[len] == '\0') return value; + + MATCH ("L", FRIBIDI_TYPE_LTR); + MATCH ("R", FRIBIDI_TYPE_RTL); + MATCH ("AL", FRIBIDI_TYPE_AL); + MATCH ("EN", FRIBIDI_TYPE_EN); + MATCH ("AN", FRIBIDI_TYPE_AN); + MATCH ("ES", FRIBIDI_TYPE_ES); + MATCH ("ET", FRIBIDI_TYPE_ET); + MATCH ("CS", FRIBIDI_TYPE_CS); + MATCH ("NSM", FRIBIDI_TYPE_NSM); + MATCH ("BN", FRIBIDI_TYPE_BN); + MATCH ("B", FRIBIDI_TYPE_BS); + MATCH ("S", FRIBIDI_TYPE_SS); + MATCH ("WS", FRIBIDI_TYPE_WS); + MATCH ("ON", FRIBIDI_TYPE_ON); + MATCH ("LRE", FRIBIDI_TYPE_LRE); + MATCH ("RLE", FRIBIDI_TYPE_RLE); + MATCH ("LRO", FRIBIDI_TYPE_LRO); + MATCH ("RLO", FRIBIDI_TYPE_RLO); + MATCH ("PDF", FRIBIDI_TYPE_PDF); + MATCH ("LRI", FRIBIDI_TYPE_LRI); + MATCH ("RLI", FRIBIDI_TYPE_RLI); + MATCH ("FSI", FRIBIDI_TYPE_FSI); + MATCH ("PDI", FRIBIDI_TYPE_PDI); + + die("Oops. I shouldn't reach here!\n"); + return -1; +} + +static FriBidiLevel * +parse_levels_line (const char *line, + FriBidiLevel *len) +{ + char_array_t *levels; + + if (!strncmp (line, "@Levels:", 8)) + line += 8; + + levels = new_char_array (); + + while (*line) + { + FriBidiLevel l; + char *end; + + errno = 0; + l = strtol (line, &end, 10); + if (errno != EINVAL && line != end) + { + char_array_add (levels, l); + line = end; + continue; + } + + while (isspace (*line)) + line++; + + if (*line == 'x') + { + char_array_add (levels, -1); + line++; + continue; + } + + if (!*line) + break; + + die("Oops. I shouldn't be here!\n"); + } + + *len = levels->len; + return (FriBidiLevel *) char_array_free(levels, FALSE); +} + +static FriBidiStrIndex * +parse_reorder_line (const char *line, + FriBidiStrIndex *len) +{ + int_array_t *map; + FriBidiStrIndex l; + char *end; + + if (!strncmp (line, "@Reorder:", 9)) + line += 9; + + map = new_int_array (); + + for(; errno = 0, l = strtol (line, &end, 10), line != end && errno != EINVAL; line = end) { + int_array_add (map, l); + } + + *len = map->len; + return (FriBidiStrIndex *) int_array_free (map, FALSE); +} + +static FriBidiCharType * +parse_test_line (const char *line, + FriBidiStrIndex *len, + int *base_dir_flags) +{ + int_array_t *types; + FriBidiCharType c; + const char *end; + + types = new_int_array(); + + for(;;) { + while (isspace (*line)) + line++; + end = line; + while (isalpha (*end)) + end++; + if (line == end) + break; + + c = parse_char_type (line, end - line); + int_array_add (types, c); + + line = end; + } + + if (*line == ';') + line++; + *base_dir_flags = strtol (line, NULL, 10); + + *len = types->len; + return (FriBidiCharType *) int_array_free (types, FALSE); +} + +int +main (int argc, char **argv) +{ + FILE *channel; + char line[LINE_SIZE]; + FriBidiStrIndex *expected_ltor = NULL; + FriBidiStrIndex expected_ltor_len = 0; + FriBidiStrIndex *ltor = NULL; + FriBidiStrIndex ltor_len = 0; + FriBidiCharType *types = NULL; + FriBidiStrIndex types_len = 0; + FriBidiLevel *expected_levels = NULL; + FriBidiLevel expected_levels_len = 0; + FriBidiLevel *levels = NULL; + FriBidiStrIndex levels_len = 0; + int base_dir_flags, base_dir_mode; + int numerrs = 0; + int numtests = 0; + int line_no = 0; + int debug = FALSE; + const char *filename; + int next_arg; + + if (argc < 2) + die ("usage: %s [--debug] test-file-name\n", argv[0]); + + next_arg = 1; + if (!strcmp (argv[next_arg], "--debug")) { + debug = TRUE; + next_arg++; + } + + filename = argv[next_arg++]; + + channel = fopen(filename, "r"); + if (!channel) + die ("Failed opening %s\n", filename); + + while (!feof(channel)) { + fgets(line, LINE_SIZE, channel); + int len = strlen(line); + if (len == LINE_SIZE-1) + die("LINE_SIZE too small at line %d!\n", line_no); + + line_no++; + + if (line[0] == '#') + continue; + + if (line[0] == '@') + { + if (!strncmp (line, "@Reorder:", 9)) { + free (expected_ltor); + expected_ltor = parse_reorder_line (line, &expected_ltor_len); + continue; + } + if (!strncmp (line, "@Levels:", 8)) { + free (expected_levels); + expected_levels = parse_levels_line (line, &expected_levels_len); + continue; + } + continue; + } + + /* Test line */ + free (types); + types = parse_test_line (line, &types_len, &base_dir_flags); + + free (levels); + levels = malloc (sizeof (FriBidiLevel) * types_len); + levels_len = types_len; + + free (ltor); + ltor = malloc (sizeof (FriBidiStrIndex) * types_len); + + /* Test it */ + for (base_dir_mode = 0; base_dir_mode < 3; base_dir_mode++) { + FriBidiParType base_dir; + int i, j; + int matches; + + if ((base_dir_flags & (1<<base_dir_mode)) == 0) + continue; + + numtests++; + + switch (base_dir_mode) { + case 0: base_dir = FRIBIDI_PAR_ON; break; + case 1: base_dir = FRIBIDI_PAR_LTR; break; + case 2: base_dir = FRIBIDI_PAR_RTL; break; + } + + if (fribidi_get_par_embedding_levels_ex (types, + NULL, /* Brackets are not used in the BidiTest.txt file */ + types_len, + &base_dir, + levels)) + {} + + for (i = 0; i < types_len; i++) + ltor[i] = i; + + if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/, + types, types_len, + 0, base_dir, + levels, + NULL, + ltor)) + {} + + j = 0; + for (i = 0; i < types_len; i++) + if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]])) + ltor[j++] = ltor[i]; + ltor_len = j; + + /* Compare */ + matches = TRUE; + if (levels_len != expected_levels_len) + matches = FALSE; + if (matches) + for (i = 0; i < levels_len; i++) + if (levels[i] != expected_levels[i] && + expected_levels[i] != (FriBidiLevel) -1) { + matches = FALSE; + break; + } + + if (ltor_len != expected_ltor_len) + matches = FALSE; + if (matches) + for (i = 0; i < ltor_len; i++) + if (ltor[i] != expected_ltor[i]) { + matches = FALSE; + break; + } + + if (!matches) + { + numerrs++; + + fprintf (stderr, "failure on line %d\n", line_no); + fprintf (stderr, "input is: %s\n", line); + fprintf (stderr, "base dir: %s\n", + base_dir_mode==0 ? "auto" + : base_dir_mode==1 ? "LTR" : "RTL"); + + fprintf (stderr, "expected levels:"); + for (i = 0; i < expected_levels_len; i++) + if (expected_levels[i] == (FriBidiLevel) -1) + fprintf (stderr," x"); + else + fprintf (stderr, " %d", expected_levels[i]); + fprintf (stderr, "\n"); + fprintf (stderr, "returned levels:"); + for (i = 0; i < levels_len; i++) + fprintf (stderr, " %d", levels[i]); + fprintf (stderr, "\n"); + + fprintf (stderr, "expected order:"); + for (i = 0; i < expected_ltor_len; i++) + fprintf (stderr, " %d", expected_ltor[i]); + fprintf (stderr, "\n"); + fprintf (stderr, "returned order:"); + for (i = 0; i < ltor_len; i++) + fprintf (stderr, " %d", ltor[i]); + fprintf (stderr, "\n"); + + if (debug) + { + FriBidiParType base_dir; + + fribidi_set_debug (1); + + switch (base_dir_mode) { + case 0: base_dir = FRIBIDI_PAR_ON; break; + case 1: base_dir = FRIBIDI_PAR_LTR; break; + case 2: base_dir = FRIBIDI_PAR_RTL; break; + } + + if (fribidi_get_par_embedding_levels_ex (types, + NULL, /* No bracket types */ + types_len, + &base_dir, + levels)) + {} + + fribidi_set_debug (0); + } + + fprintf (stderr, "\n"); + } + } + } + + free (ltor); + free (levels); + free (expected_ltor); + free (expected_levels); + free (types); + fclose(channel); + + if (numerrs) + fprintf (stderr, "%d errors out of %d total tests\n", numerrs, numtests); + else + printf("No errors found! :-)\n"); + + return numerrs; +} |