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