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"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
427 }
428 #endif
429
430 return srcDataDir;
431 }
432
getPath(char buffer[2048],const char * filename)433 const char *getPath(char buffer[2048], const char *filename) {
434 const char *testDataDirectory = getSourceTestData();
435
436 strcpy(buffer, testDataDirectory);
437 strcat(buffer, filename);
438
439 return buffer;
440 }
441
getHexArray(const UnicodeString & numbers,int32_t & arraySize)442 le_uint32 *getHexArray(const UnicodeString &numbers, int32_t &arraySize)
443 {
444 int32_t offset = -1;
445
446 arraySize = 1;
447 while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
448 arraySize += 1;
449 }
450
451 le_uint32 *array = NEW_ARRAY(le_uint32, arraySize);
452 char number[16];
453 le_int32 count = 0;
454 le_int32 start = 0, end = 0;
455 le_int32 len = 0;
456
457 // trim leading whitespace
458 while(u_isUWhiteSpace(numbers[start])) {
459 start += 1;
460 }
461
462 while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
463 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
464 number[len] = '\0';
465 start = end + 1;
466
467 sscanf(number, "%x", &array[count++]);
468
469 // trim whitespace following the comma
470 while(u_isUWhiteSpace(numbers[start])) {
471 start += 1;
472 }
473 }
474
475 // trim trailing whitespace
476 end = numbers.length();
477 while(u_isUWhiteSpace(numbers[end - 1])) {
478 end -= 1;
479 }
480
481 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
482 number[len] = '\0';
483 sscanf(number, "%x", &array[count]);
484
485 return array;
486 }
487
getFloatArray(const UnicodeString & numbers,int32_t & arraySize)488 float *getFloatArray(const UnicodeString &numbers, int32_t &arraySize)
489 {
490 int32_t offset = -1;
491
492 arraySize = 1;
493 while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
494 arraySize += 1;
495 }
496
497 float *array = NEW_ARRAY(float, arraySize);
498 char number[32];
499 le_int32 count = 0;
500 le_int32 start = 0, end = 0;
501 le_int32 len = 0;
502
503 // trim leading whitespace
504 while(u_isUWhiteSpace(numbers[start])) {
505 start += 1;
506 }
507
508 while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
509 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
510 number[len] = '\0';
511 start = end + 1;
512
513 sscanf(number, "%f", &array[count++]);
514
515 // trim whiteapce following the comma
516 while(u_isUWhiteSpace(numbers[start])) {
517 start += 1;
518 }
519 }
520
521 while(u_isUWhiteSpace(numbers[start])) {
522 start += 1;
523 }
524
525 // trim trailing whitespace
526 end = numbers.length();
527 while(u_isUWhiteSpace(numbers[end - 1])) {
528 end -= 1;
529 }
530
531 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
532 number[len] = '\0';
533 sscanf(number, "%f", &array[count]);
534
535 return array;
536 }
537
openFont(const char * fontName,const char * checksum,const char * version,const char * testID)538 LEFontInstance *openFont(const char *fontName, const char *checksum, const char *version, const char *testID)
539 {
540 char path[2048];
541 PortableFontInstance *font;
542 LEErrorCode fontStatus = LE_NO_ERROR;
543
544
545 font = new PortableFontInstance(getPath(path, fontName), 12, fontStatus);
546
547 if (LE_FAILURE(fontStatus)) {
548 log_info("Test %s: can't open font %s - test skipped.\n", testID, fontName);
549 delete font;
550 return NULL;
551 } else {
552 le_uint32 cksum = 0;
553
554 sscanf(checksum, "%x", &cksum);
555
556 checkFontVersion(font, version, cksum, testID);
557 }
558
559 return font;
560 }
561
562 U_CDECL_BEGIN
DataDrivenTest(void)563 static void U_CALLCONV DataDrivenTest(void)
564 {
565 #if !UCONFIG_NO_REGULAR_EXPRESSIONS
566 UErrorCode status = U_ZERO_ERROR;
567 char path[2048];
568 const char *testFilePath = getPath(path, "letest.xml");
569
570 UXMLParser *parser = UXMLParser::createParser(status);
571 UXMLElement *root = parser->parseFile(testFilePath, status);
572
573 if (root == NULL) {
574 log_err("Could not open the test data file: %s\n", testFilePath);
575 delete parser;
576 return;
577 }
578
579 UnicodeString test_case = UNICODE_STRING_SIMPLE("test-case");
580 UnicodeString test_text = UNICODE_STRING_SIMPLE("test-text");
581 UnicodeString test_font = UNICODE_STRING_SIMPLE("test-font");
582 UnicodeString result_glyphs = UNICODE_STRING_SIMPLE("result-glyphs");
583 UnicodeString result_indices = UNICODE_STRING_SIMPLE("result-indices");
584 UnicodeString result_positions = UNICODE_STRING_SIMPLE("result-positions");
585
586 // test-case attributes
587 UnicodeString id_attr = UNICODE_STRING_SIMPLE("id");
588 UnicodeString script_attr = UNICODE_STRING_SIMPLE("script");
589 UnicodeString lang_attr = UNICODE_STRING_SIMPLE("lang");
590
591 // test-font attributes
592 UnicodeString name_attr = UNICODE_STRING_SIMPLE("name");
593 UnicodeString ver_attr = UNICODE_STRING_SIMPLE("version");
594 UnicodeString cksum_attr = UNICODE_STRING_SIMPLE("checksum");
595
596 const UXMLElement *testCase;
597 int32_t tc = 0;
598
599 while((testCase = root->nextChildElement(tc)) != NULL) {
600 if (testCase->getTagName().compare(test_case) == 0) {
601 char *id = getCString(testCase->getAttribute(id_attr));
602 char *script = getCString(testCase->getAttribute(script_attr));
603 char *lang = getCString(testCase->getAttribute(lang_attr));
604 LEFontInstance *font = NULL;
605 const UXMLElement *element;
606 int32_t ec = 0;
607 int32_t charCount = 0;
608 int32_t typoFlags = 3; // kerning + ligatures...
609 UScriptCode scriptCode;
610 le_int32 languageCode = -1;
611 UnicodeString text, glyphs, indices, positions;
612 int32_t glyphCount = 0, indexCount = 0, positionCount = 0;
613 TestResult expected = {0, NULL, NULL, NULL};
614 TestResult actual = {0, NULL, NULL, NULL};
615 LEErrorCode success = LE_NO_ERROR;
616 LayoutEngine *engine = NULL;
617
618 uscript_getCode(script, &scriptCode, 1, &status);
619 if (LE_FAILURE(status)) {
620 log_err("invalid script name: %s.\n", script);
621 goto free_c_strings;
622 }
623
624 if (lang != NULL) {
625 languageCode = getLanguageCode(lang);
626
627 if (languageCode < 0) {
628 log_err("invalid language name: %s.\n", lang);
629 goto free_c_strings;
630 }
631 }
632
633 while((element = testCase->nextChildElement(ec)) != NULL) {
634 UnicodeString tag = element->getTagName();
635
636 // TODO: make sure that each element is only used once.
637 if (tag.compare(test_font) == 0) {
638 char *fontName = getCString(element->getAttribute(name_attr));
639 char *fontVer = getCString(element->getAttribute(ver_attr));
640 char *fontCksum = getCString(element->getAttribute(cksum_attr));
641
642 font = openFont(fontName, fontCksum, fontVer, id);
643 freeCString(fontCksum);
644 freeCString(fontVer);
645 freeCString(fontName);
646
647 if (font == NULL) {
648 // warning message already displayed...
649 goto free_c_strings;
650 }
651 } else if (tag.compare(test_text) == 0) {
652 text = element->getText(TRUE);
653 charCount = text.length();
654 } else if (tag.compare(result_glyphs) == 0) {
655 glyphs = element->getText(TRUE);
656 } else if (tag.compare(result_indices) == 0) {
657 indices = element->getText(TRUE);
658 } else if (tag.compare(result_positions) == 0) {
659 positions = element->getText(TRUE);
660 } else {
661 // an unknown tag...
662 char *cTag = getCString(&tag);
663
664 log_info("Test %s: unknown element with tag \"%s\"\n", id, cTag);
665 freeCString(cTag);
666 }
667 }
668
669 // TODO: make sure that the font, test-text, result-glyphs, result-indices and result-positions
670 // have all been provided
671 if (font == NULL) {
672 LEErrorCode fontStatus = LE_NO_ERROR;
673
674 font = new SimpleFontInstance(12, fontStatus);
675 typoFlags |= 0x80000000L; // use CharSubstitutionFilter...
676 }
677
678 expected.glyphs = (LEGlyphID *) getHexArray(glyphs, glyphCount);
679 expected.indices = (le_int32 *) getHexArray(indices, indexCount);
680 expected.positions = getFloatArray(positions, positionCount);
681
682 expected.glyphCount = glyphCount;
683
684 if (glyphCount < charCount || indexCount != glyphCount || positionCount < glyphCount * 2 + 2) {
685 log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n",
686 id, charCount, glyphCount, indexCount, positionCount);
687 goto free_expected;
688 };
689
690 engine = LayoutEngine::layoutEngineFactory(font, scriptCode, languageCode, typoFlags, success);
691
692 if (LE_FAILURE(success)) {
693 log_err("Test %s: could not create a LayoutEngine.\n", id);
694 goto free_expected;
695 }
696
697 actual.glyphCount = engine->layoutChars(text.getBuffer(), 0, charCount, charCount, getRTL(text), 0, 0, success);
698
699 actual.glyphs = NEW_ARRAY(LEGlyphID, actual.glyphCount);
700 actual.indices = NEW_ARRAY(le_int32, actual.glyphCount);
701 actual.positions = NEW_ARRAY(float, actual.glyphCount * 2 + 2);
702
703 engine->getGlyphs(actual.glyphs, success);
704 engine->getCharIndices(actual.indices, success);
705 engine->getGlyphPositions(actual.positions, success);
706
707 compareResults(id, &expected, &actual);
708
709 DELETE_ARRAY(actual.positions);
710 DELETE_ARRAY(actual.indices);
711 DELETE_ARRAY(actual.glyphs);
712
713 delete engine;
714
715 log_verbose("OK - %4d glyphs: %s\n", actual.glyphCount, id);
716 free_expected:
717 DELETE_ARRAY(expected.positions);
718 DELETE_ARRAY(expected.indices);
719 DELETE_ARRAY(expected.glyphs);
720
721 delete font;
722
723 free_c_strings:
724 freeCString(lang);
725 freeCString(script);
726 freeCString(id);
727 }
728 }
729
730 delete root;
731 delete parser;
732 #endif
733 }
734 U_CDECL_END
735
736 U_CDECL_BEGIN
737 /*
738 * From ticket:5923:
739 *
740 * Build a paragraph that contains a mixture of left to right and right to left text.
741 * Break it into multiple lines and make sure that the glyphToCharMap for run in each
742 * line is correct.
743 *
744 * Note: it might be a good idea to also check the glyphs and positions for each run,
745 * that we get the expected number of runs per line and that the line breaks are where
746 * we expect them to be. Really, it would be a good idea to make a whole test suite
747 * for ParagraphLayout.
748 */
GlyphToCharTest(void)749 static void U_CALLCONV GlyphToCharTest(void)
750 {
751 #if !UCONFIG_NO_BREAK_ITERATION
752 LEErrorCode status = LE_NO_ERROR;
753 LEFontInstance *font;
754 FontRuns fontRuns(0);
755 ParagraphLayout *paragraphLayout;
756 const ParagraphLayout::Line *line;
757 /*
758 * This is the same text that's in <icu>/source/samples/layout/Sample.txt
759 */
760 LEUnicode chars[] = {
761 /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
762 0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
763 0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
764 0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
765 0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
766 0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
767 0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
768 0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
769 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
770 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
771 0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
772 0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
773 0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
774 0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
775 0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
776 0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
777 0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
778 0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
779 0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
780 0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
781 0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
782 0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
783 0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
784 0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
785 0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
786 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
787 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
788 0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
789 0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
790 0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
791 0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
792 0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
793 0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
794 0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
795 0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
796 0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
797 0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
798 0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
799 0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
800 0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
801 0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
802 0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
803 0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
804 0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
805 0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
806 0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
807 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
808 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
809 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
810 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
811 0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
812 0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
813 0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
814 0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
815 0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
816 0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
817 0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
818 0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
819 0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
820 0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
821 0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
822 0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
823 0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
824 0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
825 0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
826 0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
827 0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
828 0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
829 0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
830 0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
831 0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
832 0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
833 0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
834 0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
835 0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
836 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
837 0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
838 0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
839 0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
840 0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
841 0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
842 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
843 0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
844 0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
845 0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
846 0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
847 0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
848 0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
849 0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
850 0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
851 0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
852 0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
853 0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
854 0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
855 0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
856 0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
857 0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
858 0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
859 0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
860 0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
861 0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
862 0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
863 0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
864 0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
865 0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
866 0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
867 0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
868 0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
869 0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
870 0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
871 0x0e25, 0x0e4c
872 };
873 le_int32 charCount = LE_ARRAY_SIZE(chars);
874 le_int32 charIndex = 0, lineNumber = 1;
875 const float lineWidth = 600;
876
877 font = new SimpleFontInstance(12, status);
878
879 if (LE_FAILURE(status)) {
880 goto finish;
881 }
882
883 fontRuns.add(font, charCount);
884
885 paragraphLayout = new ParagraphLayout(chars, charCount, &fontRuns, NULL, NULL, NULL, 0, FALSE, status);
886
887 if (LE_FAILURE(status)) {
888 goto close_font;
889 }
890
891 paragraphLayout->reflow();
892 while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
893 le_int32 runCount = line->countRuns();
894
895 for(le_int32 run = 0; run < runCount; run += 1) {
896 const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
897 le_int32 glyphCount = visualRun->getGlyphCount();
898 const le_int32 *glyphToCharMap = visualRun->getGlyphToCharMap();
899
900 if (visualRun->getDirection() == UBIDI_RTL) {
901 /*
902 * For a right to left run, make sure that the character indices
903 * increase from the right most glyph to the left most glyph. If
904 * there are any one to many glyph substitutions, we might get several
905 * glyphs in a row with the same character index.
906 */
907 for(le_int32 i = glyphCount - 1; i >= 0; i -= 1) {
908 le_int32 ix = glyphToCharMap[i];
909
910 if (ix != charIndex) {
911 if (ix != charIndex - 1) {
912 log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
913 i, lineNumber, charIndex, ix);
914 goto close_paragraph; // once there's one error, we can't count on anything else...
915 }
916 } else {
917 charIndex += 1;
918 }
919 }
920 } else {
921 /*
922 * We can't just check the order of the character indices
923 * for left to right runs because Indic text might have been
924 * reordered. What we can do is find the minimum and maximum
925 * character indices in the run and make sure that the minimum
926 * is equal to charIndex and then advance charIndex to the maximum.
927 */
928 le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
929
930 for(le_int32 i = 0; i < glyphCount; i += 1) {
931 le_int32 ix = glyphToCharMap[i];
932
933 if (ix > maxIndex) {
934 maxIndex = ix;
935 }
936
937 if (ix < minIndex) {
938 minIndex = ix;
939 }
940 }
941
942 if (minIndex != charIndex) {
943 log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
944 run, lineNumber, charIndex, minIndex);
945 goto close_paragraph; // once there's one error, we can't count on anything else...
946 }
947
948 charIndex = maxIndex + 1;
949 }
950 }
951
952 lineNumber += 1;
953 }
954 close_paragraph:
955 delete paragraphLayout;
956
957 close_font:
958 delete font;
959
960 finish:
961 return;
962 #endif
963 }
964 U_CDECL_END
965
addAllTests(TestNode ** root)966 static void addAllTests(TestNode **root)
967 {
968 addTest(root, &ScriptTest, "api/ScriptTest");
969 addTest(root, &ParamTest, "api/ParameterTest");
970 addTest(root, &FactoryTest, "api/FactoryTest");
971 addTest(root, &AccessTest, "layout/AccessTest");
972 addTest(root, &DataDrivenTest, "layout/DataDrivenTest");
973 addTest(root, &GlyphToCharTest, "paragraph/GlyphToCharTest");
974
975 addCTests(root);
976 }
977
978 /* returns the path to icu/source/data/out */
ctest_dataOutDir()979 static const char *ctest_dataOutDir()
980 {
981 static const char *dataOutDir = NULL;
982
983 if(dataOutDir) {
984 return dataOutDir;
985 }
986
987 /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
988 // to point to the top of the build hierarchy, which may or
989 // may not be the same as the source directory, depending on
990 // the configure options used. At any rate,
991 // set the data path to the built data from this directory.
992 // The value is complete with quotes, so it can be used
993 // as-is as a string constant.
994 */
995 #if defined (U_TOPBUILDDIR)
996 {
997 dataOutDir = U_TOPBUILDDIR "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
998 }
999 #else
1000
1001 /* On Windows, the file name obtained from __FILE__ includes a full path.
1002 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
1003 * Change to "wherever\icu\source\data"
1004 */
1005 {
1006 static char p[sizeof(__FILE__) + 20];
1007 char *pBackSlash;
1008 int i;
1009
1010 strcpy(p, __FILE__);
1011 /* We want to back over three '\' chars. */
1012 /* Only Windows should end up here, so looking for '\' is safe. */
1013 for (i=1; i<=3; i++) {
1014 pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
1015 if (pBackSlash != NULL) {
1016 *pBackSlash = 0; /* Truncate the string at the '\' */
1017 }
1018 }
1019
1020 if (pBackSlash != NULL) {
1021 /* We found and truncated three names from the path.
1022 * Now append "source\data" and set the environment
1023 */
1024 strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
1025 dataOutDir = p;
1026 }
1027 else {
1028 /* __FILE__ on MSVC7 does not contain the directory */
1029 FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
1030 if (file) {
1031 fclose(file);
1032 dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
1033 }
1034 else {
1035 dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
1036 }
1037 }
1038 }
1039 #endif
1040
1041 return dataOutDir;
1042 }
1043
1044 /* ctest_setICU_DATA - if the ICU_DATA environment variable is not already
1045 * set, try to deduce the directory in which ICU was built,
1046 * and set ICU_DATA to "icu/source/data" in that location.
1047 * The intent is to allow the tests to have a good chance
1048 * of running without requiring that the user manually set
1049 * ICU_DATA. Common data isn't a problem, since it is
1050 * picked up via a static (build time) reference, but the
1051 * tests dynamically load some data.
1052 */
ctest_setICU_DATA()1053 static void ctest_setICU_DATA() {
1054
1055 /* No location for the data dir was identifiable.
1056 * Add other fallbacks for the test data location here if the need arises
1057 */
1058 if (getenv("ICU_DATA") == NULL) {
1059 /* If ICU_DATA isn't set, set it to the usual location */
1060 u_setDataDirectory(ctest_dataOutDir());
1061 }
1062 }
1063
main(int argc,char * argv[])1064 int main(int argc, char* argv[])
1065 {
1066 int32_t nerrors = 0;
1067 TestNode *root = NULL;
1068 UErrorCode errorCode = U_ZERO_ERROR;
1069 UDate startTime, endTime;
1070 int32_t diffTime;
1071
1072 startTime = uprv_getUTCtime();
1073
1074 if (!initArgs(argc, argv, NULL, NULL)) {
1075 /* Error already displayed. */
1076 return -1;
1077 }
1078
1079 /* Check whether ICU will initialize without forcing the build data directory into
1080 * the ICU_DATA path. Success here means either the data dll contains data, or that
1081 * this test program was run with ICU_DATA set externally. Failure of this check
1082 * is normal when ICU data is not packaged into a shared library.
1083 *
1084 * Whether or not this test succeeds, we want to cleanup and reinitialize
1085 * with a data path so that data loading from individual files can be tested.
1086 */
1087 u_init(&errorCode);
1088
1089 if (U_FAILURE(errorCode)) {
1090 fprintf(stderr,
1091 "#### Note: ICU Init without build-specific setDataDirectory() failed.\n");
1092 }
1093
1094 u_cleanup();
1095 errorCode = U_ZERO_ERROR;
1096
1097 if (!initArgs(argc, argv, NULL, NULL)) {
1098 /* Error already displayed. */
1099 return -1;
1100 }
1101 /* Initialize ICU */
1102 ctest_setICU_DATA(); /* u_setDataDirectory() must happen Before u_init() */
1103 u_init(&errorCode);
1104
1105 if (U_FAILURE(errorCode)) {
1106 fprintf(stderr,
1107 "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
1108 "*** Check the ICU_DATA environment variable and \n"
1109 "*** check that the data files are present.\n", argv[0], u_errorName(errorCode));
1110 return 1;
1111 }
1112
1113 addAllTests(&root);
1114 nerrors = runTestRequest(root, argc, argv);
1115
1116 cleanUpTestTree(root);
1117 u_cleanup();
1118
1119 endTime = uprv_getUTCtime();
1120 diffTime = (int32_t)(endTime - startTime);
1121 printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
1122 (int)((diffTime%U_MILLIS_PER_DAY)/U_MILLIS_PER_HOUR),
1123 (int)((diffTime%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE),
1124 (int)((diffTime%U_MILLIS_PER_MINUTE)/U_MILLIS_PER_SECOND),
1125 (int)(diffTime%U_MILLIS_PER_SECOND));
1126
1127 return nerrors;
1128 }
1129
1130