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