]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/letest/letest.cpp
ICU-8.11.4.tar.gz
[apple/icu.git] / icuSources / test / letest / letest.cpp
CommitLineData
b75a7d8f
A
1/*
2 *******************************************************************************
3 *
73c04bcf 4 * Copyright (C) 1999-2006, International Business Machines
b75a7d8f
A
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
73c04bcf
A
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"
b75a7d8f 25
b75a7d8f 26#include "PortableFontInstance.h"
73c04bcf 27#include "SimpleFontInstance.h"
b75a7d8f 28
73c04bcf 29#include "letsutil.h"
b75a7d8f
A
30#include "letest.h"
31
73c04bcf
A
32#include "xmlparser.h"
33#include "putilimp.h" // for uprv_getUTCtime()
34
35#include <stdlib.h>
36#include <string.h>
37
b75a7d8f
A
38U_NAMESPACE_USE
39
73c04bcf
A
40#define CH_COMMA 0x002C
41
42U_CDECL_BEGIN
43static void U_CALLCONV ParamTest(void)
44{
45 LEErrorCode status = LE_NO_ERROR;
46 SimpleFontInstance *font = new SimpleFontInstance(12, status);
47 LayoutEngine *engine = LayoutEngine::layoutEngineFactory(font, arabScriptCode, -1, status);
48 LEGlyphID *glyphs = NULL;
49 le_int32 *indices = NULL;
50 float *positions = NULL;
51 le_int32 glyphCount = 0;
52
53 glyphCount = engine->getGlyphCount();
54 if (glyphCount != 0) {
55 log_err("Calling getGlyphCount() on an empty layout returned %d.\n", glyphCount);
56 }
57
58 glyphs = NEW_ARRAY(LEGlyphID, glyphCount + 10);
59 indices = NEW_ARRAY(le_int32, glyphCount + 10);
60 positions = NEW_ARRAY(float, glyphCount + 10);
61
62 engine->getGlyphs(NULL, status);
63
64 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
65 log_err("Calling getGlyphs(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
66 }
67
68 status = LE_NO_ERROR;
69 engine->getGlyphs(glyphs, status);
70
71 if (status != LE_NO_LAYOUT_ERROR) {
72 log_err("Calling getGlyphs(glyphs, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
73 }
74
75 status = LE_NO_ERROR;
76 engine->getGlyphs(NULL, 0xFF000000L, status);
77
78 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
79 log_err("Calling getGlyphs(NULL, 0xFF000000L, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
80 }
81
82 status = LE_NO_ERROR;
83 engine->getGlyphs(glyphs, 0xFF000000L, status);
84
85 if (status != LE_NO_LAYOUT_ERROR) {
86 log_err("Calling getGlyphs(glyphs, 0xFF000000L, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
87 }
88
89 status = LE_NO_ERROR;
90 engine->getCharIndices(NULL, status);
91
92 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
93 log_err("Calling getCharIndices(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
94 }
95
96 status = LE_NO_ERROR;
97 engine->getCharIndices(indices, status);
98
99 if (status != LE_NO_LAYOUT_ERROR) {
100 log_err("Calling getCharIndices(indices, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
101 }
102
103 status = LE_NO_ERROR;
104 engine->getCharIndices(NULL, 1024, status);
105
106 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
107 log_err("Calling getCharIndices(NULL, 1024, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
108 }
109
110 status = LE_NO_ERROR;
111 engine->getCharIndices(indices, 1024, status);
112
113 if (status != LE_NO_LAYOUT_ERROR) {
114 log_err("Calling getCharIndices(indices, 1024, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
115 }
116
117 status = LE_NO_ERROR;
118 engine->getGlyphPositions(NULL, status);
119
120 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
121 log_err("Calling getGlyphPositions(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
122 }
123
124 status = LE_NO_ERROR;
125 engine->getGlyphPositions(positions, status);
126
127 if (status != LE_NO_LAYOUT_ERROR) {
128 log_err("Calling getGlyphPositions(positions, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
129 }
130
131 DELETE_ARRAY(positions);
132 DELETE_ARRAY(indices);
133 DELETE_ARRAY(glyphs);
134
135 status = LE_NO_ERROR;
136 glyphCount = engine->layoutChars(NULL, 0, 0, 0, FALSE, 0.0, 0.0, status);
137
138 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
139 log_err("Calling layoutChars(NULL, 0, 0, 0, FALSE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
140 }
141
142 LEUnicode chars[] = {
143 0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068, 0x0020, // "English "
144 0x0645, 0x0627, 0x0646, 0x062A, 0x0648, 0x0634, // MEM ALIF KAF NOON TEH WAW SHEEN
145 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x02E // " text."
146 };
147
148 status = LE_NO_ERROR;
149 glyphCount = engine->layoutChars(chars, -1, 6, 20, TRUE, 0.0, 0.0, status);
150
151 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
152 log_err("Calling layoutChars(chars, -1, 6, 20, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
153 }
154
155 status = LE_NO_ERROR;
156 glyphCount = engine->layoutChars(chars, 8, -1, 20, TRUE, 0.0, 0.0, status);
157
158 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
159 log_err("Calling layoutChars(chars, 8, -1, 20, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
160 }
161
162 status = LE_NO_ERROR;
163 glyphCount = engine->layoutChars(chars, 8, 6, -1, TRUE, 0.0, 0.0, status);
164
165 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
166 log_err("Calling layoutChars((chars, 8, 6, -1, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
167 }
168
169 status = LE_NO_ERROR;
170 glyphCount = engine->layoutChars(chars, 8, 6, 10, TRUE, 0.0, 0.0, status);
171
172 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
173 log_err("Calling layoutChars(chars, 8, 6, 10, TRUE, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
174 }
175
176 float x = 0.0, y = 0.0;
177
178 status = LE_NO_ERROR;
179 glyphCount = engine->layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status);
180
181 if (LE_FAILURE(status)) {
182 log_err("Calling layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
183 goto bail;
184 }
185
186 engine->getGlyphPosition(-1, x, y, status);
187
188 if (status != LE_INDEX_OUT_OF_BOUNDS_ERROR) {
189 log_err("Calling getGlyphPosition(-1, x, y, status) did not fail w/ LE_INDEX_OUT_OF_BOUNDS_ERROR.\n");
190 }
191
192 status = LE_NO_ERROR;
193 engine->getGlyphPosition(glyphCount + 1, x, y, status);
194
195 if (status != LE_INDEX_OUT_OF_BOUNDS_ERROR) {
196 log_err("Calling getGlyphPosition(glyphCount + 1, x, y, status) did not fail w/ LE_INDEX_OUT_OF_BOUNDS_ERROR.\n");
197 }
198
199bail:
200 delete engine;
201 delete font;
202}
203U_CDECL_END
204
205U_CDECL_BEGIN
206static void U_CALLCONV FactoryTest(void)
207{
208 LEErrorCode status = LE_NO_ERROR;
209 SimpleFontInstance *font = new SimpleFontInstance(12, status);
210 LayoutEngine *engine = NULL;
211
212 for(le_int32 scriptCode = 0; scriptCode < scriptCodeCount; scriptCode += 1) {
213 status = LE_NO_ERROR;
214 engine = LayoutEngine::layoutEngineFactory(font, scriptCode, -1, status);
215
216 if (LE_FAILURE(status)) {
217 log_err("Could not create a LayoutEngine for script \'%s\'.\n", uscript_getShortName((UScriptCode)scriptCode));
218 }
219
220 delete engine;
221 }
222
223 delete font;
224}
225U_CDECL_END
226
227U_CDECL_BEGIN
228static void U_CALLCONV AccessTest(void)
229{
230 LEErrorCode status = LE_NO_ERROR;
231 SimpleFontInstance *font = new SimpleFontInstance(12, status);
232 LayoutEngine *engine = LayoutEngine::layoutEngineFactory(font, arabScriptCode, -1, status);
233 le_int32 glyphCount;
234 LEGlyphID glyphs[6], extraBitGlyphs[6];;
235 le_int32 biasedIndices[6], indices[6], glyph;
236 float positions[6 * 2 + 2];
237 LEUnicode chars[] = {
238 0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068, 0x0020, // "English "
239 0x0645, 0x0627, 0x0646, 0x062A, 0x0648, 0x0634, // MEM ALIF KAF NOON TEH WAW SHEEN
240 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x02E // " text."
241 };
242
243 if (LE_FAILURE(status)) {
244 log_err("Could not create LayoutEngine.\n");
245 goto bail;
246 }
247
248 glyphCount = engine->layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status);
249
250 if (LE_FAILURE(status) || glyphCount != 6) {
251 log_err("layoutChars(chars, 8, 6, 20, TRUE, 0.0, 0.0, status) failed.\n");
252 goto bail;
253 }
254
255 engine->getGlyphs(glyphs, status);
256 engine->getCharIndices(indices, status);
257 engine->getGlyphPositions(positions, status);
258
259 if (LE_FAILURE(status)) {
260 log_err("Could not get glyph, indices and position arrays.\n");
261 goto bail;
262 }
263
264 engine->getGlyphs(extraBitGlyphs, 0xFF000000L, status);
265
266 if (LE_FAILURE(status)) {
267 log_err("getGlyphs(extraBitGlyphs, 0xFF000000L, status); failed.\n");
268 } else {
269 for(glyph = 0; glyph < glyphCount; glyph += 1) {
270 if (extraBitGlyphs[glyph] != (glyphs[glyph] | 0xFF000000L)) {
271 log_err("extraBigGlyphs[%d] != glyphs[%d] | 0xFF000000L: %8X, %8X\n",
272 glyph, glyph, extraBitGlyphs[glyph], glyphs[glyph]);
273 break;
274 }
275 }
276 }
277
278 status = LE_NO_ERROR;
279 engine->getCharIndices(biasedIndices, 1024, status);
280
281 if (LE_FAILURE(status)) {
282 log_err("getCharIndices(biasedIndices, 1024, status) failed.\n");
283 } else {
284 for (glyph = 0; glyph < glyphCount; glyph += 1) {
285 if (biasedIndices[glyph] != (indices[glyph] + 1024)) {
286 log_err("biasedIndices[%d] != indices[%d] + 1024: %8X, %8X\n",
287 glyph, glyph, biasedIndices[glyph], indices[glyph]);
288 break;
289 }
290 }
291 }
292
293 status = LE_NO_ERROR;
294 for (glyph = 0; glyph <= glyphCount; glyph += 1) {
295 float x = 0.0, y = 0.0;
296
297 engine->getGlyphPosition(glyph, x, y, status);
298
299 if (LE_FAILURE(status)) {
300 log_err("getGlyphPosition(%d, x, y, status) failed.\n", glyph);
301 break;
302 }
303
304 if (x != positions[glyph*2] || y != positions[glyph*2 + 1]) {
305 log_err("getGlyphPosition(%d, x, y, status) returned bad position: (%f, %f) != (%f, %f)\n",
306 glyph, x, y, positions[glyph*2], positions[glyph*2 + 1]);
307 break;
308 }
309 }
310
311bail:
312 delete engine;
313 delete font;
314}
315U_CDECL_END
316
317le_bool compareResults(const char *testID, TestResult *expected, TestResult *actual)
b75a7d8f
A
318{
319 /* NOTE: we'll stop on the first failure 'cause once there's one error, it may cascade... */
320 if (actual->glyphCount != expected->glyphCount) {
73c04bcf
A
321 log_err("Test %s: incorrect glyph count: exptected %d, got %d\n",
322 testID, expected->glyphCount, actual->glyphCount);
374ca955 323 return FALSE;
b75a7d8f
A
324 }
325
326 le_int32 i;
327
328 for (i = 0; i < actual->glyphCount; i += 1) {
329 if (actual->glyphs[i] != expected->glyphs[i]) {
73c04bcf
A
330 log_err("Test %s: incorrect id for glyph %d: expected %4X, got %4X\n",
331 testID, i, expected->glyphs[i], actual->glyphs[i]);
374ca955 332 return FALSE;
b75a7d8f
A
333 }
334 }
335
336 for (i = 0; i < actual->glyphCount; i += 1) {
337 if (actual->indices[i] != expected->indices[i]) {
73c04bcf
A
338 log_err("Test %s: incorrect index for glyph %d: expected %8X, got %8X\n",
339 testID, i, expected->indices[i], actual->indices[i]);
374ca955 340 return FALSE;
b75a7d8f
A
341 }
342 }
343
344 for (i = 0; i <= actual->glyphCount; i += 1) {
73c04bcf 345 double xError = uprv_fabs(actual->positions[i * 2] - expected->positions[i * 2]);
b75a7d8f
A
346
347 if (xError > 0.0001) {
73c04bcf
A
348 log_err("Test %s: incorrect x position for glyph %d: expected %f, got %f\n",
349 testID, i, expected->positions[i * 2], actual->positions[i * 2]);
374ca955 350 return FALSE;
b75a7d8f
A
351 }
352
73c04bcf 353 double yError = uprv_fabs(actual->positions[i * 2 + 1] - expected->positions[i * 2 + 1]);
b75a7d8f
A
354
355 if (yError < 0) {
356 yError = -yError;
357 }
358
359 if (yError > 0.0001) {
73c04bcf
A
360 log_err("Test %s: incorrect y position for glyph %d: expected %f, got %f\n",
361 testID, i, expected->positions[i * 2 + 1], actual->positions[i * 2 + 1]);
374ca955 362 return FALSE;
b75a7d8f
A
363 }
364 }
365
374ca955 366 return TRUE;
b75a7d8f
A
367}
368
73c04bcf
A
369static void checkFontVersion(PortableFontInstance *fontInstance, const char *testVersionString,
370 le_uint32 testChecksum, const char *testID)
b75a7d8f 371{
73c04bcf
A
372 le_uint32 fontChecksum = fontInstance->getFontChecksum();
373
374 if (fontChecksum != testChecksum) {
375 const char *fontVersionString = fontInstance->getNameString(NAME_VERSION_STRING,
376 PLATFORM_MACINTOSH, MACINTOSH_ROMAN, MACINTOSH_ENGLISH);
b75a7d8f 377
73c04bcf
A
378 log_info("Test %s: this may not be the same font used to generate the test data.\n", testID);
379 log_info("Your font's version string is \"%s\"\n", fontVersionString);
380 log_info("The expected version string is \"%s\"\n", testVersionString);
381 log_info("If you see errors, they may be due to the version of the font you're using.\n");
b75a7d8f 382
73c04bcf
A
383 fontInstance->deleteNameString(fontVersionString);
384 }
385}
386
387/* Returns the path to icu/source/test/testdata/ */
388const char *getSourceTestData() {
389 const char *srcDataDir = NULL;
390#ifdef U_TOPSRCDIR
391 srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
392#else
393 srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
394 FILE *f = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"rbbitst.txt", "r");
395
396 if (f != NULL) {
397 /* We're in icu/source/test/letest/ */
398 fclose(f);
399 } else {
400 /* We're in icu/source/test/letest/(Debug|Release) */
401 srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
402 }
403#endif
404
405 return srcDataDir;
406}
b75a7d8f 407
73c04bcf
A
408const char *getPath(char buffer[2048], const char *filename) {
409 const char *testDataDirectory = getSourceTestData();
b75a7d8f 410
73c04bcf
A
411 strcpy(buffer, testDataDirectory);
412 strcat(buffer, filename);
413
414 return buffer;
415}
416
417le_uint32 *getHexArray(const UnicodeString &numbers, int32_t &arraySize)
418{
419 int32_t offset = -1;
420
421 arraySize = 1;
422 while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
423 arraySize += 1;
424 }
425
426 le_uint32 *array = NEW_ARRAY(le_uint32, arraySize);
427 char number[16];
428 le_int32 count = 0;
429 le_int32 start = 0, end = 0;
430 le_int32 len = 0;
431
432 // trim leading whitespace
433 while(u_isUWhiteSpace(numbers[start])) {
434 start += 1;
435 }
436
437 while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
438 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
439 number[len] = '\0';
440 start = end + 1;
441
442 sscanf(number, "%x", &array[count++]);
443
444 // trim whitespace following the comma
445 while(u_isUWhiteSpace(numbers[start])) {
446 start += 1;
b75a7d8f 447 }
73c04bcf
A
448 }
449
450 // trim trailing whitespace
451 end = numbers.length();
452 while(u_isUWhiteSpace(numbers[end - 1])) {
453 end -= 1;
454 }
455
456 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
457 number[len] = '\0';
458 sscanf(number, "%x", &array[count]);
459
460 return array;
461}
462
463float *getFloatArray(const UnicodeString &numbers, int32_t &arraySize)
464{
465 int32_t offset = -1;
466
467 arraySize = 1;
468 while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) {
469 arraySize += 1;
470 }
471
472 float *array = NEW_ARRAY(float, arraySize);
473 char number[32];
474 le_int32 count = 0;
475 le_int32 start = 0, end = 0;
476 le_int32 len = 0;
477
478 // trim leading whitespace
479 while(u_isUWhiteSpace(numbers[start])) {
480 start += 1;
481 }
b75a7d8f 482
73c04bcf
A
483 while((end = numbers.indexOf(CH_COMMA, start)) >= 0) {
484 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
485 number[len] = '\0';
486 start = end + 1;
b75a7d8f 487
73c04bcf
A
488 sscanf(number, "%f", &array[count++]);
489
490 // trim whiteapce following the comma
491 while(u_isUWhiteSpace(numbers[start])) {
492 start += 1;
b75a7d8f 493 }
73c04bcf
A
494 }
495
496 while(u_isUWhiteSpace(numbers[start])) {
497 start += 1;
498 }
499
500 // trim trailing whitespace
501 end = numbers.length();
502 while(u_isUWhiteSpace(numbers[end - 1])) {
503 end -= 1;
504 }
505
506 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV);
507 number[len] = '\0';
508 sscanf(number, "%f", &array[count]);
509
510 return array;
511}
512
513LEFontInstance *openFont(const char *fontName, const char *checksum, const char *version, const char *testID)
514{
515 char path[2048];
516 PortableFontInstance *font;
517 LEErrorCode fontStatus = LE_NO_ERROR;
518
519
520 font = new PortableFontInstance(getPath(path, fontName), 12, fontStatus);
521
522 if (LE_FAILURE(fontStatus)) {
523 log_info("Test %s: can't open font %s - test skipped.\n", testID, fontName);
524 delete font;
525 return NULL;
526 } else {
527 le_uint32 cksum = 0;
528
529 sscanf(checksum, "%x", &cksum);
530
531 checkFontVersion(font, version, cksum, testID);
532 }
533
534 return font;
535}
536
537U_CDECL_BEGIN
538static void U_CALLCONV DataDrivenTest(void)
539{
540#if !UCONFIG_NO_REGULAR_EXPRESSIONS
541 UErrorCode status = U_ZERO_ERROR;
542 char path[2048];
543 const char *testFilePath = getPath(path, "letest.xml");
544
545 UXMLParser *parser = UXMLParser::createParser(status);
546 UXMLElement *root = parser->parseFile(testFilePath, status);
547
548 if (root == NULL) {
549 log_err("Could not open the test data file: %s\n", testFilePath);
550 delete parser;
551 return;
552 }
553
554 UnicodeString test_case = UNICODE_STRING_SIMPLE("test-case");
555 UnicodeString test_text = UNICODE_STRING_SIMPLE("test-text");
556 UnicodeString test_font = UNICODE_STRING_SIMPLE("test-font");
557 UnicodeString result_glyphs = UNICODE_STRING_SIMPLE("result-glyphs");
558 UnicodeString result_indices = UNICODE_STRING_SIMPLE("result-indices");
559 UnicodeString result_positions = UNICODE_STRING_SIMPLE("result-positions");
560
561 // test-case attributes
562 UnicodeString id_attr = UNICODE_STRING_SIMPLE("id");
563 UnicodeString script_attr = UNICODE_STRING_SIMPLE("script");
564 UnicodeString lang_attr = UNICODE_STRING_SIMPLE("lang");
565
566 // test-font attributes
567 UnicodeString name_attr = UNICODE_STRING_SIMPLE("name");
568 UnicodeString ver_attr = UNICODE_STRING_SIMPLE("version");
569 UnicodeString cksum_attr = UNICODE_STRING_SIMPLE("checksum");
570
571 const UXMLElement *testCase;
572 int32_t tc = 0;
573
574 while((testCase = root->nextChildElement(tc)) != NULL) {
575 if (testCase->getTagName().compare(test_case) == 0) {
576 char *id = getCString(testCase->getAttribute(id_attr));
577 char *script = getCString(testCase->getAttribute(script_attr));
578 char *lang = getCString(testCase->getAttribute(lang_attr));
579 LEFontInstance *font = NULL;
580 const UXMLElement *element;
581 int32_t ec = 0;
582 int32_t charCount = 0;
583 int32_t typoFlags = 3; // kerning + ligatures...
584 UScriptCode scriptCode;
585 le_int32 languageCode = -1;
586 UnicodeString text, glyphs, indices, positions;
587 int32_t glyphCount = 0, indexCount = 0, positionCount = 0;
588 TestResult expected = {0, NULL, NULL, NULL};
589 TestResult actual = {0, NULL, NULL, NULL};
590 LEErrorCode success = LE_NO_ERROR;
591 LayoutEngine *engine = NULL;
592
593 uscript_getCode(script, &scriptCode, 1, &status);
594 if (LE_FAILURE(status)) {
595 log_err("invalid script name: %s.\n", script);
596 goto free_c_strings;
597 }
598
599 if (lang != NULL) {
600 languageCode = getLanguageCode(lang);
b75a7d8f 601
73c04bcf
A
602 if (languageCode < 0) {
603 log_err("invalid language name: %s.\n", lang);
604 goto free_c_strings;
605 }
606 }
b75a7d8f 607
73c04bcf
A
608 while((element = testCase->nextChildElement(ec)) != NULL) {
609 UnicodeString tag = element->getTagName();
b75a7d8f 610
73c04bcf
A
611 // TODO: make sure that each element is only used once.
612 if (tag.compare(test_font) == 0) {
613 char *fontName = getCString(element->getAttribute(name_attr));
614 char *fontVer = getCString(element->getAttribute(ver_attr));
615 char *fontCksum = getCString(element->getAttribute(cksum_attr));
b75a7d8f 616
73c04bcf
A
617 font = openFont(fontName, fontCksum, fontVer, id);
618 freeCString(fontCksum);
619 freeCString(fontVer);
620 freeCString(fontName);
b75a7d8f 621
73c04bcf
A
622 if (font == NULL) {
623 // warning message already displayed...
624 goto free_c_strings;
625 }
626 } else if (tag.compare(test_text) == 0) {
627 text = element->getText(TRUE);
628 charCount = text.length();
629 } else if (tag.compare(result_glyphs) == 0) {
630 glyphs = element->getText(TRUE);
631 } else if (tag.compare(result_indices) == 0) {
632 indices = element->getText(TRUE);
633 } else if (tag.compare(result_positions) == 0) {
634 positions = element->getText(TRUE);
635 } else {
636 // an unknown tag...
637 char *cTag = getCString(&tag);
638
639 log_info("Test %s: unknown element with tag \"%s\"\n", id, cTag);
640 freeCString(cTag);
641 }
642 }
643
644 // TODO: make sure that the font, test-text, result-glyphs, result-indices and result-positions
645 // have all been provided
646 if (font == NULL) {
647 LEErrorCode fontStatus = LE_NO_ERROR;
648
649 font = new SimpleFontInstance(12, fontStatus);
650 typoFlags |= 0x80000000L; // use CharSubstitutionFilter...
651 }
652
653 expected.glyphs = (LEGlyphID *) getHexArray(glyphs, glyphCount);
654 expected.indices = (le_int32 *) getHexArray(indices, indexCount);
655 expected.positions = getFloatArray(positions, positionCount);
656
657 expected.glyphCount = glyphCount;
658
659 if (glyphCount < charCount || indexCount != glyphCount || positionCount < glyphCount * 2 + 2) {
660 log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n",
661 id, charCount, glyphCount, indexCount, positionCount);
662 goto free_expected;
663 };
664
665 engine = LayoutEngine::layoutEngineFactory(font, scriptCode, languageCode, typoFlags, success);
666
667 if (LE_FAILURE(success)) {
668 log_err("Test %s: could not create a LayoutEngine.\n", id);
669 goto free_expected;
670 }
671
672 actual.glyphCount = engine->layoutChars(text.getBuffer(), 0, charCount, charCount, getRTL(text), 0, 0, success);
673
674 actual.glyphs = NEW_ARRAY(LEGlyphID, actual.glyphCount);
675 actual.indices = NEW_ARRAY(le_int32, actual.glyphCount);
676 actual.positions = NEW_ARRAY(float, actual.glyphCount * 2 + 2);
677
678 engine->getGlyphs(actual.glyphs, success);
679 engine->getCharIndices(actual.indices, success);
680 engine->getGlyphPositions(actual.positions, success);
681
682 compareResults(id, &expected, &actual);
683
684 DELETE_ARRAY(actual.positions);
685 DELETE_ARRAY(actual.indices);
686 DELETE_ARRAY(actual.glyphs);
687
688 delete engine;
689
690free_expected:
691 DELETE_ARRAY(expected.positions);
692 DELETE_ARRAY(expected.indices);
693 DELETE_ARRAY(expected.glyphs);
694
695 delete font;
696
697free_c_strings:
698 freeCString(lang);
699 freeCString(script);
700 freeCString(id);
b75a7d8f 701 }
73c04bcf
A
702 }
703
704 delete root;
705 delete parser;
706#endif
707}
708U_CDECL_END
709
710static void addAllTests(TestNode** root)
711{
712 addTest(root, &ParamTest, "api/ParameterTest");
713 addTest(root, &FactoryTest, "api/FactoryTest");
714 addTest(root, &AccessTest, "layout/AccessTest");
715 addTest(root, &DataDrivenTest, "layout/DataDrivenTest");
716}
717
718/* returns the path to icu/source/data/out */
719static const char *ctest_dataOutDir()
720{
721 static const char *dataOutDir = NULL;
b75a7d8f 722
73c04bcf
A
723 if(dataOutDir) {
724 return dataOutDir;
b75a7d8f
A
725 }
726
73c04bcf
A
727 /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
728 // to point to the top of the build hierarchy, which may or
729 // may not be the same as the source directory, depending on
730 // the configure options used. At any rate,
731 // set the data path to the built data from this directory.
732 // The value is complete with quotes, so it can be used
733 // as-is as a string constant.
734 */
735#if defined (U_TOPBUILDDIR)
736 {
737 dataOutDir = U_TOPBUILDDIR "data"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING;
738 }
739#else
740
741 /* On Windows, the file name obtained from __FILE__ includes a full path.
742 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
743 * Change to "wherever\icu\source\data"
744 */
745 {
746 static char p[sizeof(__FILE__) + 20];
747 char *pBackSlash;
748 int i;
749
750 strcpy(p, __FILE__);
751 /* We want to back over three '\' chars. */
752 /* Only Windows should end up here, so looking for '\' is safe. */
753 for (i=1; i<=3; i++) {
754 pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
755 if (pBackSlash != NULL) {
756 *pBackSlash = 0; /* Truncate the string at the '\' */
757 }
758 }
759
760 if (pBackSlash != NULL) {
761 /* We found and truncated three names from the path.
762 * Now append "source\data" and set the environment
763 */
764 strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
765 dataOutDir = p;
766 }
767 else {
768 /* __FILE__ on MSVC7 does not contain the directory */
769 FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
770 if (file) {
771 fclose(file);
772 dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
773 }
774 else {
775 dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
776 }
777 }
778 }
779#endif
780
781 return dataOutDir;
782}
783
784/* ctest_setICU_DATA - if the ICU_DATA environment variable is not already
785 * set, try to deduce the directory in which ICU was built,
786 * and set ICU_DATA to "icu/source/data" in that location.
787 * The intent is to allow the tests to have a good chance
788 * of running without requiring that the user manually set
789 * ICU_DATA. Common data isn't a problem, since it is
790 * picked up via a static (build time) reference, but the
791 * tests dynamically load some data.
792 */
793static void ctest_setICU_DATA() {
794
795 /* No location for the data dir was identifiable.
796 * Add other fallbacks for the test data location here if the need arises
797 */
798 if (getenv("ICU_DATA") == NULL) {
799 /* If ICU_DATA isn't set, set it to the usual location */
800 u_setDataDirectory(ctest_dataOutDir());
801 }
b75a7d8f 802}
73c04bcf
A
803
804int main(int argc, char* argv[])
805{
806 int32_t nerrors = 0;
807 TestNode *root = NULL;
808 UErrorCode errorCode = U_ZERO_ERROR;
809 UDate startTime, endTime;
810 int32_t diffTime;
811
812 startTime = uprv_getUTCtime();
813
814 /* Check whether ICU will initialize without forcing the build data directory into
815 * the ICU_DATA path. Success here means either the data dll contains data, or that
816 * this test program was run with ICU_DATA set externally. Failure of this check
817 * is normal when ICU data is not packaged into a shared library.
818 *
819 * Whether or not this test succeeds, we want to cleanup and reinitialize
820 * with a data path so that data loading from individual files can be tested.
821 */
822 u_init(&errorCode);
823
824 if (U_FAILURE(errorCode)) {
825 fprintf(stderr,
826 "#### Note: ICU Init without build-specific setDataDirectory() failed.\n");
827 }
828
829 u_cleanup();
830 errorCode = U_ZERO_ERROR;
831
832 /* Initialize ICU */
833 ctest_setICU_DATA(); /* u_setDataDirectory() must happen Before u_init() */
834 u_init(&errorCode);
835
836 if (U_FAILURE(errorCode)) {
837 fprintf(stderr,
838 "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
839 "*** Check the ICU_DATA environment variable and \n"
840 "*** check that the data files are present.\n", argv[0], u_errorName(errorCode));
841 return 1;
842 }
843
844 addAllTests(&root);
845 nerrors = processArgs(root, argc, argv);
846
847 cleanUpTestTree(root);
848 u_cleanup();
849
850 endTime = uprv_getUTCtime();
851 diffTime = (int32_t)(endTime - startTime);
852 printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
853 (int)((diffTime%U_MILLIS_PER_DAY)/U_MILLIS_PER_HOUR),
854 (int)((diffTime%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE),
855 (int)((diffTime%U_MILLIS_PER_MINUTE)/U_MILLIS_PER_SECOND),
856 (int)(diffTime%U_MILLIS_PER_SECOND));
857
858 return nerrors;
859}
860