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