]>
Commit | Line | Data |
---|---|---|
b75a7d8f | 1 | /* |
73c04bcf | 2 | ******************************************************************************** |
b75a7d8f | 3 | * |
46f4442e | 4 | * Copyright (C) 1996-2008, International Business Machines |
b75a7d8f A |
5 | * Corporation and others. All Rights Reserved. |
6 | * | |
73c04bcf | 7 | ******************************************************************************** |
b75a7d8f A |
8 | */ |
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | #include <assert.h> | |
13 | #include <stdarg.h> | |
14 | ||
374ca955 | 15 | #include "unicode/utrace.h" |
46f4442e A |
16 | #include "unicode/uclean.h" |
17 | #include "umutex.h" | |
374ca955 | 18 | |
b75a7d8f A |
19 | /* NOTES: |
20 | 3/20/1999 srl - strncpy called w/o setting nulls at the end | |
21 | */ | |
22 | ||
23 | #define MAXTESTNAME 128 | |
24 | #define MAXTESTS 512 | |
25 | #define MAX_TEST_LOG 4096 | |
26 | ||
27 | struct TestNode | |
28 | { | |
b75a7d8f A |
29 | void (*test)(void); |
30 | struct TestNode* sibling; | |
31 | struct TestNode* child; | |
46f4442e | 32 | char name[1]; /* This is dynamically allocated off the end with malloc. */ |
b75a7d8f A |
33 | }; |
34 | ||
35 | ||
36 | static const struct TestNode* currentTest; | |
37 | ||
38 | typedef enum { RUNTESTS, SHOWTESTS } TestMode; | |
39 | #define TEST_SEPARATOR '/' | |
40 | ||
41 | #ifndef C_TEST_IMPL | |
42 | #define C_TEST_IMPL | |
43 | #endif | |
44 | ||
45 | #include "unicode/ctest.h" | |
46 | ||
47 | static char ERROR_LOG[MAX_TEST_LOG][MAXTESTNAME]; | |
48 | ||
49 | /* Local prototypes */ | |
50 | static TestNode* addTestNode( TestNode *root, const char *name ); | |
51 | ||
46f4442e | 52 | static TestNode *createTestNode(const char* name, int32_t nameLen); |
b75a7d8f A |
53 | |
54 | static int strncmp_nullcheck( const char* s1, | |
55 | const char* s2, | |
56 | int n ); | |
57 | ||
58 | static void getNextLevel( const char* name, | |
59 | int* nameLen, | |
60 | const char** nextName ); | |
61 | ||
62 | static void iterateTestsWithLevel( const TestNode *root, int len, | |
63 | const TestNode** list, | |
64 | TestMode mode); | |
65 | ||
66 | static void help ( const char *argv0 ); | |
67 | ||
68 | /** | |
69 | * Do the work of logging an error. Doesn't increase the error count. | |
70 | * | |
71 | * @prefix optional prefix prepended to message, or NULL. | |
72 | * @param pattern printf style pattern | |
73 | * @param ap vprintf style arg list | |
74 | */ | |
75 | static void vlog_err(const char *prefix, const char *pattern, va_list ap); | |
b75a7d8f A |
76 | static void vlog_verbose(const char *prefix, const char *pattern, va_list ap); |
77 | ||
78 | /* If we need to make the framework multi-thread safe | |
79 | we need to pass around the following vars | |
80 | */ | |
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? */ | |
374ca955 | 91 | UTraceLevel ICU_TRACE = UTRACE_OFF; /* ICU tracing level */ |
46f4442e A |
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; | |
b75a7d8f A |
95 | /*-------------------------------------------*/ |
96 | ||
97 | /* strncmp that also makes sure there's a \0 at s2[0] */ | |
98 | static int strncmp_nullcheck( const char* s1, | |
99 | const char* s2, | |
100 | int n ) | |
101 | { | |
102 | if (((int)strlen(s2) >= n) && s2[n] != 0) { | |
103 | return 3; /* null check fails */ | |
104 | } | |
105 | else { | |
106 | return strncmp ( s1, s2, n ); | |
107 | } | |
108 | } | |
109 | ||
110 | static void getNextLevel( const char* name, | |
111 | int* nameLen, | |
112 | const char** nextName ) | |
113 | { | |
114 | /* Get the next component of the name */ | |
115 | *nextName = strchr(name, TEST_SEPARATOR); | |
116 | ||
117 | if( *nextName != 0 ) | |
118 | { | |
119 | char n[255]; | |
120 | *nameLen = (int)((*nextName) - name); | |
121 | (*nextName)++; /* skip '/' */ | |
122 | strncpy(n, name, *nameLen); | |
123 | n[*nameLen] = 0; | |
124 | /*printf("->%s-< [%d] -> [%s]\n", name, *nameLen, *nextName);*/ | |
125 | } | |
126 | else { | |
127 | *nameLen = (int)strlen(name); | |
128 | } | |
129 | } | |
130 | ||
46f4442e | 131 | static TestNode *createTestNode(const char* name, int32_t nameLen) |
b75a7d8f A |
132 | { |
133 | TestNode *newNode; | |
134 | ||
46f4442e | 135 | newNode = (TestNode*)malloc(sizeof(TestNode) + (nameLen + 1)); |
b75a7d8f | 136 | |
b75a7d8f A |
137 | newNode->test = NULL; |
138 | newNode->sibling = NULL; | |
139 | newNode->child = NULL; | |
140 | ||
46f4442e A |
141 | strncpy( newNode->name, name, nameLen ); |
142 | newNode->name[nameLen] = 0; | |
143 | ||
b75a7d8f A |
144 | return newNode; |
145 | } | |
146 | ||
374ca955 A |
147 | void T_CTEST_EXPORT2 |
148 | cleanUpTestTree(TestNode *tn) | |
149 | { | |
b75a7d8f A |
150 | if(tn->child != NULL) { |
151 | cleanUpTestTree(tn->child); | |
152 | } | |
153 | if(tn->sibling != NULL) { | |
154 | cleanUpTestTree(tn->sibling); | |
155 | } | |
156 | ||
157 | free(tn); | |
158 | } | |
159 | ||
160 | ||
374ca955 A |
161 | void T_CTEST_EXPORT2 |
162 | addTest(TestNode** root, | |
163 | TestFunctionPtr test, | |
164 | const char* name ) | |
b75a7d8f A |
165 | { |
166 | TestNode *newNode; | |
167 | ||
168 | /*if this is the first Test created*/ | |
169 | if (*root == NULL) | |
46f4442e | 170 | *root = createTestNode("", 0); |
b75a7d8f A |
171 | |
172 | newNode = addTestNode( *root, name ); | |
173 | assert(newNode != 0 ); | |
174 | /* printf("addTest: nreName = %s\n", newNode->name );*/ | |
175 | ||
176 | newNode->test = test; | |
177 | } | |
178 | ||
179 | /* non recursive insert function */ | |
180 | static TestNode *addTestNode ( TestNode *root, const char *name ) | |
181 | { | |
182 | const char* nextName; | |
183 | TestNode *nextNode, *curNode; | |
184 | int nameLen; /* length of current 'name' */ | |
185 | ||
186 | /* remove leading slash */ | |
187 | if ( *name == TEST_SEPARATOR ) | |
188 | name++; | |
189 | ||
190 | curNode = root; | |
191 | ||
192 | for(;;) | |
193 | { | |
194 | /* Start with the next child */ | |
195 | nextNode = curNode->child; | |
196 | ||
197 | getNextLevel ( name, &nameLen, &nextName ); | |
198 | ||
199 | /* printf("* %s\n", name );*/ | |
200 | ||
201 | /* if nextNode is already null, then curNode has no children | |
202 | -- add them */ | |
203 | if( nextNode == NULL ) | |
204 | { | |
205 | /* Add all children of the node */ | |
206 | do | |
207 | { | |
b75a7d8f | 208 | /* Get the next component of the name */ |
46f4442e | 209 | getNextLevel(name, &nameLen, &nextName); |
b75a7d8f A |
210 | |
211 | /* update curName to have the next name segment */ | |
46f4442e | 212 | curNode->child = createTestNode(name, nameLen); |
b75a7d8f A |
213 | /* printf("*** added %s\n", curNode->child->name );*/ |
214 | curNode = curNode->child; | |
215 | name = nextName; | |
216 | } | |
217 | while( name != NULL ); | |
218 | ||
219 | return curNode; | |
220 | } | |
221 | ||
222 | /* Search across for the name */ | |
223 | while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 ) | |
224 | { | |
225 | curNode = nextNode; | |
226 | nextNode = nextNode -> sibling; | |
227 | ||
228 | if ( nextNode == NULL ) | |
229 | { | |
230 | /* Did not find 'name' on this level. */ | |
46f4442e | 231 | nextNode = createTestNode(name, nameLen); |
b75a7d8f A |
232 | curNode->sibling = nextNode; |
233 | break; | |
234 | } | |
235 | } | |
236 | ||
237 | /* nextNode matches 'name' */ | |
238 | ||
239 | if (nextName == NULL) /* end of the line */ | |
240 | { | |
241 | return nextNode; | |
242 | } | |
243 | ||
244 | /* Loop again with the next item */ | |
245 | name = nextName; | |
246 | curNode = nextNode; | |
247 | } | |
248 | } | |
249 | ||
250 | static void iterateTestsWithLevel ( const TestNode* root, | |
251 | int len, | |
252 | const TestNode** list, | |
253 | TestMode mode) | |
254 | { | |
255 | int i; | |
256 | int saveIndent; | |
257 | ||
258 | char pathToFunction[MAXTESTNAME] = ""; | |
259 | char separatorString[2] = { TEST_SEPARATOR, '\0'}; | |
260 | ||
261 | if ( root == NULL ) | |
262 | return; | |
263 | ||
264 | list[len++] = root; | |
265 | ||
266 | for ( i=0;i<(len-1);i++ ) | |
267 | { | |
268 | strcat(pathToFunction, list[i]->name); | |
269 | strcat(pathToFunction, separatorString); | |
270 | } | |
271 | ||
272 | strcat(pathToFunction, list[i]->name); | |
273 | ||
274 | INDENT_LEVEL = len; | |
275 | if ( (mode == RUNTESTS) && (root->test != NULL)) | |
276 | { | |
277 | int myERROR_COUNT = ERROR_COUNT; | |
278 | currentTest = root; | |
279 | root->test(); | |
280 | currentTest = NULL; | |
281 | if (myERROR_COUNT != ERROR_COUNT) | |
282 | { | |
283 | ||
284 | log_info("---[%d ERRORS] ", ERROR_COUNT - myERROR_COUNT); | |
285 | strcpy(ERROR_LOG[ERRONEOUS_FUNCTION_COUNT++], pathToFunction); | |
286 | } | |
287 | else | |
288 | log_info("---[OK] "); | |
289 | } | |
290 | ||
291 | ||
292 | /* we want these messages to be at 0 indent. so just push the indent level breifly. */ | |
293 | saveIndent = INDENT_LEVEL; | |
294 | INDENT_LEVEL = 0; | |
295 | log_info("%s%s%c\n", (list[i]->test||mode==SHOWTESTS)?"---":"",pathToFunction, list[i]->test?' ':TEST_SEPARATOR ); | |
296 | INDENT_LEVEL = saveIndent; | |
297 | ||
298 | iterateTestsWithLevel ( root->child, len, list, mode ); | |
299 | ||
300 | len--; | |
301 | ||
302 | if ( len != 0 ) /* DO NOT iterate over siblings of the root. */ | |
303 | iterateTestsWithLevel ( root->sibling, len, list, mode ); | |
304 | } | |
305 | ||
306 | ||
307 | ||
374ca955 A |
308 | void T_CTEST_EXPORT2 |
309 | showTests ( const TestNode *root ) | |
b75a7d8f A |
310 | { |
311 | /* make up one for them */ | |
312 | const TestNode *aList[MAXTESTS]; | |
313 | ||
314 | if (root == NULL) | |
315 | log_err("TEST CAN'T BE FOUND!"); | |
316 | ||
317 | iterateTestsWithLevel ( root, 0, aList, SHOWTESTS ); | |
318 | ||
319 | } | |
320 | ||
374ca955 A |
321 | void T_CTEST_EXPORT2 |
322 | runTests ( const TestNode *root ) | |
b75a7d8f A |
323 | { |
324 | int i; | |
325 | const TestNode *aList[MAXTESTS]; | |
326 | /* make up one for them */ | |
327 | ||
328 | ||
329 | if (root == NULL) | |
330 | log_err("TEST CAN'T BE FOUND!\n"); | |
331 | ||
332 | ERRONEOUS_FUNCTION_COUNT = ERROR_COUNT = 0; | |
333 | iterateTestsWithLevel ( root, 0, aList, RUNTESTS ); | |
334 | ||
335 | /*print out result summary*/ | |
336 | ||
337 | if (ERROR_COUNT) | |
338 | { | |
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]); | |
342 | } | |
343 | else | |
344 | { | |
345 | log_info("\n[All tests passed successfully...]\n"); | |
346 | } | |
347 | ||
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"); | |
353 | } else { | |
354 | log_info("\t*WARNING* some data-loading errors were ignored by the -w option.\n"); | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
374ca955 A |
359 | const char* T_CTEST_EXPORT2 |
360 | getTestName(void) | |
b75a7d8f A |
361 | { |
362 | if(currentTest != NULL) { | |
363 | return currentTest->name; | |
364 | } else { | |
365 | return NULL; | |
366 | } | |
367 | } | |
368 | ||
374ca955 A |
369 | const TestNode* T_CTEST_EXPORT2 |
370 | getTest(const TestNode* root, const char* name) | |
b75a7d8f A |
371 | { |
372 | const char* nextName; | |
373 | TestNode *nextNode; | |
374 | const TestNode* curNode; | |
375 | int nameLen; /* length of current 'name' */ | |
376 | ||
73c04bcf | 377 | if (root == NULL) { |
b75a7d8f | 378 | log_err("TEST CAN'T BE FOUND!\n"); |
73c04bcf A |
379 | return NULL; |
380 | } | |
b75a7d8f A |
381 | /* remove leading slash */ |
382 | if ( *name == TEST_SEPARATOR ) | |
383 | name++; | |
384 | ||
385 | curNode = root; | |
386 | ||
387 | for(;;) | |
388 | { | |
389 | /* Start with the next child */ | |
390 | nextNode = curNode->child; | |
391 | ||
392 | getNextLevel ( name, &nameLen, &nextName ); | |
393 | ||
394 | /* printf("* %s\n", name );*/ | |
395 | ||
396 | /* if nextNode is already null, then curNode has no children | |
397 | -- add them */ | |
398 | if( nextNode == NULL ) | |
399 | { | |
400 | return NULL; | |
401 | } | |
402 | ||
403 | /* Search across for the name */ | |
404 | while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 ) | |
405 | { | |
406 | curNode = nextNode; | |
407 | nextNode = nextNode -> sibling; | |
408 | ||
409 | if ( nextNode == NULL ) | |
410 | { | |
411 | /* Did not find 'name' on this level. */ | |
412 | return NULL; | |
413 | } | |
414 | } | |
415 | ||
416 | /* nextNode matches 'name' */ | |
417 | ||
418 | if (nextName == NULL) /* end of the line */ | |
419 | { | |
420 | return nextNode; | |
421 | } | |
422 | ||
423 | /* Loop again with the next item */ | |
424 | name = nextName; | |
425 | curNode = nextNode; | |
426 | } | |
427 | } | |
428 | ||
429 | static void vlog_err(const char *prefix, const char *pattern, va_list ap) | |
430 | { | |
431 | if( ERR_MSG == FALSE){ | |
432 | return; | |
433 | } | |
434 | fprintf(stderr, "%-*s", INDENT_LEVEL," " ); | |
435 | if(prefix) { | |
73c04bcf | 436 | fputs(prefix, stderr); |
b75a7d8f A |
437 | } |
438 | vfprintf(stderr, pattern, ap); | |
439 | fflush(stderr); | |
440 | va_end(ap); | |
441 | } | |
442 | ||
374ca955 A |
443 | void T_CTEST_EXPORT2 |
444 | vlog_info(const char *prefix, const char *pattern, va_list ap) | |
b75a7d8f A |
445 | { |
446 | fprintf(stdout, "%-*s", INDENT_LEVEL," " ); | |
447 | if(prefix) { | |
73c04bcf | 448 | fputs(prefix, stdout); |
b75a7d8f A |
449 | } |
450 | vfprintf(stdout, pattern, ap); | |
451 | fflush(stdout); | |
452 | va_end(ap); | |
453 | } | |
454 | ||
455 | static void vlog_verbose(const char *prefix, const char *pattern, va_list ap) | |
456 | { | |
457 | if ( VERBOSITY == FALSE ) | |
458 | return; | |
459 | ||
460 | fprintf(stdout, "%-*s", INDENT_LEVEL," " ); | |
461 | if(prefix) { | |
73c04bcf | 462 | fputs(prefix, stdout); |
b75a7d8f A |
463 | } |
464 | vfprintf(stdout, pattern, ap); | |
465 | fflush(stdout); | |
466 | va_end(ap); | |
467 | } | |
468 | ||
374ca955 A |
469 | void T_CTEST_EXPORT2 |
470 | log_err(const char* pattern, ...) | |
b75a7d8f A |
471 | { |
472 | va_list ap; | |
473 | if(strchr(pattern, '\n') != NULL) { | |
474 | /* | |
475 | * Count errors only if there is a line feed in the pattern | |
476 | * so that we do not exaggerate our error count. | |
477 | */ | |
478 | ++ERROR_COUNT; | |
479 | } | |
480 | va_start(ap, pattern); | |
481 | vlog_err(NULL, pattern, ap); | |
482 | } | |
483 | ||
374ca955 A |
484 | void T_CTEST_EXPORT2 |
485 | log_info(const char* pattern, ...) | |
b75a7d8f A |
486 | { |
487 | va_list ap; | |
488 | ||
489 | va_start(ap, pattern); | |
490 | vlog_info(NULL, pattern, ap); | |
491 | } | |
492 | ||
374ca955 A |
493 | void T_CTEST_EXPORT2 |
494 | log_verbose(const char* pattern, ...) | |
b75a7d8f A |
495 | { |
496 | va_list ap; | |
497 | ||
498 | va_start(ap, pattern); | |
499 | vlog_verbose(NULL, pattern, ap); | |
500 | } | |
501 | ||
502 | ||
374ca955 A |
503 | void T_CTEST_EXPORT2 |
504 | log_data_err(const char* pattern, ...) | |
b75a7d8f | 505 | { |
46f4442e A |
506 | va_list ap; |
507 | va_start(ap, pattern); | |
b75a7d8f | 508 | |
46f4442e | 509 | ++DATA_ERROR_COUNT; /* for informational message at the end */ |
b75a7d8f | 510 | |
46f4442e A |
511 | if(WARN_ON_MISSING_DATA == 0) { |
512 | /* Fatal error. */ | |
513 | if(strchr(pattern, '\n') != NULL) { | |
514 | ++ERROR_COUNT; | |
515 | } | |
516 | vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ | |
517 | } else { | |
518 | vlog_info("[Data] ", pattern, ap); | |
b75a7d8f | 519 | } |
b75a7d8f A |
520 | } |
521 | ||
522 | ||
46f4442e A |
523 | /* |
524 | * Tracing functions. | |
525 | */ | |
526 | static int traceFnNestingDepth = 0; | |
527 | U_CDECL_BEGIN | |
528 | static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) { | |
529 | char buf[500]; | |
530 | utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; | |
531 | fputs(buf, stdout); | |
532 | traceFnNestingDepth++; | |
533 | } | |
534 | ||
535 | static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500]; | |
536 | ||
537 | if (traceFnNestingDepth>0) { | |
538 | traceFnNestingDepth--; | |
539 | } | |
540 | utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; | |
541 | fputs(buf, stdout); | |
542 | utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); | |
543 | buf[sizeof(buf)-1]=0; | |
544 | fputs(buf, stdout); | |
545 | putc('\n', stdout); | |
546 | } | |
547 | ||
548 | static void U_CALLCONV TraceData(const void *context, int32_t fnNumber, | |
549 | int32_t level, const char *fmt, va_list args) { | |
550 | char buf[500]; | |
551 | utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); | |
552 | buf[sizeof(buf)-1]=0; | |
553 | fputs(buf, stdout); | |
554 | putc('\n', stdout); | |
555 | } | |
556 | ||
557 | static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) { | |
558 | /*if (VERBOSITY) { | |
559 | printf("Allocated %ld\n", (long)size); | |
560 | }*/ | |
561 | if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { | |
562 | return NULL; | |
563 | } | |
564 | umtx_atomic_inc(&ALLOCATION_COUNT); | |
565 | return malloc(size); | |
566 | } | |
567 | static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) { | |
568 | /*if (VERBOSITY) { | |
569 | printf("Reallocated %ld\n", (long)size); | |
570 | }*/ | |
571 | if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { | |
572 | /*free(mem);*/ /* Realloc doesn't free on failure. */ | |
573 | return NULL; | |
574 | } | |
575 | if (mem == NULL) { | |
576 | /* New allocation. */ | |
577 | umtx_atomic_inc(&ALLOCATION_COUNT); | |
578 | } | |
579 | return realloc(mem, size); | |
580 | } | |
581 | static void U_CALLCONV ctest_libFree(const void *context, void *mem) { | |
582 | if (mem != NULL) { | |
583 | umtx_atomic_dec(&ALLOCATION_COUNT); | |
584 | } | |
585 | free(mem); | |
586 | } | |
587 | ||
374ca955 | 588 | int T_CTEST_EXPORT2 |
46f4442e | 589 | initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context) |
b75a7d8f | 590 | { |
b75a7d8f A |
591 | int i; |
592 | int doList = FALSE; | |
46f4442e | 593 | int argSkip = 0; |
b75a7d8f | 594 | |
b75a7d8f A |
595 | VERBOSITY = FALSE; |
596 | ERR_MSG = TRUE; | |
597 | ||
598 | for( i=1; i<argc; i++) | |
599 | { | |
600 | if ( argv[i][0] == '/' ) | |
601 | { | |
46f4442e A |
602 | /* We don't run the tests here. */ |
603 | continue; | |
604 | } | |
605 | else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) | |
606 | { | |
607 | /* We don't run the tests here. */ | |
608 | continue; | |
b75a7d8f A |
609 | } |
610 | else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0) | |
611 | { | |
612 | VERBOSITY = TRUE; | |
613 | } | |
614 | else if (strcmp( argv[i], "-l" )==0 ) | |
615 | { | |
616 | doList = TRUE; | |
617 | } | |
618 | else if (strcmp( argv[i], "-e1") == 0) | |
619 | { | |
620 | QUICK = -1; | |
621 | } | |
622 | else if (strcmp( argv[i], "-e") ==0) | |
623 | { | |
624 | QUICK = 0; | |
625 | } | |
626 | else if (strcmp( argv[i], "-w") ==0) | |
627 | { | |
628 | WARN_ON_MISSING_DATA = TRUE; | |
629 | } | |
46f4442e A |
630 | else if (strcmp( argv[i], "-m") ==0) |
631 | { | |
632 | UErrorCode errorCode = U_ZERO_ERROR; | |
633 | if (i+1 < argc) { | |
634 | char *endPtr = NULL; | |
635 | i++; | |
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]); | |
639 | help(argv[0]); | |
640 | return 0; | |
641 | } | |
642 | if (*endPtr == '-') { | |
643 | char *maxPtr = endPtr+1; | |
644 | endPtr = NULL; | |
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]); | |
648 | help(argv[0]); | |
649 | return 0; | |
650 | } | |
651 | } | |
652 | } | |
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)); | |
657 | return 0; | |
658 | } | |
659 | } | |
b75a7d8f A |
660 | else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0) |
661 | { | |
662 | ERR_MSG = FALSE; | |
663 | } | |
664 | else if (strcmp( argv[i], "-r") == 0) | |
665 | { | |
666 | if (!REPEAT_TESTS_INIT) { | |
667 | REPEAT_TESTS++; | |
668 | } | |
669 | } | |
374ca955 A |
670 | else if (strcmp( argv[i], "-t_info") == 0) { |
671 | ICU_TRACE = UTRACE_INFO; | |
672 | } | |
673 | else if (strcmp( argv[i], "-t_error") == 0) { | |
674 | ICU_TRACE = UTRACE_ERROR; | |
675 | } | |
676 | else if (strcmp( argv[i], "-t_warn") == 0) { | |
677 | ICU_TRACE = UTRACE_WARNING; | |
678 | } | |
679 | else if (strcmp( argv[i], "-t_verbose") == 0) { | |
680 | ICU_TRACE = UTRACE_VERBOSE; | |
681 | } | |
682 | else if (strcmp( argv[i], "-t_oc") == 0) { | |
683 | ICU_TRACE = UTRACE_OPEN_CLOSE; | |
684 | } | |
685 | else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0) | |
b75a7d8f A |
686 | { |
687 | help( argv[0] ); | |
688 | return 0; | |
689 | } | |
46f4442e A |
690 | else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0) |
691 | { | |
692 | i += argSkip - 1; | |
693 | } | |
b75a7d8f A |
694 | else |
695 | { | |
696 | printf("* unknown option: %s\n", argv[i]); | |
697 | help( argv[0] ); | |
46f4442e A |
698 | return 0; |
699 | } | |
700 | } | |
701 | if (ICU_TRACE != UTRACE_OFF) { | |
702 | utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData); | |
703 | utrace_setLevel(ICU_TRACE); | |
704 | } | |
705 | ||
706 | return 1; /* total error count */ | |
707 | } | |
708 | ||
709 | int T_CTEST_EXPORT2 | |
710 | runTestRequest(const TestNode* root, | |
711 | int argc, | |
712 | const char* const argv[]) | |
713 | { | |
714 | /** | |
715 | * This main will parse the l, v, h, n, and path arguments | |
716 | */ | |
717 | const TestNode* toRun; | |
718 | int i; | |
719 | int doList = FALSE; | |
720 | int subtreeOptionSeen = FALSE; | |
721 | ||
722 | int errorCount = 0; | |
723 | ||
724 | toRun = root; | |
725 | ||
726 | for( i=1; i<argc; i++) | |
727 | { | |
728 | if ( argv[i][0] == '/' ) | |
729 | { | |
730 | printf("Selecting subtree '%s'\n", argv[i]); | |
731 | ||
732 | if ( argv[i][1] == 0 ) | |
733 | toRun = root; | |
734 | else | |
735 | toRun = getTest(root, argv[i]); | |
736 | ||
737 | if ( toRun == NULL ) | |
738 | { | |
739 | printf("* Could not find any matching subtree\n"); | |
740 | return -1; | |
741 | } | |
742 | ||
743 | if( doList == TRUE) | |
744 | showTests(toRun); | |
745 | else | |
746 | runTests(toRun); | |
747 | ||
748 | errorCount += ERROR_COUNT; | |
749 | ||
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) { | |
754 | doList = TRUE; | |
b75a7d8f | 755 | } |
46f4442e | 756 | /* else option already handled by initArgs */ |
b75a7d8f A |
757 | } |
758 | ||
759 | if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */ | |
760 | { | |
761 | if( doList == TRUE) | |
762 | showTests(toRun); | |
763 | else | |
764 | runTests(toRun); | |
765 | ||
766 | errorCount += ERROR_COUNT; | |
767 | } | |
768 | else | |
769 | { | |
770 | if( ( doList == FALSE ) && ( errorCount > 0 ) ) | |
771 | printf(" Total errors: %d\n", errorCount ); | |
772 | } | |
773 | ||
774 | REPEAT_TESTS_INIT = 1; | |
775 | ||
776 | return errorCount; /* total error count */ | |
777 | } | |
778 | ||
779 | /** | |
780 | * Display program invocation arguments | |
781 | */ | |
782 | ||
783 | static void help ( const char *argv0 ) | |
784 | { | |
374ca955 | 785 | printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n" |
46f4442e A |
786 | " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n" |
787 | " [ /path/to/test ]\n", | |
b75a7d8f | 788 | argv0); |
374ca955 A |
789 | printf(" -l To get a list of test names\n"); |
790 | printf(" -e to do exhaustive testing\n"); | |
b75a7d8f | 791 | printf(" -verbose To turn ON verbosity\n"); |
374ca955 A |
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" | |
b75a7d8f | 796 | " user has reduced/changed the common set of ICU data \n"); |
374ca955 | 797 | printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n"); |
b75a7d8f | 798 | printf(" -no_err_msg (same as -n) \n"); |
46f4442e A |
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"); | |
b75a7d8f A |
803 | printf(" eg: to run just the utility tests type: cintltest /tsutil) \n"); |
804 | } | |
805 |