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