• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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