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