Home | History | Annotate | Download | only in letest
      1 /*
      2  *******************************************************************************
      3  *
      4  *   Copyright (C) 1999-2014, International Business Machines
      5  *   Corporation and others.  All Rights Reserved.
      6  *
      7  *******************************************************************************
      8  *   file name:  letest.cpp
      9  *
     10  *   created on: 11/06/2000
     11  *   created by: Eric R. Mader
     12  */
     13 
     14 #include "unicode/utypes.h"
     15 #include "unicode/uclean.h"
     16 #include "unicode/uchar.h"
     17 #include "unicode/unistr.h"
     18 #include "unicode/uscript.h"
     19 #include "unicode/putil.h"
     20 #include "unicode/ctest.h"
     21 
     22 #include "layout/LETypes.h"
     23 #include "layout/LEScripts.h"
     24 #include "layout/LayoutEngine.h"
     25 
     26 #include "layout/ParagraphLayout.h"
     27 #include "layout/RunArrays.h"
     28 
     29 #include "PortableFontInstance.h"
     30 #include "SimpleFontInstance.h"
     31 
     32 #include "letsutil.h"
     33 #include "letest.h"
     34 
     35 #include "xmlparser.h"
     36 #include "putilimp.h" // for uprv_getUTCtime()
     37 
     38 #include <stdlib.h>
     39 #include <string.h>
     40 
     41 U_NAMESPACE_USE
     42 
     43 #define CH_COMMA 0x002C
     44 
     45 U_CDECL_BEGIN
     46 
     47 static 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 
     54 static 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 
    210 bail:
    211     delete engine;
    212     delete font;
    213 }
    214 U_CDECL_END
    215 
    216 U_CDECL_BEGIN
    217 static 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 }
    236 U_CDECL_END
    237 
    238 U_CDECL_BEGIN
    239 static 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 
    322 bail:
    323     delete engine;
    324     delete font;
    325 }
    326 U_CDECL_END
    327 
    328 le_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 
    380 static 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/ */
    413 const 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 
    433 const 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 
    442 le_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 
    488 float *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 
    538 LEFontInstance *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 
    562 U_CDECL_BEGIN
    563 static void U_CALLCONV DataDrivenTest(void)
    564 {
    565 #if !UCONFIG_NO_REGULAR_EXPRESSIONS
    566     UErrorCode status = U_ZERO_ERROR;
    567     char path[2048];
    568     const char *testFilePath = getPath(path, "letest.xml");
    569 
    570     UXMLParser  *parser = UXMLParser::createParser(status);
    571     UXMLElement *root   = parser->parseFile(testFilePath, status);
    572 
    573     if (root == NULL) {
    574         log_err("Could not open the test data file: %s\n", testFilePath);
    575         delete parser;
    576         return;
    577     }
    578 
    579     UnicodeString test_case        = UNICODE_STRING_SIMPLE("test-case");
    580     UnicodeString test_text        = UNICODE_STRING_SIMPLE("test-text");
    581     UnicodeString test_font        = UNICODE_STRING_SIMPLE("test-font");
    582     UnicodeString result_glyphs    = UNICODE_STRING_SIMPLE("result-glyphs");
    583     UnicodeString result_indices   = UNICODE_STRING_SIMPLE("result-indices");
    584     UnicodeString result_positions = UNICODE_STRING_SIMPLE("result-positions");
    585 
    586     // test-case attributes
    587     UnicodeString id_attr     = UNICODE_STRING_SIMPLE("id");
    588     UnicodeString script_attr = UNICODE_STRING_SIMPLE("script");
    589     UnicodeString lang_attr   = UNICODE_STRING_SIMPLE("lang");
    590 
    591     // test-font attributes
    592     UnicodeString name_attr   = UNICODE_STRING_SIMPLE("name");
    593     UnicodeString ver_attr    = UNICODE_STRING_SIMPLE("version");
    594     UnicodeString cksum_attr  = UNICODE_STRING_SIMPLE("checksum");
    595 
    596     const UXMLElement *testCase;
    597     int32_t tc = 0;
    598 
    599     while((testCase = root->nextChildElement(tc)) != NULL) {
    600         if (testCase->getTagName().compare(test_case) == 0) {
    601             char *id = getCString(testCase->getAttribute(id_attr));
    602             char *script = getCString(testCase->getAttribute(script_attr));
    603             char *lang   = getCString(testCase->getAttribute(lang_attr));
    604             LEFontInstance *font = NULL;
    605             const UXMLElement *element;
    606             int32_t ec = 0;
    607             int32_t charCount = 0;
    608             int32_t typoFlags = 3; // kerning + ligatures...
    609             UScriptCode scriptCode;
    610             le_int32 languageCode = -1;
    611             UnicodeString text, glyphs, indices, positions;
    612             int32_t glyphCount = 0, indexCount = 0, positionCount = 0;
    613             TestResult expected = {0, NULL, NULL, NULL};
    614             TestResult actual   = {0, NULL, NULL, NULL};
    615             LEErrorCode success = LE_NO_ERROR;
    616             LayoutEngine *engine = NULL;
    617 
    618             uscript_getCode(script, &scriptCode, 1, &status);
    619             if (LE_FAILURE(status)) {
    620                 log_err("invalid script name: %s.\n", script);
    621                 goto free_c_strings;
    622             }
    623 
    624             if (lang != NULL) {
    625                 languageCode = getLanguageCode(lang);
    626 
    627                 if (languageCode < 0) {
    628                     log_err("invalid language name: %s.\n", lang);
    629                     goto free_c_strings;
    630                 }
    631             }
    632 
    633             while((element = testCase->nextChildElement(ec)) != NULL) {
    634                 UnicodeString tag = element->getTagName();
    635 
    636                 // TODO: make sure that each element is only used once.
    637                 if (tag.compare(test_font) == 0) {
    638                     char *fontName  = getCString(element->getAttribute(name_attr));
    639                     char *fontVer   = getCString(element->getAttribute(ver_attr));
    640                     char *fontCksum = getCString(element->getAttribute(cksum_attr));
    641 
    642                     font = openFont(fontName, fontCksum, fontVer, id);
    643                     freeCString(fontCksum);
    644                     freeCString(fontVer);
    645                     freeCString(fontName);
    646 
    647                     if (font == NULL) {
    648                         // warning message already displayed...
    649                         goto free_c_strings;
    650                     }
    651                 } else if (tag.compare(test_text) == 0) {
    652                     text = element->getText(TRUE);
    653                     charCount = text.length();
    654                 } else if (tag.compare(result_glyphs) == 0) {
    655                     glyphs = element->getText(TRUE);
    656                 } else if (tag.compare(result_indices) == 0) {
    657                     indices = element->getText(TRUE);
    658                 } else if (tag.compare(result_positions) == 0) {
    659                     positions = element->getText(TRUE);
    660                 } else {
    661                     // an unknown tag...
    662                     char *cTag = getCString(&tag);
    663 
    664                     log_info("Test %s: unknown element with tag \"%s\"\n", id, cTag);
    665                     freeCString(cTag);
    666                 }
    667             }
    668 
    669             // TODO: make sure that the font, test-text, result-glyphs, result-indices and result-positions
    670             // have all been provided
    671             if (font == NULL) {
    672                 LEErrorCode fontStatus = LE_NO_ERROR;
    673 
    674                 font = new SimpleFontInstance(12, fontStatus);
    675                 typoFlags |= 0x80000000L;  // use CharSubstitutionFilter...
    676             }
    677 
    678             expected.glyphs    = (LEGlyphID *) getHexArray(glyphs, glyphCount);
    679             expected.indices   = (le_int32 *)  getHexArray(indices, indexCount);
    680             expected.positions = getFloatArray(positions, positionCount);
    681 
    682             expected.glyphCount = glyphCount;
    683 
    684             if (glyphCount < charCount || indexCount != glyphCount || positionCount < glyphCount * 2 + 2) {
    685                 log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n",
    686                     id, charCount, glyphCount, indexCount, positionCount);
    687                 goto free_expected;
    688             };
    689 
    690             engine = LayoutEngine::layoutEngineFactory(font, scriptCode, languageCode, typoFlags, success);
    691 
    692             if (LE_FAILURE(success)) {
    693                 log_err("Test %s: could not create a LayoutEngine.\n", id);
    694                 goto free_expected;
    695             }
    696 
    697             actual.glyphCount = engine->layoutChars(text.getBuffer(), 0, charCount, charCount, getRTL(text), 0, 0, success);
    698 
    699             actual.glyphs    = NEW_ARRAY(LEGlyphID, actual.glyphCount);
    700             actual.indices   = NEW_ARRAY(le_int32, actual.glyphCount);
    701             actual.positions = NEW_ARRAY(float, actual.glyphCount * 2 + 2);
    702 
    703             engine->getGlyphs(actual.glyphs, success);
    704             engine->getCharIndices(actual.indices, success);
    705             engine->getGlyphPositions(actual.positions, success);
    706 
    707             compareResults(id, &expected, &actual);
    708 
    709             DELETE_ARRAY(actual.positions);
    710             DELETE_ARRAY(actual.indices);
    711             DELETE_ARRAY(actual.glyphs);
    712 
    713             delete engine;
    714 
    715             log_verbose("OK - %4d glyphs: %s\n", actual.glyphCount, id);
    716 free_expected:
    717             DELETE_ARRAY(expected.positions);
    718             DELETE_ARRAY(expected.indices);
    719             DELETE_ARRAY(expected.glyphs);
    720 
    721             delete font;
    722 
    723 free_c_strings:
    724             freeCString(lang);
    725             freeCString(script);
    726             freeCString(id);
    727         }
    728     }
    729 
    730     delete root;
    731     delete parser;
    732 #endif
    733 }
    734 U_CDECL_END
    735 
    736 U_CDECL_BEGIN
    737 /*
    738  * From ticket:5923:
    739  *
    740  * Build a paragraph that contains a mixture of left to right and right to left text.
    741  * Break it into multiple lines and make sure that the glyphToCharMap for run in each
    742  * line is correct.
    743  *
    744  * Note: it might be a good idea to also check the glyphs and positions for each run,
    745  * that we get the expected number of runs per line and that the line breaks are where
    746  * we expect them to be. Really, it would be a good idea to make a whole test suite
    747  * for ParagraphLayout.
    748  */
    749 static void U_CALLCONV GlyphToCharTest(void)
    750 {
    751 #if !UCONFIG_NO_BREAK_ITERATION
    752     LEErrorCode status = LE_NO_ERROR;
    753     LEFontInstance *font;
    754     FontRuns fontRuns(0);
    755     ParagraphLayout *paragraphLayout;
    756     const ParagraphLayout::Line *line;
    757     /*
    758      * This is the same text that's in <icu>/source/samples/layout/Sample.txt
    759      */
    760     LEUnicode chars[] = {
    761         /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
    762         0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
    763         0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
    764         0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
    765         0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
    766         0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
    767         0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
    768         0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
    769         0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
    770         0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
    771         0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
    772         0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
    773         0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
    774         0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
    775         0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
    776         0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
    777         0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
    778         0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
    779         0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
    780         0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
    781         0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
    782         0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
    783         0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
    784         0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
    785         0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
    786         0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
    787         0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
    788         0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
    789         0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
    790         0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
    791         0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
    792         0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
    793         0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
    794         0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
    795         0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
    796         0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
    797         0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
    798         0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
    799         0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
    800         0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
    801         0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
    802         0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
    803         0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
    804         0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
    805         0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
    806         0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
    807         0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
    808         0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
    809         0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
    810         0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
    811         0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
    812         0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
    813         0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
    814         0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
    815         0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
    816         0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
    817         0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
    818         0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
    819         0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
    820         0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
    821         0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
    822         0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
    823         0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
    824         0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
    825         0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
    826         0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
    827         0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
    828         0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
    829         0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
    830         0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
    831         0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
    832         0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
    833         0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
    834         0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
    835         0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
    836         0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
    837         0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
    838         0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
    839         0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
    840         0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
    841         0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
    842         0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
    843         0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
    844         0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
    845         0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
    846         0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
    847         0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
    848         0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
    849         0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
    850         0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
    851         0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
    852         0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
    853         0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
    854         0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
    855         0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
    856         0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
    857         0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
    858         0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
    859         0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
    860         0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
    861         0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
    862         0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
    863         0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
    864         0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
    865         0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
    866         0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
    867         0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
    868         0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
    869         0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
    870         0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
    871         0x0e25, 0x0e4c
    872     };
    873     le_int32 charCount = LE_ARRAY_SIZE(chars);
    874     le_int32 charIndex = 0, lineNumber = 1;
    875     const float lineWidth = 600;
    876 
    877     font = new SimpleFontInstance(12, status);
    878 
    879     if (LE_FAILURE(status)) {
    880         goto finish;
    881     }
    882 
    883     fontRuns.add(font, charCount);
    884 
    885     paragraphLayout = new ParagraphLayout(chars, charCount, &fontRuns, NULL, NULL, NULL, 0, FALSE, status);
    886 
    887     if (LE_FAILURE(status)) {
    888         goto close_font;
    889     }
    890 
    891     paragraphLayout->reflow();
    892     while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
    893         le_int32 runCount = line->countRuns();
    894 
    895         for(le_int32 run = 0; run < runCount; run += 1) {
    896             const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
    897             le_int32 glyphCount = visualRun->getGlyphCount();
    898             const le_int32 *glyphToCharMap = visualRun->getGlyphToCharMap();
    899 
    900             if (visualRun->getDirection() == UBIDI_RTL) {
    901                 /*
    902                  * For a right to left run, make sure that the character indices
    903                  * increase from the right most glyph to the left most glyph. If
    904                  * there are any one to many glyph substitutions, we might get several
    905                  * glyphs in a row with the same character index.
    906                  */
    907                 for(le_int32 i = glyphCount - 1; i >= 0; i -= 1) {
    908                     le_int32 ix = glyphToCharMap[i];
    909 
    910                     if (ix != charIndex) {
    911                         if (ix != charIndex - 1) {
    912                             log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
    913                                 i, lineNumber, charIndex, ix);
    914                             goto close_paragraph; // once there's one error, we can't count on anything else...
    915                         }
    916                     } else {
    917                         charIndex += 1;
    918                     }
    919                 }
    920             } else {
    921                 /*
    922                  * We can't just check the order of the character indices
    923                  * for left to right runs because Indic text might have been
    924                  * reordered. What we can do is find the minimum and maximum
    925                  * character indices in the run and make sure that the minimum
    926                  * is equal to charIndex and then advance charIndex to the maximum.
    927                  */
    928                 le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
    929 
    930                 for(le_int32 i = 0; i < glyphCount; i += 1) {
    931                     le_int32 ix = glyphToCharMap[i];
    932 
    933                     if (ix > maxIndex) {
    934                         maxIndex = ix;
    935                     }
    936 
    937                     if (ix < minIndex) {
    938                         minIndex = ix;
    939                     }
    940                 }
    941 
    942                 if (minIndex != charIndex) {
    943                     log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
    944                         run, lineNumber, charIndex, minIndex);
    945                     goto close_paragraph; // once there's one error, we can't count on anything else...
    946                 }
    947 
    948                 charIndex = maxIndex + 1;
    949             }
    950         }
    951 
    952         lineNumber += 1;
    953     }
    954 close_paragraph:
    955     delete paragraphLayout;
    956 
    957 close_font:
    958     delete font;
    959 
    960 finish:
    961     return;
    962 #endif
    963 }
    964 U_CDECL_END
    965 
    966 static void addAllTests(TestNode **root)
    967 {
    968     addTest(root, &ScriptTest,      "api/ScriptTest");
    969     addTest(root, &ParamTest,       "api/ParameterTest");
    970     addTest(root, &FactoryTest,     "api/FactoryTest");
    971     addTest(root, &AccessTest,      "layout/AccessTest");
    972     addTest(root, &DataDrivenTest,  "layout/DataDrivenTest");
    973     addTest(root, &GlyphToCharTest, "paragraph/GlyphToCharTest");
    974 
    975     addCTests(root);
    976 }
    977 
    978 /* returns the path to icu/source/data/out */
    979 static const char *ctest_dataOutDir()
    980 {
    981     static const char *dataOutDir = NULL;
    982 
    983     if(dataOutDir) {
    984         return dataOutDir;
    985     }
    986 
    987     /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
    988     //              to point to the top of the build hierarchy, which may or
    989     //              may not be the same as the source directory, depending on
    990     //              the configure options used.  At any rate,
    991     //              set the data path to the built data from this directory.
    992     //              The value is complete with quotes, so it can be used
    993     //              as-is as a string constant.
    994     */
    995 #if defined (U_TOPBUILDDIR)
    996     {
    997         dataOutDir = U_TOPBUILDDIR "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
    998     }
    999 #else
   1000 
   1001     /* On Windows, the file name obtained from __FILE__ includes a full path.
   1002      *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
   1003      *             Change to    "wherever\icu\source\data"
   1004      */
   1005     {
   1006         static char p[sizeof(__FILE__) + 20];
   1007         char *pBackSlash;
   1008         int i;
   1009 
   1010         strcpy(p, __FILE__);
   1011         /* We want to back over three '\' chars.                            */
   1012         /*   Only Windows should end up here, so looking for '\' is safe.   */
   1013         for (i=1; i<=3; i++) {
   1014             pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
   1015             if (pBackSlash != NULL) {
   1016                 *pBackSlash = 0;        /* Truncate the string at the '\'   */
   1017             }
   1018         }
   1019 
   1020         if (pBackSlash != NULL) {
   1021             /* We found and truncated three names from the path.
   1022              *  Now append "source\data" and set the environment
   1023              */
   1024             strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
   1025             dataOutDir = p;
   1026         }
   1027         else {
   1028             /* __FILE__ on MSVC7 does not contain the directory */
   1029             FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
   1030             if (file) {
   1031                 fclose(file);
   1032                 dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
   1033             }
   1034             else {
   1035                 dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
   1036             }
   1037         }
   1038     }
   1039 #endif
   1040 
   1041     return dataOutDir;
   1042 }
   1043 
   1044 /*  ctest_setICU_DATA  - if the ICU_DATA environment variable is not already
   1045  *                       set, try to deduce the directory in which ICU was built,
   1046  *                       and set ICU_DATA to "icu/source/data" in that location.
   1047  *                       The intent is to allow the tests to have a good chance
   1048  *                       of running without requiring that the user manually set
   1049  *                       ICU_DATA.  Common data isn't a problem, since it is
   1050  *                       picked up via a static (build time) reference, but the
   1051  *                       tests dynamically load some data.
   1052  */
   1053 static void ctest_setICU_DATA() {
   1054 
   1055     /* No location for the data dir was identifiable.
   1056      *   Add other fallbacks for the test data location here if the need arises
   1057      */
   1058     if (getenv("ICU_DATA") == NULL) {
   1059         /* If ICU_DATA isn't set, set it to the usual location */
   1060         u_setDataDirectory(ctest_dataOutDir());
   1061     }
   1062 }
   1063 
   1064 int main(int argc, char* argv[])
   1065 {
   1066     int32_t nerrors = 0;
   1067     TestNode *root = NULL;
   1068     UErrorCode errorCode = U_ZERO_ERROR;
   1069     UDate startTime, endTime;
   1070     int32_t diffTime;
   1071 
   1072     startTime = uprv_getUTCtime();
   1073 
   1074     if (!initArgs(argc, argv, NULL, NULL)) {
   1075         /* Error already displayed. */
   1076         return -1;
   1077     }
   1078 
   1079     /* Check whether ICU will initialize without forcing the build data directory into
   1080     *  the ICU_DATA path.  Success here means either the data dll contains data, or that
   1081     *  this test program was run with ICU_DATA set externally.  Failure of this check
   1082     *  is normal when ICU data is not packaged into a shared library.
   1083     *
   1084     *  Whether or not this test succeeds, we want to cleanup and reinitialize
   1085     *  with a data path so that data loading from individual files can be tested.
   1086     */
   1087     u_init(&errorCode);
   1088 
   1089     if (U_FAILURE(errorCode)) {
   1090         fprintf(stderr,
   1091             "#### Note:  ICU Init without build-specific setDataDirectory() failed.\n");
   1092     }
   1093 
   1094     u_cleanup();
   1095     errorCode = U_ZERO_ERROR;
   1096 
   1097     if (!initArgs(argc, argv, NULL, NULL)) {
   1098         /* Error already displayed. */
   1099         return -1;
   1100     }
   1101 /* Initialize ICU */
   1102     ctest_setICU_DATA();    /* u_setDataDirectory() must happen Before u_init() */
   1103     u_init(&errorCode);
   1104 
   1105     if (U_FAILURE(errorCode)) {
   1106         fprintf(stderr,
   1107             "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
   1108             "*** Check the ICU_DATA environment variable and \n"
   1109             "*** check that the data files are present.\n", argv[0], u_errorName(errorCode));
   1110         return 1;
   1111     }
   1112 
   1113     addAllTests(&root);
   1114     nerrors = runTestRequest(root, argc, argv);
   1115 
   1116     cleanUpTestTree(root);
   1117     u_cleanup();
   1118 
   1119     endTime = uprv_getUTCtime();
   1120     diffTime = (int32_t)(endTime - startTime);
   1121     printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
   1122         (int)((diffTime%U_MILLIS_PER_DAY)/U_MILLIS_PER_HOUR),
   1123         (int)((diffTime%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE),
   1124         (int)((diffTime%U_MILLIS_PER_MINUTE)/U_MILLIS_PER_SECOND),
   1125         (int)(diffTime%U_MILLIS_PER_SECOND));
   1126 
   1127     return nerrors;
   1128 }
   1129 
   1130