letest.cpp revision 85bf2e2fbc60a9f938064abc8127d61da7d19882
1/*
2 *******************************************************************************
3 *
4 *   Copyright (C) 1999-2008, 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
715free_expected:
716            DELETE_ARRAY(expected.positions);
717            DELETE_ARRAY(expected.indices);
718            DELETE_ARRAY(expected.glyphs);
719
720            delete font;
721
722free_c_strings:
723            freeCString(lang);
724            freeCString(script);
725            freeCString(id);
726        }
727    }
728
729    delete root;
730    delete parser;
731#endif
732}
733U_CDECL_END
734
735U_CDECL_BEGIN
736/*
737 * From ticket:5923:
738 *
739 * Build a paragraph that contains a mixture of left to right and right to left text.
740 * Break it into multiple lines and make sure that the glyphToCharMap for run in each
741 * line is correct.
742 *
743 * Note: it might be a good idea to also check the glyphs and positions for each run,
744 * that we get the expected number of runs per line and that the line breaks are where
745 * we expect them to be. Really, it would be a good idea to make a whole test suite
746 * for ParagraphLayout.
747 */
748static void U_CALLCONV GlyphToCharTest(void)
749{
750    LEErrorCode status = LE_NO_ERROR;
751    LEFontInstance *font;
752    FontRuns fontRuns(0);
753    ParagraphLayout *paragraphLayout;
754    const ParagraphLayout::Line *line;
755    /*
756     * This is the same text that's in <icu>/source/samples/layout/Sample.txt
757     */
758    LEUnicode chars[] = {
759        /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
760        0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
761        0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
762        0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
763        0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
764        0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
765        0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
766        0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
767        0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
768        0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
769        0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
770        0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
771        0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
772        0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
773        0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
774        0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
775        0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
776        0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
777        0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
778        0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
779        0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
780        0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
781        0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
782        0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
783        0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
784        0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
785        0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
786        0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
787        0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
788        0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
789        0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
790        0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
791        0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
792        0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
793        0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
794        0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
795        0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
796        0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
797        0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
798        0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
799        0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
800        0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
801        0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
802        0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
803        0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
804        0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
805        0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
806        0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
807        0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
808        0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
809        0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
810        0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
811        0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
812        0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
813        0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
814        0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
815        0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
816        0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
817        0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
818        0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
819        0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
820        0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
821        0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
822        0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
823        0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
824        0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
825        0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
826        0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
827        0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
828        0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
829        0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
830        0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
831        0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
832        0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
833        0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
834        0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
835        0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
836        0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
837        0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
838        0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
839        0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
840        0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
841        0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
842        0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
843        0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
844        0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
845        0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
846        0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
847        0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
848        0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
849        0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
850        0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
851        0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
852        0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
853        0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
854        0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
855        0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
856        0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
857        0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
858        0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
859        0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
860        0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
861        0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
862        0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
863        0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
864        0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
865        0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
866        0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
867        0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
868        0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
869        0x0e25, 0x0e4c
870    };
871    le_int32 charCount = LE_ARRAY_SIZE(chars);
872    le_int32 charIndex = 0, lineNumber = 1;
873    const float lineWidth = 600;
874
875    font = new SimpleFontInstance(12, status);
876
877    if (LE_FAILURE(status)) {
878        goto finish;
879    }
880
881    fontRuns.add(font, charCount);
882
883    paragraphLayout = new ParagraphLayout(chars, charCount, &fontRuns, NULL, NULL, NULL, 0, FALSE, status);
884
885    if (LE_FAILURE(status)) {
886        goto close_font;
887    }
888
889    paragraphLayout->reflow();
890    while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
891        le_int32 runCount = line->countRuns();
892
893        for(le_int32 run = 0; run < runCount; run += 1) {
894            const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
895            le_int32 glyphCount = visualRun->getGlyphCount();
896            const le_int32 *glyphToCharMap = visualRun->getGlyphToCharMap();
897
898            if (visualRun->getDirection() == UBIDI_RTL) {
899                /*
900                 * For a right to left run, make sure that the character indices
901                 * increase from the right most glyph to the left most glyph. If
902                 * there are any one to many glyph substitutions, we might get several
903                 * glyphs in a row with the same character index.
904                 */
905                for(le_int32 i = glyphCount - 1; i >= 0; i -= 1) {
906                    le_int32 ix = glyphToCharMap[i];
907
908                    if (ix != charIndex) {
909                        if (ix != charIndex - 1) {
910                            log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
911                                i, lineNumber, charIndex, ix);
912                            goto close_paragraph; // once there's one error, we can't count on anything else...
913                        }
914                    } else {
915                        charIndex += 1;
916                    }
917                }
918            } else {
919                /*
920                 * We can't just check the order of the character indices
921                 * for left to right runs because Indic text might have been
922                 * reordered. What we can do is find the minimum and maximum
923                 * character indices in the run and make sure that the minimum
924                 * is equal to charIndex and then advance charIndex to the maximum.
925                 */
926                le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
927
928                for(le_int32 i = 0; i < glyphCount; i += 1) {
929                    le_int32 ix = glyphToCharMap[i];
930
931                    if (ix > maxIndex) {
932                        maxIndex = ix;
933                    }
934
935                    if (ix < minIndex) {
936                        minIndex = ix;
937                    }
938                }
939
940                if (minIndex != charIndex) {
941                    log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
942                        run, lineNumber, charIndex, minIndex);
943                    goto close_paragraph; // once there's one error, we can't count on anything else...
944                }
945
946                charIndex = maxIndex + 1;
947            }
948        }
949
950        lineNumber += 1;
951    }
952close_paragraph:
953    delete paragraphLayout;
954
955close_font:
956    delete font;
957
958finish:
959    return;
960}
961U_CDECL_END
962
963static void addAllTests(TestNode **root)
964{
965    addTest(root, &ScriptTest,      "api/ScriptTest");
966    addTest(root, &ParamTest,       "api/ParameterTest");
967    addTest(root, &FactoryTest,     "api/FactoryTest");
968    addTest(root, &AccessTest,      "layout/AccessTest");
969    addTest(root, &DataDrivenTest,  "layout/DataDrivenTest");
970    addTest(root, &GlyphToCharTest, "paragraph/GlyphToCharTest");
971
972    addCTests(root);
973}
974
975/* returns the path to icu/source/data/out */
976static const char *ctest_dataOutDir()
977{
978    static const char *dataOutDir = NULL;
979
980    if(dataOutDir) {
981        return dataOutDir;
982    }
983
984    /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
985    //              to point to the top of the build hierarchy, which may or
986    //              may not be the same as the source directory, depending on
987    //              the configure options used.  At any rate,
988    //              set the data path to the built data from this directory.
989    //              The value is complete with quotes, so it can be used
990    //              as-is as a string constant.
991    */
992#if defined (U_TOPBUILDDIR)
993    {
994        dataOutDir = U_TOPBUILDDIR "data"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING;
995    }
996#else
997
998    /* On Windows, the file name obtained from __FILE__ includes a full path.
999     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
1000     *             Change to    "wherever\icu\source\data"
1001     */
1002    {
1003        static char p[sizeof(__FILE__) + 20];
1004        char *pBackSlash;
1005        int i;
1006
1007        strcpy(p, __FILE__);
1008        /* We want to back over three '\' chars.                            */
1009        /*   Only Windows should end up here, so looking for '\' is safe.   */
1010        for (i=1; i<=3; i++) {
1011            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
1012            if (pBackSlash != NULL) {
1013                *pBackSlash = 0;        /* Truncate the string at the '\'   */
1014            }
1015        }
1016
1017        if (pBackSlash != NULL) {
1018            /* We found and truncated three names from the path.
1019             *  Now append "source\data" and set the environment
1020             */
1021            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
1022            dataOutDir = p;
1023        }
1024        else {
1025            /* __FILE__ on MSVC7 does not contain the directory */
1026            FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
1027            if (file) {
1028                fclose(file);
1029                dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
1030            }
1031            else {
1032                dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
1033            }
1034        }
1035    }
1036#endif
1037
1038    return dataOutDir;
1039}
1040
1041/*  ctest_setICU_DATA  - if the ICU_DATA environment variable is not already
1042 *                       set, try to deduce the directory in which ICU was built,
1043 *                       and set ICU_DATA to "icu/source/data" in that location.
1044 *                       The intent is to allow the tests to have a good chance
1045 *                       of running without requiring that the user manually set
1046 *                       ICU_DATA.  Common data isn't a problem, since it is
1047 *                       picked up via a static (build time) reference, but the
1048 *                       tests dynamically load some data.
1049 */
1050static void ctest_setICU_DATA() {
1051
1052    /* No location for the data dir was identifiable.
1053     *   Add other fallbacks for the test data location here if the need arises
1054     */
1055    if (getenv("ICU_DATA") == NULL) {
1056        /* If ICU_DATA isn't set, set it to the usual location */
1057        u_setDataDirectory(ctest_dataOutDir());
1058    }
1059}
1060
1061int main(int argc, char* argv[])
1062{
1063    int32_t nerrors = 0;
1064    TestNode *root = NULL;
1065    UErrorCode errorCode = U_ZERO_ERROR;
1066    UDate startTime, endTime;
1067    int32_t diffTime;
1068
1069    startTime = uprv_getUTCtime();
1070
1071    if (!initArgs(argc, argv, NULL, NULL)) {
1072        /* Error already displayed. */
1073        return -1;
1074    }
1075
1076    /* Check whether ICU will initialize without forcing the build data directory into
1077    *  the ICU_DATA path.  Success here means either the data dll contains data, or that
1078    *  this test program was run with ICU_DATA set externally.  Failure of this check
1079    *  is normal when ICU data is not packaged into a shared library.
1080    *
1081    *  Whether or not this test succeeds, we want to cleanup and reinitialize
1082    *  with a data path so that data loading from individual files can be tested.
1083    */
1084    u_init(&errorCode);
1085
1086    if (U_FAILURE(errorCode)) {
1087        fprintf(stderr,
1088            "#### Note:  ICU Init without build-specific setDataDirectory() failed.\n");
1089    }
1090
1091    u_cleanup();
1092    errorCode = U_ZERO_ERROR;
1093
1094    if (!initArgs(argc, argv, NULL, NULL)) {
1095        /* Error already displayed. */
1096        return -1;
1097    }
1098/* Initialize ICU */
1099    ctest_setICU_DATA();    /* u_setDataDirectory() must happen Before u_init() */
1100    u_init(&errorCode);
1101
1102    if (U_FAILURE(errorCode)) {
1103        fprintf(stderr,
1104            "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
1105            "*** Check the ICU_DATA environment variable and \n"
1106            "*** check that the data files are present.\n", argv[0], u_errorName(errorCode));
1107        return 1;
1108    }
1109
1110    addAllTests(&root);
1111    nerrors = runTestRequest(root, argc, argv);
1112
1113    cleanUpTestTree(root);
1114    u_cleanup();
1115
1116    endTime = uprv_getUTCtime();
1117    diffTime = (int32_t)(endTime - startTime);
1118    printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
1119        (int)((diffTime%U_MILLIS_PER_DAY)/U_MILLIS_PER_HOUR),
1120        (int)((diffTime%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE),
1121        (int)((diffTime%U_MILLIS_PER_MINUTE)/U_MILLIS_PER_SECOND),
1122        (int)(diffTime%U_MILLIS_PER_SECOND));
1123
1124    return nerrors;
1125}
1126
1127