1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
6 * Copyright (C) 1999-2014, International Business Machines
7 * Corporation and others. All Rights Reserved.
9 *******************************************************************************
10 * file name: letest.cpp
12 * created on: 11/06/2000
13 * created by: Eric R. Mader
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"
24 #include "layout/LETypes.h"
25 #include "layout/LEScripts.h"
26 #include "layout/LayoutEngine.h"
28 #include "layout/ParagraphLayout.h"
29 #include "layout/RunArrays.h"
31 #include "PortableFontInstance.h"
32 #include "SimpleFontInstance.h"
37 #include "xmlparser.h"
38 #include "putilimp.h" // for uprv_getUTCtime()
45 #define CH_COMMA 0x002C
49 static void U_CALLCONV
ScriptTest(void)
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
);
56 static void U_CALLCONV
ParamTest(void)
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;
66 glyphCount
= engine
->getGlyphCount();
67 if (glyphCount
!= 0) {
68 log_err("Calling getGlyphCount() on an empty layout returned %d.\n", glyphCount
);
71 glyphs
= NEW_ARRAY(LEGlyphID
, glyphCount
+ 10);
72 indices
= NEW_ARRAY(le_int32
, glyphCount
+ 10);
73 positions
= NEW_ARRAY(float, glyphCount
+ 10);
75 engine
->getGlyphs(NULL
, status
);
77 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
78 log_err("Calling getGlyphs(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
82 engine
->getGlyphs(glyphs
, status
);
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");
89 engine
->getGlyphs(NULL
, 0xFF000000L
, status
);
91 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
92 log_err("Calling getGlyphs(NULL, 0xFF000000L, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
96 engine
->getGlyphs(glyphs
, 0xFF000000L
, status
);
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");
102 status
= LE_NO_ERROR
;
103 engine
->getCharIndices(NULL
, status
);
105 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
106 log_err("Calling getCharIndices(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
109 status
= LE_NO_ERROR
;
110 engine
->getCharIndices(indices
, status
);
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");
116 status
= LE_NO_ERROR
;
117 engine
->getCharIndices(NULL
, 1024, status
);
119 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
120 log_err("Calling getCharIndices(NULL, 1024, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
123 status
= LE_NO_ERROR
;
124 engine
->getCharIndices(indices
, 1024, status
);
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");
130 status
= LE_NO_ERROR
;
131 engine
->getGlyphPositions(NULL
, status
);
133 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
134 log_err("Calling getGlyphPositions(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
137 status
= LE_NO_ERROR
;
138 engine
->getGlyphPositions(positions
, status
);
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");
144 DELETE_ARRAY(positions
);
145 DELETE_ARRAY(indices
);
146 DELETE_ARRAY(glyphs
);
148 status
= LE_NO_ERROR
;
149 glyphCount
= engine
->layoutChars(NULL
, 0, 0, 0, FALSE
, 0.0, 0.0, status
);
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");
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."
161 status
= LE_NO_ERROR
;
162 glyphCount
= engine
->layoutChars(chars
, -1, 6, 20, TRUE
, 0.0, 0.0, status
);
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");
168 status
= LE_NO_ERROR
;
169 glyphCount
= engine
->layoutChars(chars
, 8, -1, 20, TRUE
, 0.0, 0.0, status
);
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");
175 status
= LE_NO_ERROR
;
176 glyphCount
= engine
->layoutChars(chars
, 8, 6, -1, TRUE
, 0.0, 0.0, status
);
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");
182 status
= LE_NO_ERROR
;
183 glyphCount
= engine
->layoutChars(chars
, 8, 6, 10, TRUE
, 0.0, 0.0, status
);
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");
189 float x
= 0.0, y
= 0.0;
191 status
= LE_NO_ERROR
;
192 glyphCount
= engine
->layoutChars(chars
, 8, 6, 20, TRUE
, 0.0, 0.0, status
);
194 if (LE_FAILURE(status
)) {
195 log_err("Calling layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
199 engine
->getGlyphPosition(-1, x
, y
, status
);
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");
205 status
= LE_NO_ERROR
;
206 engine
->getGlyphPosition(glyphCount
+ 1, x
, y
, status
);
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");
219 static void U_CALLCONV
FactoryTest(void)
221 LEErrorCode status
= LE_NO_ERROR
;
222 SimpleFontInstance
*font
= new SimpleFontInstance(12, status
);
223 LayoutEngine
*engine
= NULL
;
225 for(le_int32 scriptCode
= 0; scriptCode
< scriptCodeCount
; scriptCode
+= 1) {
226 status
= LE_NO_ERROR
;
227 engine
= LayoutEngine::layoutEngineFactory(font
, scriptCode
, -1, status
);
229 if (LE_FAILURE(status
)) {
230 log_err("Could not create a LayoutEngine for script \'%s\'.\n", uscript_getShortName((UScriptCode
)scriptCode
));
241 static void U_CALLCONV
AccessTest(void)
243 LEErrorCode status
= LE_NO_ERROR
;
244 SimpleFontInstance
*font
= new SimpleFontInstance(12, status
);
245 LayoutEngine
*engine
= LayoutEngine::layoutEngineFactory(font
, arabScriptCode
, -1, status
);
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."
256 if (LE_FAILURE(status
)) {
257 log_err("Could not create LayoutEngine.\n");
261 glyphCount
= engine
->layoutChars(chars
, 8, 6, 20, TRUE
, 0.0, 0.0, status
);
263 if (LE_FAILURE(status
) || glyphCount
!= 6) {
264 log_err("layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
268 engine
->getGlyphs(glyphs
, status
);
269 engine
->getCharIndices(indices
, status
);
270 engine
->getGlyphPositions(positions
, status
);
272 if (LE_FAILURE(status
)) {
273 log_err("Could not get glyph, indices and position arrays.\n");
277 engine
->getGlyphs(extraBitGlyphs
, 0xFF000000L
, status
);
279 if (LE_FAILURE(status
)) {
280 log_err("getGlyphs(extraBitGlyphs, 0xFF000000L, status); failed.\n");
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
]);
291 status
= LE_NO_ERROR
;
292 engine
->getCharIndices(biasedIndices
, 1024, status
);
294 if (LE_FAILURE(status
)) {
295 log_err("getCharIndices(biasedIndices, 1024, status) failed.\n");
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
]);
306 status
= LE_NO_ERROR
;
307 for (glyph
= 0; glyph
<= glyphCount
; glyph
+= 1) {
308 float x
= 0.0, y
= 0.0;
310 engine
->getGlyphPosition(glyph
, x
, y
, status
);
312 if (LE_FAILURE(status
)) {
313 log_err("getGlyphPosition(%d, x, y, status) failed.\n", glyph
);
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]);
330 le_bool
compareResults(const char *testID
, TestResult
*expected
, TestResult
*actual
)
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
);
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
]);
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
]);
357 for (i
= 0; i
<= actual
->glyphCount
; i
+= 1) {
358 double xError
= uprv_fabs(actual
->positions
[i
* 2] - expected
->positions
[i
* 2]);
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]);
366 double yError
= uprv_fabs(actual
->positions
[i
* 2 + 1] - expected
->positions
[i
* 2 + 1]);
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]);
382 static void checkFontVersion(PortableFontInstance
*fontInstance
, const char *testVersionString
,
383 le_uint32 testChecksum
, const char *testID
)
385 le_uint32 fontChecksum
= fontInstance
->getFontChecksum();
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
;
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
);
399 log_info("Test %s: this may not be the same font used to generate the test data.\n", testID
);
401 if (uFontVersionString
!= NULL
) {
402 log_info("Your font's version string is \"%S\"\n", uFontVersionString
);
403 fontInstance
->deleteNameString(uFontVersionString
);
405 log_info("Your font's version string is \"%s\"\n", fontVersionString
);
406 fontInstance
->deleteNameString(fontVersionString
);
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");
414 /* Returns the path to icu/source/test/testdata/ */
415 const char *getSourceTestData() {
416 const char *srcDataDir
= NULL
;
418 srcDataDir
= U_TOPSRCDIR U_FILE_SEP_STRING
"test" U_FILE_SEP_STRING
"testdata" U_FILE_SEP_STRING
;
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");
424 /* We're in icu/source/test/letest/ */
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
;
436 const char *getPath(char buffer
[2048], const char *filename
) {
437 const char *testDataDirectory
= getSourceTestData();
439 strcpy(buffer
, testDataDirectory
);
440 strcat(buffer
, filename
);
445 le_uint32
*getHexArray(const UnicodeString
&numbers
, int32_t &arraySize
)
450 while((offset
= numbers
.indexOf(CH_COMMA
, offset
+ 1)) >= 0) {
454 le_uint32
*array
= NEW_ARRAY(le_uint32
, arraySize
);
457 le_int32 start
= 0, end
= 0;
460 // trim leading whitespace
461 while(u_isUWhiteSpace(numbers
[start
])) {
465 while((end
= numbers
.indexOf(CH_COMMA
, start
)) >= 0) {
466 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
470 sscanf(number
, "%x", &array
[count
++]);
472 // trim whitespace following the comma
473 while(u_isUWhiteSpace(numbers
[start
])) {
478 // trim trailing whitespace
479 end
= numbers
.length();
480 while(u_isUWhiteSpace(numbers
[end
- 1])) {
484 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
486 sscanf(number
, "%x", &array
[count
]);
491 float *getFloatArray(const UnicodeString
&numbers
, int32_t &arraySize
)
496 while((offset
= numbers
.indexOf(CH_COMMA
, offset
+ 1)) >= 0) {
500 float *array
= NEW_ARRAY(float, arraySize
);
503 le_int32 start
= 0, end
= 0;
506 // trim leading whitespace
507 while(u_isUWhiteSpace(numbers
[start
])) {
511 while((end
= numbers
.indexOf(CH_COMMA
, start
)) >= 0) {
512 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
516 sscanf(number
, "%f", &array
[count
++]);
518 // trim whiteapce following the comma
519 while(u_isUWhiteSpace(numbers
[start
])) {
524 while(u_isUWhiteSpace(numbers
[start
])) {
528 // trim trailing whitespace
529 end
= numbers
.length();
530 while(u_isUWhiteSpace(numbers
[end
- 1])) {
534 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
536 sscanf(number
, "%f", &array
[count
]);
541 LEFontInstance
*openFont(const char *fontName
, const char *checksum
, const char *version
, const char *testID
)
544 PortableFontInstance
*font
;
545 LEErrorCode fontStatus
= LE_NO_ERROR
;
548 font
= new PortableFontInstance(getPath(path
, fontName
), 12, fontStatus
);
550 if (LE_FAILURE(fontStatus
)) {
551 log_info("Test %s: can't open font %s - test skipped.\n", testID
, fontName
);
557 sscanf(checksum
, "%x", &cksum
);
559 checkFontVersion(font
, version
, cksum
, testID
);
566 static void U_CALLCONV
DataDrivenTest(void)
568 #if !UCONFIG_NO_REGULAR_EXPRESSIONS
569 UErrorCode status
= U_ZERO_ERROR
;
571 const char *testFilePath
= getPath(path
, "letest.xml");
573 UXMLParser
*parser
= UXMLParser::createParser(status
);
574 UXMLElement
*root
= parser
->parseFile(testFilePath
, status
);
577 log_err("Could not open the test data file: %s\n", testFilePath
);
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");
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");
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");
599 const UXMLElement
*testCase
;
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
;
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
;
621 uscript_getCode(script
, &scriptCode
, 1, &status
);
622 if (LE_FAILURE(status
)) {
623 log_err("invalid script name: %s.\n", script
);
628 languageCode
= getLanguageCode(lang
);
630 if (languageCode
< 0) {
631 log_err("invalid language name: %s.\n", lang
);
636 while((element
= testCase
->nextChildElement(ec
)) != NULL
) {
637 UnicodeString tag
= element
->getTagName();
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
));
645 font
= openFont(fontName
, fontCksum
, fontVer
, id
);
646 freeCString(fontCksum
);
647 freeCString(fontVer
);
648 freeCString(fontName
);
651 // warning message already displayed...
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
);
665 char *cTag
= getCString(&tag
);
667 log_info("Test %s: unknown element with tag \"%s\"\n", id
, cTag
);
672 // TODO: make sure that the font, test-text, result-glyphs, result-indices and result-positions
673 // have all been provided
675 LEErrorCode fontStatus
= LE_NO_ERROR
;
677 font
= new SimpleFontInstance(12, fontStatus
);
678 typoFlags
|= 0x80000000L
; // use CharSubstitutionFilter...
681 expected
.glyphs
= (LEGlyphID
*) getHexArray(glyphs
, glyphCount
);
682 expected
.indices
= (le_int32
*) getHexArray(indices
, indexCount
);
683 expected
.positions
= getFloatArray(positions
, positionCount
);
685 expected
.glyphCount
= glyphCount
;
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
);
693 engine
= LayoutEngine::layoutEngineFactory(font
, scriptCode
, languageCode
, typoFlags
, success
);
695 if (LE_FAILURE(success
)) {
696 log_err("Test %s: could not create a LayoutEngine.\n", id
);
700 actual
.glyphCount
= engine
->layoutChars(text
.getBuffer(), 0, charCount
, charCount
, getRTL(text
), 0, 0, success
);
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);
706 engine
->getGlyphs(actual
.glyphs
, success
);
707 engine
->getCharIndices(actual
.indices
, success
);
708 engine
->getGlyphPositions(actual
.positions
, success
);
710 compareResults(id
, &expected
, &actual
);
712 DELETE_ARRAY(actual
.positions
);
713 DELETE_ARRAY(actual
.indices
);
714 DELETE_ARRAY(actual
.glyphs
);
718 log_verbose("OK - %4d glyphs: %s\n", actual
.glyphCount
, id
);
720 DELETE_ARRAY(expected
.positions
);
721 DELETE_ARRAY(expected
.indices
);
722 DELETE_ARRAY(expected
.glyphs
);
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
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.
752 static void U_CALLCONV
GlyphToCharTest(void)
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
;
761 * This is the same text that's in <icu>/source/samples/layout/Sample.txt
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,
876 le_int32 charCount
= LE_ARRAY_SIZE(chars
);
877 le_int32 charIndex
= 0, lineNumber
= 1;
878 const float lineWidth
= 600;
880 font
= new SimpleFontInstance(12, status
);
882 if (LE_FAILURE(status
)) {
886 fontRuns
.add(font
, charCount
);
888 paragraphLayout
= new ParagraphLayout(chars
, charCount
, &fontRuns
, NULL
, NULL
, NULL
, 0, FALSE
, status
);
890 if (LE_FAILURE(status
)) {
894 paragraphLayout
->reflow();
895 while ((line
= paragraphLayout
->nextLine(lineWidth
)) != NULL
) {
896 le_int32 runCount
= line
->countRuns();
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();
903 if (visualRun
->getDirection() == UBIDI_RTL
) {
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.
910 for(le_int32 i
= glyphCount
- 1; i
>= 0; i
-= 1) {
911 le_int32 ix
= glyphToCharMap
[i
];
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...
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.
931 le_int32 minIndex
= 0x7FFFFFFF, maxIndex
= -1;
933 for(le_int32 i
= 0; i
< glyphCount
; i
+= 1) {
934 le_int32 ix
= glyphToCharMap
[i
];
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...
951 charIndex
= maxIndex
+ 1;
958 delete paragraphLayout
;
969 static void addAllTests(TestNode
**root
)
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");
978 #ifndef USING_ICULEHB
983 /* returns the path to icu/source/data/out */
984 static const char *ctest_dataOutDir()
986 static const char *dataOutDir
= NULL
;
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.
1000 #if defined (U_TOPBUILDDIR)
1002 dataOutDir
= U_TOPBUILDDIR
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
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"
1011 static char p
[sizeof(__FILE__
) + 20];
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 '\' */
1025 if (pBackSlash
!= NULL
) {
1026 /* We found and truncated three names from the path.
1027 * Now append "source\data" and set the environment
1029 strcpy(pBackSlash
, U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
);
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");
1037 dataOutDir
= ".." U_FILE_SEP_STRING
".." U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
1040 dataOutDir
= ".." U_FILE_SEP_STRING
".." U_FILE_SEP_STRING
".." U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
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.
1058 static void ctest_setICU_DATA() {
1060 /* No location for the data dir was identifiable.
1061 * Add other fallbacks for the test data location here if the need arises
1063 if (getenv("ICU_DATA") == NULL
) {
1064 /* If ICU_DATA isn't set, set it to the usual location */
1065 u_setDataDirectory(ctest_dataOutDir());
1069 int main(int argc
, char* argv
[])
1071 int32_t nerrors
= 0;
1072 TestNode
*root
= NULL
;
1073 UErrorCode errorCode
= U_ZERO_ERROR
;
1074 UDate startTime
, endTime
;
1077 startTime
= uprv_getUTCtime();
1079 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
1080 /* Error already displayed. */
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.
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.
1094 if (U_FAILURE(errorCode
)) {
1096 "#### Note: ICU Init without build-specific setDataDirectory() failed.\n");
1100 errorCode
= U_ZERO_ERROR
;
1102 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
1103 /* Error already displayed. */
1106 /* Initialize ICU */
1107 ctest_setICU_DATA(); /* u_setDataDirectory() must happen Before u_init() */
1110 if (U_FAILURE(errorCode
)) {
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
));
1119 nerrors
= runTestRequest(root
, argc
, argv
);
1121 cleanUpTestTree(root
);
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
));