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