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