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