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