1/*
2 *******************************************************************************
3 *
4 *   Copyright (C) 1999-2013, International Business Machines
5 *   Corporation and others.  All Rights Reserved.
6 *
7 *******************************************************************************
8 */
9
10#include "unicode/utypes.h"
11#include "unicode/uclean.h"
12#include "unicode/uchar.h"
13#include "unicode/unistr.h"
14#include "unicode/uscript.h"
15#include "unicode/putil.h"
16#include "unicode/ctest.h"
17
18#include "layout/LETypes.h"
19#include "layout/LEScripts.h"
20
21#include "letsutil.h"
22#include "letest.h"
23
24#include "xmlreader.h"
25
26#include "xmlparser.h"
27
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31
32//U_NAMESPACE_USE
33
34#define CH_COMMA 0x002C
35
36static le_uint32 *getHexArray(const UnicodeString &numbers, int32_t &arraySize)
37{
38    int32_t offset = -1;
39
40    arraySize = 1;
41    while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
42        arraySize += 1;
43    }
44
45    le_uint32 *array = NEW_ARRAY(le_uint32, arraySize);
46    char number[16];
47    le_int32 count = 0;
48    le_int32 start = 0, end = 0;
49    le_int32 len = 0;
50
51    // trim leading whitespace
52    while(u_isUWhiteSpace(numbers[start])) {
53        start += 1;
54    }
55
56    while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
57        len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
58        number[len] = '\0';
59        start = end + 1;
60
61        sscanf(number, "%x", &array[count++]);
62
63        // trim whitespace following the comma
64        while(u_isUWhiteSpace(numbers[start])) {
65            start += 1;
66        }
67    }
68
69    // trim trailing whitespace
70    end = numbers.length();
71    while(u_isUWhiteSpace(numbers[end - 1])) {
72        end -= 1;
73    }
74
75    len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
76    number[len] = '\0';
77    sscanf(number, "%x", &array[count]);
78
79    return array;
80}
81
82static float *getFloatArray(const UnicodeString &numbers, int32_t &arraySize)
83{
84    int32_t offset = -1;
85
86    arraySize = 1;
87    while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
88        arraySize += 1;
89    }
90
91    float *array = NEW_ARRAY(float, arraySize);
92    char number[32];
93    le_int32 count = 0;
94    le_int32 start = 0, end = 0;
95    le_int32 len = 0;
96
97    // trim leading whitespace
98    while(u_isUWhiteSpace(numbers[start])) {
99        start += 1;
100    }
101
102    while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
103        len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
104        number[len] = '\0';
105        start = end + 1;
106
107        sscanf(number, "%f", &array[count++]);
108
109        // trim whiteapce following the comma
110        while(u_isUWhiteSpace(numbers[start])) {
111            start += 1;
112        }
113    }
114
115    while(u_isUWhiteSpace(numbers[start])) {
116        start += 1;
117    }
118
119    // trim trailing whitespace
120    end = numbers.length();
121    while(u_isUWhiteSpace(numbers[end - 1])) {
122        end -= 1;
123    }
124
125    len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
126    number[len] = '\0';
127    sscanf(number, "%f", &array[count]);
128
129    return array;
130}
131
132U_CDECL_BEGIN
133void readTestFile(const char *testFilePath, TestCaseCallback callback)
134{
135#if !UCONFIG_NO_REGULAR_EXPRESSIONS
136    UErrorCode status = U_ZERO_ERROR;
137    UXMLParser  *parser = UXMLParser::createParser(status);
138    UXMLElement *root   = parser->parseFile(testFilePath, status);
139
140    if (root == NULL) {
141        log_err("Could not open the test data file: %s\n", testFilePath);
142        delete parser;
143        return;
144    }
145
146    UnicodeString test_case        = UNICODE_STRING_SIMPLE("test-case");
147    UnicodeString test_text        = UNICODE_STRING_SIMPLE("test-text");
148    UnicodeString test_font        = UNICODE_STRING_SIMPLE("test-font");
149    UnicodeString result_glyphs    = UNICODE_STRING_SIMPLE("result-glyphs");
150    UnicodeString result_indices   = UNICODE_STRING_SIMPLE("result-indices");
151    UnicodeString result_positions = UNICODE_STRING_SIMPLE("result-positions");
152
153    // test-case attributes
154    UnicodeString id_attr     = UNICODE_STRING_SIMPLE("id");
155    UnicodeString script_attr = UNICODE_STRING_SIMPLE("script");
156    UnicodeString lang_attr   = UNICODE_STRING_SIMPLE("lang");
157
158    // test-font attributes
159    UnicodeString name_attr   = UNICODE_STRING_SIMPLE("name");
160    UnicodeString ver_attr    = UNICODE_STRING_SIMPLE("version");
161    UnicodeString cksum_attr  = UNICODE_STRING_SIMPLE("checksum");
162
163    const UXMLElement *testCase;
164    int32_t tc = 0;
165
166    while((testCase = root->nextChildElement(tc)) != NULL) {
167        if (testCase->getTagName().compare(test_case) == 0) {
168            char *id = getCString(testCase->getAttribute(id_attr));
169            char *script    = getCString(testCase->getAttribute(script_attr));
170            char *lang      = getCString(testCase->getAttribute(lang_attr));
171            char *fontName  = NULL;
172			char *fontVer   = NULL;
173			char *fontCksum = NULL;
174            const UXMLElement *element;
175            int32_t ec = 0;
176            int32_t charCount = 0;
177            int32_t typoFlags = 3; // kerning + ligatures...
178            UScriptCode scriptCode;
179            le_int32 languageCode = -1;
180            UnicodeString text, glyphs, indices, positions;
181            int32_t glyphCount = 0, indexCount = 0, positionCount = 0;
182            TestResult expected = {0, NULL, NULL, NULL};
183
184            uscript_getCode(script, &scriptCode, 1, &status);
185            if (LE_FAILURE(status)) {
186                log_err("invalid script name: %s.\n", script);
187                goto free_c_strings;
188            }
189
190            if (lang != NULL) {
191                languageCode = getLanguageCode(lang);
192
193                if (languageCode < 0) {
194                    log_err("invalid language name: %s.\n", lang);
195                    goto free_c_strings;
196                }
197            }
198
199            while((element = testCase->nextChildElement(ec)) != NULL) {
200                UnicodeString tag = element->getTagName();
201
202                // TODO: make sure that each element is only used once.
203                if (tag.compare(test_font) == 0) {
204                    fontName  = getCString(element->getAttribute(name_attr));
205                    fontVer   = getCString(element->getAttribute(ver_attr));
206                    fontCksum = getCString(element->getAttribute(cksum_attr));
207
208                } else if (tag.compare(test_text) == 0) {
209                    text = element->getText(TRUE);
210                    charCount = text.length();
211                } else if (tag.compare(result_glyphs) == 0) {
212                    glyphs = element->getText(TRUE);
213                } else if (tag.compare(result_indices) == 0) {
214                    indices = element->getText(TRUE);
215                } else if (tag.compare(result_positions) == 0) {
216                    positions = element->getText(TRUE);
217                } else {
218                    // an unknown tag...
219                    char *cTag = getCString(&tag);
220
221                    log_info("Test %s: unknown element with tag \"%s\"\n", id, cTag);
222                    freeCString(cTag);
223                }
224            }
225
226            expected.glyphs    = (LEGlyphID *) getHexArray(glyphs, glyphCount);
227            expected.indices   = (le_int32 *)  getHexArray(indices, indexCount);
228            expected.positions = getFloatArray(positions, positionCount);
229
230            expected.glyphCount = glyphCount;
231
232            if (glyphCount < charCount || indexCount != glyphCount || positionCount < glyphCount * 2 + 2) {
233                log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n",
234                    id, charCount, glyphCount, indexCount, positionCount);
235                goto free_expected;
236            };
237
238			(*callback)(id, fontName, fontVer, fontCksum, scriptCode, languageCode, text.getBuffer(), charCount, &expected);
239
240free_expected:
241            DELETE_ARRAY(expected.positions);
242            DELETE_ARRAY(expected.indices);
243            DELETE_ARRAY(expected.glyphs);
244
245free_c_strings:
246			freeCString(fontCksum);
247			freeCString(fontVer);
248			freeCString(fontName);
249            freeCString(lang);
250            freeCString(script);
251            freeCString(id);
252        }
253    }
254
255    delete root;
256    delete parser;
257#endif
258}
259U_CDECL_END
260