2 *******************************************************************************
4 * Copyright (C) 1999-2013, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
8 * file name: letest.cpp
10 * created on: 11/06/2000
11 * created by: Eric R. Mader
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"
22 #include "layout/LETypes.h"
23 #include "layout/LEScripts.h"
24 #include "layout/LayoutEngine.h"
26 #include "layout/ParagraphLayout.h"
27 #include "layout/RunArrays.h"
29 #include "PortableFontInstance.h"
30 #include "SimpleFontInstance.h"
35 #include "xmlparser.h"
36 #include "putilimp.h" // for uprv_getUTCtime()
43 #define CH_COMMA 0x002C
47 static void U_CALLCONV
ScriptTest(void)
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
);
54 static void U_CALLCONV
ParamTest(void)
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;
64 glyphCount
= engine
->getGlyphCount();
65 if (glyphCount
!= 0) {
66 log_err("Calling getGlyphCount() on an empty layout returned %d.\n", glyphCount
);
69 glyphs
= NEW_ARRAY(LEGlyphID
, glyphCount
+ 10);
70 indices
= NEW_ARRAY(le_int32
, glyphCount
+ 10);
71 positions
= NEW_ARRAY(float, glyphCount
+ 10);
73 engine
->getGlyphs(NULL
, status
);
75 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
76 log_err("Calling getGlyphs(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
80 engine
->getGlyphs(glyphs
, status
);
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");
87 engine
->getGlyphs(NULL
, 0xFF000000L
, status
);
89 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
90 log_err("Calling getGlyphs(NULL, 0xFF000000L, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
94 engine
->getGlyphs(glyphs
, 0xFF000000L
, status
);
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");
100 status
= LE_NO_ERROR
;
101 engine
->getCharIndices(NULL
, status
);
103 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
104 log_err("Calling getCharIndices(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
107 status
= LE_NO_ERROR
;
108 engine
->getCharIndices(indices
, status
);
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");
114 status
= LE_NO_ERROR
;
115 engine
->getCharIndices(NULL
, 1024, status
);
117 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
118 log_err("Calling getCharIndices(NULL, 1024, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
121 status
= LE_NO_ERROR
;
122 engine
->getCharIndices(indices
, 1024, status
);
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");
128 status
= LE_NO_ERROR
;
129 engine
->getGlyphPositions(NULL
, status
);
131 if (status
!= LE_ILLEGAL_ARGUMENT_ERROR
) {
132 log_err("Calling getGlyphPositions(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
135 status
= LE_NO_ERROR
;
136 engine
->getGlyphPositions(positions
, status
);
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");
142 DELETE_ARRAY(positions
);
143 DELETE_ARRAY(indices
);
144 DELETE_ARRAY(glyphs
);
146 status
= LE_NO_ERROR
;
147 glyphCount
= engine
->layoutChars(NULL
, 0, 0, 0, FALSE
, 0.0, 0.0, status
);
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");
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."
159 status
= LE_NO_ERROR
;
160 glyphCount
= engine
->layoutChars(chars
, -1, 6, 20, TRUE
, 0.0, 0.0, status
);
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");
166 status
= LE_NO_ERROR
;
167 glyphCount
= engine
->layoutChars(chars
, 8, -1, 20, TRUE
, 0.0, 0.0, status
);
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");
173 status
= LE_NO_ERROR
;
174 glyphCount
= engine
->layoutChars(chars
, 8, 6, -1, TRUE
, 0.0, 0.0, status
);
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");
180 status
= LE_NO_ERROR
;
181 glyphCount
= engine
->layoutChars(chars
, 8, 6, 10, TRUE
, 0.0, 0.0, status
);
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");
187 float x
= 0.0, y
= 0.0;
189 status
= LE_NO_ERROR
;
190 glyphCount
= engine
->layoutChars(chars
, 8, 6, 20, TRUE
, 0.0, 0.0, status
);
192 if (LE_FAILURE(status
)) {
193 log_err("Calling layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
197 engine
->getGlyphPosition(-1, x
, y
, status
);
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");
203 status
= LE_NO_ERROR
;
204 engine
->getGlyphPosition(glyphCount
+ 1, x
, y
, status
);
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");
217 static void U_CALLCONV
FactoryTest(void)
219 LEErrorCode status
= LE_NO_ERROR
;
220 SimpleFontInstance
*font
= new SimpleFontInstance(12, status
);
221 LayoutEngine
*engine
= NULL
;
223 for(le_int32 scriptCode
= 0; scriptCode
< scriptCodeCount
; scriptCode
+= 1) {
224 status
= LE_NO_ERROR
;
225 engine
= LayoutEngine::layoutEngineFactory(font
, scriptCode
, -1, status
);
227 if (LE_FAILURE(status
)) {
228 log_err("Could not create a LayoutEngine for script \'%s\'.\n", uscript_getShortName((UScriptCode
)scriptCode
));
239 static void U_CALLCONV
AccessTest(void)
241 LEErrorCode status
= LE_NO_ERROR
;
242 SimpleFontInstance
*font
= new SimpleFontInstance(12, status
);
243 LayoutEngine
*engine
= LayoutEngine::layoutEngineFactory(font
, arabScriptCode
, -1, status
);
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."
254 if (LE_FAILURE(status
)) {
255 log_err("Could not create LayoutEngine.\n");
259 glyphCount
= engine
->layoutChars(chars
, 8, 6, 20, TRUE
, 0.0, 0.0, status
);
261 if (LE_FAILURE(status
) || glyphCount
!= 6) {
262 log_err("layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
266 engine
->getGlyphs(glyphs
, status
);
267 engine
->getCharIndices(indices
, status
);
268 engine
->getGlyphPositions(positions
, status
);
270 if (LE_FAILURE(status
)) {
271 log_err("Could not get glyph, indices and position arrays.\n");
275 engine
->getGlyphs(extraBitGlyphs
, 0xFF000000L
, status
);
277 if (LE_FAILURE(status
)) {
278 log_err("getGlyphs(extraBitGlyphs, 0xFF000000L, status); failed.\n");
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
]);
289 status
= LE_NO_ERROR
;
290 engine
->getCharIndices(biasedIndices
, 1024, status
);
292 if (LE_FAILURE(status
)) {
293 log_err("getCharIndices(biasedIndices, 1024, status) failed.\n");
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
]);
304 status
= LE_NO_ERROR
;
305 for (glyph
= 0; glyph
<= glyphCount
; glyph
+= 1) {
306 float x
= 0.0, y
= 0.0;
308 engine
->getGlyphPosition(glyph
, x
, y
, status
);
310 if (LE_FAILURE(status
)) {
311 log_err("getGlyphPosition(%d, x, y, status) failed.\n", glyph
);
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]);
328 le_bool
compareResults(const char *testID
, TestResult
*expected
, TestResult
*actual
)
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
);
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
]);
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
]);
355 for (i
= 0; i
<= actual
->glyphCount
; i
+= 1) {
356 double xError
= uprv_fabs(actual
->positions
[i
* 2] - expected
->positions
[i
* 2]);
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]);
364 double yError
= uprv_fabs(actual
->positions
[i
* 2 + 1] - expected
->positions
[i
* 2 + 1]);
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]);
380 static void checkFontVersion(PortableFontInstance
*fontInstance
, const char *testVersionString
,
381 le_uint32 testChecksum
, const char *testID
)
383 le_uint32 fontChecksum
= fontInstance
->getFontChecksum();
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
;
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
);
397 log_info("Test %s: this may not be the same font used to generate the test data.\n", testID
);
399 if (uFontVersionString
!= NULL
) {
400 log_info("Your font's version string is \"%S\"\n", uFontVersionString
);
401 fontInstance
->deleteNameString(uFontVersionString
);
403 log_info("Your font's version string is \"%s\"\n", fontVersionString
);
404 fontInstance
->deleteNameString(fontVersionString
);
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");
412 /* Returns the path to icu/source/test/testdata/ */
413 const char *getSourceTestData() {
414 const char *srcDataDir
= NULL
;
416 srcDataDir
= U_TOPSRCDIR U_FILE_SEP_STRING
"test" U_FILE_SEP_STRING
"testdata" U_FILE_SEP_STRING
;
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");
422 /* We're in icu/source/test/letest/ */
425 /* We're in icu/source/test/letest/(Debug|Release) */
426 srcDataDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"test"U_FILE_SEP_STRING
"testdata"U_FILE_SEP_STRING
;
433 const char *getPath(char buffer
[2048], const char *filename
) {
434 const char *testDataDirectory
= getSourceTestData();
436 strcpy(buffer
, testDataDirectory
);
437 strcat(buffer
, filename
);
442 le_uint32
*getHexArray(const UnicodeString
&numbers
, int32_t &arraySize
)
447 while((offset
= numbers
.indexOf(CH_COMMA
, offset
+ 1)) >= 0) {
451 le_uint32
*array
= NEW_ARRAY(le_uint32
, arraySize
);
454 le_int32 start
= 0, end
= 0;
457 // trim leading whitespace
458 while(u_isUWhiteSpace(numbers
[start
])) {
462 while((end
= numbers
.indexOf(CH_COMMA
, start
)) >= 0) {
463 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
467 sscanf(number
, "%x", &array
[count
++]);
469 // trim whitespace following the comma
470 while(u_isUWhiteSpace(numbers
[start
])) {
475 // trim trailing whitespace
476 end
= numbers
.length();
477 while(u_isUWhiteSpace(numbers
[end
- 1])) {
481 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
483 sscanf(number
, "%x", &array
[count
]);
488 float *getFloatArray(const UnicodeString
&numbers
, int32_t &arraySize
)
493 while((offset
= numbers
.indexOf(CH_COMMA
, offset
+ 1)) >= 0) {
497 float *array
= NEW_ARRAY(float, arraySize
);
500 le_int32 start
= 0, end
= 0;
503 // trim leading whitespace
504 while(u_isUWhiteSpace(numbers
[start
])) {
508 while((end
= numbers
.indexOf(CH_COMMA
, start
)) >= 0) {
509 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
513 sscanf(number
, "%f", &array
[count
++]);
515 // trim whiteapce following the comma
516 while(u_isUWhiteSpace(numbers
[start
])) {
521 while(u_isUWhiteSpace(numbers
[start
])) {
525 // trim trailing whitespace
526 end
= numbers
.length();
527 while(u_isUWhiteSpace(numbers
[end
- 1])) {
531 len
= numbers
.extract(start
, end
- start
, number
, ARRAY_SIZE(number
), US_INV
);
533 sscanf(number
, "%f", &array
[count
]);
538 LEFontInstance
*openFont(const char *fontName
, const char *checksum
, const char *version
, const char *testID
)
541 PortableFontInstance
*font
;
542 LEErrorCode fontStatus
= LE_NO_ERROR
;
545 font
= new PortableFontInstance(getPath(path
, fontName
), 12, fontStatus
);
547 if (LE_FAILURE(fontStatus
)) {
548 log_info("Test %s: can't open font %s - test skipped.\n", testID
, fontName
);
554 sscanf(checksum
, "%x", &cksum
);
556 checkFontVersion(font
, version
, cksum
, testID
);
563 static void U_CALLCONV
DataDrivenTest(void)
565 #if !UCONFIG_NO_REGULAR_EXPRESSIONS
566 UErrorCode status
= U_ZERO_ERROR
;
568 const char *testFilePath
= getPath(path
, "letest.xml");
570 UXMLParser
*parser
= UXMLParser::createParser(status
);
571 UXMLElement
*root
= parser
->parseFile(testFilePath
, status
);
574 log_err("Could not open the test data file: %s\n", testFilePath
);
579 UnicodeString test_case
= UNICODE_STRING_SIMPLE("test-case");
580 UnicodeString test_text
= UNICODE_STRING_SIMPLE("test-text");
581 UnicodeString test_font
= UNICODE_STRING_SIMPLE("test-font");
582 UnicodeString result_glyphs
= UNICODE_STRING_SIMPLE("result-glyphs");
583 UnicodeString result_indices
= UNICODE_STRING_SIMPLE("result-indices");
584 UnicodeString result_positions
= UNICODE_STRING_SIMPLE("result-positions");
586 // test-case attributes
587 UnicodeString id_attr
= UNICODE_STRING_SIMPLE("id");
588 UnicodeString script_attr
= UNICODE_STRING_SIMPLE("script");
589 UnicodeString lang_attr
= UNICODE_STRING_SIMPLE("lang");
591 // test-font attributes
592 UnicodeString name_attr
= UNICODE_STRING_SIMPLE("name");
593 UnicodeString ver_attr
= UNICODE_STRING_SIMPLE("version");
594 UnicodeString cksum_attr
= UNICODE_STRING_SIMPLE("checksum");
596 const UXMLElement
*testCase
;
599 while((testCase
= root
->nextChildElement(tc
)) != NULL
) {
600 if (testCase
->getTagName().compare(test_case
) == 0) {
601 char *id
= getCString(testCase
->getAttribute(id_attr
));
602 char *script
= getCString(testCase
->getAttribute(script_attr
));
603 char *lang
= getCString(testCase
->getAttribute(lang_attr
));
604 LEFontInstance
*font
= NULL
;
605 const UXMLElement
*element
;
607 int32_t charCount
= 0;
608 int32_t typoFlags
= 3; // kerning + ligatures...
609 UScriptCode scriptCode
;
610 le_int32 languageCode
= -1;
611 UnicodeString text
, glyphs
, indices
, positions
;
612 int32_t glyphCount
= 0, indexCount
= 0, positionCount
= 0;
613 TestResult expected
= {0, NULL
, NULL
, NULL
};
614 TestResult actual
= {0, NULL
, NULL
, NULL
};
615 LEErrorCode success
= LE_NO_ERROR
;
616 LayoutEngine
*engine
= NULL
;
618 uscript_getCode(script
, &scriptCode
, 1, &status
);
619 if (LE_FAILURE(status
)) {
620 log_err("invalid script name: %s.\n", script
);
625 languageCode
= getLanguageCode(lang
);
627 if (languageCode
< 0) {
628 log_err("invalid language name: %s.\n", lang
);
633 while((element
= testCase
->nextChildElement(ec
)) != NULL
) {
634 UnicodeString tag
= element
->getTagName();
636 // TODO: make sure that each element is only used once.
637 if (tag
.compare(test_font
) == 0) {
638 char *fontName
= getCString(element
->getAttribute(name_attr
));
639 char *fontVer
= getCString(element
->getAttribute(ver_attr
));
640 char *fontCksum
= getCString(element
->getAttribute(cksum_attr
));
642 font
= openFont(fontName
, fontCksum
, fontVer
, id
);
643 freeCString(fontCksum
);
644 freeCString(fontVer
);
645 freeCString(fontName
);
648 // warning message already displayed...
651 } else if (tag
.compare(test_text
) == 0) {
652 text
= element
->getText(TRUE
);
653 charCount
= text
.length();
654 } else if (tag
.compare(result_glyphs
) == 0) {
655 glyphs
= element
->getText(TRUE
);
656 } else if (tag
.compare(result_indices
) == 0) {
657 indices
= element
->getText(TRUE
);
658 } else if (tag
.compare(result_positions
) == 0) {
659 positions
= element
->getText(TRUE
);
662 char *cTag
= getCString(&tag
);
664 log_info("Test %s: unknown element with tag \"%s\"\n", id
, cTag
);
669 // TODO: make sure that the font, test-text, result-glyphs, result-indices and result-positions
670 // have all been provided
672 LEErrorCode fontStatus
= LE_NO_ERROR
;
674 font
= new SimpleFontInstance(12, fontStatus
);
675 typoFlags
|= 0x80000000L
; // use CharSubstitutionFilter...
678 expected
.glyphs
= (LEGlyphID
*) getHexArray(glyphs
, glyphCount
);
679 expected
.indices
= (le_int32
*) getHexArray(indices
, indexCount
);
680 expected
.positions
= getFloatArray(positions
, positionCount
);
682 expected
.glyphCount
= glyphCount
;
684 if (glyphCount
< charCount
|| indexCount
!= glyphCount
|| positionCount
< glyphCount
* 2 + 2) {
685 log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n",
686 id
, charCount
, glyphCount
, indexCount
, positionCount
);
690 engine
= LayoutEngine::layoutEngineFactory(font
, scriptCode
, languageCode
, typoFlags
, success
);
692 if (LE_FAILURE(success
)) {
693 log_err("Test %s: could not create a LayoutEngine.\n", id
);
697 actual
.glyphCount
= engine
->layoutChars(text
.getBuffer(), 0, charCount
, charCount
, getRTL(text
), 0, 0, success
);
699 actual
.glyphs
= NEW_ARRAY(LEGlyphID
, actual
.glyphCount
);
700 actual
.indices
= NEW_ARRAY(le_int32
, actual
.glyphCount
);
701 actual
.positions
= NEW_ARRAY(float, actual
.glyphCount
* 2 + 2);
703 engine
->getGlyphs(actual
.glyphs
, success
);
704 engine
->getCharIndices(actual
.indices
, success
);
705 engine
->getGlyphPositions(actual
.positions
, success
);
707 compareResults(id
, &expected
, &actual
);
709 DELETE_ARRAY(actual
.positions
);
710 DELETE_ARRAY(actual
.indices
);
711 DELETE_ARRAY(actual
.glyphs
);
716 DELETE_ARRAY(expected
.positions
);
717 DELETE_ARRAY(expected
.indices
);
718 DELETE_ARRAY(expected
.glyphs
);
739 * Build a paragraph that contains a mixture of left to right and right to left text.
740 * Break it into multiple lines and make sure that the glyphToCharMap for run in each
743 * Note: it might be a good idea to also check the glyphs and positions for each run,
744 * that we get the expected number of runs per line and that the line breaks are where
745 * we expect them to be. Really, it would be a good idea to make a whole test suite
746 * for ParagraphLayout.
748 static void U_CALLCONV
GlyphToCharTest(void)
750 LEErrorCode status
= LE_NO_ERROR
;
751 LEFontInstance
*font
;
752 FontRuns
fontRuns(0);
753 ParagraphLayout
*paragraphLayout
;
754 const ParagraphLayout::Line
*line
;
756 * This is the same text that's in <icu>/source/samples/layout/Sample.txt
758 LEUnicode chars
[] = {
759 /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
760 0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
761 0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
762 0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
763 0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
764 0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
765 0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
766 0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
767 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
768 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
769 0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
770 0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
771 0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
772 0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
773 0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
774 0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
775 0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
776 0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
777 0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
778 0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
779 0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
780 0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
781 0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
782 0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
783 0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
784 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
785 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
786 0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
787 0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
788 0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
789 0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
790 0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
791 0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
792 0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
793 0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
794 0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
795 0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
796 0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
797 0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
798 0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
799 0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
800 0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
801 0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
802 0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
803 0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
804 0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
805 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
806 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
807 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
808 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
809 0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
810 0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
811 0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
812 0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
813 0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
814 0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
815 0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
816 0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
817 0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
818 0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
819 0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
820 0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
821 0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
822 0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
823 0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
824 0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
825 0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
826 0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
827 0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
828 0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
829 0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
830 0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
831 0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
832 0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
833 0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
834 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
835 0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
836 0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
837 0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
838 0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
839 0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
840 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
841 0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
842 0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
843 0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
844 0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
845 0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
846 0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
847 0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
848 0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
849 0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
850 0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
851 0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
852 0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
853 0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
854 0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
855 0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
856 0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
857 0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
858 0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
859 0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
860 0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
861 0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
862 0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
863 0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
864 0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
865 0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
866 0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
867 0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
868 0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
871 le_int32 charCount
= LE_ARRAY_SIZE(chars
);
872 le_int32 charIndex
= 0, lineNumber
= 1;
873 const float lineWidth
= 600;
875 font
= new SimpleFontInstance(12, status
);
877 if (LE_FAILURE(status
)) {
881 fontRuns
.add(font
, charCount
);
883 paragraphLayout
= new ParagraphLayout(chars
, charCount
, &fontRuns
, NULL
, NULL
, NULL
, 0, FALSE
, status
);
885 if (LE_FAILURE(status
)) {
889 paragraphLayout
->reflow();
890 while ((line
= paragraphLayout
->nextLine(lineWidth
)) != NULL
) {
891 le_int32 runCount
= line
->countRuns();
893 for(le_int32 run
= 0; run
< runCount
; run
+= 1) {
894 const ParagraphLayout::VisualRun
*visualRun
= line
->getVisualRun(run
);
895 le_int32 glyphCount
= visualRun
->getGlyphCount();
896 const le_int32
*glyphToCharMap
= visualRun
->getGlyphToCharMap();
898 if (visualRun
->getDirection() == UBIDI_RTL
) {
900 * For a right to left run, make sure that the character indices
901 * increase from the right most glyph to the left most glyph. If
902 * there are any one to many glyph substitutions, we might get several
903 * glyphs in a row with the same character index.
905 for(le_int32 i
= glyphCount
- 1; i
>= 0; i
-= 1) {
906 le_int32 ix
= glyphToCharMap
[i
];
908 if (ix
!= charIndex
) {
909 if (ix
!= charIndex
- 1) {
910 log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
911 i
, lineNumber
, charIndex
, ix
);
912 goto close_paragraph
; // once there's one error, we can't count on anything else...
920 * We can't just check the order of the character indices
921 * for left to right runs because Indic text might have been
922 * reordered. What we can do is find the minimum and maximum
923 * character indices in the run and make sure that the minimum
924 * is equal to charIndex and then advance charIndex to the maximum.
926 le_int32 minIndex
= 0x7FFFFFFF, maxIndex
= -1;
928 for(le_int32 i
= 0; i
< glyphCount
; i
+= 1) {
929 le_int32 ix
= glyphToCharMap
[i
];
940 if (minIndex
!= charIndex
) {
941 log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
942 run
, lineNumber
, charIndex
, minIndex
);
943 goto close_paragraph
; // once there's one error, we can't count on anything else...
946 charIndex
= maxIndex
+ 1;
953 delete paragraphLayout
;
963 static void addAllTests(TestNode
**root
)
965 addTest(root
, &ScriptTest
, "api/ScriptTest");
966 addTest(root
, &ParamTest
, "api/ParameterTest");
967 addTest(root
, &FactoryTest
, "api/FactoryTest");
968 addTest(root
, &AccessTest
, "layout/AccessTest");
969 addTest(root
, &DataDrivenTest
, "layout/DataDrivenTest");
970 addTest(root
, &GlyphToCharTest
, "paragraph/GlyphToCharTest");
975 /* returns the path to icu/source/data/out */
976 static const char *ctest_dataOutDir()
978 static const char *dataOutDir
= NULL
;
984 /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
985 // to point to the top of the build hierarchy, which may or
986 // may not be the same as the source directory, depending on
987 // the configure options used. At any rate,
988 // set the data path to the built data from this directory.
989 // The value is complete with quotes, so it can be used
990 // as-is as a string constant.
992 #if defined (U_TOPBUILDDIR)
994 dataOutDir
= U_TOPBUILDDIR
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
998 /* On Windows, the file name obtained from __FILE__ includes a full path.
999 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
1000 * Change to "wherever\icu\source\data"
1003 static char p
[sizeof(__FILE__
) + 20];
1007 strcpy(p
, __FILE__
);
1008 /* We want to back over three '\' chars. */
1009 /* Only Windows should end up here, so looking for '\' is safe. */
1010 for (i
=1; i
<=3; i
++) {
1011 pBackSlash
= strrchr(p
, U_FILE_SEP_CHAR
);
1012 if (pBackSlash
!= NULL
) {
1013 *pBackSlash
= 0; /* Truncate the string at the '\' */
1017 if (pBackSlash
!= NULL
) {
1018 /* We found and truncated three names from the path.
1019 * Now append "source\data" and set the environment
1021 strcpy(pBackSlash
, U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
);
1025 /* __FILE__ on MSVC7 does not contain the directory */
1026 FILE *file
= fopen(".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"Makefile.in", "r");
1029 dataOutDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
1032 dataOutDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
1041 /* ctest_setICU_DATA - if the ICU_DATA environment variable is not already
1042 * set, try to deduce the directory in which ICU was built,
1043 * and set ICU_DATA to "icu/source/data" in that location.
1044 * The intent is to allow the tests to have a good chance
1045 * of running without requiring that the user manually set
1046 * ICU_DATA. Common data isn't a problem, since it is
1047 * picked up via a static (build time) reference, but the
1048 * tests dynamically load some data.
1050 static void ctest_setICU_DATA() {
1052 /* No location for the data dir was identifiable.
1053 * Add other fallbacks for the test data location here if the need arises
1055 if (getenv("ICU_DATA") == NULL
) {
1056 /* If ICU_DATA isn't set, set it to the usual location */
1057 u_setDataDirectory(ctest_dataOutDir());
1061 int main(int argc
, char* argv
[])
1063 int32_t nerrors
= 0;
1064 TestNode
*root
= NULL
;
1065 UErrorCode errorCode
= U_ZERO_ERROR
;
1066 UDate startTime
, endTime
;
1069 startTime
= uprv_getUTCtime();
1071 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
1072 /* Error already displayed. */
1076 /* Check whether ICU will initialize without forcing the build data directory into
1077 * the ICU_DATA path. Success here means either the data dll contains data, or that
1078 * this test program was run with ICU_DATA set externally. Failure of this check
1079 * is normal when ICU data is not packaged into a shared library.
1081 * Whether or not this test succeeds, we want to cleanup and reinitialize
1082 * with a data path so that data loading from individual files can be tested.
1086 if (U_FAILURE(errorCode
)) {
1088 "#### Note: ICU Init without build-specific setDataDirectory() failed.\n");
1092 errorCode
= U_ZERO_ERROR
;
1094 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
1095 /* Error already displayed. */
1098 /* Initialize ICU */
1099 ctest_setICU_DATA(); /* u_setDataDirectory() must happen Before u_init() */
1102 if (U_FAILURE(errorCode
)) {
1104 "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
1105 "*** Check the ICU_DATA environment variable and \n"
1106 "*** check that the data files are present.\n", argv
[0], u_errorName(errorCode
));
1111 nerrors
= runTestRequest(root
, argc
, argv
);
1113 cleanUpTestTree(root
);
1116 endTime
= uprv_getUTCtime();
1117 diffTime
= (int32_t)(endTime
- startTime
);
1118 printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
1119 (int)((diffTime%U_MILLIS_PER_DAY
)/U_MILLIS_PER_HOUR
),
1120 (int)((diffTime%U_MILLIS_PER_HOUR
)/U_MILLIS_PER_MINUTE
),
1121 (int)((diffTime%U_MILLIS_PER_MINUTE
)/U_MILLIS_PER_SECOND
),
1122 (int)(diffTime%U_MILLIS_PER_SECOND
));