1/* 2******************************************************************************** 3* 4* Copyright (C) 1996-2010, 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 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); 92 93/** 94 * Log test structure, with indent 95 * @param pattern printf pattern 96 */ 97static void log_testinfo_i(const char *pattern, ...); 98 99/** 100 * Log test structure, NO indent 101 * @param pattern printf pattern 102 */ 103static 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*/ 108static int ERRONEOUS_FUNCTION_COUNT = 0; 109static int ERROR_COUNT = 0; /* Count of errors from all tests. */ 110static int ONE_ERROR = 0; /* were there any other errors? */ 111static int DATA_ERROR_COUNT = 0; /* count of data related errors or warnings */ 112static int INDENT_LEVEL = 0; 113static UBool ON_LINE = FALSE; /* are we on the top line with our test name? */ 114static UBool HANGING_OUTPUT = FALSE; /* did the user leave us without a trailing \n ? */ 115static int GLOBAL_PRINT_COUNT = 0; /* global count of printouts */ 116int REPEAT_TESTS_INIT = 0; /* Was REPEAT_TESTS initialized? */ 117int REPEAT_TESTS = 1; /* Number of times to run the test */ 118int VERBOSITY = 0; /* be No-verbose by default */ 119int ERR_MSG =1; /* error messages will be displayed by default*/ 120int QUICK = 1; /* Skip some of the slower tests? */ 121int WARN_ON_MISSING_DATA = 0; /* Reduce data errs to warnings? */ 122UTraceLevel ICU_TRACE = UTRACE_OFF; /* ICU tracing level */ 123size_t MINIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Minimum library memory allocation window that will fail. */ 124size_t MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Maximum library memory allocation window that will fail. */ 125int32_t ALLOCATION_COUNT = 0; 126static const char *ARGV_0 = "[ALL]"; 127static const char *XML_FILE_NAME=NULL; 128static char XML_PREFIX[256]; 129 130FILE *XML_FILE = NULL; 131/*-------------------------------------------*/ 132 133/* strncmp that also makes sure there's a \0 at s2[0] */ 134static 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 146static 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 167static 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 183void T_CTEST_EXPORT2 184cleanUpTestTree(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 198void T_CTEST_EXPORT2 199addTest(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 */ 217static 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 */ 291void 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 306static 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 */ 322static 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 486void T_CTEST_EXPORT2 487showTests ( 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 499void T_CTEST_EXPORT2 500runTests ( 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 543const char* T_CTEST_EXPORT2 544getTestName(void) 545{ 546 if(currentTest != NULL) { 547 return currentTest->name; 548 } else { 549 return NULL; 550 } 551} 552 553const TestNode* T_CTEST_EXPORT2 554getTest(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 615static 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 630static void go_offline() { 631 go_offline_with_marker(NULL); 632} 633 634static void go_offline_err() { 635 go_offline(); 636} 637 638static void first_line_verbose() { 639 go_offline_with_marker("v"); 640} 641 642static void first_line_err() { 643 go_offline_with_marker("!"); 644} 645 646static void first_line_info() { 647 go_offline_with_marker("\""); 648} 649 650static void first_line_test() { 651 fputs(" ", stdout); 652} 653 654 655static 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 676void T_CTEST_EXPORT2 677vlog_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 */ 697static 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 */ 711static 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 723static 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 744void T_CTEST_EXPORT2 745log_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 763void T_CTEST_EXPORT2 764log_err_status(UErrorCode status, const char* pattern, ...) 765{ 766 va_list ap; 767 va_start(ap, pattern); 768 769 first_line_err(); 770 if ((status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR)) { 771 ++DATA_ERROR_COUNT; /* for informational message at the end */ 772 773 if (WARN_ON_MISSING_DATA == 0) { 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 /* Fatal error. */ 786 if(strchr(pattern, '\n') != NULL) { 787 ++ERROR_COUNT; 788 } else { 789 ++ONE_ERROR; 790 } 791 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ 792 } 793} 794 795void T_CTEST_EXPORT2 796log_info(const char* pattern, ...) 797{ 798 va_list ap; 799 800 va_start(ap, pattern); 801 vlog_info(NULL, pattern, ap); 802} 803 804void T_CTEST_EXPORT2 805log_verbose(const char* pattern, ...) 806{ 807 va_list ap; 808 809 va_start(ap, pattern); 810 vlog_verbose(NULL, pattern, ap); 811} 812 813 814void T_CTEST_EXPORT2 815log_data_err(const char* pattern, ...) 816{ 817 va_list ap; 818 va_start(ap, pattern); 819 820 go_offline_err(); 821 ++DATA_ERROR_COUNT; /* for informational message at the end */ 822 823 if(WARN_ON_MISSING_DATA == 0) { 824 /* Fatal error. */ 825 if(strchr(pattern, '\n') != NULL) { 826 ++ERROR_COUNT; 827 } 828 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ 829 } else { 830 vlog_info("[DATA] ", pattern, ap); 831 } 832} 833 834 835/* 836 * Tracing functions. 837 */ 838static int traceFnNestingDepth = 0; 839U_CDECL_BEGIN 840static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) { 841 char buf[500]; 842 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; 843 fputs(buf, stdout); 844 traceFnNestingDepth++; 845} 846 847static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500]; 848 849 if (traceFnNestingDepth>0) { 850 traceFnNestingDepth--; 851 } 852 utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; 853 fputs(buf, stdout); 854 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); 855 buf[sizeof(buf)-1]=0; 856 fputs(buf, stdout); 857 putc('\n', stdout); 858} 859 860static void U_CALLCONV TraceData(const void *context, int32_t fnNumber, 861 int32_t level, const char *fmt, va_list args) { 862 char buf[500]; 863 utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); 864 buf[sizeof(buf)-1]=0; 865 fputs(buf, stdout); 866 putc('\n', stdout); 867} 868 869static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) { 870 /*if (VERBOSITY) { 871 printf("Allocated %ld\n", (long)size); 872 }*/ 873 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { 874 return NULL; 875 } 876 umtx_atomic_inc(&ALLOCATION_COUNT); 877 return malloc(size); 878} 879static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) { 880 /*if (VERBOSITY) { 881 printf("Reallocated %ld\n", (long)size); 882 }*/ 883 if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { 884 /*free(mem);*/ /* Realloc doesn't free on failure. */ 885 return NULL; 886 } 887 if (mem == NULL) { 888 /* New allocation. */ 889 umtx_atomic_inc(&ALLOCATION_COUNT); 890 } 891 return realloc(mem, size); 892} 893static void U_CALLCONV ctest_libFree(const void *context, void *mem) { 894 if (mem != NULL) { 895 umtx_atomic_dec(&ALLOCATION_COUNT); 896 } 897 free(mem); 898} 899 900int T_CTEST_EXPORT2 901initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context) 902{ 903 int i; 904 int doList = FALSE; 905 int argSkip = 0; 906 907 VERBOSITY = FALSE; 908 ERR_MSG = TRUE; 909 910 ARGV_0=argv[0]; 911 912 for( i=1; i<argc; i++) 913 { 914 if ( argv[i][0] == '/' ) 915 { 916 /* We don't run the tests here. */ 917 continue; 918 } 919 else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) 920 { 921 /* We don't run the tests here. */ 922 continue; 923 } 924 else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0) 925 { 926 VERBOSITY = TRUE; 927 } 928 else if (strcmp( argv[i], "-l" )==0 ) 929 { 930 doList = TRUE; 931 } 932 else if (strcmp( argv[i], "-e1") == 0) 933 { 934 QUICK = -1; 935 } 936 else if (strcmp( argv[i], "-e") ==0) 937 { 938 QUICK = 0; 939 } 940 else if (strcmp( argv[i], "-w") ==0) 941 { 942 WARN_ON_MISSING_DATA = TRUE; 943 } 944 else if (strcmp( argv[i], "-m") ==0) 945 { 946 UErrorCode errorCode = U_ZERO_ERROR; 947 if (i+1 < argc) { 948 char *endPtr = NULL; 949 i++; 950 MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10); 951 if (endPtr == argv[i]) { 952 printf("Can't parse %s\n", argv[i]); 953 help(argv[0]); 954 return 0; 955 } 956 if (*endPtr == '-') { 957 char *maxPtr = endPtr+1; 958 endPtr = NULL; 959 MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10); 960 if (endPtr == argv[i]) { 961 printf("Can't parse %s\n", argv[i]); 962 help(argv[0]); 963 return 0; 964 } 965 } 966 } 967 /* Use the default value */ 968 u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode); 969 if (U_FAILURE(errorCode)) { 970 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode)); 971 return 0; 972 } 973 } 974 else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0) 975 { 976 ERR_MSG = FALSE; 977 } 978 else if (strcmp( argv[i], "-r") == 0) 979 { 980 if (!REPEAT_TESTS_INIT) { 981 REPEAT_TESTS++; 982 } 983 } 984 else if (strcmp( argv[i], "-x") == 0) 985 { 986 if(++i>=argc) { 987 printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n"); 988 return 0; 989 } 990 if(ctest_xml_setFileName(argv[i])) { /* set the name */ 991 return 0; 992 } 993 } 994 else if (strcmp( argv[i], "-t_info") == 0) { 995 ICU_TRACE = UTRACE_INFO; 996 } 997 else if (strcmp( argv[i], "-t_error") == 0) { 998 ICU_TRACE = UTRACE_ERROR; 999 } 1000 else if (strcmp( argv[i], "-t_warn") == 0) { 1001 ICU_TRACE = UTRACE_WARNING; 1002 } 1003 else if (strcmp( argv[i], "-t_verbose") == 0) { 1004 ICU_TRACE = UTRACE_VERBOSE; 1005 } 1006 else if (strcmp( argv[i], "-t_oc") == 0) { 1007 ICU_TRACE = UTRACE_OPEN_CLOSE; 1008 } 1009 else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0) 1010 { 1011 help( argv[0] ); 1012 return 0; 1013 } 1014 else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0) 1015 { 1016 i += argSkip - 1; 1017 } 1018 else 1019 { 1020 printf("* unknown option: %s\n", argv[i]); 1021 help( argv[0] ); 1022 return 0; 1023 } 1024 } 1025 if (ICU_TRACE != UTRACE_OFF) { 1026 utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData); 1027 utrace_setLevel(ICU_TRACE); 1028 } 1029 1030 return 1; /* total error count */ 1031} 1032 1033int T_CTEST_EXPORT2 1034runTestRequest(const TestNode* root, 1035 int argc, 1036 const char* const argv[]) 1037{ 1038 /** 1039 * This main will parse the l, v, h, n, and path arguments 1040 */ 1041 const TestNode* toRun; 1042 int i; 1043 int doList = FALSE; 1044 int subtreeOptionSeen = FALSE; 1045 1046 int errorCount = 0; 1047 1048 toRun = root; 1049 1050 if(ctest_xml_init(ARGV_0)) { 1051 return 1; /* couldn't fire up XML thing */ 1052 } 1053 1054 for( i=1; i<argc; i++) 1055 { 1056 if ( argv[i][0] == '/' ) 1057 { 1058 printf("Selecting subtree '%s'\n", argv[i]); 1059 1060 if ( argv[i][1] == 0 ) 1061 toRun = root; 1062 else 1063 toRun = getTest(root, argv[i]); 1064 1065 if ( toRun == NULL ) 1066 { 1067 printf("* Could not find any matching subtree\n"); 1068 return -1; 1069 } 1070 1071 ON_LINE=FALSE; /* just in case */ 1072 1073 if( doList == TRUE) 1074 showTests(toRun); 1075 else 1076 runTests(toRun); 1077 1078 ON_LINE=FALSE; /* just in case */ 1079 1080 errorCount += ERROR_COUNT; 1081 1082 subtreeOptionSeen = TRUE; 1083 } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) { 1084 subtreeOptionSeen=FALSE; 1085 } else if (strcmp( argv[i], "-l") == 0) { 1086 doList = TRUE; 1087 } 1088 /* else option already handled by initArgs */ 1089 } 1090 1091 if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */ 1092 { 1093 ON_LINE=FALSE; /* just in case */ 1094 if( doList == TRUE) 1095 showTests(toRun); 1096 else 1097 runTests(toRun); 1098 ON_LINE=FALSE; /* just in case */ 1099 1100 errorCount += ERROR_COUNT; 1101 } 1102 else 1103 { 1104 if( ( doList == FALSE ) && ( errorCount > 0 ) ) 1105 printf(" Total errors: %d\n", errorCount ); 1106 } 1107 1108 REPEAT_TESTS_INIT = 1; 1109 1110 if(ctest_xml_fini()) { 1111 errorCount++; 1112 } 1113 1114 return errorCount; /* total error count */ 1115} 1116 1117/** 1118 * Display program invocation arguments 1119 */ 1120 1121static void help ( const char *argv0 ) 1122{ 1123 printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n" 1124 " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n" 1125 " [ /path/to/test ]\n", 1126 argv0); 1127 printf(" -l To get a list of test names\n"); 1128 printf(" -e to do exhaustive testing\n"); 1129 printf(" -verbose To turn ON verbosity\n"); 1130 printf(" -v To turn ON verbosity(same as -verbose)\n"); 1131 printf(" -x file.xml Write junit format output to file.xml\n"); 1132 printf(" -h To print this message\n"); 1133 printf(" -n To turn OFF printing error messages\n"); 1134 printf(" -w Don't fail on data-loading errs, just warn. Useful if\n" 1135 " user has reduced/changed the common set of ICU data \n"); 1136 printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n"); 1137 printf(" -no_err_msg (same as -n) \n"); 1138 printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n"); 1139 printf(" The default is the maximum value of size_t. Max is optional.\n"); 1140 printf(" -r Repeat tests after calling u_cleanup \n"); 1141 printf(" [/subtest] To run a subtest \n"); 1142 printf(" eg: to run just the utility tests type: cintltest /tsutil) \n"); 1143} 1144 1145int32_t T_CTEST_EXPORT2 1146getTestOption ( int32_t testOption ) { 1147 switch (testOption) { 1148 case VERBOSITY_OPTION: 1149 return VERBOSITY; 1150 case WARN_ON_MISSING_DATA_OPTION: 1151 return WARN_ON_MISSING_DATA; 1152 case QUICK_OPTION: 1153 return QUICK; 1154 case REPEAT_TESTS_OPTION: 1155 return REPEAT_TESTS; 1156 case ERR_MSG_OPTION: 1157 return ERR_MSG; 1158 case ICU_TRACE_OPTION: 1159 return ICU_TRACE; 1160 default : 1161 return 0; 1162 } 1163} 1164 1165void T_CTEST_EXPORT2 1166setTestOption ( int32_t testOption, int32_t value) { 1167 if (value == DECREMENT_OPTION_VALUE) { 1168 value = getTestOption(testOption); 1169 --value; 1170 } 1171 switch (testOption) { 1172 case VERBOSITY_OPTION: 1173 VERBOSITY = value; 1174 break; 1175 case WARN_ON_MISSING_DATA_OPTION: 1176 WARN_ON_MISSING_DATA = value; 1177 break; 1178 case QUICK_OPTION: 1179 QUICK = value; 1180 break; 1181 case REPEAT_TESTS_OPTION: 1182 REPEAT_TESTS = value; 1183 break; 1184 case ICU_TRACE_OPTION: 1185 ICU_TRACE = value; 1186 break; 1187 default : 1188 break; 1189 } 1190} 1191 1192 1193/* 1194 * ================== JUnit support ================================ 1195 */ 1196 1197int32_t 1198T_CTEST_EXPORT2 1199ctest_xml_setFileName(const char *name) { 1200 XML_FILE_NAME=name; 1201 return 0; 1202} 1203 1204 1205int32_t 1206T_CTEST_EXPORT2 1207ctest_xml_init(const char *rootName) { 1208 if(!XML_FILE_NAME) return 0; 1209 XML_FILE = fopen(XML_FILE_NAME,"w"); 1210 if(!XML_FILE) { 1211 perror("fopen"); 1212 fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME); 1213 return 1; 1214 } 1215 while(*rootName&&!isalnum(*rootName)) { 1216 rootName++; 1217 } 1218 strcpy(XML_PREFIX,rootName); 1219 { 1220 char *p = XML_PREFIX+strlen(XML_PREFIX); 1221 for(p--;*p&&p>XML_PREFIX&&!isalnum(*p);p--) { 1222 *p=0; 1223 } 1224 } 1225 /* write prefix */ 1226 fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX); 1227 1228 return 0; 1229} 1230 1231int32_t 1232T_CTEST_EXPORT2 1233ctest_xml_fini(void) { 1234 if(!XML_FILE) return 0; 1235 1236 fprintf(XML_FILE, "</testsuite>\n"); 1237 fclose(XML_FILE); 1238 printf(" ( test results written to %s )\n", XML_FILE_NAME); 1239 XML_FILE=0; 1240 return 0; 1241} 1242 1243 1244int32_t 1245T_CTEST_EXPORT2 1246ctest_xml_testcase(const char *classname, const char *name, const char *time, const char *failMsg) { 1247 if(!XML_FILE) return 0; 1248 1249 fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, time); 1250 if(failMsg) { 1251 fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg); 1252 } else { 1253 fprintf(XML_FILE, "/>\n"); 1254 } 1255 1256 return 0; 1257} 1258 1259 1260