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