1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ********************************************************************************
5 *
6 * Copyright (C) 1996-2014, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 ********************************************************************************
10 */
11 #include <assert.h>
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #if defined(__ANDROID__)
20 #include <errno.h>
21 #include <libgen.h>
22 #include <unistd.h>
23 #endif
24
25 #if defined(__linux__)
26 #include <features.h>
27 #endif
28
29 #include <limits.h>
30 #if defined(__GLIBC__)
31 // Glibc's PATH_MAX is not in limits.h.
32 #include <linux/limits.h>
33 #endif
34
35 #include "unicode/ctest.h"
36 #include "unicode/utrace.h"
37 #include "unicode/uclean.h"
38 #include "unicode/ures.h"
39 #include "putilimp.h"
40 #include "udbgutil.h"
41
42 /* NOTES:
43 3/20/1999 srl - strncpy called w/o setting nulls at the end
44 */
45
46 #define MAXTESTNAME 128
47 #define MAXTESTS 512
48 #define MAX_TEST_LOG 4096
49
50 /**
51 * How may columns to indent the 'OK' markers.
52 */
53 #define FLAG_INDENT 45
54 /**
55 * How many lines of scrollage can go by before we need to remind the user what the test is.
56 */
57 #define PAGE_SIZE_LIMIT 25
58
59 #ifndef SHOW_TIMES
60 #define SHOW_TIMES 1
61 #endif
62
63 #if defined(_WIN32)
64 #define PATH_MAX MAX_PATH
65 #elif !defined(PATH_MAX)
66 #define PATH_MAX 256 // The minimum value allowed by POSIX.
67 #endif
68
69 struct TestNode
70 {
71 void (*test)(void);
72 struct TestNode* sibling;
73 struct TestNode* child;
74 char name[1]; /* This is dynamically allocated off the end with malloc. */
75 };
76
77
78 static const struct TestNode* currentTest;
79
80 typedef enum { RUNTESTS, SHOWTESTS } TestMode;
81 #define TEST_SEPARATOR '/'
82
83 #ifndef C_TEST_IMPL
84 #define C_TEST_IMPL
85 #endif
86
87 #include "unicode/ctest.h"
88
89 static char ERROR_LOG[MAX_TEST_LOG][MAXTESTNAME];
90
91 /* Local prototypes */
92 static TestNode* addTestNode( TestNode *root, const char *name );
93
94 static TestNode *createTestNode(const char* name, int32_t nameLen);
95
96 static int strncmp_nullcheck( const char* s1,
97 const char* s2,
98 int n );
99
100 static void getNextLevel( const char* name,
101 int* nameLen,
102 const char** nextName );
103
104 static void iterateTestsWithLevel( const TestNode *root, int depth,
105 const TestNode** nodeList,
106 TestMode mode);
107
108 static void help ( const char *argv0 );
109
110 /**
111 * Do the work of logging an error. Doesn't increase the error count.
112 *
113 * @prefix optional prefix prepended to message, or NULL.
114 * @param pattern printf style pattern
115 * @param ap vprintf style arg list
116 */
117 static void vlog_err(const char *prefix, const char *pattern, va_list ap);
118 static void vlog_verbose(const char *prefix, const char *pattern, va_list ap);
119 static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap);
120
121 /**
122 * Log test structure, with indent
123 * @param pattern printf pattern
124 */
125 static void log_testinfo_i(const char *pattern, ...);
126
127 /**
128 * Log test structure, NO indent
129 * @param pattern printf pattern
130 */
131 static void log_testinfo(const char *pattern, ...);
132
133 /* If we need to make the framework multi-thread safe
134 we need to pass around the following vars
135 */
136 static int ERRONEOUS_FUNCTION_COUNT = 0;
137 static int ERROR_COUNT = 0; /* Count of errors from all tests. */
138 static int ONE_ERROR = 0; /* were there any other errors? */
139 static int DATA_ERROR_COUNT = 0; /* count of data related errors or warnings */
140 static int INDENT_LEVEL = 0;
141 static UBool NO_KNOWN = false;
142 static void *knownList = NULL;
143 static char gTestName[1024] = "";
144 static UBool ON_LINE = false; /* are we on the top line with our test name? */
145 static UBool HANGING_OUTPUT = false; /* did the user leave us without a trailing \n ? */
146 static int GLOBAL_PRINT_COUNT = 0; /* global count of printouts */
147 int REPEAT_TESTS_INIT = 0; /* Was REPEAT_TESTS initialized? */
148 int REPEAT_TESTS = 1; /* Number of times to run the test */
149 int VERBOSITY = 0; /* be No-verbose by default */
150 int ERR_MSG =1; /* error messages will be displayed by default*/
151 int QUICK = 1; /* Skip some of the slower tests? */
152 int WARN_ON_MISSING_DATA = 0; /* Reduce data errs to warnings? */
153 UTraceLevel ICU_TRACE = UTRACE_OFF; /* ICU tracing level */
154 int WRITE_GOLDEN_DATA = 0; /* Overwrite golden data files? */
155 size_t MINIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Minimum library memory allocation window that will fail. */
156 size_t MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Maximum library memory allocation window that will fail. */
157 static const char *ARGV_0 = "[ALL]";
158 static const char *XML_FILE_NAME=NULL;
159 static char XML_PREFIX[256];
160 static const char *SUMMARY_FILE = NULL;
161 FILE *XML_FILE = NULL;
162 /*-------------------------------------------*/
163
164 /* strncmp that also makes sure there's a \0 at s2[0] */
strncmp_nullcheck(const char * s1,const char * s2,int n)165 static int strncmp_nullcheck( const char* s1,
166 const char* s2,
167 int n )
168 {
169 if (((int)strlen(s2) >= n) && s2[n] != 0) {
170 return 3; /* null check fails */
171 }
172 else {
173 return strncmp ( s1, s2, n );
174 }
175 }
176
getNextLevel(const char * name,int * nameLen,const char ** nextName)177 static void getNextLevel( const char* name,
178 int* nameLen,
179 const char** nextName )
180 {
181 /* Get the next component of the name */
182 *nextName = strchr(name, TEST_SEPARATOR);
183
184 if( *nextName != 0 )
185 {
186 char n[255];
187 *nameLen = (int)((*nextName) - name);
188 (*nextName)++; /* skip '/' */
189 strncpy(n, name, *nameLen);
190 n[*nameLen] = 0;
191 /*printf("->%s-< [%d] -> [%s]\n", name, *nameLen, *nextName);*/
192 }
193 else {
194 *nameLen = (int)strlen(name);
195 }
196 }
197
createTestNode(const char * name,int32_t nameLen)198 static TestNode *createTestNode(const char* name, int32_t nameLen)
199 {
200 TestNode *newNode;
201
202 newNode = (TestNode*)malloc(sizeof(TestNode) + (nameLen + 1));
203
204 newNode->test = NULL;
205 newNode->sibling = NULL;
206 newNode->child = NULL;
207
208 strncpy( newNode->name, name, nameLen );
209 newNode->name[nameLen] = 0;
210
211 return newNode;
212 }
213
214 void T_CTEST_EXPORT2
cleanUpTestTree(TestNode * tn)215 cleanUpTestTree(TestNode *tn)
216 {
217 if(tn->child != NULL) {
218 cleanUpTestTree(tn->child);
219 }
220 if(tn->sibling != NULL) {
221 cleanUpTestTree(tn->sibling);
222 }
223
224 free(tn);
225
226 }
227
228
229 void T_CTEST_EXPORT2
addTest(TestNode ** root,TestFunctionPtr test,const char * name)230 addTest(TestNode** root,
231 TestFunctionPtr test,
232 const char* name )
233 {
234 TestNode *newNode;
235
236 /*if this is the first Test created*/
237 if (*root == NULL)
238 *root = createTestNode("", 0);
239
240 newNode = addTestNode( *root, name );
241 assert(newNode != 0 );
242 /* printf("addTest: nreName = %s\n", newNode->name );*/
243
244 newNode->test = test;
245 }
246
247 /* non recursive insert function */
addTestNode(TestNode * root,const char * name)248 static TestNode *addTestNode ( TestNode *root, const char *name )
249 {
250 const char* nextName;
251 TestNode *nextNode, *curNode;
252 int nameLen; /* length of current 'name' */
253
254 /* remove leading slash */
255 if ( *name == TEST_SEPARATOR )
256 name++;
257
258 curNode = root;
259
260 for(;;)
261 {
262 /* Start with the next child */
263 nextNode = curNode->child;
264
265 getNextLevel ( name, &nameLen, &nextName );
266
267 /* printf("* %s\n", name );*/
268
269 /* if nextNode is already null, then curNode has no children
270 -- add them */
271 if( nextNode == NULL )
272 {
273 /* Add all children of the node */
274 do
275 {
276 /* Get the next component of the name */
277 getNextLevel(name, &nameLen, &nextName);
278
279 /* update curName to have the next name segment */
280 curNode->child = createTestNode(name, nameLen);
281 /* printf("*** added %s\n", curNode->child->name );*/
282 curNode = curNode->child;
283 name = nextName;
284 }
285 while( name != NULL );
286
287 return curNode;
288 }
289
290 /* Search across for the name */
291 while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 )
292 {
293 curNode = nextNode;
294 nextNode = nextNode -> sibling;
295
296 if ( nextNode == NULL )
297 {
298 /* Did not find 'name' on this level. */
299 nextNode = createTestNode(name, nameLen);
300 curNode->sibling = nextNode;
301 break;
302 }
303 }
304
305 /* nextNode matches 'name' */
306
307 if (nextName == NULL) /* end of the line */
308 {
309 return nextNode;
310 }
311
312 /* Loop again with the next item */
313 name = nextName;
314 curNode = nextNode;
315 }
316 }
317
318 /**
319 * Log the time taken. May not output anything.
320 * @param deltaTime change in time
321 */
str_timeDelta(char * str,UDate deltaTime)322 void T_CTEST_EXPORT2 str_timeDelta(char *str, UDate deltaTime) {
323 if (deltaTime > 110000.0 ) {
324 double mins = uprv_floor(deltaTime/60000.0);
325 sprintf(str, "[(%.0fm %.1fs)]", mins, (deltaTime-(mins*60000.0))/1000.0);
326 } else if (deltaTime > 1500.0) {
327 sprintf(str, "((%.1fs))", deltaTime/1000.0);
328 } else if(deltaTime>900.0) {
329 sprintf(str, "( %.2fs )", deltaTime/1000.0);
330 } else if(deltaTime > 5.0) {
331 sprintf(str, " (%.0fms) ", deltaTime);
332 } else {
333 str[0]=0; /* at least terminate it. */
334 }
335 }
336
print_timeDelta(UDate deltaTime)337 static void print_timeDelta(UDate deltaTime) {
338 char str[256];
339 str_timeDelta(str, deltaTime);
340 if(str[0]) {
341 printf("%s", str);
342 }
343 }
344
345 /**
346 * Run or list tests (according to mode) in a subtree.
347 *
348 * @param root root of the subtree to operate on
349 * @param depth The depth of this tree (0=root)
350 * @param nodeList an array of MAXTESTS depth that's used for keeping track of where we are. nodeList[depth] points to the 'parent' at depth depth.
351 * @param mode what mode we are operating in.
352 */
iterateTestsWithLevel(const TestNode * root,int depth,const TestNode ** nodeList,TestMode mode)353 static void iterateTestsWithLevel ( const TestNode* root,
354 int depth,
355 const TestNode** nodeList,
356 TestMode mode)
357 {
358 int i;
359
360 char pathToFunction[MAXTESTNAME] = "";
361 char separatorString[2] = { TEST_SEPARATOR, '\0'};
362 #if SHOW_TIMES
363 UDate allStartTime = -1, allStopTime = -1;
364 #endif
365
366 if(depth<2) {
367 allStartTime = uprv_getRawUTCtime();
368 }
369
370 if ( root == NULL )
371 return;
372
373 /* record the current root node, and increment depth. */
374 nodeList[depth++] = root;
375 /* depth is now the depth of root's children. */
376
377 /* Collect the 'path' to the current subtree. */
378 for ( i=0;i<(depth-1);i++ )
379 {
380 strcat(pathToFunction, nodeList[i]->name);
381 strcat(pathToFunction, separatorString);
382 }
383 strcat(pathToFunction, nodeList[i]->name); /* including 'root' */
384
385 /* print test name and space. */
386 INDENT_LEVEL = depth-1;
387 if(root->name[0]) {
388 log_testinfo_i("%s ", root->name);
389 } else {
390 log_testinfo_i("(%s) ", ARGV_0);
391 }
392 ON_LINE = true; /* we are still on the line with the test name */
393
394
395 if ( (mode == RUNTESTS) &&
396 (root->test != NULL)) /* if root is a leaf node, run it */
397 {
398 int myERROR_COUNT = ERROR_COUNT;
399 int myGLOBAL_PRINT_COUNT = GLOBAL_PRINT_COUNT;
400 #if SHOW_TIMES
401 UDate startTime, stopTime;
402 char timeDelta[256];
403 char timeSeconds[256];
404 #else
405 const char timeDelta[] = "(unknown)";
406 const char timeSeconds[] = "0.000";
407 #endif
408 currentTest = root;
409 INDENT_LEVEL = depth; /* depth of subitems */
410 ONE_ERROR=0;
411 HANGING_OUTPUT=false;
412 #if SHOW_TIMES
413 startTime = uprv_getRawUTCtime();
414 #endif
415 strcpy(gTestName, pathToFunction);
416 root->test(); /* PERFORM THE TEST ************************/
417 #if SHOW_TIMES
418 stopTime = uprv_getRawUTCtime();
419 #endif
420 if(HANGING_OUTPUT) {
421 log_testinfo("\n");
422 HANGING_OUTPUT=false;
423 }
424 INDENT_LEVEL = depth-1; /* depth of root */
425 currentTest = NULL;
426 if((ONE_ERROR>0)&&(ERROR_COUNT==0)) {
427 ERROR_COUNT++; /* There was an error without a newline */
428 }
429 ONE_ERROR=0;
430
431 #if SHOW_TIMES
432 str_timeDelta(timeDelta, stopTime-startTime);
433 sprintf(timeSeconds, "%f", (stopTime-startTime)/1000.0);
434 #endif
435 ctest_xml_testcase(pathToFunction, pathToFunction, timeSeconds, (myERROR_COUNT!=ERROR_COUNT)?"error":NULL);
436
437 if (myERROR_COUNT != ERROR_COUNT) {
438 log_testinfo_i("} ---[%d ERRORS in %s] ", ERROR_COUNT - myERROR_COUNT, pathToFunction);
439 strcpy(ERROR_LOG[ERRONEOUS_FUNCTION_COUNT++], pathToFunction);
440 } else {
441 if(!ON_LINE) { /* had some output */
442 int spaces = FLAG_INDENT-(depth-1);
443 log_testinfo_i("} %*s[OK] ", spaces, "---");
444 if((GLOBAL_PRINT_COUNT-myGLOBAL_PRINT_COUNT)>PAGE_SIZE_LIMIT) {
445 log_testinfo(" %s ", pathToFunction); /* in case they forgot. */
446 }
447 } else {
448 /* put -- out at 30 sp. */
449 int spaces = FLAG_INDENT - ((int)strlen(root->name) + depth);
450 if(spaces<0) spaces=0;
451 log_testinfo(" %*s[OK] ", spaces,"---");
452 }
453 }
454
455 #if SHOW_TIMES
456 if(timeDelta[0]) printf("%s", timeDelta);
457 #endif
458
459 ON_LINE = true; /* we are back on-line */
460 }
461
462 INDENT_LEVEL = depth-1; /* root */
463
464 /* we want these messages to be at 0 indent. so just push the indent level briefly. */
465 if(mode==SHOWTESTS) {
466 log_testinfo("---%s%c\n",pathToFunction, nodeList[i]->test?' ':TEST_SEPARATOR );
467 }
468
469 INDENT_LEVEL = depth;
470
471 if(root->child) {
472 int myERROR_COUNT = ERROR_COUNT;
473 int myGLOBAL_PRINT_COUNT = GLOBAL_PRINT_COUNT;
474 if(mode!=SHOWTESTS) {
475 INDENT_LEVEL=depth-1;
476 log_testinfo("{\n");
477 INDENT_LEVEL=depth;
478 }
479
480 iterateTestsWithLevel ( root->child, depth, nodeList, mode );
481
482 if(mode!=SHOWTESTS) {
483 INDENT_LEVEL=depth-1;
484 log_testinfo_i("} "); /* TODO: summarize subtests */
485 if((depth>1) && (ERROR_COUNT > myERROR_COUNT)) {
486 log_testinfo("[%d %s in %s] ", ERROR_COUNT-myERROR_COUNT, (ERROR_COUNT-myERROR_COUNT)==1?"error":"errors", pathToFunction);
487 } else if((GLOBAL_PRINT_COUNT-myGLOBAL_PRINT_COUNT)>PAGE_SIZE_LIMIT || (depth<1)) {
488 if(pathToFunction[0]) {
489 log_testinfo(" %s ", pathToFunction); /* in case they forgot. */
490 } else {
491 log_testinfo(" / (%s) ", ARGV_0);
492 }
493 }
494
495 ON_LINE=true;
496 }
497 }
498 depth--;
499
500 #if SHOW_TIMES
501 if(depth<2) {
502 allStopTime = uprv_getRawUTCtime();
503 print_timeDelta(allStopTime-allStartTime);
504 }
505 #endif
506
507 if(mode!=SHOWTESTS && ON_LINE) {
508 log_testinfo("\n");
509 }
510
511 if ( depth != 0 ) { /* DO NOT iterate over siblings of the root. TODO: why not? */
512 iterateTestsWithLevel ( root->sibling, depth, nodeList, mode );
513 }
514 }
515
516
517
518 void T_CTEST_EXPORT2
showTests(const TestNode * root)519 showTests ( const TestNode *root )
520 {
521 /* make up one for them */
522 const TestNode *nodeList[MAXTESTS];
523
524 if (root == NULL)
525 log_err("TEST CAN'T BE FOUND!");
526
527 iterateTestsWithLevel ( root, 0, nodeList, SHOWTESTS );
528
529 }
530
531 void T_CTEST_EXPORT2
runTests(const TestNode * root)532 runTests ( const TestNode *root )
533 {
534 int i;
535 const TestNode *nodeList[MAXTESTS];
536 /* make up one for them */
537
538
539 if (root == NULL)
540 log_err("TEST CAN'T BE FOUND!\n");
541
542 ERRONEOUS_FUNCTION_COUNT = ERROR_COUNT = 0;
543 iterateTestsWithLevel ( root, 0, nodeList, RUNTESTS );
544
545 /*print out result summary*/
546
547 ON_LINE=false; /* just in case */
548
549 if(knownList != NULL) {
550 if( udbg_knownIssue_print(knownList) ) {
551 fprintf(stdout, "(To run suppressed tests, use the -K option.) \n\n");
552 }
553 udbg_knownIssue_close(knownList);
554 knownList = NULL;
555 }
556
557 if (ERROR_COUNT)
558 {
559 fprintf(stdout,"\nSUMMARY:\n");
560 fflush(stdout);
561 fprintf(stdout,"******* [Total error count:\t%d]\n", ERROR_COUNT);
562 fflush(stdout);
563 fprintf(stdout, " Errors in\n");
564 for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++)
565 fprintf(stdout, "[%s]\n",ERROR_LOG[i]);
566 if(SUMMARY_FILE != NULL) {
567 FILE *summf = fopen(SUMMARY_FILE, "w");
568 if(summf!=NULL) {
569 for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++)
570 fprintf(summf, "%s\n",ERROR_LOG[i]);
571 fclose(summf);
572 }
573 }
574 }
575 else
576 {
577 log_testinfo("\n[All tests passed successfully...]\n");
578 }
579
580 if(DATA_ERROR_COUNT) {
581 if(WARN_ON_MISSING_DATA==0) {
582 log_testinfo("\t*Note* some errors are data-loading related. If the data used is not the \n"
583 "\tstock ICU data (i.e some have been added or removed), consider using\n"
584 "\tthe '-w' option to turn these errors into warnings.\n");
585 } else {
586 log_testinfo("\t*WARNING* some data-loading errors were ignored by the -w option.\n");
587 }
588 }
589 }
590
591 const char* T_CTEST_EXPORT2
getTestName(void)592 getTestName(void)
593 {
594 if(currentTest != NULL) {
595 return currentTest->name;
596 } else {
597 return NULL;
598 }
599 }
600
601 const TestNode* T_CTEST_EXPORT2
getTest(const TestNode * root,const char * name)602 getTest(const TestNode* root, const char* name)
603 {
604 const char* nextName;
605 TestNode *nextNode;
606 const TestNode* curNode;
607 int nameLen; /* length of current 'name' */
608
609 if (root == NULL) {
610 log_err("TEST CAN'T BE FOUND!\n");
611 return NULL;
612 }
613 /* remove leading slash */
614 if ( *name == TEST_SEPARATOR )
615 name++;
616
617 curNode = root;
618
619 for(;;)
620 {
621 /* Start with the next child */
622 nextNode = curNode->child;
623
624 getNextLevel ( name, &nameLen, &nextName );
625
626 /* printf("* %s\n", name );*/
627
628 /* if nextNode is already null, then curNode has no children
629 -- add them */
630 if( nextNode == NULL )
631 {
632 return NULL;
633 }
634
635 /* Search across for the name */
636 while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 )
637 {
638 curNode = nextNode;
639 nextNode = nextNode -> sibling;
640
641 if ( nextNode == NULL )
642 {
643 /* Did not find 'name' on this level. */
644 return NULL;
645 }
646 }
647
648 /* nextNode matches 'name' */
649
650 if (nextName == NULL) /* end of the line */
651 {
652 return nextNode;
653 }
654
655 /* Loop again with the next item */
656 name = nextName;
657 curNode = nextNode;
658 }
659 }
660
661 /* =========== io functions ======== */
662
go_offline_with_marker(const char * mrk)663 static void go_offline_with_marker(const char *mrk) {
664 UBool wasON_LINE = ON_LINE;
665
666 if(ON_LINE) {
667 log_testinfo(" {\n");
668 ON_LINE=false;
669 }
670
671 if(!HANGING_OUTPUT || wasON_LINE) {
672 if(mrk != NULL) {
673 fputs(mrk, stdout);
674 }
675 }
676 }
677
go_offline(void)678 static void go_offline(void) {
679 go_offline_with_marker(NULL);
680 }
681
go_offline_err(void)682 static void go_offline_err(void) {
683 go_offline();
684 }
685
first_line_verbose(void)686 static void first_line_verbose(void) {
687 go_offline_with_marker("v");
688 }
689
first_line_err(void)690 static void first_line_err(void) {
691 go_offline_with_marker("!");
692 }
693
first_line_info(void)694 static void first_line_info(void) {
695 go_offline_with_marker("\"");
696 }
697
first_line_test(void)698 static void first_line_test(void) {
699 fputs(" ", stdout);
700 }
701
702
vlog_err(const char * prefix,const char * pattern,va_list ap)703 static void vlog_err(const char *prefix, const char *pattern, va_list ap)
704 {
705 if( ERR_MSG == false){
706 return;
707 }
708 fputs("!", stdout); /* col 1 - bang */
709 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
710 if(prefix) {
711 fputs(prefix, stdout);
712 }
713 vfprintf(stdout, pattern, ap);
714 fflush(stdout);
715 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
716 HANGING_OUTPUT=1;
717 } else {
718 HANGING_OUTPUT=0;
719 }
720 GLOBAL_PRINT_COUNT++;
721 }
722
vlog_knownIssue(const char * ticket,const char * pattern,va_list ap)723 static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap)
724 {
725 char buf[2048];
726 UBool firstForTicket;
727 UBool firstForWhere;
728
729 if(NO_KNOWN) return false;
730 if(pattern==NULL) pattern="";
731
732 vsprintf(buf, pattern, ap);
733 knownList = udbg_knownIssue_open(knownList, ticket, gTestName, buf,
734 &firstForTicket, &firstForWhere);
735
736 if(firstForTicket || firstForWhere) {
737 log_info("(Known issue %s) %s\n", ticket, buf);
738 } else {
739 log_verbose("(Known issue %s) %s\n", ticket, buf);
740 }
741
742 return true;
743 }
744
745
746 void T_CTEST_EXPORT2
vlog_info(const char * prefix,const char * pattern,va_list ap)747 vlog_info(const char *prefix, const char *pattern, va_list ap)
748 {
749 first_line_info();
750 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
751 if(prefix) {
752 fputs(prefix, stdout);
753 }
754 vfprintf(stdout, pattern, ap);
755 fflush(stdout);
756 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
757 HANGING_OUTPUT=1;
758 } else {
759 HANGING_OUTPUT=0;
760 }
761 GLOBAL_PRINT_COUNT++;
762 }
763 /**
764 * Log test structure, with indent
765 */
log_testinfo_i(const char * pattern,...)766 static void log_testinfo_i(const char *pattern, ...)
767 {
768 va_list ap;
769 first_line_test();
770 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
771 va_start(ap, pattern);
772 vfprintf(stdout, pattern, ap);
773 fflush(stdout);
774 va_end(ap);
775 GLOBAL_PRINT_COUNT++;
776 }
777 /**
778 * Log test structure (no ident)
779 */
log_testinfo(const char * pattern,...)780 static void log_testinfo(const char *pattern, ...)
781 {
782 va_list ap;
783 va_start(ap, pattern);
784 first_line_test();
785 vfprintf(stdout, pattern, ap);
786 fflush(stdout);
787 va_end(ap);
788 GLOBAL_PRINT_COUNT++;
789 }
790
791
vlog_verbose(const char * prefix,const char * pattern,va_list ap)792 static void vlog_verbose(const char *prefix, const char *pattern, va_list ap)
793 {
794 if ( VERBOSITY == false )
795 return;
796
797 first_line_verbose();
798 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
799 if(prefix) {
800 fputs(prefix, stdout);
801 }
802 vfprintf(stdout, pattern, ap);
803 fflush(stdout);
804 GLOBAL_PRINT_COUNT++;
805 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
806 HANGING_OUTPUT=1;
807 } else {
808 HANGING_OUTPUT=0;
809 }
810 }
811
812 void T_CTEST_EXPORT2
log_err(const char * pattern,...)813 log_err(const char* pattern, ...)
814 {
815 va_list ap;
816 first_line_err();
817 if(strchr(pattern, '\n') != NULL) {
818 /*
819 * Count errors only if there is a line feed in the pattern
820 * so that we do not exaggerate our error count.
821 */
822 ++ERROR_COUNT;
823 } else {
824 /* Count at least one error. */
825 ONE_ERROR=1;
826 }
827 va_start(ap, pattern);
828 vlog_err(NULL, pattern, ap);
829 va_end(ap);
830 }
831
832 UBool T_CTEST_EXPORT2
log_knownIssue(const char * ticket,const char * pattern,...)833 log_knownIssue(const char *ticket, const char *pattern, ...) {
834 va_list ap;
835 va_start(ap, pattern);
836 UBool result = vlog_knownIssue(ticket, pattern, ap);
837 va_end(ap);
838 return result;
839 }
840
841 void T_CTEST_EXPORT2
log_err_status(UErrorCode status,const char * pattern,...)842 log_err_status(UErrorCode status, const char* pattern, ...)
843 {
844 va_list ap;
845 va_start(ap, pattern);
846
847 if ((status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR)) {
848 ++DATA_ERROR_COUNT; /* for informational message at the end */
849
850 if (WARN_ON_MISSING_DATA == 0) {
851 first_line_err();
852 /* Fatal error. */
853 if (strchr(pattern, '\n') != NULL) {
854 ++ERROR_COUNT;
855 } else {
856 ++ONE_ERROR;
857 }
858 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
859 } else {
860 vlog_info("[DATA] ", pattern, ap);
861 }
862 } else {
863 first_line_err();
864 /* Fatal error. */
865 if(strchr(pattern, '\n') != NULL) {
866 ++ERROR_COUNT;
867 } else {
868 ++ONE_ERROR;
869 }
870 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
871 }
872 va_end(ap);
873 }
874
875 void T_CTEST_EXPORT2
log_info(const char * pattern,...)876 log_info(const char* pattern, ...)
877 {
878 va_list ap;
879
880 va_start(ap, pattern);
881 vlog_info(NULL, pattern, ap);
882 va_end(ap);
883 }
884
885 void T_CTEST_EXPORT2
log_verbose(const char * pattern,...)886 log_verbose(const char* pattern, ...)
887 {
888 va_list ap;
889
890 va_start(ap, pattern);
891 vlog_verbose(NULL, pattern, ap);
892 va_end(ap);
893 }
894
895
896 void T_CTEST_EXPORT2
log_data_err(const char * pattern,...)897 log_data_err(const char* pattern, ...)
898 {
899 va_list ap;
900 va_start(ap, pattern);
901
902 go_offline_err();
903 ++DATA_ERROR_COUNT; /* for informational message at the end */
904
905 if(WARN_ON_MISSING_DATA == 0) {
906 /* Fatal error. */
907 if(strchr(pattern, '\n') != NULL) {
908 ++ERROR_COUNT;
909 }
910 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
911 } else {
912 vlog_info("[DATA] ", pattern, ap);
913 }
914 va_end(ap);
915 }
916
917
918 /*
919 * Tracing functions.
920 */
921 static int traceFnNestingDepth = 0;
922 U_CDECL_BEGIN
TraceEntry(const void * context,int32_t fnNumber)923 static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) {
924 char buf[500];
925 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0;
926 fputs(buf, stdout);
927 traceFnNestingDepth++;
928 }
929
TraceExit(const void * context,int32_t fnNumber,const char * fmt,va_list args)930 static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500];
931
932 if (traceFnNestingDepth>0) {
933 traceFnNestingDepth--;
934 }
935 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0;
936 fputs(buf, stdout);
937 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
938 buf[sizeof(buf)-1]=0;
939 fputs(buf, stdout);
940 putc('\n', stdout);
941 }
942
TraceData(const void * context,int32_t fnNumber,int32_t level,const char * fmt,va_list args)943 static void U_CALLCONV TraceData(const void *context, int32_t fnNumber,
944 int32_t level, const char *fmt, va_list args) {
945 char buf[500];
946 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
947 buf[sizeof(buf)-1]=0;
948 fputs(buf, stdout);
949 putc('\n', stdout);
950 }
951
ctest_libMalloc(const void * context,size_t size)952 static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) {
953 /*if (VERBOSITY) {
954 printf("Allocated %ld\n", (long)size);
955 }*/
956 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
957 return NULL;
958 }
959 return malloc(size);
960 }
ctest_libRealloc(const void * context,void * mem,size_t size)961 static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) {
962 /*if (VERBOSITY) {
963 printf("Reallocated %ld\n", (long)size);
964 }*/
965 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
966 /*free(mem);*/ /* Realloc doesn't free on failure. */
967 return NULL;
968 }
969 return realloc(mem, size);
970 }
ctest_libFree(const void * context,void * mem)971 static void U_CALLCONV ctest_libFree(const void *context, void *mem) {
972 free(mem);
973 }
974
975 int T_CTEST_EXPORT2
initArgs(int argc,const char * const argv[],ArgHandlerPtr argHandler,void * context)976 initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context)
977 {
978 int i;
979 int argSkip = 0;
980
981 VERBOSITY = false;
982 ERR_MSG = true;
983
984 ARGV_0=argv[0];
985
986 for( i=1; i<argc; i++)
987 {
988 if ( argv[i][0] == '/' )
989 {
990 /* We don't run the tests here. */
991 continue;
992 }
993 else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0))
994 {
995 /* We don't run the tests here. */
996 continue;
997 }
998 else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0)
999 {
1000 VERBOSITY = true;
1001 }
1002 else if (strcmp( argv[i], "-l" )==0 )
1003 {
1004 /* doList = true; */
1005 }
1006 else if (strcmp( argv[i], "-e1") == 0)
1007 {
1008 QUICK = -1;
1009 }
1010 else if (strcmp( argv[i], "-e") ==0)
1011 {
1012 QUICK = 0;
1013 }
1014 else if (strcmp( argv[i], "-K") ==0)
1015 {
1016 NO_KNOWN = 1;
1017 }
1018 else if (strncmp( argv[i], "-E",2) ==0)
1019 {
1020 SUMMARY_FILE=argv[i]+2;
1021 }
1022 else if (strcmp( argv[i], "-w") ==0)
1023 {
1024 WARN_ON_MISSING_DATA = true;
1025 }
1026 else if (strcmp( argv[i], "-m") ==0)
1027 {
1028 UErrorCode errorCode = U_ZERO_ERROR;
1029 if (i+1 < argc) {
1030 char *endPtr = NULL;
1031 i++;
1032 MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10);
1033 if (endPtr == argv[i]) {
1034 printf("Can't parse %s\n", argv[i]);
1035 help(argv[0]);
1036 return 0;
1037 }
1038 if (*endPtr == '-') {
1039 char *maxPtr = endPtr+1;
1040 endPtr = NULL;
1041 MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10);
1042 if (endPtr == argv[i]) {
1043 printf("Can't parse %s\n", argv[i]);
1044 help(argv[0]);
1045 return 0;
1046 }
1047 }
1048 }
1049 /* Use the default value */
1050 u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode);
1051 if (U_FAILURE(errorCode)) {
1052 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode));
1053 return 0;
1054 }
1055 }
1056 else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0)
1057 {
1058 ERR_MSG = false;
1059 }
1060 else if (strcmp( argv[i], "-r") == 0)
1061 {
1062 if (!REPEAT_TESTS_INIT) {
1063 REPEAT_TESTS++;
1064 }
1065 }
1066 else if (strcmp( argv[i], "-x") == 0)
1067 {
1068 if(++i>=argc) {
1069 printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n");
1070 return 0;
1071 }
1072 if(ctest_xml_setFileName(argv[i])) { /* set the name */
1073 return 0;
1074 }
1075 }
1076 else if (strcmp( argv[i], "-t_info") == 0) {
1077 ICU_TRACE = UTRACE_INFO;
1078 }
1079 else if (strcmp( argv[i], "-t_error") == 0) {
1080 ICU_TRACE = UTRACE_ERROR;
1081 }
1082 else if (strcmp( argv[i], "-t_warn") == 0) {
1083 ICU_TRACE = UTRACE_WARNING;
1084 }
1085 else if (strcmp( argv[i], "-t_verbose") == 0) {
1086 ICU_TRACE = UTRACE_VERBOSE;
1087 }
1088 else if (strcmp( argv[i], "-t_oc") == 0) {
1089 ICU_TRACE = UTRACE_OPEN_CLOSE;
1090 }
1091 else if (strcmp( argv[i], "-G") == 0) {
1092 WRITE_GOLDEN_DATA = 1;
1093 }
1094 else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0)
1095 {
1096 help( argv[0] );
1097 return 0;
1098 }
1099 else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0)
1100 {
1101 i += argSkip - 1;
1102 }
1103 else
1104 {
1105 printf("* unknown option: %s\n", argv[i]);
1106 help( argv[0] );
1107 return 0;
1108 }
1109 }
1110 if (ICU_TRACE != UTRACE_OFF) {
1111 utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData);
1112 utrace_setLevel(ICU_TRACE);
1113 }
1114
1115 return 1; /* total error count */
1116 }
1117
1118 int T_CTEST_EXPORT2
runTestRequest(const TestNode * root,int argc,const char * const argv[])1119 runTestRequest(const TestNode* root,
1120 int argc,
1121 const char* const argv[])
1122 {
1123 /**
1124 * This main will parse the l, v, h, n, and path arguments
1125 */
1126 const TestNode* toRun;
1127 int i;
1128 int doList = false;
1129 int subtreeOptionSeen = false;
1130 int skipNext = false;
1131
1132 int errorCount = 0;
1133
1134 toRun = root;
1135
1136 if(ctest_xml_init(ARGV_0)) {
1137 return 1; /* couldn't fire up XML thing */
1138 }
1139
1140 for( i=1; i<argc; i++)
1141 {
1142 if (skipNext) {
1143 skipNext = false;
1144 continue;
1145 }
1146
1147 if ( argv[i][0] == '/' )
1148 {
1149 printf("Selecting subtree '%s'\n", argv[i]);
1150
1151 if ( argv[i][1] == 0 )
1152 toRun = root;
1153 else
1154 toRun = getTest(root, argv[i]);
1155
1156 if ( toRun == NULL )
1157 {
1158 printf("* Could not find any matching subtree\n");
1159 return -1;
1160 }
1161
1162 ON_LINE=false; /* just in case */
1163
1164 if( doList == true)
1165 showTests(toRun);
1166 else
1167 runTests(toRun);
1168
1169 ON_LINE=false; /* just in case */
1170
1171 errorCount += ERROR_COUNT;
1172
1173 subtreeOptionSeen = true;
1174 } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) {
1175 subtreeOptionSeen=false;
1176 } else if (strcmp( argv[i], "-l") == 0) {
1177 doList = true;
1178 } else if (strcmp( argv[i], "-x") == 0) {
1179 // Need to skip the next argument since it will be wrongly
1180 // identified as a test filter if it is an absolute path.
1181 skipNext = true;
1182 }
1183 /* else option already handled by initArgs */
1184 }
1185
1186 if( subtreeOptionSeen == false) /* no other subtree given, run the default */
1187 {
1188 ON_LINE=false; /* just in case */
1189 if( doList == true)
1190 showTests(toRun);
1191 else
1192 runTests(toRun);
1193 ON_LINE=false; /* just in case */
1194
1195 errorCount += ERROR_COUNT;
1196 }
1197 else
1198 {
1199 if( ( doList == false ) && ( errorCount > 0 ) )
1200 printf(" Total errors: %d\n", errorCount );
1201 }
1202
1203 REPEAT_TESTS_INIT = 1;
1204
1205 if(ctest_xml_fini()) {
1206 errorCount++;
1207 }
1208
1209 return errorCount; /* total error count */
1210 }
1211
1212 /**
1213 * Display program invocation arguments
1214 */
1215
help(const char * argv0)1216 static void help ( const char *argv0 )
1217 {
1218 printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
1219 " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
1220 " [ /path/to/test ]\n",
1221 argv0);
1222 printf(" -l To get a list of test names\n");
1223 printf(" -e to do exhaustive testing\n");
1224 printf(" -verbose To turn ON verbosity\n");
1225 printf(" -v To turn ON verbosity(same as -verbose)\n");
1226 printf(" -x file.xml Write junit format output to file.xml\n");
1227 printf(" -h To print this message\n");
1228 printf(" -K to turn OFF suppressing known issues\n");
1229 printf(" -n To turn OFF printing error messages\n");
1230 printf(" -w Don't fail on data-loading errs, just warn. Useful if\n"
1231 " user has reduced/changed the common set of ICU data \n");
1232 printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n");
1233 printf(" -no_err_msg (same as -n) \n");
1234 printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n");
1235 printf(" The default is the maximum value of size_t. Max is optional.\n");
1236 printf(" -r Repeat tests after calling u_cleanup \n");
1237 printf(" -G Write golden data files \n");
1238 printf(" [/subtest] To run a subtest \n");
1239 printf(" eg: to run just the utility tests type: cintltest /tsutil) \n");
1240 }
1241
1242 int32_t T_CTEST_EXPORT2
getTestOption(int32_t testOption)1243 getTestOption ( int32_t testOption ) {
1244 switch (testOption) {
1245 case VERBOSITY_OPTION:
1246 return VERBOSITY;
1247 case WARN_ON_MISSING_DATA_OPTION:
1248 return WARN_ON_MISSING_DATA;
1249 case QUICK_OPTION:
1250 return QUICK;
1251 case REPEAT_TESTS_OPTION:
1252 return REPEAT_TESTS;
1253 case ERR_MSG_OPTION:
1254 return ERR_MSG;
1255 case ICU_TRACE_OPTION:
1256 return ICU_TRACE;
1257 case WRITE_GOLDEN_DATA_OPTION:
1258 return WRITE_GOLDEN_DATA;
1259 default :
1260 return 0;
1261 }
1262 }
1263
1264 void T_CTEST_EXPORT2
setTestOption(int32_t testOption,int32_t value)1265 setTestOption ( int32_t testOption, int32_t value) {
1266 if (value == DECREMENT_OPTION_VALUE) {
1267 value = getTestOption(testOption);
1268 --value;
1269 }
1270 switch (testOption) {
1271 case VERBOSITY_OPTION:
1272 VERBOSITY = value;
1273 break;
1274 case WARN_ON_MISSING_DATA_OPTION:
1275 WARN_ON_MISSING_DATA = value;
1276 break;
1277 case QUICK_OPTION:
1278 QUICK = value;
1279 break;
1280 case REPEAT_TESTS_OPTION:
1281 REPEAT_TESTS = value;
1282 break;
1283 case ICU_TRACE_OPTION:
1284 ICU_TRACE = (UTraceLevel)value;
1285 break;
1286 case WRITE_GOLDEN_DATA_OPTION:
1287 WRITE_GOLDEN_DATA = value;
1288 default :
1289 break;
1290 }
1291 }
1292
1293
1294 /*
1295 * ================== JUnit support ================================
1296 */
1297
1298 int32_t
1299 T_CTEST_EXPORT2
ctest_xml_setFileName(const char * name)1300 ctest_xml_setFileName(const char *name) {
1301 XML_FILE_NAME=name;
1302 return 0;
1303 }
1304
1305
1306 int32_t
1307 T_CTEST_EXPORT2
ctest_xml_init(const char * rootName)1308 ctest_xml_init(const char *rootName) {
1309 if(!XML_FILE_NAME) return 0;
1310 XML_FILE = fopen(XML_FILE_NAME,"w");
1311 if(!XML_FILE) {
1312 perror("fopen");
1313 fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME);
1314 return 1;
1315 }
1316 while(*rootName&&!isalnum((int)*rootName)) {
1317 rootName++;
1318 }
1319 strcpy(XML_PREFIX,rootName);
1320 {
1321 char *p = XML_PREFIX+strlen(XML_PREFIX);
1322 for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) {
1323 *p=0;
1324 }
1325 }
1326 /* write prefix */
1327 fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX);
1328
1329 return 0;
1330 }
1331
1332 int32_t
1333 T_CTEST_EXPORT2
ctest_xml_fini(void)1334 ctest_xml_fini(void) {
1335 if(!XML_FILE) return 0;
1336
1337 fprintf(XML_FILE, "</testsuite>\n");
1338 fclose(XML_FILE);
1339 printf(" ( test results written to %s )\n", XML_FILE_NAME);
1340 XML_FILE=0;
1341 return 0;
1342 }
1343
1344
1345 int32_t
1346 T_CTEST_EXPORT2
ctest_xml_testcase(const char * classname,const char * name,const char * timeSeconds,const char * failMsg)1347 ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) {
1348 if(!XML_FILE) return 0;
1349
1350 fprintf(XML_FILE, "\t<testcase classname=\"%s\" name=\"%s\" time=\"%s\"", name, classname, timeSeconds);
1351 if(failMsg) {
1352 fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg);
1353 } else {
1354 fprintf(XML_FILE, "/>\n");
1355 }
1356
1357 return 0;
1358 }
1359
ctest_icuSrcDir(void)1360 static const char* ctest_icuSrcDir(void) {
1361 static const char* srcDir = NULL;
1362
1363 if (srcDir) {
1364 return srcDir;
1365 }
1366
1367 #if defined(__ANDROID__)
1368 /*
1369 * On Android, the source tree is not available as the tests are cross
1370 * compiled. Test data is found at paths relative to the test binary.
1371 */
1372 char exePath[PATH_MAX];
1373 ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath));
1374 if (len == -1) {
1375 fprintf(stderr, "Failed to read /proc/self/exe: %s\n", strerror(errno));
1376 abort();
1377 }
1378 exePath[len] = '\0';
1379
1380 static char path[PATH_MAX];
1381 snprintf(path, sizeof(path), "%s/", dirname(exePath));
1382 srcDir = path;
1383 #elif defined(U_TOPSRCDIR)
1384 /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and
1385 * intltst to point to the top of the build hierarchy, which may or may not
1386 * be the same as the source directory, depending on the configure options
1387 * used. At any rate, set the data path to the built data from this
1388 * directory. The value is complete with quotes, so it can be used as-is as
1389 * a string constant.
1390 */
1391 srcDir = U_TOPSRCDIR U_FILE_SEP_STRING;
1392 #elif defined(_WIN32)
1393 /* On Windows, the file name obtained from __FILE__ includes a full path.
1394 * This file is "wherever\icu\source\test\cintltst\cintltst.c" Change to
1395 * "wherever\icu\source\data"
1396 */
1397 static char p[sizeof(__FILE__) + 20];
1398 char* pBackSlash;
1399 int i;
1400
1401 strcpy(p, __FILE__);
1402 /* We want to back over three '\' chars. */
1403 /* Only Windows should end up here, so looking for '\' is safe. */
1404 for (i=1; i<=3; i++) {
1405 pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
1406 if (pBackSlash != NULL) {
1407 *pBackSlash = 0; /* Truncate the string at the '\' */
1408 }
1409 }
1410
1411 if (pBackSlash != NULL) {
1412 /* We found and truncated three names from the path.
1413 * Now append "source\data" and set the environment
1414 */
1415 strcpy(pBackSlash, U_FILE_SEP_STRING);
1416 srcDir = p;
1417 }
1418 else {
1419 /* __FILE__ on MSVC7 does not contain the directory */
1420 FILE* file = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
1421 "runConfigureICU",
1422 "r");
1423 if (file) {
1424 fclose(file);
1425 srcDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING;
1426 }
1427 else {
1428 srcDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
1429 ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING;
1430 }
1431 }
1432 #else
1433 #error ctest_icuSrcDir not implemented on this platform.
1434 #endif
1435
1436 return srcDir;
1437 }
1438
1439 const char* T_CTEST_EXPORT2
ctest_dataSrcDir(void)1440 ctest_dataSrcDir(void) {
1441 static char path[PATH_MAX];
1442
1443 if (path[0]) {
1444 return path;
1445 }
1446
1447 snprintf(path, sizeof(path), "%sdata%s", ctest_icuSrcDir(),
1448 U_FILE_SEP_STRING);
1449 return path;
1450 }
1451
1452 const char* T_CTEST_EXPORT2
ctest_dataOutDir(void)1453 ctest_dataOutDir(void) {
1454 static char path[PATH_MAX];
1455
1456 if (path[0]) {
1457 return path;
1458 }
1459
1460 // Try the ICU_DATA environment variable first. This is the default location
1461 // since the user will have explicitly requested it.
1462 const char* fromEnv = getenv("ICU_DATA");
1463 if (fromEnv != NULL && fromEnv[0] != '\0') {
1464 snprintf(path, sizeof(path), "%s%s", fromEnv, U_FILE_SEP_STRING);
1465 return path;
1466 }
1467
1468 #if defined(__ANDROID__)
1469 // Android has the ICU data installed to a known location on the device. Use
1470 // that if ICU_DATA is not set.
1471 snprintf(path, sizeof(path), "/system/usr/icu/");
1472 return path;
1473 #else
1474 // But fallback to the source directory if needed.
1475 snprintf(path, sizeof(path), "%sout%s", ctest_dataSrcDir(),
1476 U_FILE_SEP_STRING);
1477 return path;
1478 #endif
1479 }
1480
1481 const char* T_CTEST_EXPORT2
ctest_testDataDir(void)1482 ctest_testDataDir(void) {
1483 static char path[PATH_MAX];
1484
1485 if (path[0]) {
1486 return path;
1487 }
1488
1489 snprintf(path, sizeof(path), "%stest%stestdata%s", ctest_icuSrcDir(),
1490 U_FILE_SEP_STRING, U_FILE_SEP_STRING);
1491 return path;
1492 }
1493
1494 const char* T_CTEST_EXPORT2
ctest_loadTestData(UErrorCode * err)1495 ctest_loadTestData(UErrorCode* err) {
1496 static char path[PATH_MAX];
1497
1498 if (path[0]) {
1499 return path;
1500 }
1501
1502 snprintf(path, sizeof(path), "%sout%stestdata", ctest_testDataDir(),
1503 U_FILE_SEP_STRING);
1504
1505 UResourceBundle* test = ures_open(path, "testtypes", err);
1506 if (U_FAILURE(*err)) {
1507 *err = U_FILE_ACCESS_ERROR;
1508 log_data_err(
1509 "Could not load testtypes.res in testdata bundle with path %s - "
1510 "%s\n",
1511 path, u_errorName(*err));
1512 return "";
1513 }
1514 ures_close(test);
1515
1516 return path;
1517 }
1518