Home | History | Annotate | Download | only in ctestfw
      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] */
    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 
    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 
    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
    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
    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 */
    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  */
    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 
    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  */
    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
    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
    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
    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
    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 
    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 
    630 static void go_offline() {
    631 	go_offline_with_marker(NULL);
    632 }
    633 
    634 static void go_offline_err() {
    635 	go_offline();
    636 }
    637 
    638 static void first_line_verbose() {
    639     go_offline_with_marker("v");
    640 }
    641 
    642 static void first_line_err() {
    643     go_offline_with_marker("!");
    644 }
    645 
    646 static void first_line_info() {
    647     go_offline_with_marker("\"");
    648 }
    649 
    650 static void first_line_test() {
    651 	fputs(" ", stdout);
    652 }
    653 
    654 
    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
    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  */
    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  */
    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 
    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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    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 }
    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 }
    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
    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
   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 
   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
   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
   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
   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
   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
   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
   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