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