• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ********************************************************************************
3 *
4 *   Copyright (C) 1996-2011, 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                doList = FALSE;
906 	int                argSkip = 0;
907 
908     VERBOSITY = FALSE;
909     ERR_MSG = TRUE;
910 
911     ARGV_0=argv[0];
912 
913     for( i=1; i<argc; i++)
914     {
915         if ( argv[i][0] == '/' )
916         {
917             /* We don't run the tests here. */
918             continue;
919         }
920         else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0))
921         {
922             /* We don't run the tests here. */
923             continue;
924         }
925         else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0)
926         {
927             VERBOSITY = TRUE;
928         }
929         else if (strcmp( argv[i], "-l" )==0 )
930         {
931             doList = TRUE;
932         }
933         else if (strcmp( argv[i], "-e1") == 0)
934         {
935             QUICK = -1;
936         }
937         else if (strcmp( argv[i], "-e") ==0)
938         {
939             QUICK = 0;
940         }
941         else if (strcmp( argv[i], "-w") ==0)
942         {
943             WARN_ON_MISSING_DATA = TRUE;
944         }
945         else if (strcmp( argv[i], "-m") ==0)
946         {
947             UErrorCode errorCode = U_ZERO_ERROR;
948             if (i+1 < argc) {
949                 char *endPtr = NULL;
950                 i++;
951                 MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10);
952                 if (endPtr == argv[i]) {
953                     printf("Can't parse %s\n", argv[i]);
954                     help(argv[0]);
955                     return 0;
956                 }
957                 if (*endPtr == '-') {
958                     char *maxPtr = endPtr+1;
959                     endPtr = NULL;
960                     MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10);
961                     if (endPtr == argv[i]) {
962                         printf("Can't parse %s\n", argv[i]);
963                         help(argv[0]);
964                         return 0;
965                     }
966                 }
967             }
968             /* Use the default value */
969             u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode);
970             if (U_FAILURE(errorCode)) {
971                 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode));
972                 return 0;
973             }
974         }
975         else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0)
976         {
977             ERR_MSG = FALSE;
978         }
979         else if (strcmp( argv[i], "-r") == 0)
980         {
981             if (!REPEAT_TESTS_INIT) {
982                 REPEAT_TESTS++;
983             }
984         }
985         else if (strcmp( argv[i], "-x") == 0)
986         {
987           if(++i>=argc) {
988             printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n");
989             return 0;
990           }
991           if(ctest_xml_setFileName(argv[i])) { /* set the name */
992             return 0;
993           }
994         }
995         else if (strcmp( argv[i], "-t_info") == 0) {
996             ICU_TRACE = UTRACE_INFO;
997         }
998         else if (strcmp( argv[i], "-t_error") == 0) {
999             ICU_TRACE = UTRACE_ERROR;
1000         }
1001         else if (strcmp( argv[i], "-t_warn") == 0) {
1002             ICU_TRACE = UTRACE_WARNING;
1003         }
1004         else if (strcmp( argv[i], "-t_verbose") == 0) {
1005             ICU_TRACE = UTRACE_VERBOSE;
1006         }
1007         else if (strcmp( argv[i], "-t_oc") == 0) {
1008             ICU_TRACE = UTRACE_OPEN_CLOSE;
1009         }
1010         else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0)
1011         {
1012             help( argv[0] );
1013             return 0;
1014         }
1015         else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0)
1016         {
1017             i += argSkip - 1;
1018         }
1019         else
1020         {
1021             printf("* unknown option: %s\n", argv[i]);
1022             help( argv[0] );
1023             return 0;
1024         }
1025     }
1026     if (ICU_TRACE != UTRACE_OFF) {
1027         utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData);
1028         utrace_setLevel(ICU_TRACE);
1029     }
1030 
1031     return 1; /* total error count */
1032 }
1033 
1034 int T_CTEST_EXPORT2
runTestRequest(const TestNode * root,int argc,const char * const argv[])1035 runTestRequest(const TestNode* root,
1036              int argc,
1037              const char* const argv[])
1038 {
1039     /**
1040      * This main will parse the l, v, h, n, and path arguments
1041      */
1042     const TestNode*    toRun;
1043     int                i;
1044     int                doList = FALSE;
1045     int                subtreeOptionSeen = FALSE;
1046 
1047     int                errorCount = 0;
1048 
1049     toRun = root;
1050 
1051     if(ctest_xml_init(ARGV_0)) {
1052       return 1; /* couldn't fire up XML thing */
1053     }
1054 
1055     for( i=1; i<argc; i++)
1056     {
1057         if ( argv[i][0] == '/' )
1058         {
1059             printf("Selecting subtree '%s'\n", argv[i]);
1060 
1061             if ( argv[i][1] == 0 )
1062                 toRun = root;
1063             else
1064                 toRun = getTest(root, argv[i]);
1065 
1066             if ( toRun == NULL )
1067             {
1068                 printf("* Could not find any matching subtree\n");
1069                 return -1;
1070             }
1071 
1072             ON_LINE=FALSE; /* just in case */
1073 
1074             if( doList == TRUE)
1075                 showTests(toRun);
1076             else
1077                 runTests(toRun);
1078 
1079             ON_LINE=FALSE; /* just in case */
1080 
1081             errorCount += ERROR_COUNT;
1082 
1083             subtreeOptionSeen = TRUE;
1084         } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) {
1085             subtreeOptionSeen=FALSE;
1086         } else if (strcmp( argv[i], "-l") == 0) {
1087             doList = TRUE;
1088         }
1089         /* else option already handled by initArgs */
1090     }
1091 
1092     if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */
1093     {
1094         ON_LINE=FALSE; /* just in case */
1095         if( doList == TRUE)
1096             showTests(toRun);
1097         else
1098             runTests(toRun);
1099         ON_LINE=FALSE; /* just in case */
1100 
1101         errorCount += ERROR_COUNT;
1102     }
1103     else
1104     {
1105         if( ( doList == FALSE ) && ( errorCount > 0 ) )
1106             printf(" Total errors: %d\n", errorCount );
1107     }
1108 
1109     REPEAT_TESTS_INIT = 1;
1110 
1111     if(ctest_xml_fini()) {
1112       errorCount++;
1113     }
1114 
1115     return errorCount; /* total error count */
1116 }
1117 
1118 /**
1119  * Display program invocation arguments
1120  */
1121 
help(const char * argv0)1122 static void help ( const char *argv0 )
1123 {
1124     printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
1125            "    [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
1126            "    [ /path/to/test ]\n",
1127             argv0);
1128     printf("    -l  To get a list of test names\n");
1129     printf("    -e  to do exhaustive testing\n");
1130     printf("    -verbose To turn ON verbosity\n");
1131     printf("    -v  To turn ON verbosity(same as -verbose)\n");
1132     printf("    -x file.xml   Write junit format output to file.xml\n");
1133     printf("    -h  To print this message\n");
1134     printf("    -n  To turn OFF printing error messages\n");
1135     printf("    -w  Don't fail on data-loading errs, just warn. Useful if\n"
1136            "        user has reduced/changed the common set of ICU data \n");
1137     printf("    -t_info | -t_error | -t_warn | -t_oc | -t_verbose  Enable ICU tracing\n");
1138     printf("    -no_err_msg (same as -n) \n");
1139     printf("    -m n[-q] Min-Max memory size that will cause an allocation failure.\n");
1140     printf("        The default is the maximum value of size_t. Max is optional.\n");
1141     printf("    -r  Repeat tests after calling u_cleanup \n");
1142     printf("    [/subtest]  To run a subtest \n");
1143     printf("    eg: to run just the utility tests type: cintltest /tsutil) \n");
1144 }
1145 
1146 int32_t T_CTEST_EXPORT2
getTestOption(int32_t testOption)1147 getTestOption ( int32_t testOption ) {
1148     switch (testOption) {
1149         case VERBOSITY_OPTION:
1150             return VERBOSITY;
1151         case WARN_ON_MISSING_DATA_OPTION:
1152             return WARN_ON_MISSING_DATA;
1153         case QUICK_OPTION:
1154             return QUICK;
1155         case REPEAT_TESTS_OPTION:
1156             return REPEAT_TESTS;
1157         case ERR_MSG_OPTION:
1158             return ERR_MSG;
1159         case ICU_TRACE_OPTION:
1160             return ICU_TRACE;
1161         default :
1162             return 0;
1163     }
1164 }
1165 
1166 void T_CTEST_EXPORT2
setTestOption(int32_t testOption,int32_t value)1167 setTestOption ( int32_t testOption, int32_t value) {
1168     if (value == DECREMENT_OPTION_VALUE) {
1169         value = getTestOption(testOption);
1170         --value;
1171     }
1172     switch (testOption) {
1173         case VERBOSITY_OPTION:
1174             VERBOSITY = value;
1175             break;
1176         case WARN_ON_MISSING_DATA_OPTION:
1177             WARN_ON_MISSING_DATA = value;
1178             break;
1179         case QUICK_OPTION:
1180             QUICK = value;
1181             break;
1182         case REPEAT_TESTS_OPTION:
1183             REPEAT_TESTS = value;
1184             break;
1185         case ICU_TRACE_OPTION:
1186             ICU_TRACE = value;
1187             break;
1188         default :
1189             break;
1190     }
1191 }
1192 
1193 
1194 /*
1195  * ================== JUnit support ================================
1196  */
1197 
1198 int32_t
1199 T_CTEST_EXPORT2
ctest_xml_setFileName(const char * name)1200 ctest_xml_setFileName(const char *name) {
1201   XML_FILE_NAME=name;
1202   return 0;
1203 }
1204 
1205 
1206 int32_t
1207 T_CTEST_EXPORT2
ctest_xml_init(const char * rootName)1208 ctest_xml_init(const char *rootName) {
1209   if(!XML_FILE_NAME) return 0;
1210   XML_FILE = fopen(XML_FILE_NAME,"w");
1211   if(!XML_FILE) {
1212     perror("fopen");
1213     fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME);
1214     return 1;
1215   }
1216   while(*rootName&&!isalnum(*rootName)) {
1217     rootName++;
1218   }
1219   strcpy(XML_PREFIX,rootName);
1220   {
1221     char *p = XML_PREFIX+strlen(XML_PREFIX);
1222     for(p--;*p&&p>XML_PREFIX&&!isalnum(*p);p--) {
1223       *p=0;
1224     }
1225   }
1226   /* write prefix */
1227   fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX);
1228 
1229   return 0;
1230 }
1231 
1232 int32_t
1233 T_CTEST_EXPORT2
ctest_xml_fini(void)1234 ctest_xml_fini(void) {
1235   if(!XML_FILE) return 0;
1236 
1237   fprintf(XML_FILE, "</testsuite>\n");
1238   fclose(XML_FILE);
1239   printf(" ( test results written to %s )\n", XML_FILE_NAME);
1240   XML_FILE=0;
1241   return 0;
1242 }
1243 
1244 
1245 int32_t
1246 T_CTEST_EXPORT2
ctest_xml_testcase(const char * classname,const char * name,const char * time,const char * failMsg)1247 ctest_xml_testcase(const char *classname, const char *name, const char *time, const char *failMsg) {
1248   if(!XML_FILE) return 0;
1249 
1250   fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, time);
1251   if(failMsg) {
1252     fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg);
1253   } else {
1254     fprintf(XML_FILE, "/>\n");
1255   }
1256 
1257   return 0;
1258 }
1259 
1260 
1261