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