]>
Commit | Line | Data |
---|---|---|
b75a7d8f | 1 | /* |
73c04bcf | 2 | ******************************************************************************** |
b75a7d8f | 3 | * |
b331163b | 4 | * Copyright (C) 1996-2014, 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> | |
729e4ab9 | 14 | #include <ctype.h> |
b75a7d8f | 15 | |
374ca955 | 16 | #include "unicode/utrace.h" |
46f4442e | 17 | #include "unicode/uclean.h" |
729e4ab9 | 18 | #include "putilimp.h" |
57a6839d | 19 | #include "udbgutil.h" |
374ca955 | 20 | |
b75a7d8f A |
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 | ||
729e4ab9 A |
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 | ||
b75a7d8f A |
42 | struct TestNode |
43 | { | |
b75a7d8f A |
44 | void (*test)(void); |
45 | struct TestNode* sibling; | |
46 | struct TestNode* child; | |
46f4442e | 47 | char name[1]; /* This is dynamically allocated off the end with malloc. */ |
b75a7d8f A |
48 | }; |
49 | ||
50 | ||
51 | static const struct TestNode* currentTest; | |
52 | ||
53 | typedef 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 | ||
62 | static char ERROR_LOG[MAX_TEST_LOG][MAXTESTNAME]; | |
63 | ||
64 | /* Local prototypes */ | |
65 | static TestNode* addTestNode( TestNode *root, const char *name ); | |
66 | ||
46f4442e | 67 | static TestNode *createTestNode(const char* name, int32_t nameLen); |
b75a7d8f A |
68 | |
69 | static int strncmp_nullcheck( const char* s1, | |
70 | const char* s2, | |
71 | int n ); | |
72 | ||
73 | static void getNextLevel( const char* name, | |
74 | int* nameLen, | |
75 | const char** nextName ); | |
76 | ||
729e4ab9 A |
77 | static void iterateTestsWithLevel( const TestNode *root, int depth, |
78 | const TestNode** nodeList, | |
b75a7d8f A |
79 | TestMode mode); |
80 | ||
81 | static 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 | */ | |
90 | static void vlog_err(const char *prefix, const char *pattern, va_list ap); | |
b75a7d8f | 91 | static void vlog_verbose(const char *prefix, const char *pattern, va_list ap); |
57a6839d | 92 | static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap); |
b75a7d8f | 93 | |
729e4ab9 A |
94 | /** |
95 | * Log test structure, with indent | |
96 | * @param pattern printf pattern | |
97 | */ | |
98 | static void log_testinfo_i(const char *pattern, ...); | |
99 | ||
100 | /** | |
101 | * Log test structure, NO indent | |
102 | * @param pattern printf pattern | |
103 | */ | |
104 | static void log_testinfo(const char *pattern, ...); | |
105 | ||
b75a7d8f A |
106 | /* If we need to make the framework multi-thread safe |
107 | we need to pass around the following vars | |
108 | */ | |
109 | static int ERRONEOUS_FUNCTION_COUNT = 0; | |
110 | static int ERROR_COUNT = 0; /* Count of errors from all tests. */ | |
729e4ab9 | 111 | static int ONE_ERROR = 0; /* were there any other errors? */ |
b75a7d8f A |
112 | static int DATA_ERROR_COUNT = 0; /* count of data related errors or warnings */ |
113 | static int INDENT_LEVEL = 0; | |
57a6839d A |
114 | static UBool NO_KNOWN = FALSE; |
115 | static void *knownList = NULL; | |
116 | static char gTestName[1024] = ""; | |
729e4ab9 A |
117 | static UBool ON_LINE = FALSE; /* are we on the top line with our test name? */ |
118 | static UBool HANGING_OUTPUT = FALSE; /* did the user leave us without a trailing \n ? */ | |
119 | static int GLOBAL_PRINT_COUNT = 0; /* global count of printouts */ | |
b75a7d8f A |
120 | int REPEAT_TESTS_INIT = 0; /* Was REPEAT_TESTS initialized? */ |
121 | int REPEAT_TESTS = 1; /* Number of times to run the test */ | |
122 | int VERBOSITY = 0; /* be No-verbose by default */ | |
123 | int ERR_MSG =1; /* error messages will be displayed by default*/ | |
124 | int QUICK = 1; /* Skip some of the slower tests? */ | |
125 | int WARN_ON_MISSING_DATA = 0; /* Reduce data errs to warnings? */ | |
374ca955 | 126 | UTraceLevel ICU_TRACE = UTRACE_OFF; /* ICU tracing level */ |
46f4442e A |
127 | size_t MINIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Minimum library memory allocation window that will fail. */ |
128 | size_t MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Maximum library memory allocation window that will fail. */ | |
729e4ab9 A |
129 | static const char *ARGV_0 = "[ALL]"; |
130 | static const char *XML_FILE_NAME=NULL; | |
131 | static char XML_PREFIX[256]; | |
57a6839d | 132 | static const char *SUMMARY_FILE = NULL; |
729e4ab9 | 133 | FILE *XML_FILE = NULL; |
b75a7d8f A |
134 | /*-------------------------------------------*/ |
135 | ||
136 | /* strncmp that also makes sure there's a \0 at s2[0] */ | |
137 | static 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 | ||
149 | static 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 | ||
46f4442e | 170 | static TestNode *createTestNode(const char* name, int32_t nameLen) |
b75a7d8f A |
171 | { |
172 | TestNode *newNode; | |
173 | ||
46f4442e | 174 | newNode = (TestNode*)malloc(sizeof(TestNode) + (nameLen + 1)); |
b75a7d8f | 175 | |
b75a7d8f A |
176 | newNode->test = NULL; |
177 | newNode->sibling = NULL; | |
178 | newNode->child = NULL; | |
179 | ||
46f4442e A |
180 | strncpy( newNode->name, name, nameLen ); |
181 | newNode->name[nameLen] = 0; | |
182 | ||
b75a7d8f A |
183 | return newNode; |
184 | } | |
185 | ||
374ca955 A |
186 | void T_CTEST_EXPORT2 |
187 | cleanUpTestTree(TestNode *tn) | |
188 | { | |
b75a7d8f A |
189 | if(tn->child != NULL) { |
190 | cleanUpTestTree(tn->child); | |
191 | } | |
192 | if(tn->sibling != NULL) { | |
193 | cleanUpTestTree(tn->sibling); | |
194 | } | |
195 | ||
196 | free(tn); | |
729e4ab9 | 197 | |
b75a7d8f A |
198 | } |
199 | ||
200 | ||
374ca955 A |
201 | void T_CTEST_EXPORT2 |
202 | addTest(TestNode** root, | |
203 | TestFunctionPtr test, | |
204 | const char* name ) | |
b75a7d8f A |
205 | { |
206 | TestNode *newNode; | |
207 | ||
208 | /*if this is the first Test created*/ | |
209 | if (*root == NULL) | |
46f4442e | 210 | *root = createTestNode("", 0); |
b75a7d8f A |
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 */ | |
220 | static 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 | { | |
b75a7d8f | 248 | /* Get the next component of the name */ |
46f4442e | 249 | getNextLevel(name, &nameLen, &nextName); |
b75a7d8f A |
250 | |
251 | /* update curName to have the next name segment */ | |
46f4442e | 252 | curNode->child = createTestNode(name, nameLen); |
b75a7d8f A |
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. */ | |
46f4442e | 271 | nextNode = createTestNode(name, nameLen); |
b75a7d8f A |
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 | ||
729e4ab9 A |
290 | /** |
291 | * Log the time taken. May not output anything. | |
292 | * @param deltaTime change in time | |
293 | */ | |
294 | void 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 | ||
309 | static 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 | */ | |
b75a7d8f | 325 | static void iterateTestsWithLevel ( const TestNode* root, |
729e4ab9 A |
326 | int depth, |
327 | const TestNode** nodeList, | |
b75a7d8f A |
328 | TestMode mode) |
329 | { | |
330 | int i; | |
b75a7d8f A |
331 | |
332 | char pathToFunction[MAXTESTNAME] = ""; | |
333 | char separatorString[2] = { TEST_SEPARATOR, '\0'}; | |
729e4ab9 A |
334 | #if SHOW_TIMES |
335 | UDate allStartTime = -1, allStopTime = -1; | |
336 | #endif | |
337 | ||
338 | if(depth<2) { | |
339 | allStartTime = uprv_getRawUTCtime(); | |
340 | } | |
b75a7d8f A |
341 | |
342 | if ( root == NULL ) | |
343 | return; | |
344 | ||
729e4ab9 A |
345 | /* record the current root node, and increment depth. */ |
346 | nodeList[depth++] = root; | |
347 | /* depth is now the depth of root's children. */ | |
b75a7d8f | 348 | |
729e4ab9 A |
349 | /* Collect the 'path' to the current subtree. */ |
350 | for ( i=0;i<(depth-1);i++ ) | |
b75a7d8f | 351 | { |
729e4ab9 | 352 | strcat(pathToFunction, nodeList[i]->name); |
b75a7d8f A |
353 | strcat(pathToFunction, separatorString); |
354 | } | |
729e4ab9 A |
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 */ | |
b75a7d8f | 365 | |
b75a7d8f | 366 | |
729e4ab9 A |
367 | if ( (mode == RUNTESTS) && |
368 | (root->test != NULL)) /* if root is a leaf node, run it */ | |
b75a7d8f A |
369 | { |
370 | int myERROR_COUNT = ERROR_COUNT; | |
729e4ab9 A |
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 | |
b75a7d8f | 380 | currentTest = root; |
729e4ab9 A |
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 | |
57a6839d | 387 | strcpy(gTestName, pathToFunction); |
729e4ab9 A |
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 */ | |
b75a7d8f | 397 | currentTest = NULL; |
729e4ab9 A |
398 | if((ONE_ERROR>0)&&(ERROR_COUNT==0)) { |
399 | ERROR_COUNT++; /* There was an error without a newline */ | |
b75a7d8f | 400 | } |
729e4ab9 A |
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 */ | |
b75a7d8f A |
432 | } |
433 | ||
729e4ab9 | 434 | INDENT_LEVEL = depth-1; /* root */ |
b75a7d8f A |
435 | |
436 | /* we want these messages to be at 0 indent. so just push the indent level breifly. */ | |
729e4ab9 A |
437 | if(mode==SHOWTESTS) { |
438 | log_testinfo("---%s%c\n",pathToFunction, nodeList[i]->test?' ':TEST_SEPARATOR ); | |
439 | } | |
b75a7d8f | 440 | |
729e4ab9 A |
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 | } | |
b75a7d8f | 466 | |
729e4ab9 A |
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 | } | |
b75a7d8f | 482 | |
729e4ab9 A |
483 | if ( depth != 0 ) { /* DO NOT iterate over siblings of the root. TODO: why not? */ |
484 | iterateTestsWithLevel ( root->sibling, depth, nodeList, mode ); | |
485 | } | |
b75a7d8f A |
486 | } |
487 | ||
488 | ||
489 | ||
374ca955 A |
490 | void T_CTEST_EXPORT2 |
491 | showTests ( const TestNode *root ) | |
b75a7d8f A |
492 | { |
493 | /* make up one for them */ | |
729e4ab9 | 494 | const TestNode *nodeList[MAXTESTS]; |
b75a7d8f A |
495 | |
496 | if (root == NULL) | |
497 | log_err("TEST CAN'T BE FOUND!"); | |
498 | ||
729e4ab9 | 499 | iterateTestsWithLevel ( root, 0, nodeList, SHOWTESTS ); |
b75a7d8f A |
500 | |
501 | } | |
502 | ||
374ca955 A |
503 | void T_CTEST_EXPORT2 |
504 | runTests ( const TestNode *root ) | |
b75a7d8f A |
505 | { |
506 | int i; | |
729e4ab9 | 507 | const TestNode *nodeList[MAXTESTS]; |
b75a7d8f A |
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; | |
729e4ab9 | 515 | iterateTestsWithLevel ( root, 0, nodeList, RUNTESTS ); |
b75a7d8f A |
516 | |
517 | /*print out result summary*/ | |
518 | ||
729e4ab9 A |
519 | ON_LINE=FALSE; /* just in case */ |
520 | ||
57a6839d A |
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); | |
b331163b | 526 | knownList = NULL; |
57a6839d A |
527 | } |
528 | ||
b75a7d8f A |
529 | if (ERROR_COUNT) |
530 | { | |
729e4ab9 A |
531 | fprintf(stdout,"\nSUMMARY:\n"); |
532 | fflush(stdout); | |
533 | fprintf(stdout,"******* [Total error count:\t%d]\n", ERROR_COUNT); | |
534 | fflush(stdout); | |
535 | fprintf(stdout, " Errors in\n"); | |
b75a7d8f | 536 | for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++) |
729e4ab9 | 537 | fprintf(stdout, "[%s]\n",ERROR_LOG[i]); |
57a6839d A |
538 | if(SUMMARY_FILE != NULL) { |
539 | FILE *summf = fopen(SUMMARY_FILE, "w"); | |
540 | if(summf!=NULL) { | |
541 | for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++) | |
542 | fprintf(summf, "%s\n",ERROR_LOG[i]); | |
543 | fclose(summf); | |
544 | } | |
545 | } | |
b75a7d8f A |
546 | } |
547 | else | |
548 | { | |
729e4ab9 | 549 | log_testinfo("\n[All tests passed successfully...]\n"); |
b75a7d8f A |
550 | } |
551 | ||
552 | if(DATA_ERROR_COUNT) { | |
553 | if(WARN_ON_MISSING_DATA==0) { | |
729e4ab9 | 554 | log_testinfo("\t*Note* some errors are data-loading related. If the data used is not the \n" |
b75a7d8f A |
555 | "\tstock ICU data (i.e some have been added or removed), consider using\n" |
556 | "\tthe '-w' option to turn these errors into warnings.\n"); | |
557 | } else { | |
729e4ab9 | 558 | log_testinfo("\t*WARNING* some data-loading errors were ignored by the -w option.\n"); |
b75a7d8f A |
559 | } |
560 | } | |
561 | } | |
562 | ||
374ca955 A |
563 | const char* T_CTEST_EXPORT2 |
564 | getTestName(void) | |
b75a7d8f A |
565 | { |
566 | if(currentTest != NULL) { | |
567 | return currentTest->name; | |
568 | } else { | |
569 | return NULL; | |
570 | } | |
571 | } | |
572 | ||
374ca955 A |
573 | const TestNode* T_CTEST_EXPORT2 |
574 | getTest(const TestNode* root, const char* name) | |
b75a7d8f A |
575 | { |
576 | const char* nextName; | |
577 | TestNode *nextNode; | |
578 | const TestNode* curNode; | |
579 | int nameLen; /* length of current 'name' */ | |
580 | ||
73c04bcf | 581 | if (root == NULL) { |
b75a7d8f | 582 | log_err("TEST CAN'T BE FOUND!\n"); |
73c04bcf A |
583 | return NULL; |
584 | } | |
b75a7d8f A |
585 | /* remove leading slash */ |
586 | if ( *name == TEST_SEPARATOR ) | |
587 | name++; | |
588 | ||
589 | curNode = root; | |
590 | ||
591 | for(;;) | |
592 | { | |
593 | /* Start with the next child */ | |
594 | nextNode = curNode->child; | |
595 | ||
596 | getNextLevel ( name, &nameLen, &nextName ); | |
597 | ||
598 | /* printf("* %s\n", name );*/ | |
599 | ||
600 | /* if nextNode is already null, then curNode has no children | |
601 | -- add them */ | |
602 | if( nextNode == NULL ) | |
603 | { | |
604 | return NULL; | |
605 | } | |
606 | ||
607 | /* Search across for the name */ | |
608 | while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 ) | |
609 | { | |
610 | curNode = nextNode; | |
611 | nextNode = nextNode -> sibling; | |
612 | ||
613 | if ( nextNode == NULL ) | |
614 | { | |
615 | /* Did not find 'name' on this level. */ | |
616 | return NULL; | |
617 | } | |
618 | } | |
619 | ||
620 | /* nextNode matches 'name' */ | |
621 | ||
622 | if (nextName == NULL) /* end of the line */ | |
623 | { | |
624 | return nextNode; | |
625 | } | |
626 | ||
627 | /* Loop again with the next item */ | |
628 | name = nextName; | |
629 | curNode = nextNode; | |
630 | } | |
631 | } | |
632 | ||
729e4ab9 A |
633 | /* =========== io functions ======== */ |
634 | ||
635 | static void go_offline_with_marker(const char *mrk) { | |
636 | UBool wasON_LINE = ON_LINE; | |
637 | ||
638 | if(ON_LINE) { | |
639 | log_testinfo(" {\n"); | |
640 | ON_LINE=FALSE; | |
641 | } | |
642 | ||
643 | if(!HANGING_OUTPUT || wasON_LINE) { | |
644 | if(mrk != NULL) { | |
645 | fputs(mrk, stdout); | |
646 | } | |
647 | } | |
648 | } | |
649 | ||
650 | static void go_offline() { | |
651 | go_offline_with_marker(NULL); | |
652 | } | |
653 | ||
654 | static void go_offline_err() { | |
655 | go_offline(); | |
656 | } | |
657 | ||
658 | static void first_line_verbose() { | |
659 | go_offline_with_marker("v"); | |
660 | } | |
661 | ||
662 | static void first_line_err() { | |
663 | go_offline_with_marker("!"); | |
664 | } | |
665 | ||
666 | static void first_line_info() { | |
667 | go_offline_with_marker("\""); | |
668 | } | |
669 | ||
670 | static void first_line_test() { | |
671 | fputs(" ", stdout); | |
672 | } | |
673 | ||
674 | ||
b75a7d8f A |
675 | static void vlog_err(const char *prefix, const char *pattern, va_list ap) |
676 | { | |
677 | if( ERR_MSG == FALSE){ | |
678 | return; | |
679 | } | |
729e4ab9 A |
680 | fputs("!", stdout); /* col 1 - bang */ |
681 | fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); | |
b75a7d8f | 682 | if(prefix) { |
729e4ab9 | 683 | fputs(prefix, stdout); |
b75a7d8f | 684 | } |
729e4ab9 A |
685 | vfprintf(stdout, pattern, ap); |
686 | fflush(stdout); | |
b75a7d8f | 687 | va_end(ap); |
729e4ab9 A |
688 | if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) { |
689 | HANGING_OUTPUT=1; | |
690 | } else { | |
691 | HANGING_OUTPUT=0; | |
692 | } | |
693 | GLOBAL_PRINT_COUNT++; | |
b75a7d8f A |
694 | } |
695 | ||
57a6839d A |
696 | static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap) |
697 | { | |
698 | char buf[2048]; | |
699 | UBool firstForTicket; | |
700 | UBool firstForWhere; | |
701 | ||
702 | if(NO_KNOWN) return FALSE; | |
703 | if(pattern==NULL) pattern=""; | |
704 | ||
705 | vsprintf(buf, pattern, ap); | |
706 | knownList = udbg_knownIssue_open(knownList, ticket, gTestName, buf, | |
707 | &firstForTicket, &firstForWhere); | |
708 | ||
709 | if(firstForTicket || firstForWhere) { | |
b331163b | 710 | log_info("(Known issue #%s) %s\n", ticket, buf); |
57a6839d | 711 | } else { |
b331163b | 712 | log_verbose("(Known issue #%s) %s\n", ticket, buf); |
57a6839d A |
713 | } |
714 | ||
715 | return TRUE; | |
716 | } | |
717 | ||
718 | ||
374ca955 A |
719 | void T_CTEST_EXPORT2 |
720 | vlog_info(const char *prefix, const char *pattern, va_list ap) | |
b75a7d8f | 721 | { |
729e4ab9 A |
722 | first_line_info(); |
723 | fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); | |
b75a7d8f | 724 | if(prefix) { |
73c04bcf | 725 | fputs(prefix, stdout); |
b75a7d8f A |
726 | } |
727 | vfprintf(stdout, pattern, ap); | |
728 | fflush(stdout); | |
729 | va_end(ap); | |
729e4ab9 A |
730 | if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) { |
731 | HANGING_OUTPUT=1; | |
732 | } else { | |
733 | HANGING_OUTPUT=0; | |
734 | } | |
735 | GLOBAL_PRINT_COUNT++; | |
736 | } | |
737 | /** | |
738 | * Log test structure, with indent | |
739 | */ | |
740 | static void log_testinfo_i(const char *pattern, ...) | |
741 | { | |
742 | va_list ap; | |
743 | first_line_test(); | |
744 | fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); | |
745 | va_start(ap, pattern); | |
746 | vfprintf(stdout, pattern, ap); | |
747 | fflush(stdout); | |
748 | va_end(ap); | |
749 | GLOBAL_PRINT_COUNT++; | |
750 | } | |
751 | /** | |
752 | * Log test structure (no ident) | |
753 | */ | |
754 | static void log_testinfo(const char *pattern, ...) | |
755 | { | |
756 | va_list ap; | |
757 | va_start(ap, pattern); | |
758 | first_line_test(); | |
759 | vfprintf(stdout, pattern, ap); | |
760 | fflush(stdout); | |
761 | va_end(ap); | |
762 | GLOBAL_PRINT_COUNT++; | |
b75a7d8f A |
763 | } |
764 | ||
729e4ab9 | 765 | |
b75a7d8f A |
766 | static void vlog_verbose(const char *prefix, const char *pattern, va_list ap) |
767 | { | |
768 | if ( VERBOSITY == FALSE ) | |
769 | return; | |
770 | ||
729e4ab9 A |
771 | first_line_verbose(); |
772 | fprintf(stdout, "%-*s", INDENT_LEVEL,"" ); | |
b75a7d8f | 773 | if(prefix) { |
73c04bcf | 774 | fputs(prefix, stdout); |
b75a7d8f A |
775 | } |
776 | vfprintf(stdout, pattern, ap); | |
777 | fflush(stdout); | |
778 | va_end(ap); | |
729e4ab9 A |
779 | GLOBAL_PRINT_COUNT++; |
780 | if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) { | |
781 | HANGING_OUTPUT=1; | |
782 | } else { | |
783 | HANGING_OUTPUT=0; | |
784 | } | |
b75a7d8f A |
785 | } |
786 | ||
374ca955 A |
787 | void T_CTEST_EXPORT2 |
788 | log_err(const char* pattern, ...) | |
b75a7d8f A |
789 | { |
790 | va_list ap; | |
729e4ab9 | 791 | first_line_err(); |
b75a7d8f A |
792 | if(strchr(pattern, '\n') != NULL) { |
793 | /* | |
794 | * Count errors only if there is a line feed in the pattern | |
795 | * so that we do not exaggerate our error count. | |
796 | */ | |
797 | ++ERROR_COUNT; | |
729e4ab9 A |
798 | } else { |
799 | /* Count at least one error. */ | |
800 | ONE_ERROR=1; | |
b75a7d8f A |
801 | } |
802 | va_start(ap, pattern); | |
803 | vlog_err(NULL, pattern, ap); | |
804 | } | |
805 | ||
57a6839d A |
806 | UBool T_CTEST_EXPORT2 |
807 | log_knownIssue(const char *ticket, const char *pattern, ...) { | |
808 | va_list ap; | |
809 | va_start(ap, pattern); | |
810 | return vlog_knownIssue(ticket, pattern, ap); | |
811 | } | |
812 | ||
729e4ab9 A |
813 | void T_CTEST_EXPORT2 |
814 | log_err_status(UErrorCode status, const char* pattern, ...) | |
815 | { | |
816 | va_list ap; | |
817 | va_start(ap, pattern); | |
818 | ||
729e4ab9 A |
819 | if ((status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR)) { |
820 | ++DATA_ERROR_COUNT; /* for informational message at the end */ | |
821 | ||
822 | if (WARN_ON_MISSING_DATA == 0) { | |
4388f060 | 823 | first_line_err(); |
729e4ab9 A |
824 | /* Fatal error. */ |
825 | if (strchr(pattern, '\n') != NULL) { | |
826 | ++ERROR_COUNT; | |
827 | } else { | |
4388f060 | 828 | ++ONE_ERROR; |
729e4ab9 A |
829 | } |
830 | vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ | |
831 | } else { | |
832 | vlog_info("[DATA] ", pattern, ap); | |
833 | } | |
834 | } else { | |
4388f060 | 835 | first_line_err(); |
729e4ab9 A |
836 | /* Fatal error. */ |
837 | if(strchr(pattern, '\n') != NULL) { | |
838 | ++ERROR_COUNT; | |
839 | } else { | |
4388f060 | 840 | ++ONE_ERROR; |
729e4ab9 A |
841 | } |
842 | vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ | |
843 | } | |
844 | } | |
845 | ||
374ca955 A |
846 | void T_CTEST_EXPORT2 |
847 | log_info(const char* pattern, ...) | |
b75a7d8f A |
848 | { |
849 | va_list ap; | |
850 | ||
851 | va_start(ap, pattern); | |
852 | vlog_info(NULL, pattern, ap); | |
853 | } | |
854 | ||
374ca955 A |
855 | void T_CTEST_EXPORT2 |
856 | log_verbose(const char* pattern, ...) | |
b75a7d8f A |
857 | { |
858 | va_list ap; | |
859 | ||
860 | va_start(ap, pattern); | |
861 | vlog_verbose(NULL, pattern, ap); | |
862 | } | |
863 | ||
864 | ||
374ca955 A |
865 | void T_CTEST_EXPORT2 |
866 | log_data_err(const char* pattern, ...) | |
b75a7d8f | 867 | { |
46f4442e A |
868 | va_list ap; |
869 | va_start(ap, pattern); | |
b75a7d8f | 870 | |
729e4ab9 | 871 | go_offline_err(); |
46f4442e | 872 | ++DATA_ERROR_COUNT; /* for informational message at the end */ |
b75a7d8f | 873 | |
46f4442e A |
874 | if(WARN_ON_MISSING_DATA == 0) { |
875 | /* Fatal error. */ | |
876 | if(strchr(pattern, '\n') != NULL) { | |
877 | ++ERROR_COUNT; | |
878 | } | |
879 | vlog_err(NULL, pattern, ap); /* no need for prefix in default case */ | |
880 | } else { | |
729e4ab9 | 881 | vlog_info("[DATA] ", pattern, ap); |
b75a7d8f | 882 | } |
b75a7d8f A |
883 | } |
884 | ||
885 | ||
46f4442e A |
886 | /* |
887 | * Tracing functions. | |
888 | */ | |
889 | static int traceFnNestingDepth = 0; | |
890 | U_CDECL_BEGIN | |
891 | static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) { | |
892 | char buf[500]; | |
893 | utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; | |
894 | fputs(buf, stdout); | |
895 | traceFnNestingDepth++; | |
896 | } | |
897 | ||
898 | static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500]; | |
899 | ||
900 | if (traceFnNestingDepth>0) { | |
901 | traceFnNestingDepth--; | |
902 | } | |
903 | utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0; | |
904 | fputs(buf, stdout); | |
905 | utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); | |
906 | buf[sizeof(buf)-1]=0; | |
907 | fputs(buf, stdout); | |
908 | putc('\n', stdout); | |
909 | } | |
910 | ||
911 | static void U_CALLCONV TraceData(const void *context, int32_t fnNumber, | |
912 | int32_t level, const char *fmt, va_list args) { | |
913 | char buf[500]; | |
914 | utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args); | |
915 | buf[sizeof(buf)-1]=0; | |
916 | fputs(buf, stdout); | |
917 | putc('\n', stdout); | |
918 | } | |
919 | ||
920 | static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) { | |
921 | /*if (VERBOSITY) { | |
922 | printf("Allocated %ld\n", (long)size); | |
923 | }*/ | |
924 | if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { | |
925 | return NULL; | |
926 | } | |
46f4442e A |
927 | return malloc(size); |
928 | } | |
929 | static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) { | |
930 | /*if (VERBOSITY) { | |
931 | printf("Reallocated %ld\n", (long)size); | |
932 | }*/ | |
933 | if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) { | |
934 | /*free(mem);*/ /* Realloc doesn't free on failure. */ | |
935 | return NULL; | |
936 | } | |
46f4442e A |
937 | return realloc(mem, size); |
938 | } | |
939 | static void U_CALLCONV ctest_libFree(const void *context, void *mem) { | |
46f4442e A |
940 | free(mem); |
941 | } | |
942 | ||
374ca955 | 943 | int T_CTEST_EXPORT2 |
46f4442e | 944 | initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context) |
b75a7d8f | 945 | { |
b75a7d8f | 946 | int i; |
4388f060 | 947 | int argSkip = 0; |
b75a7d8f | 948 | |
b75a7d8f A |
949 | VERBOSITY = FALSE; |
950 | ERR_MSG = TRUE; | |
951 | ||
729e4ab9 A |
952 | ARGV_0=argv[0]; |
953 | ||
b75a7d8f A |
954 | for( i=1; i<argc; i++) |
955 | { | |
956 | if ( argv[i][0] == '/' ) | |
957 | { | |
46f4442e A |
958 | /* We don't run the tests here. */ |
959 | continue; | |
960 | } | |
961 | else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) | |
962 | { | |
963 | /* We don't run the tests here. */ | |
964 | continue; | |
b75a7d8f A |
965 | } |
966 | else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0) | |
967 | { | |
968 | VERBOSITY = TRUE; | |
969 | } | |
970 | else if (strcmp( argv[i], "-l" )==0 ) | |
971 | { | |
4388f060 | 972 | /* doList = TRUE; */ |
b75a7d8f A |
973 | } |
974 | else if (strcmp( argv[i], "-e1") == 0) | |
975 | { | |
976 | QUICK = -1; | |
977 | } | |
978 | else if (strcmp( argv[i], "-e") ==0) | |
979 | { | |
980 | QUICK = 0; | |
981 | } | |
57a6839d A |
982 | else if (strcmp( argv[i], "-K") ==0) |
983 | { | |
984 | NO_KNOWN = 1; | |
985 | } | |
986 | else if (strncmp( argv[i], "-E",2) ==0) | |
987 | { | |
988 | SUMMARY_FILE=argv[i]+2; | |
989 | } | |
b75a7d8f A |
990 | else if (strcmp( argv[i], "-w") ==0) |
991 | { | |
992 | WARN_ON_MISSING_DATA = TRUE; | |
993 | } | |
46f4442e A |
994 | else if (strcmp( argv[i], "-m") ==0) |
995 | { | |
996 | UErrorCode errorCode = U_ZERO_ERROR; | |
997 | if (i+1 < argc) { | |
998 | char *endPtr = NULL; | |
999 | i++; | |
1000 | MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10); | |
1001 | if (endPtr == argv[i]) { | |
1002 | printf("Can't parse %s\n", argv[i]); | |
1003 | help(argv[0]); | |
1004 | return 0; | |
1005 | } | |
1006 | if (*endPtr == '-') { | |
1007 | char *maxPtr = endPtr+1; | |
1008 | endPtr = NULL; | |
1009 | MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10); | |
1010 | if (endPtr == argv[i]) { | |
1011 | printf("Can't parse %s\n", argv[i]); | |
1012 | help(argv[0]); | |
1013 | return 0; | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | /* Use the default value */ | |
1018 | u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode); | |
1019 | if (U_FAILURE(errorCode)) { | |
1020 | printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode)); | |
1021 | return 0; | |
1022 | } | |
1023 | } | |
b75a7d8f A |
1024 | else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0) |
1025 | { | |
1026 | ERR_MSG = FALSE; | |
1027 | } | |
1028 | else if (strcmp( argv[i], "-r") == 0) | |
1029 | { | |
1030 | if (!REPEAT_TESTS_INIT) { | |
1031 | REPEAT_TESTS++; | |
1032 | } | |
1033 | } | |
729e4ab9 A |
1034 | else if (strcmp( argv[i], "-x") == 0) |
1035 | { | |
1036 | if(++i>=argc) { | |
1037 | printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n"); | |
1038 | return 0; | |
1039 | } | |
1040 | if(ctest_xml_setFileName(argv[i])) { /* set the name */ | |
1041 | return 0; | |
1042 | } | |
1043 | } | |
374ca955 A |
1044 | else if (strcmp( argv[i], "-t_info") == 0) { |
1045 | ICU_TRACE = UTRACE_INFO; | |
1046 | } | |
1047 | else if (strcmp( argv[i], "-t_error") == 0) { | |
1048 | ICU_TRACE = UTRACE_ERROR; | |
1049 | } | |
1050 | else if (strcmp( argv[i], "-t_warn") == 0) { | |
1051 | ICU_TRACE = UTRACE_WARNING; | |
1052 | } | |
1053 | else if (strcmp( argv[i], "-t_verbose") == 0) { | |
1054 | ICU_TRACE = UTRACE_VERBOSE; | |
1055 | } | |
1056 | else if (strcmp( argv[i], "-t_oc") == 0) { | |
1057 | ICU_TRACE = UTRACE_OPEN_CLOSE; | |
1058 | } | |
1059 | else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0) | |
b75a7d8f A |
1060 | { |
1061 | help( argv[0] ); | |
1062 | return 0; | |
1063 | } | |
46f4442e A |
1064 | else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0) |
1065 | { | |
1066 | i += argSkip - 1; | |
1067 | } | |
b75a7d8f A |
1068 | else |
1069 | { | |
1070 | printf("* unknown option: %s\n", argv[i]); | |
1071 | help( argv[0] ); | |
46f4442e A |
1072 | return 0; |
1073 | } | |
1074 | } | |
1075 | if (ICU_TRACE != UTRACE_OFF) { | |
1076 | utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData); | |
1077 | utrace_setLevel(ICU_TRACE); | |
1078 | } | |
1079 | ||
1080 | return 1; /* total error count */ | |
1081 | } | |
1082 | ||
1083 | int T_CTEST_EXPORT2 | |
1084 | runTestRequest(const TestNode* root, | |
1085 | int argc, | |
1086 | const char* const argv[]) | |
1087 | { | |
1088 | /** | |
1089 | * This main will parse the l, v, h, n, and path arguments | |
1090 | */ | |
1091 | const TestNode* toRun; | |
1092 | int i; | |
1093 | int doList = FALSE; | |
1094 | int subtreeOptionSeen = FALSE; | |
1095 | ||
1096 | int errorCount = 0; | |
1097 | ||
1098 | toRun = root; | |
1099 | ||
729e4ab9 A |
1100 | if(ctest_xml_init(ARGV_0)) { |
1101 | return 1; /* couldn't fire up XML thing */ | |
1102 | } | |
1103 | ||
46f4442e A |
1104 | for( i=1; i<argc; i++) |
1105 | { | |
1106 | if ( argv[i][0] == '/' ) | |
1107 | { | |
1108 | printf("Selecting subtree '%s'\n", argv[i]); | |
1109 | ||
1110 | if ( argv[i][1] == 0 ) | |
1111 | toRun = root; | |
1112 | else | |
1113 | toRun = getTest(root, argv[i]); | |
1114 | ||
1115 | if ( toRun == NULL ) | |
1116 | { | |
1117 | printf("* Could not find any matching subtree\n"); | |
1118 | return -1; | |
1119 | } | |
1120 | ||
729e4ab9 A |
1121 | ON_LINE=FALSE; /* just in case */ |
1122 | ||
46f4442e A |
1123 | if( doList == TRUE) |
1124 | showTests(toRun); | |
1125 | else | |
1126 | runTests(toRun); | |
1127 | ||
729e4ab9 A |
1128 | ON_LINE=FALSE; /* just in case */ |
1129 | ||
46f4442e A |
1130 | errorCount += ERROR_COUNT; |
1131 | ||
1132 | subtreeOptionSeen = TRUE; | |
1133 | } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) { | |
1134 | subtreeOptionSeen=FALSE; | |
1135 | } else if (strcmp( argv[i], "-l") == 0) { | |
1136 | doList = TRUE; | |
b75a7d8f | 1137 | } |
46f4442e | 1138 | /* else option already handled by initArgs */ |
b75a7d8f A |
1139 | } |
1140 | ||
1141 | if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */ | |
1142 | { | |
729e4ab9 | 1143 | ON_LINE=FALSE; /* just in case */ |
b75a7d8f A |
1144 | if( doList == TRUE) |
1145 | showTests(toRun); | |
1146 | else | |
1147 | runTests(toRun); | |
729e4ab9 | 1148 | ON_LINE=FALSE; /* just in case */ |
b75a7d8f A |
1149 | |
1150 | errorCount += ERROR_COUNT; | |
1151 | } | |
1152 | else | |
1153 | { | |
1154 | if( ( doList == FALSE ) && ( errorCount > 0 ) ) | |
1155 | printf(" Total errors: %d\n", errorCount ); | |
1156 | } | |
1157 | ||
1158 | REPEAT_TESTS_INIT = 1; | |
729e4ab9 A |
1159 | |
1160 | if(ctest_xml_fini()) { | |
1161 | errorCount++; | |
1162 | } | |
b75a7d8f A |
1163 | |
1164 | return errorCount; /* total error count */ | |
1165 | } | |
1166 | ||
1167 | /** | |
1168 | * Display program invocation arguments | |
1169 | */ | |
1170 | ||
1171 | static void help ( const char *argv0 ) | |
1172 | { | |
374ca955 | 1173 | printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n" |
46f4442e A |
1174 | " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n" |
1175 | " [ /path/to/test ]\n", | |
b75a7d8f | 1176 | argv0); |
374ca955 A |
1177 | printf(" -l To get a list of test names\n"); |
1178 | printf(" -e to do exhaustive testing\n"); | |
b75a7d8f | 1179 | printf(" -verbose To turn ON verbosity\n"); |
374ca955 | 1180 | printf(" -v To turn ON verbosity(same as -verbose)\n"); |
729e4ab9 | 1181 | printf(" -x file.xml Write junit format output to file.xml\n"); |
374ca955 | 1182 | printf(" -h To print this message\n"); |
57a6839d | 1183 | printf(" -K to turn OFF suppressing known issues\n"); |
374ca955 A |
1184 | printf(" -n To turn OFF printing error messages\n"); |
1185 | printf(" -w Don't fail on data-loading errs, just warn. Useful if\n" | |
b75a7d8f | 1186 | " user has reduced/changed the common set of ICU data \n"); |
374ca955 | 1187 | printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n"); |
b75a7d8f | 1188 | printf(" -no_err_msg (same as -n) \n"); |
46f4442e A |
1189 | printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n"); |
1190 | printf(" The default is the maximum value of size_t. Max is optional.\n"); | |
1191 | printf(" -r Repeat tests after calling u_cleanup \n"); | |
1192 | printf(" [/subtest] To run a subtest \n"); | |
b75a7d8f A |
1193 | printf(" eg: to run just the utility tests type: cintltest /tsutil) \n"); |
1194 | } | |
1195 | ||
729e4ab9 A |
1196 | int32_t T_CTEST_EXPORT2 |
1197 | getTestOption ( int32_t testOption ) { | |
1198 | switch (testOption) { | |
1199 | case VERBOSITY_OPTION: | |
1200 | return VERBOSITY; | |
1201 | case WARN_ON_MISSING_DATA_OPTION: | |
1202 | return WARN_ON_MISSING_DATA; | |
1203 | case QUICK_OPTION: | |
1204 | return QUICK; | |
1205 | case REPEAT_TESTS_OPTION: | |
1206 | return REPEAT_TESTS; | |
1207 | case ERR_MSG_OPTION: | |
1208 | return ERR_MSG; | |
1209 | case ICU_TRACE_OPTION: | |
1210 | return ICU_TRACE; | |
1211 | default : | |
1212 | return 0; | |
1213 | } | |
1214 | } | |
1215 | ||
1216 | void T_CTEST_EXPORT2 | |
1217 | setTestOption ( int32_t testOption, int32_t value) { | |
1218 | if (value == DECREMENT_OPTION_VALUE) { | |
1219 | value = getTestOption(testOption); | |
1220 | --value; | |
1221 | } | |
1222 | switch (testOption) { | |
1223 | case VERBOSITY_OPTION: | |
1224 | VERBOSITY = value; | |
1225 | break; | |
1226 | case WARN_ON_MISSING_DATA_OPTION: | |
1227 | WARN_ON_MISSING_DATA = value; | |
1228 | break; | |
1229 | case QUICK_OPTION: | |
1230 | QUICK = value; | |
1231 | break; | |
1232 | case REPEAT_TESTS_OPTION: | |
1233 | REPEAT_TESTS = value; | |
1234 | break; | |
1235 | case ICU_TRACE_OPTION: | |
51004dcb | 1236 | ICU_TRACE = (UTraceLevel)value; |
729e4ab9 A |
1237 | break; |
1238 | default : | |
1239 | break; | |
1240 | } | |
1241 | } | |
1242 | ||
1243 | ||
1244 | /* | |
1245 | * ================== JUnit support ================================ | |
1246 | */ | |
1247 | ||
1248 | int32_t | |
1249 | T_CTEST_EXPORT2 | |
1250 | ctest_xml_setFileName(const char *name) { | |
1251 | XML_FILE_NAME=name; | |
1252 | return 0; | |
1253 | } | |
1254 | ||
1255 | ||
1256 | int32_t | |
1257 | T_CTEST_EXPORT2 | |
1258 | ctest_xml_init(const char *rootName) { | |
1259 | if(!XML_FILE_NAME) return 0; | |
1260 | XML_FILE = fopen(XML_FILE_NAME,"w"); | |
1261 | if(!XML_FILE) { | |
1262 | perror("fopen"); | |
1263 | fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME); | |
1264 | return 1; | |
1265 | } | |
4388f060 | 1266 | while(*rootName&&!isalnum((int)*rootName)) { |
729e4ab9 A |
1267 | rootName++; |
1268 | } | |
1269 | strcpy(XML_PREFIX,rootName); | |
1270 | { | |
1271 | char *p = XML_PREFIX+strlen(XML_PREFIX); | |
4388f060 | 1272 | for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) { |
729e4ab9 A |
1273 | *p=0; |
1274 | } | |
1275 | } | |
1276 | /* write prefix */ | |
1277 | fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX); | |
1278 | ||
1279 | return 0; | |
1280 | } | |
1281 | ||
1282 | int32_t | |
1283 | T_CTEST_EXPORT2 | |
1284 | ctest_xml_fini(void) { | |
1285 | if(!XML_FILE) return 0; | |
1286 | ||
1287 | fprintf(XML_FILE, "</testsuite>\n"); | |
1288 | fclose(XML_FILE); | |
1289 | printf(" ( test results written to %s )\n", XML_FILE_NAME); | |
1290 | XML_FILE=0; | |
1291 | return 0; | |
1292 | } | |
1293 | ||
1294 | ||
1295 | int32_t | |
1296 | T_CTEST_EXPORT2 | |
57a6839d | 1297 | ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) { |
729e4ab9 A |
1298 | if(!XML_FILE) return 0; |
1299 | ||
57a6839d | 1300 | fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, timeSeconds); |
729e4ab9 A |
1301 | if(failMsg) { |
1302 | fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg); | |
1303 | } else { | |
1304 | fprintf(XML_FILE, "/>\n"); | |
1305 | } | |
1306 | ||
1307 | return 0; | |
1308 | } | |
1309 | ||
1310 |