2 ********************************************************************************
4 * Copyright (C) 1996-2008, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 ********************************************************************************
15 #include "unicode/utrace.h"
16 #include "unicode/uclean.h"
20 3/20/1999 srl - strncpy called w/o setting nulls at the end
23 #define MAXTESTNAME 128
25 #define MAX_TEST_LOG 4096
30 struct TestNode
* sibling
;
31 struct TestNode
* child
;
32 char name
[1]; /* This is dynamically allocated off the end with malloc. */
36 static const struct TestNode
* currentTest
;
38 typedef enum { RUNTESTS
, SHOWTESTS
} TestMode
;
39 #define TEST_SEPARATOR '/'
45 #include "unicode/ctest.h"
47 static char ERROR_LOG
[MAX_TEST_LOG
][MAXTESTNAME
];
49 /* Local prototypes */
50 static TestNode
* addTestNode( TestNode
*root
, const char *name
);
52 static TestNode
*createTestNode(const char* name
, int32_t nameLen
);
54 static int strncmp_nullcheck( const char* s1
,
58 static void getNextLevel( const char* name
,
60 const char** nextName
);
62 static void iterateTestsWithLevel( const TestNode
*root
, int len
,
63 const TestNode
** list
,
66 static void help ( const char *argv0
);
69 * Do the work of logging an error. Doesn't increase the error count.
71 * @prefix optional prefix prepended to message, or NULL.
72 * @param pattern printf style pattern
73 * @param ap vprintf style arg list
75 static void vlog_err(const char *prefix
, const char *pattern
, va_list ap
);
76 static void vlog_verbose(const char *prefix
, const char *pattern
, va_list ap
);
78 /* If we need to make the framework multi-thread safe
79 we need to pass around the following vars
81 static int ERRONEOUS_FUNCTION_COUNT
= 0;
82 static int ERROR_COUNT
= 0; /* Count of errors from all tests. */
83 static int DATA_ERROR_COUNT
= 0; /* count of data related errors or warnings */
84 static int INDENT_LEVEL
= 0;
85 int REPEAT_TESTS_INIT
= 0; /* Was REPEAT_TESTS initialized? */
86 int REPEAT_TESTS
= 1; /* Number of times to run the test */
87 int VERBOSITY
= 0; /* be No-verbose by default */
88 int ERR_MSG
=1; /* error messages will be displayed by default*/
89 int QUICK
= 1; /* Skip some of the slower tests? */
90 int WARN_ON_MISSING_DATA
= 0; /* Reduce data errs to warnings? */
91 UTraceLevel ICU_TRACE
= UTRACE_OFF
; /* ICU tracing level */
92 size_t MINIMUM_MEMORY_SIZE_FAILURE
= (size_t)-1; /* Minimum library memory allocation window that will fail. */
93 size_t MAXIMUM_MEMORY_SIZE_FAILURE
= (size_t)-1; /* Maximum library memory allocation window that will fail. */
94 int32_t ALLOCATION_COUNT
= 0;
95 /*-------------------------------------------*/
97 /* strncmp that also makes sure there's a \0 at s2[0] */
98 static int strncmp_nullcheck( const char* s1
,
102 if (((int)strlen(s2
) >= n
) && s2
[n
] != 0) {
103 return 3; /* null check fails */
106 return strncmp ( s1
, s2
, n
);
110 static void getNextLevel( const char* name
,
112 const char** nextName
)
114 /* Get the next component of the name */
115 *nextName
= strchr(name
, TEST_SEPARATOR
);
120 *nameLen
= (int)((*nextName
) - name
);
121 (*nextName
)++; /* skip '/' */
122 strncpy(n
, name
, *nameLen
);
124 /*printf("->%s-< [%d] -> [%s]\n", name, *nameLen, *nextName);*/
127 *nameLen
= (int)strlen(name
);
131 static TestNode
*createTestNode(const char* name
, int32_t nameLen
)
135 newNode
= (TestNode
*)malloc(sizeof(TestNode
) + (nameLen
+ 1));
137 newNode
->test
= NULL
;
138 newNode
->sibling
= NULL
;
139 newNode
->child
= NULL
;
141 strncpy( newNode
->name
, name
, nameLen
);
142 newNode
->name
[nameLen
] = 0;
148 cleanUpTestTree(TestNode
*tn
)
150 if(tn
->child
!= NULL
) {
151 cleanUpTestTree(tn
->child
);
153 if(tn
->sibling
!= NULL
) {
154 cleanUpTestTree(tn
->sibling
);
162 addTest(TestNode
** root
,
163 TestFunctionPtr test
,
168 /*if this is the first Test created*/
170 *root
= createTestNode("", 0);
172 newNode
= addTestNode( *root
, name
);
173 assert(newNode
!= 0 );
174 /* printf("addTest: nreName = %s\n", newNode->name );*/
176 newNode
->test
= test
;
179 /* non recursive insert function */
180 static TestNode
*addTestNode ( TestNode
*root
, const char *name
)
182 const char* nextName
;
183 TestNode
*nextNode
, *curNode
;
184 int nameLen
; /* length of current 'name' */
186 /* remove leading slash */
187 if ( *name
== TEST_SEPARATOR
)
194 /* Start with the next child */
195 nextNode
= curNode
->child
;
197 getNextLevel ( name
, &nameLen
, &nextName
);
199 /* printf("* %s\n", name );*/
201 /* if nextNode is already null, then curNode has no children
203 if( nextNode
== NULL
)
205 /* Add all children of the node */
208 /* Get the next component of the name */
209 getNextLevel(name
, &nameLen
, &nextName
);
211 /* update curName to have the next name segment */
212 curNode
->child
= createTestNode(name
, nameLen
);
213 /* printf("*** added %s\n", curNode->child->name );*/
214 curNode
= curNode
->child
;
217 while( name
!= NULL
);
222 /* Search across for the name */
223 while (strncmp_nullcheck ( name
, nextNode
->name
, nameLen
) != 0 )
226 nextNode
= nextNode
-> sibling
;
228 if ( nextNode
== NULL
)
230 /* Did not find 'name' on this level. */
231 nextNode
= createTestNode(name
, nameLen
);
232 curNode
->sibling
= nextNode
;
237 /* nextNode matches 'name' */
239 if (nextName
== NULL
) /* end of the line */
244 /* Loop again with the next item */
250 static void iterateTestsWithLevel ( const TestNode
* root
,
252 const TestNode
** list
,
258 char pathToFunction
[MAXTESTNAME
] = "";
259 char separatorString
[2] = { TEST_SEPARATOR
, '\0'};
266 for ( i
=0;i
<(len
-1);i
++ )
268 strcat(pathToFunction
, list
[i
]->name
);
269 strcat(pathToFunction
, separatorString
);
272 strcat(pathToFunction
, list
[i
]->name
);
275 if ( (mode
== RUNTESTS
) && (root
->test
!= NULL
))
277 int myERROR_COUNT
= ERROR_COUNT
;
281 if (myERROR_COUNT
!= ERROR_COUNT
)
284 log_info("---[%d ERRORS] ", ERROR_COUNT
- myERROR_COUNT
);
285 strcpy(ERROR_LOG
[ERRONEOUS_FUNCTION_COUNT
++], pathToFunction
);
288 log_info("---[OK] ");
292 /* we want these messages to be at 0 indent. so just push the indent level breifly. */
293 saveIndent
= INDENT_LEVEL
;
295 log_info("%s%s%c\n", (list
[i
]->test
||mode
==SHOWTESTS
)?"---":"",pathToFunction
, list
[i
]->test
?' ':TEST_SEPARATOR
);
296 INDENT_LEVEL
= saveIndent
;
298 iterateTestsWithLevel ( root
->child
, len
, list
, mode
);
302 if ( len
!= 0 ) /* DO NOT iterate over siblings of the root. */
303 iterateTestsWithLevel ( root
->sibling
, len
, list
, mode
);
309 showTests ( const TestNode
*root
)
311 /* make up one for them */
312 const TestNode
*aList
[MAXTESTS
];
315 log_err("TEST CAN'T BE FOUND!");
317 iterateTestsWithLevel ( root
, 0, aList
, SHOWTESTS
);
322 runTests ( const TestNode
*root
)
325 const TestNode
*aList
[MAXTESTS
];
326 /* make up one for them */
330 log_err("TEST CAN'T BE FOUND!\n");
332 ERRONEOUS_FUNCTION_COUNT
= ERROR_COUNT
= 0;
333 iterateTestsWithLevel ( root
, 0, aList
, RUNTESTS
);
335 /*print out result summary*/
339 log_info("\nSUMMARY:\n******* [Total error count:\t%d]\n Errors in\n", ERROR_COUNT
);
340 for (i
=0;i
< ERRONEOUS_FUNCTION_COUNT
; i
++)
341 log_info("[%s]\n",ERROR_LOG
[i
]);
345 log_info("\n[All tests passed successfully...]\n");
348 if(DATA_ERROR_COUNT
) {
349 if(WARN_ON_MISSING_DATA
==0) {
350 log_info("\t*Note* some errors are data-loading related. If the data used is not the \n"
351 "\tstock ICU data (i.e some have been added or removed), consider using\n"
352 "\tthe '-w' option to turn these errors into warnings.\n");
354 log_info("\t*WARNING* some data-loading errors were ignored by the -w option.\n");
359 const char* T_CTEST_EXPORT2
362 if(currentTest
!= NULL
) {
363 return currentTest
->name
;
369 const TestNode
* T_CTEST_EXPORT2
370 getTest(const TestNode
* root
, const char* name
)
372 const char* nextName
;
374 const TestNode
* curNode
;
375 int nameLen
; /* length of current 'name' */
378 log_err("TEST CAN'T BE FOUND!\n");
381 /* remove leading slash */
382 if ( *name
== TEST_SEPARATOR
)
389 /* Start with the next child */
390 nextNode
= curNode
->child
;
392 getNextLevel ( name
, &nameLen
, &nextName
);
394 /* printf("* %s\n", name );*/
396 /* if nextNode is already null, then curNode has no children
398 if( nextNode
== NULL
)
403 /* Search across for the name */
404 while (strncmp_nullcheck ( name
, nextNode
->name
, nameLen
) != 0 )
407 nextNode
= nextNode
-> sibling
;
409 if ( nextNode
== NULL
)
411 /* Did not find 'name' on this level. */
416 /* nextNode matches 'name' */
418 if (nextName
== NULL
) /* end of the line */
423 /* Loop again with the next item */
429 static void vlog_err(const char *prefix
, const char *pattern
, va_list ap
)
431 if( ERR_MSG
== FALSE
){
434 fprintf(stderr
, "%-*s", INDENT_LEVEL
," " );
436 fputs(prefix
, stderr
);
438 vfprintf(stderr
, pattern
, ap
);
444 vlog_info(const char *prefix
, const char *pattern
, va_list ap
)
446 fprintf(stdout
, "%-*s", INDENT_LEVEL
," " );
448 fputs(prefix
, stdout
);
450 vfprintf(stdout
, pattern
, ap
);
455 static void vlog_verbose(const char *prefix
, const char *pattern
, va_list ap
)
457 if ( VERBOSITY
== FALSE
)
460 fprintf(stdout
, "%-*s", INDENT_LEVEL
," " );
462 fputs(prefix
, stdout
);
464 vfprintf(stdout
, pattern
, ap
);
470 log_err(const char* pattern
, ...)
473 if(strchr(pattern
, '\n') != NULL
) {
475 * Count errors only if there is a line feed in the pattern
476 * so that we do not exaggerate our error count.
480 va_start(ap
, pattern
);
481 vlog_err(NULL
, pattern
, ap
);
485 log_info(const char* pattern
, ...)
489 va_start(ap
, pattern
);
490 vlog_info(NULL
, pattern
, ap
);
494 log_verbose(const char* pattern
, ...)
498 va_start(ap
, pattern
);
499 vlog_verbose(NULL
, pattern
, ap
);
504 log_data_err(const char* pattern
, ...)
507 va_start(ap
, pattern
);
509 ++DATA_ERROR_COUNT
; /* for informational message at the end */
511 if(WARN_ON_MISSING_DATA
== 0) {
513 if(strchr(pattern
, '\n') != NULL
) {
516 vlog_err(NULL
, pattern
, ap
); /* no need for prefix in default case */
518 vlog_info("[Data] ", pattern
, ap
);
526 static int traceFnNestingDepth
= 0;
528 static void U_CALLCONV
TraceEntry(const void *context
, int32_t fnNumber
) {
530 utrace_format(buf
, sizeof(buf
), traceFnNestingDepth
*3, "%s() enter.\n", utrace_functionName(fnNumber
)); buf
[sizeof(buf
)-1]=0;
532 traceFnNestingDepth
++;
535 static void U_CALLCONV
TraceExit(const void *context
, int32_t fnNumber
, const char *fmt
, va_list args
) { char buf
[500];
537 if (traceFnNestingDepth
>0) {
538 traceFnNestingDepth
--;
540 utrace_format(buf
, sizeof(buf
), traceFnNestingDepth
*3, "%s() ", utrace_functionName(fnNumber
)); buf
[sizeof(buf
)-1]=0;
542 utrace_vformat(buf
, sizeof(buf
), traceFnNestingDepth
*3, fmt
, args
);
543 buf
[sizeof(buf
)-1]=0;
548 static void U_CALLCONV
TraceData(const void *context
, int32_t fnNumber
,
549 int32_t level
, const char *fmt
, va_list args
) {
551 utrace_vformat(buf
, sizeof(buf
), traceFnNestingDepth
*3, fmt
, args
);
552 buf
[sizeof(buf
)-1]=0;
557 static void *U_CALLCONV
ctest_libMalloc(const void *context
, size_t size
) {
559 printf("Allocated %ld\n", (long)size);
561 if (MINIMUM_MEMORY_SIZE_FAILURE
<= size
&& size
<= MAXIMUM_MEMORY_SIZE_FAILURE
) {
564 umtx_atomic_inc(&ALLOCATION_COUNT
);
567 static void *U_CALLCONV
ctest_libRealloc(const void *context
, void *mem
, size_t size
) {
569 printf("Reallocated %ld\n", (long)size);
571 if (MINIMUM_MEMORY_SIZE_FAILURE
<= size
&& size
<= MAXIMUM_MEMORY_SIZE_FAILURE
) {
572 /*free(mem);*/ /* Realloc doesn't free on failure. */
576 /* New allocation. */
577 umtx_atomic_inc(&ALLOCATION_COUNT
);
579 return realloc(mem
, size
);
581 static void U_CALLCONV
ctest_libFree(const void *context
, void *mem
) {
583 umtx_atomic_dec(&ALLOCATION_COUNT
);
589 initArgs( int argc
, const char* const argv
[], ArgHandlerPtr argHandler
, void *context
)
598 for( i
=1; i
<argc
; i
++)
600 if ( argv
[i
][0] == '/' )
602 /* We don't run the tests here. */
605 else if ((strcmp( argv
[i
], "-a") == 0) || (strcmp(argv
[i
],"-all") == 0))
607 /* We don't run the tests here. */
610 else if (strcmp( argv
[i
], "-v" )==0 || strcmp( argv
[i
], "-verbose")==0)
614 else if (strcmp( argv
[i
], "-l" )==0 )
618 else if (strcmp( argv
[i
], "-e1") == 0)
622 else if (strcmp( argv
[i
], "-e") ==0)
626 else if (strcmp( argv
[i
], "-w") ==0)
628 WARN_ON_MISSING_DATA
= TRUE
;
630 else if (strcmp( argv
[i
], "-m") ==0)
632 UErrorCode errorCode
= U_ZERO_ERROR
;
636 MINIMUM_MEMORY_SIZE_FAILURE
= (size_t)strtol(argv
[i
], &endPtr
, 10);
637 if (endPtr
== argv
[i
]) {
638 printf("Can't parse %s\n", argv
[i
]);
642 if (*endPtr
== '-') {
643 char *maxPtr
= endPtr
+1;
645 MAXIMUM_MEMORY_SIZE_FAILURE
= (size_t)strtol(maxPtr
, &endPtr
, 10);
646 if (endPtr
== argv
[i
]) {
647 printf("Can't parse %s\n", argv
[i
]);
653 /* Use the default value */
654 u_setMemoryFunctions(NULL
, ctest_libMalloc
, ctest_libRealloc
, ctest_libFree
, &errorCode
);
655 if (U_FAILURE(errorCode
)) {
656 printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode
));
660 else if(strcmp( argv
[i
], "-n") == 0 || strcmp( argv
[i
], "-no_err_msg") == 0)
664 else if (strcmp( argv
[i
], "-r") == 0)
666 if (!REPEAT_TESTS_INIT
) {
670 else if (strcmp( argv
[i
], "-t_info") == 0) {
671 ICU_TRACE
= UTRACE_INFO
;
673 else if (strcmp( argv
[i
], "-t_error") == 0) {
674 ICU_TRACE
= UTRACE_ERROR
;
676 else if (strcmp( argv
[i
], "-t_warn") == 0) {
677 ICU_TRACE
= UTRACE_WARNING
;
679 else if (strcmp( argv
[i
], "-t_verbose") == 0) {
680 ICU_TRACE
= UTRACE_VERBOSE
;
682 else if (strcmp( argv
[i
], "-t_oc") == 0) {
683 ICU_TRACE
= UTRACE_OPEN_CLOSE
;
685 else if (strcmp( argv
[i
], "-h" )==0 || strcmp( argv
[i
], "--help" )==0)
690 else if (argHandler
!= NULL
&& (argSkip
= argHandler(i
, argc
, argv
, context
)) > 0)
696 printf("* unknown option: %s\n", argv
[i
]);
701 if (ICU_TRACE
!= UTRACE_OFF
) {
702 utrace_setFunctions(NULL
, TraceEntry
, TraceExit
, TraceData
);
703 utrace_setLevel(ICU_TRACE
);
706 return 1; /* total error count */
710 runTestRequest(const TestNode
* root
,
712 const char* const argv
[])
715 * This main will parse the l, v, h, n, and path arguments
717 const TestNode
* toRun
;
720 int subtreeOptionSeen
= FALSE
;
726 for( i
=1; i
<argc
; i
++)
728 if ( argv
[i
][0] == '/' )
730 printf("Selecting subtree '%s'\n", argv
[i
]);
732 if ( argv
[i
][1] == 0 )
735 toRun
= getTest(root
, argv
[i
]);
739 printf("* Could not find any matching subtree\n");
748 errorCount
+= ERROR_COUNT
;
750 subtreeOptionSeen
= TRUE
;
751 } else if ((strcmp( argv
[i
], "-a") == 0) || (strcmp(argv
[i
],"-all") == 0)) {
752 subtreeOptionSeen
=FALSE
;
753 } else if (strcmp( argv
[i
], "-l") == 0) {
756 /* else option already handled by initArgs */
759 if( subtreeOptionSeen
== FALSE
) /* no other subtree given, run the default */
766 errorCount
+= ERROR_COUNT
;
770 if( ( doList
== FALSE
) && ( errorCount
> 0 ) )
771 printf(" Total errors: %d\n", errorCount
);
774 REPEAT_TESTS_INIT
= 1;
776 return errorCount
; /* total error count */
780 * Display program invocation arguments
783 static void help ( const char *argv0
)
785 printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
786 " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
787 " [ /path/to/test ]\n",
789 printf(" -l To get a list of test names\n");
790 printf(" -e to do exhaustive testing\n");
791 printf(" -verbose To turn ON verbosity\n");
792 printf(" -v To turn ON verbosity(same as -verbose)\n");
793 printf(" -h To print this message\n");
794 printf(" -n To turn OFF printing error messages\n");
795 printf(" -w Don't fail on data-loading errs, just warn. Useful if\n"
796 " user has reduced/changed the common set of ICU data \n");
797 printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n");
798 printf(" -no_err_msg (same as -n) \n");
799 printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n");
800 printf(" The default is the maximum value of size_t. Max is optional.\n");
801 printf(" -r Repeat tests after calling u_cleanup \n");
802 printf(" [/subtest] To run a subtest \n");
803 printf(" eg: to run just the utility tests type: cintltest /tsutil) \n");