1/*
2 *******************************************************************************
3 *
4 *   Copyright (C) 1999-2014, International Business Machines
5 *   Corporation and others.  All Rights Reserved.
6 *
7 *******************************************************************************
8 *   file name:  letest.cpp
9 *
10 *   created on: 11/06/2000
11 *   created by: Eric R. Mader
12 */
13
14#include "unicode/utypes.h"
15#include "unicode/uclean.h"
16#include "unicode/uchar.h"
17#include "unicode/unistr.h"
18#include "unicode/uscript.h"
19#include "unicode/putil.h"
20#include "unicode/ctest.h"
21
22#include "layout/LETypes.h"
23#include "layout/LEScripts.h"
24#include "layout/LayoutEngine.h"
25
26#include "layout/ParagraphLayout.h"
27#include "layout/RunArrays.h"
28
29#include "PortableFontInstance.h"
30#include "SimpleFontInstance.h"
31
32#include "letsutil.h"
33#include "letest.h"
34
35#include "xmlparser.h"
36#include "putilimp.h" // for uprv_getUTCtime()
37
38#include <stdlib.h>
39#include <string.h>
40
41U_NAMESPACE_USE
42
43#define CH_COMMA 0x002C
44
45U_CDECL_BEGIN
46
47static void U_CALLCONV ScriptTest(void)
48{
49    if ((int)scriptCodeCount != (int)USCRIPT_CODE_LIMIT) {
50        log_err("ScriptCodes::scriptCodeCount = %d, but UScriptCode::USCRIPT_CODE_LIMIT = %d\n", scriptCodeCount, USCRIPT_CODE_LIMIT);
51    }
52}
53
54static void U_CALLCONV ParamTest(void)
55{
56    LEErrorCode status = LE_NO_ERROR;
57    SimpleFontInstance *font = new SimpleFontInstance(12, status);
58    LayoutEngine *engine = LayoutEngine::layoutEngineFactory(font, arabScriptCode, -1, status);
59    LEGlyphID *glyphs    = NULL;
60    le_int32  *indices   = NULL;
61    float     *positions = NULL;
62    le_int32   glyphCount = 0;
63
64    glyphCount = engine->getGlyphCount();
65    if (glyphCount != 0) {
66        log_err("Calling getGlyphCount() on an empty layout returned %d.\n", glyphCount);
67    }
68
69    glyphs    = NEW_ARRAY(LEGlyphID, glyphCount + 10);
70    indices   = NEW_ARRAY(le_int32, glyphCount + 10);
71    positions = NEW_ARRAY(float, glyphCount + 10);
72
73    engine->getGlyphs(NULL, status);
74
75    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
76        log_err("Calling getGlyphs(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
77    }
78
79    status = LE_NO_ERROR;
80    engine->getGlyphs(glyphs, status);
81
82    if (status != LE_NO_LAYOUT_ERROR) {
83        log_err("Calling getGlyphs(glyphs, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
84    }
85
86    status = LE_NO_ERROR;
87    engine->getGlyphs(NULL, 0xFF000000L, status);
88
89    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
90        log_err("Calling getGlyphs(NULL, 0xFF000000L, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
91    }
92
93    status = LE_NO_ERROR;
94    engine->getGlyphs(glyphs, 0xFF000000L, status);
95
96    if (status != LE_NO_LAYOUT_ERROR) {
97        log_err("Calling getGlyphs(glyphs, 0xFF000000L, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
98    }
99
100    status = LE_NO_ERROR;
101    engine->getCharIndices(NULL, status);
102
103    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
104        log_err("Calling getCharIndices(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
105    }
106
107    status = LE_NO_ERROR;
108    engine->getCharIndices(indices, status);
109
110    if (status != LE_NO_LAYOUT_ERROR) {
111        log_err("Calling getCharIndices(indices, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
112    }
113
114    status = LE_NO_ERROR;
115    engine->getCharIndices(NULL, 1024, status);
116
117    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
118        log_err("Calling getCharIndices(NULL, 1024, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
119    }
120
121    status = LE_NO_ERROR;
122    engine->getCharIndices(indices, 1024, status);
123
124    if (status != LE_NO_LAYOUT_ERROR) {
125        log_err("Calling getCharIndices(indices, 1024, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
126    }
127
128    status = LE_NO_ERROR;
129    engine->getGlyphPositions(NULL, status);
130
131    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
132        log_err("Calling getGlyphPositions(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
133    }
134
135    status = LE_NO_ERROR;
136    engine->getGlyphPositions(positions, status);
137
138    if (status != LE_NO_LAYOUT_ERROR) {
139        log_err("Calling getGlyphPositions(positions, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
140    }
141
142    DELETE_ARRAY(positions);
143    DELETE_ARRAY(indices);
144    DELETE_ARRAY(glyphs);
145
146    status = LE_NO_ERROR;
147    glyphCount = engine->layoutChars(NULL, 0, 0, 0, FALSE, 0.0, 0.0, status);
148
149    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
150        log_err("Calling layoutChars(NULL, 0, 0, 0, FALSE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
151    }
152
153    LEUnicode chars[] = {
154        0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068, 0x0020, // "English "
155        0x0645, 0x0627, 0x0646, 0x062A, 0x0648, 0x0634,                 // MEM ALIF KAF NOON TEH WAW SHEEN
156        0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x02E                   // " text."
157    };
158
159    status = LE_NO_ERROR;
160    glyphCount = engine->layoutChars(chars, -1, 6, 20, TRUE, 0.0, 0.0, status);
161
162    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
163        log_err("Calling layoutChars(chars, -1, 6, 20, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
164    }
165
166    status = LE_NO_ERROR;
167    glyphCount = engine->layoutChars(chars, 8, -1, 20, TRUE, 0.0, 0.0, status);
168
169    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
170        log_err("Calling layoutChars(chars, 8, -1, 20, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
171    }
172
173    status = LE_NO_ERROR;
174    glyphCount = engine->layoutChars(chars, 8, 6, -1, TRUE, 0.0, 0.0, status);
175
176    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
177        log_err("Calling layoutChars((chars, 8, 6, -1, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
178    }
179
180    status = LE_NO_ERROR;
181    glyphCount = engine->layoutChars(chars, 8, 6, 10, TRUE, 0.0, 0.0, status);
182
183    if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
184        log_err("Calling layoutChars(chars, 8, 6, 10, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
185    }
186
187    float x = 0.0, y = 0.0;
188
189    status = LE_NO_ERROR;
190    glyphCount = engine->layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status);
191
192    if (LE_FAILURE(status)) {
193        log_err("Calling layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
194        goto bail;
195    }
196
197    engine->getGlyphPosition(-1, x, y, status);
198
199    if (status != LE_INDEX_OUT_OF_BOUNDS_ERROR) {
200        log_err("Calling getGlyphPosition(-1, x, y, status) did not fail w/ LE_INDEX_OUT_OF_BOUNDS_ERROR.\n");
201    }
202
203    status = LE_NO_ERROR;
204    engine->getGlyphPosition(glyphCount + 1, x, y, status);
205
206    if (status != LE_INDEX_OUT_OF_BOUNDS_ERROR) {
207        log_err("Calling getGlyphPosition(glyphCount + 1, x, y, status) did not fail w/ LE_INDEX_OUT_OF_BOUNDS_ERROR.\n");
208    }
209
210bail:
211    delete engine;
212    delete font;
213}
214U_CDECL_END
215
216U_CDECL_BEGIN
217static void U_CALLCONV FactoryTest(void)
218{
219    LEErrorCode status = LE_NO_ERROR;
220    SimpleFontInstance *font = new SimpleFontInstance(12, status);
221    LayoutEngine *engine = NULL;
222
223    for(le_int32 scriptCode = 0; scriptCode < scriptCodeCount; scriptCode += 1) {
224        status = LE_NO_ERROR;
225        engine = LayoutEngine::layoutEngineFactory(font, scriptCode, -1, status);
226
227        if (LE_FAILURE(status)) {
228            log_err("Could not create a LayoutEngine for script \'%s\'.\n", uscript_getShortName((UScriptCode)scriptCode));
229        }
230
231        delete engine;
232    }
233
234    delete font;
235}
236U_CDECL_END
237
238U_CDECL_BEGIN
239static void U_CALLCONV AccessTest(void)
240{
241    LEErrorCode status = LE_NO_ERROR;
242    SimpleFontInstance *font = new SimpleFontInstance(12, status);
243    LayoutEngine *engine = LayoutEngine::layoutEngineFactory(font, arabScriptCode, -1, status);
244    le_int32 glyphCount;
245    LEGlyphID glyphs[6], extraBitGlyphs[6];;
246    le_int32 biasedIndices[6], indices[6], glyph;
247    float positions[6 * 2 + 2];
248    LEUnicode chars[] = {
249        0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068, 0x0020, // "English "
250        0x0645, 0x0627, 0x0646, 0x062A, 0x0648, 0x0634,                 // MEM ALIF KAF NOON TEH WAW SHEEN
251        0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x02E                   // " text."
252    };
253
254    if (LE_FAILURE(status)) {
255        log_err("Could not create LayoutEngine.\n");
256        goto bail;
257    }
258
259    glyphCount = engine->layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status);
260
261    if (LE_FAILURE(status) || glyphCount != 6) {
262        log_err("layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
263        goto bail;
264    }
265
266    engine->getGlyphs(glyphs, status);
267    engine->getCharIndices(indices, status);
268    engine->getGlyphPositions(positions, status);
269
270    if (LE_FAILURE(status)) {
271        log_err("Could not get glyph, indices and position arrays.\n");
272        goto bail;
273    }
274
275    engine->getGlyphs(extraBitGlyphs, 0xFF000000L, status);
276
277    if (LE_FAILURE(status)) {
278        log_err("getGlyphs(extraBitGlyphs, 0xFF000000L, status); failed.\n");
279    } else {
280        for(glyph = 0; glyph < glyphCount; glyph += 1) {
281            if (extraBitGlyphs[glyph] != (glyphs[glyph] | 0xFF000000L)) {
282                log_err("extraBigGlyphs[%d] != glyphs[%d] | 0xFF000000L: %8X, %8X\n",
283                    glyph, glyph, extraBitGlyphs[glyph], glyphs[glyph]);
284                break;
285            }
286        }
287    }
288
289    status = LE_NO_ERROR;
290    engine->getCharIndices(biasedIndices, 1024, status);
291
292    if (LE_FAILURE(status)) {
293        log_err("getCharIndices(biasedIndices, 1024, status) failed.\n");
294    } else {
295        for (glyph = 0; glyph < glyphCount; glyph += 1) {
296            if (biasedIndices[glyph] != (indices[glyph] + 1024)) {
297                log_err("biasedIndices[%d] != indices[%d] + 1024: %8X, %8X\n",
298                    glyph, glyph, biasedIndices[glyph], indices[glyph]);
299                break;
300            }
301        }
302    }
303
304    status = LE_NO_ERROR;
305    for (glyph = 0; glyph <= glyphCount; glyph += 1) {
306        float x = 0.0, y = 0.0;
307
308        engine->getGlyphPosition(glyph, x, y, status);
309
310        if (LE_FAILURE(status)) {
311            log_err("getGlyphPosition(%d, x, y, status) failed.\n", glyph);
312            break;
313        }
314
315        if (x != positions[glyph*2] || y != positions[glyph*2 + 1]) {
316            log_err("getGlyphPosition(%d, x, y, status) returned bad position: (%f, %f) != (%f, %f)\n",
317                glyph, x, y, positions[glyph*2], positions[glyph*2 + 1]);
318            break;
319        }
320    }
321
322bail:
323    delete engine;
324    delete font;
325}
326U_CDECL_END
327
328le_bool compareResults(const char *testID, TestResult *expected, TestResult *actual)
329{
330    /* NOTE: we'll stop on the first failure 'cause once there's one error, it may cascade... */
331    if (actual->glyphCount != expected->glyphCount) {
332        log_err("Test %s: incorrect glyph count: exptected %d, got %d\n",
333            testID, expected->glyphCount, actual->glyphCount);
334        return FALSE;
335    }
336
337    le_int32 i;
338
339    for (i = 0; i < actual->glyphCount; i += 1) {
340        if (actual->glyphs[i] != expected->glyphs[i]) {
341            log_err("Test %s: incorrect id for glyph %d: expected %4X, got %4X\n",
342                testID, i, expected->glyphs[i], actual->glyphs[i]);
343            return FALSE;
344        }
345    }
346
347    for (i = 0; i < actual->glyphCount; i += 1) {
348        if (actual->indices[i] != expected->indices[i]) {
349            log_err("Test %s: incorrect index for glyph %d: expected %8X, got %8X\n",
350                testID, i, expected->indices[i], actual->indices[i]);
351            return FALSE;
352        }
353    }
354
355    for (i = 0; i <= actual->glyphCount; i += 1) {
356        double xError = uprv_fabs(actual->positions[i * 2] - expected->positions[i * 2]);
357
358        if (xError > 0.0001) {
359            log_err("Test %s: incorrect x position for glyph %d: expected %f, got %f\n",
360                testID, i, expected->positions[i * 2], actual->positions[i * 2]);
361            return FALSE;
362        }
363
364        double yError = uprv_fabs(actual->positions[i * 2 + 1] - expected->positions[i * 2 + 1]);
365
366        if (yError < 0) {
367            yError = -yError;
368        }
369
370        if (yError > 0.0001) {
371            log_err("Test %s: incorrect y position for glyph %d: expected %f, got %f\n",
372                testID, i, expected->positions[i * 2 + 1], actual->positions[i * 2 + 1]);
373            return FALSE;
374        }
375    }
376
377    return TRUE;
378}
379
380static void checkFontVersion(PortableFontInstance *fontInstance, const char *testVersionString,
381                             le_uint32 testChecksum, const char *testID)
382{
383    le_uint32 fontChecksum = fontInstance->getFontChecksum();
384
385    if (fontChecksum != testChecksum) {
386        const char *fontVersionString = fontInstance->getNameString(NAME_VERSION_STRING,
387            PLATFORM_MACINTOSH, MACINTOSH_ROMAN, MACINTOSH_ENGLISH);
388        const LEUnicode *uFontVersionString = NULL;
389
390            // The standard recommends that the Macintosh Roman/English name string be present, but
391            // if it's not, try the Microsoft Unicode/English string.
392            if (fontVersionString == NULL) {
393                uFontVersionString = fontInstance->getUnicodeNameString(NAME_VERSION_STRING,
394                    PLATFORM_MICROSOFT, MICROSOFT_UNICODE_BMP, MICROSOFT_ENGLISH);
395            }
396
397        log_info("Test %s: this may not be the same font used to generate the test data.\n", testID);
398
399        if (uFontVersionString != NULL) {
400            log_info("Your font's version string is \"%S\"\n", uFontVersionString);
401            fontInstance->deleteNameString(uFontVersionString);
402        } else {
403            log_info("Your font's version string is \"%s\"\n", fontVersionString);
404            fontInstance->deleteNameString(fontVersionString);
405        }
406
407        log_info("The expected version string is \"%s\"\n", testVersionString);
408        log_info("If you see errors, they may be due to the version of the font you're using.\n");
409    }
410}
411
412/* Returns the path to icu/source/test/testdata/ */
413const char *getSourceTestData() {
414    const char *srcDataDir = NULL;
415#ifdef U_TOPSRCDIR
416    srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING;
417#else
418    srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
419    FILE *f = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"rbbitst.txt", "r");
420
421    if (f != NULL) {
422        /* We're in icu/source/test/letest/ */
423        fclose(f);
424    } else {
425        /* We're in icu/source/test/letest/(Debug|Release) */
426        srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
427    }
428#endif
429
430    return srcDataDir;
431}
432
433const char *getPath(char buffer[2048], const char *filename) {
434    const char *testDataDirectory = getSourceTestData();
435
436    strcpy(buffer, testDataDirectory);
437    strcat(buffer, filename);
438
439    return buffer;
440}
441
442le_uint32 *getHexArray(const UnicodeString &numbers, int32_t &arraySize)
443{
444    int32_t offset = -1;
445
446    arraySize = 1;
447    while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
448        arraySize += 1;
449    }
450
451    le_uint32 *array = NEW_ARRAY(le_uint32, arraySize);
452    char number[16];
453    le_int32 count = 0;
454    le_int32 start = 0, end = 0;
455    le_int32 len = 0;
456
457    // trim leading whitespace
458    while(u_isUWhiteSpace(numbers[start])) {
459        start += 1;
460    }
461
462    while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
463        len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
464        number[len] = '\0';
465        start = end + 1;
466
467        sscanf(number, "%x", &array[count++]);
468
469        // trim whitespace following the comma
470        while(u_isUWhiteSpace(numbers[start])) {
471            start += 1;
472        }
473    }
474
475    // trim trailing whitespace
476    end = numbers.length();
477    while(u_isUWhiteSpace(numbers[end - 1])) {
478        end -= 1;
479    }
480
481    len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
482    number[len] = '\0';
483    sscanf(number, "%x", &array[count]);
484
485    return array;
486}
487
488float *getFloatArray(const UnicodeString &numbers, int32_t &arraySize)
489{
490    int32_t offset = -1;
491
492    arraySize = 1;
493    while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
494        arraySize += 1;
495    }
496
497    float *array = NEW_ARRAY(float, arraySize);
498    char number[32];
499    le_int32 count = 0;
500    le_int32 start = 0, end = 0;
501    le_int32 len = 0;
502
503    // trim leading whitespace
504    while(u_isUWhiteSpace(numbers[start])) {
505        start += 1;
506    }
507
508    while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
509        len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
510        number[len] = '\0';
511        start = end + 1;
512
513        sscanf(number, "%f", &array[count++]);
514
515        // trim whiteapce following the comma
516        while(u_isUWhiteSpace(numbers[start])) {
517            start += 1;
518        }
519    }
520
521    while(u_isUWhiteSpace(numbers[start])) {
522        start += 1;
523    }
524
525    // trim trailing whitespace
526    end = numbers.length();
527    while(u_isUWhiteSpace(numbers[end - 1])) {
528        end -= 1;
529    }
530
531    len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
532    number[len] = '\0';
533    sscanf(number, "%f", &array[count]);
534
535    return array;
536}
537
538LEFontInstance *openFont(const char *fontName, const char *checksum, const char *version, const char *testID)
539{
540    char path[2048];
541    PortableFontInstance *font;
542    LEErrorCode fontStatus = LE_NO_ERROR;
543
544
545    font = new PortableFontInstance(getPath(path, fontName), 12, fontStatus);
546
547    if (LE_FAILURE(fontStatus)) {
548        log_info("Test %s: can't open font %s - test skipped.\n", testID, fontName);
549        delete font;
550        return NULL;
551    } else {
552        le_uint32 cksum = 0;
553
554        sscanf(checksum, "%x", &cksum);
555
556        checkFontVersion(font, version, cksum, testID);
557    }
558
559    return font;
560}
561
562U_CDECL_BEGIN
563static void U_CALLCONV DataDrivenTest(void)
564{
565#if !UCONFIG_NO_REGULAR_EXPRESSIONS
566    UErrorCode status = U_ZERO_ERROR;
567    char path[2048];
568    const char *testFilePath = getPath(path, "letest.xml");
569
570    UXMLParser  *parser = UXMLParser::createParser(status);
571    UXMLElement *root   = parser->parseFile(testFilePath, status);
572
573    if (root == NULL) {
574        log_err("Could not open the test data file: %s\n", testFilePath);
575        delete parser;
576        return;
577    }
578
579    UnicodeString test_case        = UNICODE_STRING_SIMPLE("test-case");
580    UnicodeString test_text        = UNICODE_STRING_SIMPLE("test-text");
581    UnicodeString test_font        = UNICODE_STRING_SIMPLE("test-font");
582    UnicodeString result_glyphs    = UNICODE_STRING_SIMPLE("result-glyphs");
583    UnicodeString result_indices   = UNICODE_STRING_SIMPLE("result-indices");
584    UnicodeString result_positions = UNICODE_STRING_SIMPLE("result-positions");
585
586    // test-case attributes
587    UnicodeString id_attr     = UNICODE_STRING_SIMPLE("id");
588    UnicodeString script_attr = UNICODE_STRING_SIMPLE("script");
589    UnicodeString lang_attr   = UNICODE_STRING_SIMPLE("lang");
590
591    // test-font attributes
592    UnicodeString name_attr   = UNICODE_STRING_SIMPLE("name");
593    UnicodeString ver_attr    = UNICODE_STRING_SIMPLE("version");
594    UnicodeString cksum_attr  = UNICODE_STRING_SIMPLE("checksum");
595
596    const UXMLElement *testCase;
597    int32_t tc = 0;
598
599    while((testCase = root->nextChildElement(tc)) != NULL) {
600        if (testCase->getTagName().compare(test_case) == 0) {
601            char *id = getCString(testCase->getAttribute(id_attr));
602            char *script = getCString(testCase->getAttribute(script_attr));
603            char *lang   = getCString(testCase->getAttribute(lang_attr));
604            LEFontInstance *font = NULL;
605            const UXMLElement *element;
606            int32_t ec = 0;
607            int32_t charCount = 0;
608            int32_t typoFlags = 3; // kerning + ligatures...
609            UScriptCode scriptCode;
610            le_int32 languageCode = -1;
611            UnicodeString text, glyphs, indices, positions;
612            int32_t glyphCount = 0, indexCount = 0, positionCount = 0;
613            TestResult expected = {0, NULL, NULL, NULL};
614            TestResult actual   = {0, NULL, NULL, NULL};
615            LEErrorCode success = LE_NO_ERROR;
616            LayoutEngine *engine = NULL;
617
618            uscript_getCode(script, &scriptCode, 1, &status);
619            if (LE_FAILURE(status)) {
620                log_err("invalid script name: %s.\n", script);
621                goto free_c_strings;
622            }
623
624            if (lang != NULL) {
625                languageCode = getLanguageCode(lang);
626
627                if (languageCode < 0) {
628                    log_err("invalid language name: %s.\n", lang);
629                    goto free_c_strings;
630                }
631            }
632
633            while((element = testCase->nextChildElement(ec)) != NULL) {
634                UnicodeString tag = element->getTagName();
635
636                // TODO: make sure that each element is only used once.
637                if (tag.compare(test_font) == 0) {
638                    char *fontName  = getCString(element->getAttribute(name_attr));
639                    char *fontVer   = getCString(element->getAttribute(ver_attr));
640                    char *fontCksum = getCString(element->getAttribute(cksum_attr));
641
642                    font = openFont(fontName, fontCksum, fontVer, id);
643                    freeCString(fontCksum);
644                    freeCString(fontVer);
645                    freeCString(fontName);
646
647                    if (font == NULL) {
648                        // warning message already displayed...
649                        goto free_c_strings;
650                    }
651                } else if (tag.compare(test_text) == 0) {
652                    text = element->getText(TRUE);
653                    charCount = text.length();
654                } else if (tag.compare(result_glyphs) == 0) {
655                    glyphs = element->getText(TRUE);
656                } else if (tag.compare(result_indices) == 0) {
657                    indices = element->getText(TRUE);
658                } else if (tag.compare(result_positions) == 0) {
659                    positions = element->getText(TRUE);
660                } else {
661                    // an unknown tag...
662                    char *cTag = getCString(&tag);
663
664                    log_info("Test %s: unknown element with tag \"%s\"\n", id, cTag);
665                    freeCString(cTag);
666                }
667            }
668
669            // TODO: make sure that the font, test-text, result-glyphs, result-indices and result-positions
670            // have all been provided
671            if (font == NULL) {
672                LEErrorCode fontStatus = LE_NO_ERROR;
673
674                font = new SimpleFontInstance(12, fontStatus);
675                typoFlags |= 0x80000000L;  // use CharSubstitutionFilter...
676            }
677
678            expected.glyphs    = (LEGlyphID *) getHexArray(glyphs, glyphCount);
679            expected.indices   = (le_int32 *)  getHexArray(indices, indexCount);
680            expected.positions = getFloatArray(positions, positionCount);
681
682            expected.glyphCount = glyphCount;
683
684            if (glyphCount < charCount || indexCount != glyphCount || positionCount < glyphCount * 2 + 2) {
685                log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n",
686                    id, charCount, glyphCount, indexCount, positionCount);
687                goto free_expected;
688            };
689
690            engine = LayoutEngine::layoutEngineFactory(font, scriptCode, languageCode, typoFlags, success);
691
692            if (LE_FAILURE(success)) {
693                log_err("Test %s: could not create a LayoutEngine.\n", id);
694                goto free_expected;
695            }
696
697            actual.glyphCount = engine->layoutChars(text.getBuffer(), 0, charCount, charCount, getRTL(text), 0, 0, success);
698
699            actual.glyphs    = NEW_ARRAY(LEGlyphID, actual.glyphCount);
700            actual.indices   = NEW_ARRAY(le_int32, actual.glyphCount);
701            actual.positions = NEW_ARRAY(float, actual.glyphCount * 2 + 2);
702
703            engine->getGlyphs(actual.glyphs, success);
704            engine->getCharIndices(actual.indices, success);
705            engine->getGlyphPositions(actual.positions, success);
706
707            compareResults(id, &expected, &actual);
708
709            DELETE_ARRAY(actual.positions);
710            DELETE_ARRAY(actual.indices);
711            DELETE_ARRAY(actual.glyphs);
712
713            delete engine;
714
715            log_verbose("OK - %4d glyphs: %s\n", actual.glyphCount, id);
716free_expected:
717            DELETE_ARRAY(expected.positions);
718            DELETE_ARRAY(expected.indices);
719            DELETE_ARRAY(expected.glyphs);
720
721            delete font;
722
723free_c_strings:
724            freeCString(lang);
725            freeCString(script);
726            freeCString(id);
727        }
728    }
729
730    delete root;
731    delete parser;
732#endif
733}
734U_CDECL_END
735
736U_CDECL_BEGIN
737/*
738 * From ticket:5923:
739 *
740 * Build a paragraph that contains a mixture of left to right and right to left text.
741 * Break it into multiple lines and make sure that the glyphToCharMap for run in each
742 * line is correct.
743 *
744 * Note: it might be a good idea to also check the glyphs and positions for each run,
745 * that we get the expected number of runs per line and that the line breaks are where
746 * we expect them to be. Really, it would be a good idea to make a whole test suite
747 * for ParagraphLayout.
748 */
749static void U_CALLCONV GlyphToCharTest(void)
750{
751#if !UCONFIG_NO_BREAK_ITERATION
752    LEErrorCode status = LE_NO_ERROR;
753    LEFontInstance *font;
754    FontRuns fontRuns(0);
755    ParagraphLayout *paragraphLayout;
756    const ParagraphLayout::Line *line;
757    /*
758     * This is the same text that's in <icu>/source/samples/layout/Sample.txt
759     */
760    LEUnicode chars[] = {
761        /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
762        0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
763        0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
764        0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
765        0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
766        0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
767        0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
768        0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
769        0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
770        0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
771        0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
772        0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
773        0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
774        0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
775        0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
776        0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
777        0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
778        0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
779        0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
780        0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
781        0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
782        0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
783        0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
784        0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
785        0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
786        0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
787        0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
788        0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
789        0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
790        0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
791        0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
792        0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
793        0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
794        0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
795        0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
796        0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
797        0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
798        0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
799        0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
800        0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
801        0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
802        0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
803        0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
804        0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
805        0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
806        0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
807        0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
808        0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
809        0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
810        0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
811        0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
812        0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
813        0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
814        0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
815        0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
816        0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
817        0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
818        0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
819        0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
820        0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
821        0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
822        0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
823        0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
824        0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
825        0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
826        0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
827        0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
828        0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
829        0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
830        0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
831        0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
832        0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
833        0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
834        0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
835        0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
836        0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
837        0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
838        0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
839        0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
840        0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
841        0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
842        0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
843        0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
844        0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
845        0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
846        0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
847        0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
848        0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
849        0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
850        0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
851        0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
852        0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
853        0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
854        0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
855        0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
856        0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
857        0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
858        0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
859        0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
860        0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
861        0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
862        0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
863        0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
864        0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
865        0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
866        0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
867        0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
868        0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
869        0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
870        0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
871        0x0e25, 0x0e4c
872    };
873    le_int32 charCount = LE_ARRAY_SIZE(chars);
874    le_int32 charIndex = 0, lineNumber = 1;
875    const float lineWidth = 600;
876
877    font = new SimpleFontInstance(12, status);
878
879    if (LE_FAILURE(status)) {
880        goto finish;
881    }
882
883    fontRuns.add(font, charCount);
884
885    paragraphLayout = new ParagraphLayout(chars, charCount, &fontRuns, NULL, NULL, NULL, 0, FALSE, status);
886
887    if (LE_FAILURE(status)) {
888        goto close_font;
889    }
890
891    paragraphLayout->reflow();
892    while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
893        le_int32 runCount = line->countRuns();
894
895        for(le_int32 run = 0; run < runCount; run += 1) {
896            const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
897            le_int32 glyphCount = visualRun->getGlyphCount();
898            const le_int32 *glyphToCharMap = visualRun->getGlyphToCharMap();
899
900            if (visualRun->getDirection() == UBIDI_RTL) {
901                /*
902                 * For a right to left run, make sure that the character indices
903                 * increase from the right most glyph to the left most glyph. If
904                 * there are any one to many glyph substitutions, we might get several
905                 * glyphs in a row with the same character index.
906                 */
907                for(le_int32 i = glyphCount - 1; i >= 0; i -= 1) {
908                    le_int32 ix = glyphToCharMap[i];
909
910                    if (ix != charIndex) {
911                        if (ix != charIndex - 1) {
912                            log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
913                                i, lineNumber, charIndex, ix);
914                            goto close_paragraph; // once there's one error, we can't count on anything else...
915                        }
916                    } else {
917                        charIndex += 1;
918                    }
919                }
920            } else {
921                /*
922                 * We can't just check the order of the character indices
923                 * for left to right runs because Indic text might have been
924                 * reordered. What we can do is find the minimum and maximum
925                 * character indices in the run and make sure that the minimum
926                 * is equal to charIndex and then advance charIndex to the maximum.
927                 */
928                le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
929
930                for(le_int32 i = 0; i < glyphCount; i += 1) {
931                    le_int32 ix = glyphToCharMap[i];
932
933                    if (ix > maxIndex) {
934                        maxIndex = ix;
935                    }
936
937                    if (ix < minIndex) {
938                        minIndex = ix;
939                    }
940                }
941
942                if (minIndex != charIndex) {
943                    log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
944                        run, lineNumber, charIndex, minIndex);
945                    goto close_paragraph; // once there's one error, we can't count on anything else...
946                }
947
948                charIndex = maxIndex + 1;
949            }
950        }
951
952        lineNumber += 1;
953    }
954close_paragraph:
955    delete paragraphLayout;
956
957close_font:
958    delete font;
959
960finish:
961    return;
962#endif
963}
964U_CDECL_END
965
966static void addAllTests(TestNode **root)
967{
968    addTest(root, &ScriptTest,      "api/ScriptTest");
969    addTest(root, &ParamTest,       "api/ParameterTest");
970    addTest(root, &FactoryTest,     "api/FactoryTest");
971    addTest(root, &AccessTest,      "layout/AccessTest");
972    addTest(root, &DataDrivenTest,  "layout/DataDrivenTest");
973    addTest(root, &GlyphToCharTest, "paragraph/GlyphToCharTest");
974
975    addCTests(root);
976}
977
978/* returns the path to icu/source/data/out */
979static const char *ctest_dataOutDir()
980{
981    static const char *dataOutDir = NULL;
982
983    if(dataOutDir) {
984        return dataOutDir;
985    }
986
987    /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
988    //              to point to the top of the build hierarchy, which may or
989    //              may not be the same as the source directory, depending on
990    //              the configure options used.  At any rate,
991    //              set the data path to the built data from this directory.
992    //              The value is complete with quotes, so it can be used
993    //              as-is as a string constant.
994    */
995#if defined (U_TOPBUILDDIR)
996    {
997        dataOutDir = U_TOPBUILDDIR "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
998    }
999#else
1000
1001    /* On Windows, the file name obtained from __FILE__ includes a full path.
1002     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
1003     *             Change to    "wherever\icu\source\data"
1004     */
1005    {
1006        static char p[sizeof(__FILE__) + 20];
1007        char *pBackSlash;
1008        int i;
1009
1010        strcpy(p, __FILE__);
1011        /* We want to back over three '\' chars.                            */
1012        /*   Only Windows should end up here, so looking for '\' is safe.   */
1013        for (i=1; i<=3; i++) {
1014            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
1015            if (pBackSlash != NULL) {
1016                *pBackSlash = 0;        /* Truncate the string at the '\'   */
1017            }
1018        }
1019
1020        if (pBackSlash != NULL) {
1021            /* We found and truncated three names from the path.
1022             *  Now append "source\data" and set the environment
1023             */
1024            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
1025            dataOutDir = p;
1026        }
1027        else {
1028            /* __FILE__ on MSVC7 does not contain the directory */
1029            FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
1030            if (file) {
1031                fclose(file);
1032                dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
1033            }
1034            else {
1035                dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
1036            }
1037        }
1038    }
1039#endif
1040
1041    return dataOutDir;
1042}
1043
1044/*  ctest_setICU_DATA  - if the ICU_DATA environment variable is not already
1045 *                       set, try to deduce the directory in which ICU was built,
1046 *                       and set ICU_DATA to "icu/source/data" in that location.
1047 *                       The intent is to allow the tests to have a good chance
1048 *                       of running without requiring that the user manually set
1049 *                       ICU_DATA.  Common data isn't a problem, since it is
1050 *                       picked up via a static (build time) reference, but the
1051 *                       tests dynamically load some data.
1052 */
1053static void ctest_setICU_DATA() {
1054
1055    /* No location for the data dir was identifiable.
1056     *   Add other fallbacks for the test data location here if the need arises
1057     */
1058    if (getenv("ICU_DATA") == NULL) {
1059        /* If ICU_DATA isn't set, set it to the usual location */
1060        u_setDataDirectory(ctest_dataOutDir());
1061    }
1062}
1063
1064int main(int argc, char* argv[])
1065{
1066    int32_t nerrors = 0;
1067    TestNode *root = NULL;
1068    UErrorCode errorCode = U_ZERO_ERROR;
1069    UDate startTime, endTime;
1070    int32_t diffTime;
1071
1072    startTime = uprv_getUTCtime();
1073
1074    if (!initArgs(argc, argv, NULL, NULL)) {
1075        /* Error already displayed. */
1076        return -1;
1077    }
1078
1079    /* Check whether ICU will initialize without forcing the build data directory into
1080    *  the ICU_DATA path.  Success here means either the data dll contains data, or that
1081    *  this test program was run with ICU_DATA set externally.  Failure of this check
1082    *  is normal when ICU data is not packaged into a shared library.
1083    *
1084    *  Whether or not this test succeeds, we want to cleanup and reinitialize
1085    *  with a data path so that data loading from individual files can be tested.
1086    */
1087    u_init(&errorCode);
1088
1089    if (U_FAILURE(errorCode)) {
1090        fprintf(stderr,
1091            "#### Note:  ICU Init without build-specific setDataDirectory() failed.\n");
1092    }
1093
1094    u_cleanup();
1095    errorCode = U_ZERO_ERROR;
1096
1097    if (!initArgs(argc, argv, NULL, NULL)) {
1098        /* Error already displayed. */
1099        return -1;
1100    }
1101/* Initialize ICU */
1102    ctest_setICU_DATA();    /* u_setDataDirectory() must happen Before u_init() */
1103    u_init(&errorCode);
1104
1105    if (U_FAILURE(errorCode)) {
1106        fprintf(stderr,
1107            "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
1108            "*** Check the ICU_DATA environment variable and \n"
1109            "*** check that the data files are present.\n", argv[0], u_errorName(errorCode));
1110        return 1;
1111    }
1112
1113    addAllTests(&root);
1114    nerrors = runTestRequest(root, argc, argv);
1115
1116    cleanUpTestTree(root);
1117    u_cleanup();
1118
1119    endTime = uprv_getUTCtime();
1120    diffTime = (int32_t)(endTime - startTime);
1121    printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
1122        (int)((diffTime%U_MILLIS_PER_DAY)/U_MILLIS_PER_HOUR),
1123        (int)((diffTime%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE),
1124        (int)((diffTime%U_MILLIS_PER_MINUTE)/U_MILLIS_PER_SECOND),
1125        (int)(diffTime%U_MILLIS_PER_SECOND));
1126
1127    return nerrors;
1128}
1129
1130