• 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     va_end(ap);
692     if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
693     	HANGING_OUTPUT=1;
694     } else {
695     	HANGING_OUTPUT=0;
696     }
697     GLOBAL_PRINT_COUNT++;
698 }
699 
vlog_knownIssue(const char * ticket,const char * pattern,va_list ap)700 static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap)
701 {
702     char buf[2048];
703     UBool firstForTicket;
704     UBool firstForWhere;
705 
706     if(NO_KNOWN) return false;
707     if(pattern==NULL) pattern="";
708 
709     vsprintf(buf, pattern, ap);
710     knownList = udbg_knownIssue_open(knownList, ticket, gTestName, buf,
711                                      &firstForTicket, &firstForWhere);
712 
713     if(firstForTicket || firstForWhere) {
714       log_info("(Known issue %s) %s\n", ticket, buf);
715     } else {
716       log_verbose("(Known issue %s) %s\n", ticket, buf);
717     }
718 
719     return true;
720 }
721 
722 
723 void T_CTEST_EXPORT2
vlog_info(const char * prefix,const char * pattern,va_list ap)724 vlog_info(const char *prefix, const char *pattern, va_list ap)
725 {
726 	first_line_info();
727     fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
728     if(prefix) {
729         fputs(prefix, stdout);
730     }
731     vfprintf(stdout, pattern, ap);
732     fflush(stdout);
733     va_end(ap);
734     if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
735     	HANGING_OUTPUT=1;
736     } else {
737     	HANGING_OUTPUT=0;
738     }
739     GLOBAL_PRINT_COUNT++;
740 }
741 /**
742  * Log test structure, with indent
743  */
log_testinfo_i(const char * pattern,...)744 static void log_testinfo_i(const char *pattern, ...)
745 {
746     va_list ap;
747     first_line_test();
748     fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
749     va_start(ap, pattern);
750     vfprintf(stdout, pattern, ap);
751     fflush(stdout);
752     va_end(ap);
753     GLOBAL_PRINT_COUNT++;
754 }
755 /**
756  * Log test structure (no ident)
757  */
log_testinfo(const char * pattern,...)758 static void log_testinfo(const char *pattern, ...)
759 {
760     va_list ap;
761     va_start(ap, pattern);
762     first_line_test();
763     vfprintf(stdout, pattern, ap);
764     fflush(stdout);
765     va_end(ap);
766     GLOBAL_PRINT_COUNT++;
767 }
768 
769 
vlog_verbose(const char * prefix,const char * pattern,va_list ap)770 static void vlog_verbose(const char *prefix, const char *pattern, va_list ap)
771 {
772     if ( VERBOSITY == false )
773         return;
774 
775     first_line_verbose();
776     fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
777     if(prefix) {
778         fputs(prefix, stdout);
779     }
780     vfprintf(stdout, pattern, ap);
781     fflush(stdout);
782     va_end(ap);
783     GLOBAL_PRINT_COUNT++;
784     if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
785     	HANGING_OUTPUT=1;
786     } else {
787     	HANGING_OUTPUT=0;
788     }
789 }
790 
791 void T_CTEST_EXPORT2
log_err(const char * pattern,...)792 log_err(const char* pattern, ...)
793 {
794     va_list ap;
795     first_line_err();
796     if(strchr(pattern, '\n') != NULL) {
797         /*
798          * Count errors only if there is a line feed in the pattern
799          * so that we do not exaggerate our error count.
800          */
801         ++ERROR_COUNT;
802     } else {
803     	/* Count at least one error. */
804     	ONE_ERROR=1;
805     }
806     va_start(ap, pattern);
807     vlog_err(NULL, pattern, ap);
808 }
809 
810 UBool T_CTEST_EXPORT2
log_knownIssue(const char * ticket,const char * pattern,...)811 log_knownIssue(const char *ticket, const char *pattern, ...) {
812   va_list ap;
813   va_start(ap, pattern);
814   return vlog_knownIssue(ticket, pattern, ap);
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 }
849 
850 void T_CTEST_EXPORT2
log_info(const char * pattern,...)851 log_info(const char* pattern, ...)
852 {
853     va_list ap;
854 
855     va_start(ap, pattern);
856     vlog_info(NULL, pattern, ap);
857 }
858 
859 void T_CTEST_EXPORT2
log_verbose(const char * pattern,...)860 log_verbose(const char* pattern, ...)
861 {
862     va_list ap;
863 
864     va_start(ap, pattern);
865     vlog_verbose(NULL, pattern, ap);
866 }
867 
868 
869 void T_CTEST_EXPORT2
log_data_err(const char * pattern,...)870 log_data_err(const char* pattern, ...)
871 {
872     va_list ap;
873     va_start(ap, pattern);
874 
875     go_offline_err();
876     ++DATA_ERROR_COUNT; /* for informational message at the end */
877 
878     if(WARN_ON_MISSING_DATA == 0) {
879         /* Fatal error. */
880         if(strchr(pattern, '\n') != NULL) {
881             ++ERROR_COUNT;
882         }
883         vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
884     } else {
885         vlog_info("[DATA] ", pattern, ap);
886     }
887 }
888 
889 
890 /*
891  * Tracing functions.
892  */
893 static int traceFnNestingDepth = 0;
894 U_CDECL_BEGIN
TraceEntry(const void * context,int32_t fnNumber)895 static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) {
896     (void)context; // suppress compiler warnings about unused variable
897     char buf[500];
898     utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber));
899     buf[sizeof(buf)-1]=0;
900     fputs(buf, stdout);
901     traceFnNestingDepth++;
902 }
903 
TraceExit(const void * context,int32_t fnNumber,const char * fmt,va_list args)904 static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) {
905     (void)context; // suppress compiler warnings about unused variable
906     char buf[500];
907     if (traceFnNestingDepth>0) {
908         traceFnNestingDepth--;
909     }
910     utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber));
911     buf[sizeof(buf)-1]=0;
912     fputs(buf, stdout);
913     utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
914     buf[sizeof(buf)-1]=0;
915     fputs(buf, stdout);
916     putc('\n', stdout);
917 }
918 
TraceData(const void * context,int32_t fnNumber,int32_t level,const char * fmt,va_list args)919 static void U_CALLCONV TraceData(const void *context, int32_t fnNumber,
920                           int32_t level, const char *fmt, va_list args) {
921     // suppress compiler warnings about unused variables
922     (void)context;
923     (void)fnNumber;
924     (void)level;
925     char buf[500];
926     utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
927     buf[sizeof(buf)-1]=0;
928     fputs(buf, stdout);
929     putc('\n', stdout);
930 }
931 
ctest_libMalloc(const void * context,size_t size)932 static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) {
933     (void)context; // suppress compiler warnings about unused variable
934     /*if (VERBOSITY) {
935         printf("Allocated %ld\n", (long)size);
936     }*/
937     if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
938         return NULL;
939     }
940     return malloc(size);
941 }
ctest_libRealloc(const void * context,void * mem,size_t size)942 static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) {
943     (void)context; // suppress compiler warnings about unused variable
944     /*if (VERBOSITY) {
945         printf("Reallocated %ld\n", (long)size);
946     }*/
947     if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
948         /*free(mem);*/ /* Realloc doesn't free on failure. */
949         return NULL;
950     }
951     return realloc(mem, size);
952 }
ctest_libFree(const void * context,void * mem)953 static void U_CALLCONV ctest_libFree(const void *context, void *mem) {
954     (void)context; // suppress compiler warnings about unused variable
955     free(mem);
956 }
957 
958 int T_CTEST_EXPORT2
initArgs(int argc,const char * const argv[],ArgHandlerPtr argHandler,void * context)959 initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context)
960 {
961     int                i;
962     int                argSkip = 0;
963 
964     VERBOSITY = false;
965     ERR_MSG = true;
966 
967     ARGV_0=argv[0];
968 
969     for( i=1; i<argc; i++)
970     {
971         if ( argv[i][0] == '/' )
972         {
973             /* We don't run the tests here. */
974             continue;
975         }
976         else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0))
977         {
978             /* We don't run the tests here. */
979             continue;
980         }
981         else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0)
982         {
983             VERBOSITY = true;
984         }
985         else if (strcmp( argv[i], "-l" )==0 )
986         {
987             /* doList = true; */
988         }
989         else if (strcmp( argv[i], "-e1") == 0)
990         {
991             QUICK = -1;
992         }
993         else if (strcmp( argv[i], "-e") ==0)
994         {
995             QUICK = 0;
996         }
997         else if (strcmp( argv[i], "-K") ==0)
998         {
999             NO_KNOWN = 1;
1000         }
1001         else if (strncmp( argv[i], "-E",2) ==0)
1002         {
1003 	    SUMMARY_FILE=argv[i]+2;
1004         }
1005         else if (strcmp( argv[i], "-w") ==0)
1006         {
1007             WARN_ON_MISSING_DATA = true;
1008         }
1009         else if (strcmp( argv[i], "-m") ==0)
1010         {
1011             UErrorCode errorCode = U_ZERO_ERROR;
1012             if (i+1 < argc) {
1013                 char *endPtr = NULL;
1014                 i++;
1015                 MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10);
1016                 if (endPtr == argv[i]) {
1017                     printf("Can't parse %s\n", argv[i]);
1018                     help(argv[0]);
1019                     return 0;
1020                 }
1021                 if (*endPtr == '-') {
1022                     char *maxPtr = endPtr+1;
1023                     endPtr = NULL;
1024                     MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10);
1025                     if (endPtr == argv[i]) {
1026                         printf("Can't parse %s\n", argv[i]);
1027                         help(argv[0]);
1028                         return 0;
1029                     }
1030                 }
1031             }
1032             /* Use the default value */
1033             u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode);
1034             if (U_FAILURE(errorCode)) {
1035                 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode));
1036                 return 0;
1037             }
1038         }
1039         else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0)
1040         {
1041             ERR_MSG = false;
1042         }
1043         else if (strcmp( argv[i], "-r") == 0)
1044         {
1045             if (!REPEAT_TESTS_INIT) {
1046                 REPEAT_TESTS++;
1047             }
1048         }
1049         else if (strcmp( argv[i], "-x") == 0)
1050         {
1051           if(++i>=argc) {
1052             printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n");
1053             return 0;
1054           }
1055           if(ctest_xml_setFileName(argv[i])) { /* set the name */
1056             return 0;
1057           }
1058         }
1059         else if (strcmp( argv[i], "-t_info") == 0) {
1060             ICU_TRACE = UTRACE_INFO;
1061         }
1062         else if (strcmp( argv[i], "-t_error") == 0) {
1063             ICU_TRACE = UTRACE_ERROR;
1064         }
1065         else if (strcmp( argv[i], "-t_warn") == 0) {
1066             ICU_TRACE = UTRACE_WARNING;
1067         }
1068         else if (strcmp( argv[i], "-t_verbose") == 0) {
1069             ICU_TRACE = UTRACE_VERBOSE;
1070         }
1071         else if (strcmp( argv[i], "-t_oc") == 0) {
1072             ICU_TRACE = UTRACE_OPEN_CLOSE;
1073         }
1074         else if (strcmp( argv[i], "-G") == 0) {
1075             WRITE_GOLDEN_DATA = 1;
1076         }
1077         else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0)
1078         {
1079             help( argv[0] );
1080             return 0;
1081         }
1082         else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0)
1083         {
1084             i += argSkip - 1;
1085         }
1086         else
1087         {
1088             printf("* unknown option: %s\n", argv[i]);
1089             help( argv[0] );
1090             return 0;
1091         }
1092     }
1093     if (ICU_TRACE != UTRACE_OFF) {
1094         utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData);
1095         utrace_setLevel(ICU_TRACE);
1096     }
1097 
1098     return 1; /* total error count */
1099 }
1100 
1101 int T_CTEST_EXPORT2
runTestRequest(const TestNode * root,int argc,const char * const argv[])1102 runTestRequest(const TestNode* root,
1103              int argc,
1104              const char* const argv[])
1105 {
1106     /**
1107      * This main will parse the l, v, h, n, and path arguments
1108      */
1109     const TestNode*    toRun;
1110     int                i;
1111     int                doList = false;
1112     int                subtreeOptionSeen = false;
1113 
1114     int                errorCount = 0;
1115 
1116     toRun = root;
1117 
1118     if(ctest_xml_init(ARGV_0)) {
1119       return 1; /* couldn't fire up XML thing */
1120     }
1121 
1122     for( i=1; i<argc; i++)
1123     {
1124         if ( argv[i][0] == '/' )
1125         {
1126             printf("Selecting subtree '%s'\n", argv[i]);
1127 
1128             if ( argv[i][1] == 0 )
1129                 toRun = root;
1130             else
1131                 toRun = getTest(root, argv[i]);
1132 
1133             if ( toRun == NULL )
1134             {
1135                 printf("* Could not find any matching subtree\n");
1136                 return -1;
1137             }
1138 
1139             ON_LINE=false; /* just in case */
1140 
1141             if( doList == true)
1142                 showTests(toRun);
1143             else
1144                 runTests(toRun);
1145 
1146             ON_LINE=false; /* just in case */
1147 
1148             errorCount += ERROR_COUNT;
1149 
1150             subtreeOptionSeen = true;
1151         } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) {
1152             subtreeOptionSeen=false;
1153         } else if (strcmp( argv[i], "-l") == 0) {
1154             doList = true;
1155         }
1156         /* else option already handled by initArgs */
1157     }
1158 
1159     if( subtreeOptionSeen == false) /* no other subtree given, run the default */
1160     {
1161         ON_LINE=false; /* just in case */
1162         if( doList == true)
1163             showTests(toRun);
1164         else
1165             runTests(toRun);
1166         ON_LINE=false; /* just in case */
1167 
1168         errorCount += ERROR_COUNT;
1169     }
1170     else
1171     {
1172         if( ( doList == false ) && ( errorCount > 0 ) )
1173             printf(" Total errors: %d\n", errorCount );
1174     }
1175 
1176     REPEAT_TESTS_INIT = 1;
1177 
1178     if(ctest_xml_fini()) {
1179       errorCount++;
1180     }
1181 
1182     return errorCount; /* total error count */
1183 }
1184 
1185 /**
1186  * Display program invocation arguments
1187  */
1188 
help(const char * argv0)1189 static void help ( const char *argv0 )
1190 {
1191     printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
1192            "    [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
1193            "    [ /path/to/test ]\n",
1194             argv0);
1195     printf("    -l  To get a list of test names\n");
1196     printf("    -e  to do exhaustive testing\n");
1197     printf("    -verbose To turn ON verbosity\n");
1198     printf("    -v  To turn ON verbosity(same as -verbose)\n");
1199     printf("    -x file.xml   Write junit format output to file.xml\n");
1200     printf("    -h  To print this message\n");
1201     printf("    -K  to turn OFF suppressing known issues\n");
1202     printf("    -n  To turn OFF printing error messages\n");
1203     printf("    -w  Don't fail on data-loading errs, just warn. Useful if\n"
1204            "        user has reduced/changed the common set of ICU data \n");
1205     printf("    -t_info | -t_error | -t_warn | -t_oc | -t_verbose  Enable ICU tracing\n");
1206     printf("    -no_err_msg (same as -n) \n");
1207     printf("    -m n[-q] Min-Max memory size that will cause an allocation failure.\n");
1208     printf("        The default is the maximum value of size_t. Max is optional.\n");
1209     printf("    -r  Repeat tests after calling u_cleanup \n");
1210     printf("    -G  Write golden data files \n");
1211     printf("    [/subtest]  To run a subtest \n");
1212     printf("    eg: to run just the utility tests type: cintltest /tsutil) \n");
1213 }
1214 
1215 int32_t T_CTEST_EXPORT2
getTestOption(int32_t testOption)1216 getTestOption ( int32_t testOption ) {
1217     switch (testOption) {
1218         case VERBOSITY_OPTION:
1219             return VERBOSITY;
1220         case WARN_ON_MISSING_DATA_OPTION:
1221             return WARN_ON_MISSING_DATA;
1222         case QUICK_OPTION:
1223             return QUICK;
1224         case REPEAT_TESTS_OPTION:
1225             return REPEAT_TESTS;
1226         case ERR_MSG_OPTION:
1227             return ERR_MSG;
1228         case ICU_TRACE_OPTION:
1229             return ICU_TRACE;
1230         case WRITE_GOLDEN_DATA_OPTION:
1231             return WRITE_GOLDEN_DATA;
1232         default :
1233             return 0;
1234     }
1235 }
1236 
1237 void T_CTEST_EXPORT2
setTestOption(int32_t testOption,int32_t value)1238 setTestOption ( int32_t testOption, int32_t value) {
1239     if (value == DECREMENT_OPTION_VALUE) {
1240         value = getTestOption(testOption);
1241         --value;
1242     }
1243     switch (testOption) {
1244         case VERBOSITY_OPTION:
1245             VERBOSITY = value;
1246             break;
1247         case WARN_ON_MISSING_DATA_OPTION:
1248             WARN_ON_MISSING_DATA = value;
1249             break;
1250         case QUICK_OPTION:
1251             QUICK = value;
1252             break;
1253         case REPEAT_TESTS_OPTION:
1254             REPEAT_TESTS = value;
1255             break;
1256         case ICU_TRACE_OPTION:
1257             ICU_TRACE = (UTraceLevel)value;
1258             break;
1259         case WRITE_GOLDEN_DATA_OPTION:
1260             WRITE_GOLDEN_DATA = value;
1261         default :
1262             break;
1263     }
1264 }
1265 
1266 
1267 /*
1268  * ================== JUnit support ================================
1269  */
1270 
1271 int32_t
1272 T_CTEST_EXPORT2
ctest_xml_setFileName(const char * name)1273 ctest_xml_setFileName(const char *name) {
1274   XML_FILE_NAME=name;
1275   return 0;
1276 }
1277 
1278 
1279 int32_t
1280 T_CTEST_EXPORT2
ctest_xml_init(const char * rootName)1281 ctest_xml_init(const char *rootName) {
1282   if(!XML_FILE_NAME) return 0;
1283   XML_FILE = fopen(XML_FILE_NAME,"w");
1284   if(!XML_FILE) {
1285     perror("fopen");
1286     fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME);
1287     return 1;
1288   }
1289   while(*rootName&&!isalnum((int)*rootName)) {
1290     rootName++;
1291   }
1292   strcpy(XML_PREFIX,rootName);
1293   {
1294     char *p = XML_PREFIX+strlen(XML_PREFIX);
1295     for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) {
1296       *p=0;
1297     }
1298   }
1299   /* write prefix */
1300   fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX);
1301 
1302   return 0;
1303 }
1304 
1305 int32_t
1306 T_CTEST_EXPORT2
ctest_xml_fini(void)1307 ctest_xml_fini(void) {
1308   if(!XML_FILE) return 0;
1309 
1310   fprintf(XML_FILE, "</testsuite>\n");
1311   fclose(XML_FILE);
1312   printf(" ( test results written to %s )\n", XML_FILE_NAME);
1313   XML_FILE=0;
1314   return 0;
1315 }
1316 
1317 
1318 int32_t
1319 T_CTEST_EXPORT2
ctest_xml_testcase(const char * classname,const char * name,const char * timeSeconds,const char * failMsg)1320 ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) {
1321   if(!XML_FILE) return 0;
1322 
1323   fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, timeSeconds);
1324   if(failMsg) {
1325     fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg);
1326   } else {
1327     fprintf(XML_FILE, "/>\n");
1328   }
1329 
1330   return 0;
1331 }
1332 
1333 
1334