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