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