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