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