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