1 /* 2 ******************************************************************************** 3 * 4 * Copyright (C) 1996-2014, 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 knownList = NULL; 527 } 528 529 if (ERROR_COUNT) 530 { 531 fprintf(stdout,"\nSUMMARY:\n"); 532 fflush(stdout); 533 fprintf(stdout,"******* [Total error count:\t%d]\n", ERROR_COUNT); 534 fflush(stdout); 535 fprintf(stdout, " Errors in\n"); 536 for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++) 537 fprintf(stdout, "[%s]\n",ERROR_LOG[i]); 538 if(SUMMARY_FILE != NULL) { 539 FILE *summf = fopen(SUMMARY_FILE, "w"); 540 if(summf!=NULL) { 541 for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++) 542 fprintf(summf, "%s\n",ERROR_LOG[i]); 543 fclose(summf); 544 } 545 } 546 } 547 else 548 { 549 log_testinfo("\n[All tests passed successfully...]\n"); 550 } 551 552 if(DATA_ERROR_COUNT) { 553 if(WARN_ON_MISSING_DATA==0) { 554 log_testinfo("\t*Note* some errors are data-loading related. If the data used is not the \n" 555 "\tstock ICU data (i.e some have been added or removed), consider using\n" 556 "\tthe '-w' option to turn these errors into warnings.\n"); 557 } else { 558 log_testinfo("\t*WARNING* some data-loading errors were ignored by the -w option.\n"); 559 } 560 } 561 } 562 563 const char* T_CTEST_EXPORT2 564 getTestName(void) 565 { 566 if(currentTest != NULL) { 567 return currentTest->name; 568 } else { 569 return NULL; 570 } 571 } 572 573 const TestNode* T_CTEST_EXPORT2 574 getTest(const TestNode* root, const char* name) 575 { 576 const char* nextName; 577 TestNode *nextNode; 578 const TestNode* curNode; 579 int nameLen; /* length of current 'name' */ 580 581 if (root == NULL) { 582 log_err("TEST CAN'T BE FOUND!\n"); 583 return NULL; 584 } 585 /* remove leading slash */ 586 if ( *name == TEST_SEPARATOR ) 587 name++; 588 589 curNode = root; 590 591 for(;;) 592 { 593 /* Start with the next child */ 594 nextNode = curNode->child; 595 596 getNextLevel ( name, &nameLen, &nextName ); 597 598 /* printf("* %s\n", name );*/ 599 600 /* if nextNode is already null, then curNode has no children 601 -- add them */ 602 if( nextNode == NULL ) 603 { 604 return NULL; 605 } 606 607 /* Search across for the name */ 608 while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 ) 609 { 610 curNode = nextNode; 611 nextNode = nextNode -> sibling; 612 613 if ( nextNode == NULL ) 614 { 615 /* Did not find 'name' on this level. */ 616 return NULL; 617 } 618 } 619 620 /* nextNode matches 'name' */ 621 622 if (nextName == NULL) /* end of the line */ 623 { 624 return nextNode; 625 } 626 627 /* Loop again with the next item */ 628 name = nextName; 629 curNode = nextNode; 630 } 631 } 632 633 /* =========== io functions ======== */ 634 635 static void go_offline_with_marker(const char *mrk) { 636 UBool wasON_LINE = ON_LINE; 637 638 if(ON_LINE) { 639 log_testinfo(" {\n"); 640 ON_LINE=FALSE; 641 } 642 643 if(!HANGING_OUTPUT || wasON_LINE) { 644 if(mrk != NULL) { 645 fputs(mrk, stdout); 646 } 647 } 648 } 649 650 static void go_offline() { 651 go_offline_with_marker(NULL); 652 } 653 654 static void go_offline_err() { 655 go_offline(); 656 } 657 658 static void first_line_verbose() { 659 go_offline_with_marker("v"); 660 } 661 662 static void first_line_err() { 663 go_offline_with_marker("!"); 664 } 665 666 static void first_line_info() { 667 go_offline_with_marker("\""); 668 } 669 670 static void first_line_test() { 671 fputs(" ", stdout); 672 } 673 674 675 static void vlog_err(const char *prefix, const char *pattern, va_list ap) 676 { 677 if( ERR_MSG == FALSE){ 678 return; 679 } 680 fputs("!", stdout); /* col 1 - bang */ 681 fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); 682 if(prefix) { 683 fputs(prefix, stdout); 684 } 685 vfprintf(stdout, pattern, ap); 686 fflush(stdout); 687 va_end(ap); 688 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) { 689 HANGING_OUTPUT=1; 690 } else { 691 HANGING_OUTPUT=0; 692 } 693 GLOBAL_PRINT_COUNT++; 694 } 695 696 static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap) 697 { 698 char buf[2048]; 699 UBool firstForTicket; 700 UBool firstForWhere; 701 702 if(NO_KNOWN) return FALSE; 703 if(pattern==NULL) pattern=""; 704 705 vsprintf(buf, pattern, ap); 706 knownList = udbg_knownIssue_open(knownList, ticket, gTestName, buf, 707 &firstForTicket, &firstForWhere); 708 709 if(firstForTicket || firstForWhere) { 710 log_info("(Known issue #%s) %s\n", ticket, buf); 711 } else { 712 log_verbose("(Known issue #%s) %s\n", ticket, buf); 713 } 714 715 return TRUE; 716 } 717 718 719 void T_CTEST_EXPORT2 720 vlog_info(const char *prefix, const char *pattern, va_list ap) 721 { 722 first_line_info(); 723 fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); 724 if(prefix) { 725 fputs(prefix, stdout); 726 } 727 vfprintf(stdout, pattern, ap); 728 fflush(stdout); 729 va_end(ap); 730 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) { 731 HANGING_OUTPUT=1; 732 } else { 733 HANGING_OUTPUT=0; 734 } 735 GLOBAL_PRINT_COUNT++; 736 } 737 /** 738 * Log test structure, with indent 739 */ 740 static void log_testinfo_i(const char *pattern, ...) 741 { 742 va_list ap; 743 first_line_test(); 744 fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); 745 va_start(ap, pattern); 746 vfprintf(stdout, pattern, ap); 747 fflush(stdout); 748 va_end(ap); 749 GLOBAL_PRINT_COUNT++; 750 } 751 /** 752 * Log test structure (no ident) 753 */ 754 static void log_testinfo(const char *pattern, ...) 755 { 756 va_list ap; 757 va_start(ap, pattern); 758 first_line_test(); 759 vfprintf(stdout, pattern, ap); 760 fflush(stdout); 761 va_end(ap); 762 GLOBAL_PRINT_COUNT++; 763 } 764 765 766 static void vlog_verbose(const char *prefix, const char *pattern, va_list ap) 767 { 768 if ( VERBOSITY == FALSE ) 769 return; 770 771 first_line_verbose(); 772 fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); 773 if(prefix) { 774 fputs(prefix, stdout); 775 } 776 vfprintf(stdout, pattern, ap); 777 fflush(stdout); 778 va_end(ap); 779 GLOBAL_PRINT_COUNT++; 780 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) { 781 HANGING_OUTPUT=1; 782 } else { 783 HANGING_OUTPUT=0; 784 } 785 } 786 787 void T_CTEST_EXPORT2 788 log_err(const char* pattern, ...) 789 { 790 va_list ap; 791 first_line_err(); 792 if(strchr(pattern, '\n') != NULL) { 793 /* 794 * Count errors only if there is a line feed in the pattern 795 * so that we do not exaggerate our error count. 796 */ 797 ++ERROR_COUNT; 798 } else { 799 /* Count at least one error. */ 800 ONE_ERROR=1; 801 } 802 va_start(ap, pattern); 803 vlog_err(NULL, pattern, ap); 804 } 805 806 UBool T_CTEST_EXPORT2 807 log_knownIssue(const char *ticket, const char *pattern, ...) { 808 va_list ap; 809 va_start(ap, pattern); 810 return vlog_knownIssue(ticket, pattern, ap); 811 } 812 813 void T_CTEST_EXPORT2 814 log_err_status(UErrorCode status, const char* pattern, ...) 815 { 816 va_list ap; 817 va_start(ap, pattern); 818 819 if ((status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR)) { 820 ++DATA_ERROR_COUNT; /* for informational message at the end */ 821 822 if (WARN_ON_MISSING_DATA == 0) { 823 first_line_err(); 824 /* Fatal error. */ 825 if (strchr(pattern, '\n') != NULL) { 826 ++ERROR_COUNT; 827 } else { 828 ++ONE_ERROR; 829 } 830 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ 831 } else { 832 vlog_info("[DATA] ", pattern, ap); 833 } 834 } else { 835 first_line_err(); 836 /* Fatal error. */ 837 if(strchr(pattern, '\n') != NULL) { 838 ++ERROR_COUNT; 839 } else { 840 ++ONE_ERROR; 841 } 842 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ 843 } 844 } 845 846 void T_CTEST_EXPORT2 847 log_info(const char* pattern, ...) 848 { 849 va_list ap; 850 851 va_start(ap, pattern); 852 vlog_info(NULL, pattern, ap); 853 } 854 855 void T_CTEST_EXPORT2 856 log_verbose(const char* pattern, ...) 857 { 858 va_list ap; 859 860 va_start(ap, pattern); 861 vlog_verbose(NULL, pattern, ap); 862 } 863 864 865 void T_CTEST_EXPORT2 866 log_data_err(const char* pattern, ...) 867 { 868 va_list ap; 869 va_start(ap, pattern); 870 871 go_offline_err(); 872 ++DATA_ERROR_COUNT; /* for informational message at the end */ 873 874 if(WARN_ON_MISSING_DATA == 0) { 875 /* Fatal error. */ 876 if(strchr(pattern, '\n') != NULL) { 877 ++ERROR_COUNT; 878 } 879 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ 880 } else { 881 vlog_info("[DATA] ", pattern, ap); 882 } 883 } 884 885 886 /* 887 * Tracing functions. 888 */ 889 static int traceFnNestingDepth = 0; 890 U_CDECL_BEGIN 891 static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) { 892 char buf[500]; 893 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; 894 fputs(buf, stdout); 895 traceFnNestingDepth++; 896 } 897 898 static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500]; 899 900 if (traceFnNestingDepth>0) { 901 traceFnNestingDepth--; 902 } 903 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; 904 fputs(buf, stdout); 905 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); 906 buf[sizeof(buf)-1]=0; 907 fputs(buf, stdout); 908 putc('\n', stdout); 909 } 910 911 static void U_CALLCONV TraceData(const void *context, int32_t fnNumber, 912 int32_t level, const char *fmt, va_list args) { 913 char buf[500]; 914 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); 915 buf[sizeof(buf)-1]=0; 916 fputs(buf, stdout); 917 putc('\n', stdout); 918 } 919 920 static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) { 921 /*if (VERBOSITY) { 922 printf("Allocated %ld\n", (long)size); 923 }*/ 924 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { 925 return NULL; 926 } 927 return malloc(size); 928 } 929 static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) { 930 /*if (VERBOSITY) { 931 printf("Reallocated %ld\n", (long)size); 932 }*/ 933 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { 934 /*free(mem);*/ /* Realloc doesn't free on failure. */ 935 return NULL; 936 } 937 return realloc(mem, size); 938 } 939 static void U_CALLCONV ctest_libFree(const void *context, void *mem) { 940 free(mem); 941 } 942 943 int T_CTEST_EXPORT2 944 initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context) 945 { 946 int i; 947 int argSkip = 0; 948 949 VERBOSITY = FALSE; 950 ERR_MSG = TRUE; 951 952 ARGV_0=argv[0]; 953 954 for( i=1; i<argc; i++) 955 { 956 if ( argv[i][0] == '/' ) 957 { 958 /* We don't run the tests here. */ 959 continue; 960 } 961 else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) 962 { 963 /* We don't run the tests here. */ 964 continue; 965 } 966 else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0) 967 { 968 VERBOSITY = TRUE; 969 } 970 else if (strcmp( argv[i], "-l" )==0 ) 971 { 972 /* doList = TRUE; */ 973 } 974 else if (strcmp( argv[i], "-e1") == 0) 975 { 976 QUICK = -1; 977 } 978 else if (strcmp( argv[i], "-e") ==0) 979 { 980 QUICK = 0; 981 } 982 else if (strcmp( argv[i], "-K") ==0) 983 { 984 NO_KNOWN = 1; 985 } 986 else if (strncmp( argv[i], "-E",2) ==0) 987 { 988 SUMMARY_FILE=argv[i]+2; 989 } 990 else if (strcmp( argv[i], "-w") ==0) 991 { 992 WARN_ON_MISSING_DATA = TRUE; 993 } 994 else if (strcmp( argv[i], "-m") ==0) 995 { 996 UErrorCode errorCode = U_ZERO_ERROR; 997 if (i+1 < argc) { 998 char *endPtr = NULL; 999 i++; 1000 MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10); 1001 if (endPtr == argv[i]) { 1002 printf("Can't parse %s\n", argv[i]); 1003 help(argv[0]); 1004 return 0; 1005 } 1006 if (*endPtr == '-') { 1007 char *maxPtr = endPtr+1; 1008 endPtr = NULL; 1009 MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10); 1010 if (endPtr == argv[i]) { 1011 printf("Can't parse %s\n", argv[i]); 1012 help(argv[0]); 1013 return 0; 1014 } 1015 } 1016 } 1017 /* Use the default value */ 1018 u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode); 1019 if (U_FAILURE(errorCode)) { 1020 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode)); 1021 return 0; 1022 } 1023 } 1024 else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0) 1025 { 1026 ERR_MSG = FALSE; 1027 } 1028 else if (strcmp( argv[i], "-r") == 0) 1029 { 1030 if (!REPEAT_TESTS_INIT) { 1031 REPEAT_TESTS++; 1032 } 1033 } 1034 else if (strcmp( argv[i], "-x") == 0) 1035 { 1036 if(++i>=argc) { 1037 printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n"); 1038 return 0; 1039 } 1040 if(ctest_xml_setFileName(argv[i])) { /* set the name */ 1041 return 0; 1042 } 1043 } 1044 else if (strcmp( argv[i], "-t_info") == 0) { 1045 ICU_TRACE = UTRACE_INFO; 1046 } 1047 else if (strcmp( argv[i], "-t_error") == 0) { 1048 ICU_TRACE = UTRACE_ERROR; 1049 } 1050 else if (strcmp( argv[i], "-t_warn") == 0) { 1051 ICU_TRACE = UTRACE_WARNING; 1052 } 1053 else if (strcmp( argv[i], "-t_verbose") == 0) { 1054 ICU_TRACE = UTRACE_VERBOSE; 1055 } 1056 else if (strcmp( argv[i], "-t_oc") == 0) { 1057 ICU_TRACE = UTRACE_OPEN_CLOSE; 1058 } 1059 else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0) 1060 { 1061 help( argv[0] ); 1062 return 0; 1063 } 1064 else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0) 1065 { 1066 i += argSkip - 1; 1067 } 1068 else 1069 { 1070 printf("* unknown option: %s\n", argv[i]); 1071 help( argv[0] ); 1072 return 0; 1073 } 1074 } 1075 if (ICU_TRACE != UTRACE_OFF) { 1076 utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData); 1077 utrace_setLevel(ICU_TRACE); 1078 } 1079 1080 return 1; /* total error count */ 1081 } 1082 1083 int T_CTEST_EXPORT2 1084 runTestRequest(const TestNode* root, 1085 int argc, 1086 const char* const argv[]) 1087 { 1088 /** 1089 * This main will parse the l, v, h, n, and path arguments 1090 */ 1091 const TestNode* toRun; 1092 int i; 1093 int doList = FALSE; 1094 int subtreeOptionSeen = FALSE; 1095 1096 int errorCount = 0; 1097 1098 toRun = root; 1099 1100 if(ctest_xml_init(ARGV_0)) { 1101 return 1; /* couldn't fire up XML thing */ 1102 } 1103 1104 for( i=1; i<argc; i++) 1105 { 1106 if ( argv[i][0] == '/' ) 1107 { 1108 printf("Selecting subtree '%s'\n", argv[i]); 1109 1110 if ( argv[i][1] == 0 ) 1111 toRun = root; 1112 else 1113 toRun = getTest(root, argv[i]); 1114 1115 if ( toRun == NULL ) 1116 { 1117 printf("* Could not find any matching subtree\n"); 1118 return -1; 1119 } 1120 1121 ON_LINE=FALSE; /* just in case */ 1122 1123 if( doList == TRUE) 1124 showTests(toRun); 1125 else 1126 runTests(toRun); 1127 1128 ON_LINE=FALSE; /* just in case */ 1129 1130 errorCount += ERROR_COUNT; 1131 1132 subtreeOptionSeen = TRUE; 1133 } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) { 1134 subtreeOptionSeen=FALSE; 1135 } else if (strcmp( argv[i], "-l") == 0) { 1136 doList = TRUE; 1137 } 1138 /* else option already handled by initArgs */ 1139 } 1140 1141 if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */ 1142 { 1143 ON_LINE=FALSE; /* just in case */ 1144 if( doList == TRUE) 1145 showTests(toRun); 1146 else 1147 runTests(toRun); 1148 ON_LINE=FALSE; /* just in case */ 1149 1150 errorCount += ERROR_COUNT; 1151 } 1152 else 1153 { 1154 if( ( doList == FALSE ) && ( errorCount > 0 ) ) 1155 printf(" Total errors: %d\n", errorCount ); 1156 } 1157 1158 REPEAT_TESTS_INIT = 1; 1159 1160 if(ctest_xml_fini()) { 1161 errorCount++; 1162 } 1163 1164 return errorCount; /* total error count */ 1165 } 1166 1167 /** 1168 * Display program invocation arguments 1169 */ 1170 1171 static void help ( const char *argv0 ) 1172 { 1173 printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n" 1174 " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n" 1175 " [ /path/to/test ]\n", 1176 argv0); 1177 printf(" -l To get a list of test names\n"); 1178 printf(" -e to do exhaustive testing\n"); 1179 printf(" -verbose To turn ON verbosity\n"); 1180 printf(" -v To turn ON verbosity(same as -verbose)\n"); 1181 printf(" -x file.xml Write junit format output to file.xml\n"); 1182 printf(" -h To print this message\n"); 1183 printf(" -K to turn OFF suppressing known issues\n"); 1184 printf(" -n To turn OFF printing error messages\n"); 1185 printf(" -w Don't fail on data-loading errs, just warn. Useful if\n" 1186 " user has reduced/changed the common set of ICU data \n"); 1187 printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n"); 1188 printf(" -no_err_msg (same as -n) \n"); 1189 printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n"); 1190 printf(" The default is the maximum value of size_t. Max is optional.\n"); 1191 printf(" -r Repeat tests after calling u_cleanup \n"); 1192 printf(" [/subtest] To run a subtest \n"); 1193 printf(" eg: to run just the utility tests type: cintltest /tsutil) \n"); 1194 } 1195 1196 int32_t T_CTEST_EXPORT2 1197 getTestOption ( int32_t testOption ) { 1198 switch (testOption) { 1199 case VERBOSITY_OPTION: 1200 return VERBOSITY; 1201 case WARN_ON_MISSING_DATA_OPTION: 1202 return WARN_ON_MISSING_DATA; 1203 case QUICK_OPTION: 1204 return QUICK; 1205 case REPEAT_TESTS_OPTION: 1206 return REPEAT_TESTS; 1207 case ERR_MSG_OPTION: 1208 return ERR_MSG; 1209 case ICU_TRACE_OPTION: 1210 return ICU_TRACE; 1211 default : 1212 return 0; 1213 } 1214 } 1215 1216 void T_CTEST_EXPORT2 1217 setTestOption ( int32_t testOption, int32_t value) { 1218 if (value == DECREMENT_OPTION_VALUE) { 1219 value = getTestOption(testOption); 1220 --value; 1221 } 1222 switch (testOption) { 1223 case VERBOSITY_OPTION: 1224 VERBOSITY = value; 1225 break; 1226 case WARN_ON_MISSING_DATA_OPTION: 1227 WARN_ON_MISSING_DATA = value; 1228 break; 1229 case QUICK_OPTION: 1230 QUICK = value; 1231 break; 1232 case REPEAT_TESTS_OPTION: 1233 REPEAT_TESTS = value; 1234 break; 1235 case ICU_TRACE_OPTION: 1236 ICU_TRACE = (UTraceLevel)value; 1237 break; 1238 default : 1239 break; 1240 } 1241 } 1242 1243 1244 /* 1245 * ================== JUnit support ================================ 1246 */ 1247 1248 int32_t 1249 T_CTEST_EXPORT2 1250 ctest_xml_setFileName(const char *name) { 1251 XML_FILE_NAME=name; 1252 return 0; 1253 } 1254 1255 1256 int32_t 1257 T_CTEST_EXPORT2 1258 ctest_xml_init(const char *rootName) { 1259 if(!XML_FILE_NAME) return 0; 1260 XML_FILE = fopen(XML_FILE_NAME,"w"); 1261 if(!XML_FILE) { 1262 perror("fopen"); 1263 fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME); 1264 return 1; 1265 } 1266 while(*rootName&&!isalnum((int)*rootName)) { 1267 rootName++; 1268 } 1269 strcpy(XML_PREFIX,rootName); 1270 { 1271 char *p = XML_PREFIX+strlen(XML_PREFIX); 1272 for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) { 1273 *p=0; 1274 } 1275 } 1276 /* write prefix */ 1277 fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX); 1278 1279 return 0; 1280 } 1281 1282 int32_t 1283 T_CTEST_EXPORT2 1284 ctest_xml_fini(void) { 1285 if(!XML_FILE) return 0; 1286 1287 fprintf(XML_FILE, "</testsuite>\n"); 1288 fclose(XML_FILE); 1289 printf(" ( test results written to %s )\n", XML_FILE_NAME); 1290 XML_FILE=0; 1291 return 0; 1292 } 1293 1294 1295 int32_t 1296 T_CTEST_EXPORT2 1297 ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) { 1298 if(!XML_FILE) return 0; 1299 1300 fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, timeSeconds); 1301 if(failMsg) { 1302 fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg); 1303 } else { 1304 fprintf(XML_FILE, "/>\n"); 1305 } 1306 1307 return 0; 1308 } 1309 1310 1311