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()678 static void go_offline() {
679 go_offline_with_marker(NULL);
680 }
681
go_offline_err()682 static void go_offline_err() {
683 go_offline();
684 }
685
first_line_verbose()686 static void first_line_verbose() {
687 go_offline_with_marker("v");
688 }
689
first_line_err()690 static void first_line_err() {
691 go_offline_with_marker("!");
692 }
693
first_line_info()694 static void first_line_info() {
695 go_offline_with_marker("\"");
696 }
697
first_line_test()698 static void first_line_test() {
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 va_end(ap);
716 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
717 HANGING_OUTPUT=1;
718 } else {
719 HANGING_OUTPUT=0;
720 }
721 GLOBAL_PRINT_COUNT++;
722 }
723
vlog_knownIssue(const char * ticket,const char * pattern,va_list ap)724 static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap)
725 {
726 char buf[2048];
727 UBool firstForTicket;
728 UBool firstForWhere;
729
730 if(NO_KNOWN) return false;
731 if(pattern==NULL) pattern="";
732
733 vsprintf(buf, pattern, ap);
734 knownList = udbg_knownIssue_open(knownList, ticket, gTestName, buf,
735 &firstForTicket, &firstForWhere);
736
737 if(firstForTicket || firstForWhere) {
738 log_info("(Known issue %s) %s\n", ticket, buf);
739 } else {
740 log_verbose("(Known issue %s) %s\n", ticket, buf);
741 }
742
743 return true;
744 }
745
746
747 void T_CTEST_EXPORT2
vlog_info(const char * prefix,const char * pattern,va_list ap)748 vlog_info(const char *prefix, const char *pattern, va_list ap)
749 {
750 first_line_info();
751 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
752 if(prefix) {
753 fputs(prefix, stdout);
754 }
755 vfprintf(stdout, pattern, ap);
756 fflush(stdout);
757 va_end(ap);
758 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
759 HANGING_OUTPUT=1;
760 } else {
761 HANGING_OUTPUT=0;
762 }
763 GLOBAL_PRINT_COUNT++;
764 }
765 /**
766 * Log test structure, with indent
767 */
log_testinfo_i(const char * pattern,...)768 static void log_testinfo_i(const char *pattern, ...)
769 {
770 va_list ap;
771 first_line_test();
772 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
773 va_start(ap, pattern);
774 vfprintf(stdout, pattern, ap);
775 fflush(stdout);
776 va_end(ap);
777 GLOBAL_PRINT_COUNT++;
778 }
779 /**
780 * Log test structure (no ident)
781 */
log_testinfo(const char * pattern,...)782 static void log_testinfo(const char *pattern, ...)
783 {
784 va_list ap;
785 va_start(ap, pattern);
786 first_line_test();
787 vfprintf(stdout, pattern, ap);
788 fflush(stdout);
789 va_end(ap);
790 GLOBAL_PRINT_COUNT++;
791 }
792
793
vlog_verbose(const char * prefix,const char * pattern,va_list ap)794 static void vlog_verbose(const char *prefix, const char *pattern, va_list ap)
795 {
796 if ( VERBOSITY == false )
797 return;
798
799 first_line_verbose();
800 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
801 if(prefix) {
802 fputs(prefix, stdout);
803 }
804 vfprintf(stdout, pattern, ap);
805 fflush(stdout);
806 va_end(ap);
807 GLOBAL_PRINT_COUNT++;
808 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
809 HANGING_OUTPUT=1;
810 } else {
811 HANGING_OUTPUT=0;
812 }
813 }
814
815 void T_CTEST_EXPORT2
log_err(const char * pattern,...)816 log_err(const char* pattern, ...)
817 {
818 va_list ap;
819 first_line_err();
820 if(strchr(pattern, '\n') != NULL) {
821 /*
822 * Count errors only if there is a line feed in the pattern
823 * so that we do not exaggerate our error count.
824 */
825 ++ERROR_COUNT;
826 } else {
827 /* Count at least one error. */
828 ONE_ERROR=1;
829 }
830 va_start(ap, pattern);
831 vlog_err(NULL, pattern, ap);
832 }
833
834 UBool T_CTEST_EXPORT2
log_knownIssue(const char * ticket,const char * pattern,...)835 log_knownIssue(const char *ticket, const char *pattern, ...) {
836 va_list ap;
837 va_start(ap, pattern);
838 return vlog_knownIssue(ticket, pattern, ap);
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 }
873
874 void T_CTEST_EXPORT2
log_info(const char * pattern,...)875 log_info(const char* pattern, ...)
876 {
877 va_list ap;
878
879 va_start(ap, pattern);
880 vlog_info(NULL, pattern, ap);
881 }
882
883 void T_CTEST_EXPORT2
log_verbose(const char * pattern,...)884 log_verbose(const char* pattern, ...)
885 {
886 va_list ap;
887
888 va_start(ap, pattern);
889 vlog_verbose(NULL, pattern, ap);
890 }
891
892
893 void T_CTEST_EXPORT2
log_data_err(const char * pattern,...)894 log_data_err(const char* pattern, ...)
895 {
896 va_list ap;
897 va_start(ap, pattern);
898
899 go_offline_err();
900 ++DATA_ERROR_COUNT; /* for informational message at the end */
901
902 if(WARN_ON_MISSING_DATA == 0) {
903 /* Fatal error. */
904 if(strchr(pattern, '\n') != NULL) {
905 ++ERROR_COUNT;
906 }
907 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
908 } else {
909 vlog_info("[DATA] ", pattern, ap);
910 }
911 }
912
913
914 /*
915 * Tracing functions.
916 */
917 static int traceFnNestingDepth = 0;
918 U_CDECL_BEGIN
TraceEntry(const void * context,int32_t fnNumber)919 static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) {
920 char buf[500];
921 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0;
922 fputs(buf, stdout);
923 traceFnNestingDepth++;
924 }
925
TraceExit(const void * context,int32_t fnNumber,const char * fmt,va_list args)926 static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500];
927
928 if (traceFnNestingDepth>0) {
929 traceFnNestingDepth--;
930 }
931 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0;
932 fputs(buf, stdout);
933 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
934 buf[sizeof(buf)-1]=0;
935 fputs(buf, stdout);
936 putc('\n', stdout);
937 }
938
TraceData(const void * context,int32_t fnNumber,int32_t level,const char * fmt,va_list args)939 static void U_CALLCONV TraceData(const void *context, int32_t fnNumber,
940 int32_t level, const char *fmt, va_list args) {
941 char buf[500];
942 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
943 buf[sizeof(buf)-1]=0;
944 fputs(buf, stdout);
945 putc('\n', stdout);
946 }
947
ctest_libMalloc(const void * context,size_t size)948 static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) {
949 /*if (VERBOSITY) {
950 printf("Allocated %ld\n", (long)size);
951 }*/
952 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
953 return NULL;
954 }
955 return malloc(size);
956 }
ctest_libRealloc(const void * context,void * mem,size_t size)957 static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) {
958 /*if (VERBOSITY) {
959 printf("Reallocated %ld\n", (long)size);
960 }*/
961 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
962 /*free(mem);*/ /* Realloc doesn't free on failure. */
963 return NULL;
964 }
965 return realloc(mem, size);
966 }
ctest_libFree(const void * context,void * mem)967 static void U_CALLCONV ctest_libFree(const void *context, void *mem) {
968 free(mem);
969 }
970
971 int T_CTEST_EXPORT2
initArgs(int argc,const char * const argv[],ArgHandlerPtr argHandler,void * context)972 initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context)
973 {
974 int i;
975 int argSkip = 0;
976
977 VERBOSITY = false;
978 ERR_MSG = true;
979
980 ARGV_0=argv[0];
981
982 for( i=1; i<argc; i++)
983 {
984 if ( argv[i][0] == '/' )
985 {
986 /* We don't run the tests here. */
987 continue;
988 }
989 else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0))
990 {
991 /* We don't run the tests here. */
992 continue;
993 }
994 else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0)
995 {
996 VERBOSITY = true;
997 }
998 else if (strcmp( argv[i], "-l" )==0 )
999 {
1000 /* doList = true; */
1001 }
1002 else if (strcmp( argv[i], "-e1") == 0)
1003 {
1004 QUICK = -1;
1005 }
1006 else if (strcmp( argv[i], "-e") ==0)
1007 {
1008 QUICK = 0;
1009 }
1010 else if (strcmp( argv[i], "-K") ==0)
1011 {
1012 NO_KNOWN = 1;
1013 }
1014 else if (strncmp( argv[i], "-E",2) ==0)
1015 {
1016 SUMMARY_FILE=argv[i]+2;
1017 }
1018 else if (strcmp( argv[i], "-w") ==0)
1019 {
1020 WARN_ON_MISSING_DATA = true;
1021 }
1022 else if (strcmp( argv[i], "-m") ==0)
1023 {
1024 UErrorCode errorCode = U_ZERO_ERROR;
1025 if (i+1 < argc) {
1026 char *endPtr = NULL;
1027 i++;
1028 MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10);
1029 if (endPtr == argv[i]) {
1030 printf("Can't parse %s\n", argv[i]);
1031 help(argv[0]);
1032 return 0;
1033 }
1034 if (*endPtr == '-') {
1035 char *maxPtr = endPtr+1;
1036 endPtr = NULL;
1037 MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10);
1038 if (endPtr == argv[i]) {
1039 printf("Can't parse %s\n", argv[i]);
1040 help(argv[0]);
1041 return 0;
1042 }
1043 }
1044 }
1045 /* Use the default value */
1046 u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode);
1047 if (U_FAILURE(errorCode)) {
1048 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode));
1049 return 0;
1050 }
1051 }
1052 else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0)
1053 {
1054 ERR_MSG = false;
1055 }
1056 else if (strcmp( argv[i], "-r") == 0)
1057 {
1058 if (!REPEAT_TESTS_INIT) {
1059 REPEAT_TESTS++;
1060 }
1061 }
1062 else if (strcmp( argv[i], "-x") == 0)
1063 {
1064 if(++i>=argc) {
1065 printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n");
1066 return 0;
1067 }
1068 if(ctest_xml_setFileName(argv[i])) { /* set the name */
1069 return 0;
1070 }
1071 }
1072 else if (strcmp( argv[i], "-t_info") == 0) {
1073 ICU_TRACE = UTRACE_INFO;
1074 }
1075 else if (strcmp( argv[i], "-t_error") == 0) {
1076 ICU_TRACE = UTRACE_ERROR;
1077 }
1078 else if (strcmp( argv[i], "-t_warn") == 0) {
1079 ICU_TRACE = UTRACE_WARNING;
1080 }
1081 else if (strcmp( argv[i], "-t_verbose") == 0) {
1082 ICU_TRACE = UTRACE_VERBOSE;
1083 }
1084 else if (strcmp( argv[i], "-t_oc") == 0) {
1085 ICU_TRACE = UTRACE_OPEN_CLOSE;
1086 }
1087 else if (strcmp( argv[i], "-G") == 0) {
1088 WRITE_GOLDEN_DATA = 1;
1089 }
1090 else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0)
1091 {
1092 help( argv[0] );
1093 return 0;
1094 }
1095 else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0)
1096 {
1097 i += argSkip - 1;
1098 }
1099 else
1100 {
1101 printf("* unknown option: %s\n", argv[i]);
1102 help( argv[0] );
1103 return 0;
1104 }
1105 }
1106 if (ICU_TRACE != UTRACE_OFF) {
1107 utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData);
1108 utrace_setLevel(ICU_TRACE);
1109 }
1110
1111 return 1; /* total error count */
1112 }
1113
1114 int T_CTEST_EXPORT2
runTestRequest(const TestNode * root,int argc,const char * const argv[])1115 runTestRequest(const TestNode* root,
1116 int argc,
1117 const char* const argv[])
1118 {
1119 /**
1120 * This main will parse the l, v, h, n, and path arguments
1121 */
1122 const TestNode* toRun;
1123 int i;
1124 int doList = false;
1125 int subtreeOptionSeen = false;
1126 int skipNext = false;
1127
1128 int errorCount = 0;
1129
1130 toRun = root;
1131
1132 if(ctest_xml_init(ARGV_0)) {
1133 return 1; /* couldn't fire up XML thing */
1134 }
1135
1136 for( i=1; i<argc; i++)
1137 {
1138 if (skipNext) {
1139 skipNext = false;
1140 continue;
1141 }
1142
1143 if ( argv[i][0] == '/' )
1144 {
1145 printf("Selecting subtree '%s'\n", argv[i]);
1146
1147 if ( argv[i][1] == 0 )
1148 toRun = root;
1149 else
1150 toRun = getTest(root, argv[i]);
1151
1152 if ( toRun == NULL )
1153 {
1154 printf("* Could not find any matching subtree\n");
1155 return -1;
1156 }
1157
1158 ON_LINE=false; /* just in case */
1159
1160 if( doList == true)
1161 showTests(toRun);
1162 else
1163 runTests(toRun);
1164
1165 ON_LINE=false; /* just in case */
1166
1167 errorCount += ERROR_COUNT;
1168
1169 subtreeOptionSeen = true;
1170 } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) {
1171 subtreeOptionSeen=false;
1172 } else if (strcmp( argv[i], "-l") == 0) {
1173 doList = true;
1174 } else if (strcmp( argv[i], "-x") == 0) {
1175 // Need to skip the next argument since it will be wrongly
1176 // identified as a test filter if it is an absolute path.
1177 skipNext = true;
1178 }
1179 /* else option already handled by initArgs */
1180 }
1181
1182 if( subtreeOptionSeen == false) /* no other subtree given, run the default */
1183 {
1184 ON_LINE=false; /* just in case */
1185 if( doList == true)
1186 showTests(toRun);
1187 else
1188 runTests(toRun);
1189 ON_LINE=false; /* just in case */
1190
1191 errorCount += ERROR_COUNT;
1192 }
1193 else
1194 {
1195 if( ( doList == false ) && ( errorCount > 0 ) )
1196 printf(" Total errors: %d\n", errorCount );
1197 }
1198
1199 REPEAT_TESTS_INIT = 1;
1200
1201 if(ctest_xml_fini()) {
1202 errorCount++;
1203 }
1204
1205 return errorCount; /* total error count */
1206 }
1207
1208 /**
1209 * Display program invocation arguments
1210 */
1211
help(const char * argv0)1212 static void help ( const char *argv0 )
1213 {
1214 printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
1215 " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
1216 " [ /path/to/test ]\n",
1217 argv0);
1218 printf(" -l To get a list of test names\n");
1219 printf(" -e to do exhaustive testing\n");
1220 printf(" -verbose To turn ON verbosity\n");
1221 printf(" -v To turn ON verbosity(same as -verbose)\n");
1222 printf(" -x file.xml Write junit format output to file.xml\n");
1223 printf(" -h To print this message\n");
1224 printf(" -K to turn OFF suppressing known issues\n");
1225 printf(" -n To turn OFF printing error messages\n");
1226 printf(" -w Don't fail on data-loading errs, just warn. Useful if\n"
1227 " user has reduced/changed the common set of ICU data \n");
1228 printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n");
1229 printf(" -no_err_msg (same as -n) \n");
1230 printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n");
1231 printf(" The default is the maximum value of size_t. Max is optional.\n");
1232 printf(" -r Repeat tests after calling u_cleanup \n");
1233 printf(" -G Write golden data files \n");
1234 printf(" [/subtest] To run a subtest \n");
1235 printf(" eg: to run just the utility tests type: cintltest /tsutil) \n");
1236 }
1237
1238 int32_t T_CTEST_EXPORT2
getTestOption(int32_t testOption)1239 getTestOption ( int32_t testOption ) {
1240 switch (testOption) {
1241 case VERBOSITY_OPTION:
1242 return VERBOSITY;
1243 case WARN_ON_MISSING_DATA_OPTION:
1244 return WARN_ON_MISSING_DATA;
1245 case QUICK_OPTION:
1246 return QUICK;
1247 case REPEAT_TESTS_OPTION:
1248 return REPEAT_TESTS;
1249 case ERR_MSG_OPTION:
1250 return ERR_MSG;
1251 case ICU_TRACE_OPTION:
1252 return ICU_TRACE;
1253 case WRITE_GOLDEN_DATA_OPTION:
1254 return WRITE_GOLDEN_DATA;
1255 default :
1256 return 0;
1257 }
1258 }
1259
1260 void T_CTEST_EXPORT2
setTestOption(int32_t testOption,int32_t value)1261 setTestOption ( int32_t testOption, int32_t value) {
1262 if (value == DECREMENT_OPTION_VALUE) {
1263 value = getTestOption(testOption);
1264 --value;
1265 }
1266 switch (testOption) {
1267 case VERBOSITY_OPTION:
1268 VERBOSITY = value;
1269 break;
1270 case WARN_ON_MISSING_DATA_OPTION:
1271 WARN_ON_MISSING_DATA = value;
1272 break;
1273 case QUICK_OPTION:
1274 QUICK = value;
1275 break;
1276 case REPEAT_TESTS_OPTION:
1277 REPEAT_TESTS = value;
1278 break;
1279 case ICU_TRACE_OPTION:
1280 ICU_TRACE = (UTraceLevel)value;
1281 break;
1282 case WRITE_GOLDEN_DATA_OPTION:
1283 WRITE_GOLDEN_DATA = value;
1284 default :
1285 break;
1286 }
1287 }
1288
1289
1290 /*
1291 * ================== JUnit support ================================
1292 */
1293
1294 int32_t
1295 T_CTEST_EXPORT2
ctest_xml_setFileName(const char * name)1296 ctest_xml_setFileName(const char *name) {
1297 XML_FILE_NAME=name;
1298 return 0;
1299 }
1300
1301
1302 int32_t
1303 T_CTEST_EXPORT2
ctest_xml_init(const char * rootName)1304 ctest_xml_init(const char *rootName) {
1305 if(!XML_FILE_NAME) return 0;
1306 XML_FILE = fopen(XML_FILE_NAME,"w");
1307 if(!XML_FILE) {
1308 perror("fopen");
1309 fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME);
1310 return 1;
1311 }
1312 while(*rootName&&!isalnum((int)*rootName)) {
1313 rootName++;
1314 }
1315 strcpy(XML_PREFIX,rootName);
1316 {
1317 char *p = XML_PREFIX+strlen(XML_PREFIX);
1318 for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) {
1319 *p=0;
1320 }
1321 }
1322 /* write prefix */
1323 fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX);
1324
1325 return 0;
1326 }
1327
1328 int32_t
1329 T_CTEST_EXPORT2
ctest_xml_fini(void)1330 ctest_xml_fini(void) {
1331 if(!XML_FILE) return 0;
1332
1333 fprintf(XML_FILE, "</testsuite>\n");
1334 fclose(XML_FILE);
1335 printf(" ( test results written to %s )\n", XML_FILE_NAME);
1336 XML_FILE=0;
1337 return 0;
1338 }
1339
1340
1341 int32_t
1342 T_CTEST_EXPORT2
ctest_xml_testcase(const char * classname,const char * name,const char * timeSeconds,const char * failMsg)1343 ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) {
1344 if(!XML_FILE) return 0;
1345
1346 fprintf(XML_FILE, "\t<testcase classname=\"%s\" name=\"%s\" time=\"%s\"", name, classname, timeSeconds);
1347 if(failMsg) {
1348 fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg);
1349 } else {
1350 fprintf(XML_FILE, "/>\n");
1351 }
1352
1353 return 0;
1354 }
1355
ctest_icuSrcDir(void)1356 static const char* ctest_icuSrcDir(void) {
1357 static const char* srcDir = NULL;
1358
1359 if (srcDir) {
1360 return srcDir;
1361 }
1362
1363 #if defined(__ANDROID__)
1364 /*
1365 * On Android, the source tree is not available as the tests are cross
1366 * compiled. Test data is found at paths relative to the test binary.
1367 */
1368 char exePath[PATH_MAX];
1369 ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath));
1370 if (len == -1) {
1371 fprintf(stderr, "Failed to read /proc/self/exe: %s\n", strerror(errno));
1372 abort();
1373 }
1374 exePath[len] = '\0';
1375
1376 static char path[PATH_MAX];
1377 snprintf(path, sizeof(path), "%s/", dirname(exePath));
1378 srcDir = path;
1379 #elif defined(U_TOPSRCDIR)
1380 /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and
1381 * intltst to point to the top of the build hierarchy, which may or may not
1382 * be the same as the source directory, depending on the configure options
1383 * used. At any rate, set the data path to the built data from this
1384 * directory. The value is complete with quotes, so it can be used as-is as
1385 * a string constant.
1386 */
1387 srcDir = U_TOPSRCDIR U_FILE_SEP_STRING;
1388 #elif defined(_WIN32)
1389 /* On Windows, the file name obtained from __FILE__ includes a full path.
1390 * This file is "wherever\icu\source\test\cintltst\cintltst.c" Change to
1391 * "wherever\icu\source\data"
1392 */
1393 static char p[sizeof(__FILE__) + 20];
1394 char* pBackSlash;
1395 int i;
1396
1397 strcpy(p, __FILE__);
1398 /* We want to back over three '\' chars. */
1399 /* Only Windows should end up here, so looking for '\' is safe. */
1400 for (i=1; i<=3; i++) {
1401 pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
1402 if (pBackSlash != NULL) {
1403 *pBackSlash = 0; /* Truncate the string at the '\' */
1404 }
1405 }
1406
1407 if (pBackSlash != NULL) {
1408 /* We found and truncated three names from the path.
1409 * Now append "source\data" and set the environment
1410 */
1411 strcpy(pBackSlash, U_FILE_SEP_STRING);
1412 srcDir = p;
1413 }
1414 else {
1415 /* __FILE__ on MSVC7 does not contain the directory */
1416 FILE* file = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
1417 "runConfigureICU",
1418 "r");
1419 if (file) {
1420 fclose(file);
1421 srcDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING;
1422 }
1423 else {
1424 srcDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
1425 ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING;
1426 }
1427 }
1428 #else
1429 #error ctest_icuSrcDir not implemented on this platform.
1430 #endif
1431
1432 return srcDir;
1433 }
1434
1435 const char* T_CTEST_EXPORT2
ctest_dataSrcDir(void)1436 ctest_dataSrcDir(void) {
1437 static char path[PATH_MAX];
1438
1439 if (path[0]) {
1440 return path;
1441 }
1442
1443 snprintf(path, sizeof(path), "%sdata%s", ctest_icuSrcDir(),
1444 U_FILE_SEP_STRING);
1445 return path;
1446 }
1447
1448 const char* T_CTEST_EXPORT2
ctest_dataOutDir(void)1449 ctest_dataOutDir(void) {
1450 static char path[PATH_MAX];
1451
1452 if (path[0]) {
1453 return path;
1454 }
1455
1456 // Try the ICU_DATA environment variable first. This is the default location
1457 // since the user will have explicitly requested it.
1458 const char* fromEnv = getenv("ICU_DATA");
1459 if (fromEnv != NULL && fromEnv[0] != '\0') {
1460 snprintf(path, sizeof(path), "%s%s", fromEnv, U_FILE_SEP_STRING);
1461 return path;
1462 }
1463
1464 #if defined(__ANDROID__)
1465 // Android has the ICU data installed to a known location on the device. Use
1466 // that if ICU_DATA is not set.
1467 snprintf(path, sizeof(path), "/system/usr/icu/");
1468 return path;
1469 #else
1470 // But fallback to the source directory if needed.
1471 snprintf(path, sizeof(path), "%sout%s", ctest_dataSrcDir(),
1472 U_FILE_SEP_STRING);
1473 return path;
1474 #endif
1475 }
1476
1477 const char* T_CTEST_EXPORT2
ctest_testDataDir(void)1478 ctest_testDataDir(void) {
1479 static char path[PATH_MAX];
1480
1481 if (path[0]) {
1482 return path;
1483 }
1484
1485 snprintf(path, sizeof(path), "%stest%stestdata%s", ctest_icuSrcDir(),
1486 U_FILE_SEP_STRING, U_FILE_SEP_STRING);
1487 return path;
1488 }
1489
1490 const char* T_CTEST_EXPORT2
ctest_loadTestData(UErrorCode * err)1491 ctest_loadTestData(UErrorCode* err) {
1492 static char path[PATH_MAX];
1493
1494 if (path[0]) {
1495 return path;
1496 }
1497
1498 snprintf(path, sizeof(path), "%sout%stestdata", ctest_testDataDir(),
1499 U_FILE_SEP_STRING);
1500
1501 UResourceBundle* test = ures_open(path, "testtypes", err);
1502 if (U_FAILURE(*err)) {
1503 *err = U_FILE_ACCESS_ERROR;
1504 log_data_err(
1505 "Could not load testtypes.res in testdata bundle with path %s - "
1506 "%s\n",
1507 path, u_errorName(*err));
1508 return "";
1509 }
1510 ures_close(test);
1511
1512 return path;
1513 }
1514