1 /*
2 ********************************************************************************
3 *
4 * Copyright (C) 1996-2014, 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] */
strncmp_nullcheck(const char * s1,const char * s2,int n)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
getNextLevel(const char * name,int * nameLen,const char ** nextName)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
createTestNode(const char * name,int32_t nameLen)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
cleanUpTestTree(TestNode * tn)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
addTest(TestNode ** root,TestFunctionPtr test,const char * name)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 */
addTestNode(TestNode * root,const char * name)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 */
str_timeDelta(char * str,UDate deltaTime)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
print_timeDelta(UDate deltaTime)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 */
iterateTestsWithLevel(const TestNode * root,int depth,const TestNode ** nodeList,TestMode mode)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
showTests(const TestNode * root)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
runTests(const TestNode * root)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 knownList = NULL;
527 }
528
529 if (ERROR_COUNT)
530 {
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");
536 for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++)
537 fprintf(stdout, "[%s]\n",ERROR_LOG[i]);
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 }
546 }
547 else
548 {
549 log_testinfo("\n[All tests passed successfully...]\n");
550 }
551
552 if(DATA_ERROR_COUNT) {
553 if(WARN_ON_MISSING_DATA==0) {
554 log_testinfo("\t*Note* some errors are data-loading related. If the data used is not the \n"
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 {
558 log_testinfo("\t*WARNING* some data-loading errors were ignored by the -w option.\n");
559 }
560 }
561 }
562
563 const char* T_CTEST_EXPORT2
getTestName(void)564 getTestName(void)
565 {
566 if(currentTest != NULL) {
567 return currentTest->name;
568 } else {
569 return NULL;
570 }
571 }
572
573 const TestNode* T_CTEST_EXPORT2
getTest(const TestNode * root,const char * name)574 getTest(const TestNode* root, const char* name)
575 {
576 const char* nextName;
577 TestNode *nextNode;
578 const TestNode* curNode;
579 int nameLen; /* length of current 'name' */
580
581 if (root == NULL) {
582 log_err("TEST CAN'T BE FOUND!\n");
583 return NULL;
584 }
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
633 /* =========== io functions ======== */
634
go_offline_with_marker(const char * mrk)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
go_offline()650 static void go_offline() {
651 go_offline_with_marker(NULL);
652 }
653
go_offline_err()654 static void go_offline_err() {
655 go_offline();
656 }
657
first_line_verbose()658 static void first_line_verbose() {
659 go_offline_with_marker("v");
660 }
661
first_line_err()662 static void first_line_err() {
663 go_offline_with_marker("!");
664 }
665
first_line_info()666 static void first_line_info() {
667 go_offline_with_marker("\"");
668 }
669
first_line_test()670 static void first_line_test() {
671 fputs(" ", stdout);
672 }
673
674
vlog_err(const char * prefix,const char * pattern,va_list ap)675 static void vlog_err(const char *prefix, const char *pattern, va_list ap)
676 {
677 if( ERR_MSG == FALSE){
678 return;
679 }
680 fputs("!", stdout); /* col 1 - bang */
681 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
682 if(prefix) {
683 fputs(prefix, stdout);
684 }
685 vfprintf(stdout, pattern, ap);
686 fflush(stdout);
687 va_end(ap);
688 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
689 HANGING_OUTPUT=1;
690 } else {
691 HANGING_OUTPUT=0;
692 }
693 GLOBAL_PRINT_COUNT++;
694 }
695
vlog_knownIssue(const char * ticket,const char * pattern,va_list ap)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) {
710 log_info("(Known issue #%s) %s\n", ticket, buf);
711 } else {
712 log_verbose("(Known issue #%s) %s\n", ticket, buf);
713 }
714
715 return TRUE;
716 }
717
718
719 void T_CTEST_EXPORT2
vlog_info(const char * prefix,const char * pattern,va_list ap)720 vlog_info(const char *prefix, const char *pattern, va_list ap)
721 {
722 first_line_info();
723 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
724 if(prefix) {
725 fputs(prefix, stdout);
726 }
727 vfprintf(stdout, pattern, ap);
728 fflush(stdout);
729 va_end(ap);
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 */
log_testinfo_i(const char * pattern,...)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 */
log_testinfo(const char * pattern,...)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++;
763 }
764
765
vlog_verbose(const char * prefix,const char * pattern,va_list ap)766 static void vlog_verbose(const char *prefix, const char *pattern, va_list ap)
767 {
768 if ( VERBOSITY == FALSE )
769 return;
770
771 first_line_verbose();
772 fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
773 if(prefix) {
774 fputs(prefix, stdout);
775 }
776 vfprintf(stdout, pattern, ap);
777 fflush(stdout);
778 va_end(ap);
779 GLOBAL_PRINT_COUNT++;
780 if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
781 HANGING_OUTPUT=1;
782 } else {
783 HANGING_OUTPUT=0;
784 }
785 }
786
787 void T_CTEST_EXPORT2
log_err(const char * pattern,...)788 log_err(const char* pattern, ...)
789 {
790 va_list ap;
791 first_line_err();
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;
798 } else {
799 /* Count at least one error. */
800 ONE_ERROR=1;
801 }
802 va_start(ap, pattern);
803 vlog_err(NULL, pattern, ap);
804 }
805
806 UBool T_CTEST_EXPORT2
log_knownIssue(const char * ticket,const char * pattern,...)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
813 void T_CTEST_EXPORT2
log_err_status(UErrorCode status,const char * pattern,...)814 log_err_status(UErrorCode status, const char* pattern, ...)
815 {
816 va_list ap;
817 va_start(ap, pattern);
818
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) {
823 first_line_err();
824 /* Fatal error. */
825 if (strchr(pattern, '\n') != NULL) {
826 ++ERROR_COUNT;
827 } else {
828 ++ONE_ERROR;
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 {
835 first_line_err();
836 /* Fatal error. */
837 if(strchr(pattern, '\n') != NULL) {
838 ++ERROR_COUNT;
839 } else {
840 ++ONE_ERROR;
841 }
842 vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
843 }
844 }
845
846 void T_CTEST_EXPORT2
log_info(const char * pattern,...)847 log_info(const char* pattern, ...)
848 {
849 va_list ap;
850
851 va_start(ap, pattern);
852 vlog_info(NULL, pattern, ap);
853 }
854
855 void T_CTEST_EXPORT2
log_verbose(const char * pattern,...)856 log_verbose(const char* pattern, ...)
857 {
858 va_list ap;
859
860 va_start(ap, pattern);
861 vlog_verbose(NULL, pattern, ap);
862 }
863
864
865 void T_CTEST_EXPORT2
log_data_err(const char * pattern,...)866 log_data_err(const char* pattern, ...)
867 {
868 va_list ap;
869 va_start(ap, pattern);
870
871 go_offline_err();
872 ++DATA_ERROR_COUNT; /* for informational message at the end */
873
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 {
881 vlog_info("[DATA] ", pattern, ap);
882 }
883 }
884
885
886 /*
887 * Tracing functions.
888 */
889 static int traceFnNestingDepth = 0;
890 U_CDECL_BEGIN
TraceEntry(const void * context,int32_t fnNumber)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
TraceExit(const void * context,int32_t fnNumber,const char * fmt,va_list args)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
TraceData(const void * context,int32_t fnNumber,int32_t level,const char * fmt,va_list args)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
ctest_libMalloc(const void * context,size_t size)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 }
927 return malloc(size);
928 }
ctest_libRealloc(const void * context,void * mem,size_t size)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 }
937 return realloc(mem, size);
938 }
ctest_libFree(const void * context,void * mem)939 static void U_CALLCONV ctest_libFree(const void *context, void *mem) {
940 free(mem);
941 }
942
943 int T_CTEST_EXPORT2
initArgs(int argc,const char * const argv[],ArgHandlerPtr argHandler,void * context)944 initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context)
945 {
946 int i;
947 int argSkip = 0;
948
949 VERBOSITY = FALSE;
950 ERR_MSG = TRUE;
951
952 ARGV_0=argv[0];
953
954 for( i=1; i<argc; i++)
955 {
956 if ( argv[i][0] == '/' )
957 {
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;
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 {
972 /* doList = TRUE; */
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 }
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 }
990 else if (strcmp( argv[i], "-w") ==0)
991 {
992 WARN_ON_MISSING_DATA = TRUE;
993 }
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 }
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 }
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 }
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)
1060 {
1061 help( argv[0] );
1062 return 0;
1063 }
1064 else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0)
1065 {
1066 i += argSkip - 1;
1067 }
1068 else
1069 {
1070 printf("* unknown option: %s\n", argv[i]);
1071 help( argv[0] );
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
runTestRequest(const TestNode * root,int argc,const char * const argv[])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
1100 if(ctest_xml_init(ARGV_0)) {
1101 return 1; /* couldn't fire up XML thing */
1102 }
1103
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
1121 ON_LINE=FALSE; /* just in case */
1122
1123 if( doList == TRUE)
1124 showTests(toRun);
1125 else
1126 runTests(toRun);
1127
1128 ON_LINE=FALSE; /* just in case */
1129
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;
1137 }
1138 /* else option already handled by initArgs */
1139 }
1140
1141 if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */
1142 {
1143 ON_LINE=FALSE; /* just in case */
1144 if( doList == TRUE)
1145 showTests(toRun);
1146 else
1147 runTests(toRun);
1148 ON_LINE=FALSE; /* just in case */
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;
1159
1160 if(ctest_xml_fini()) {
1161 errorCount++;
1162 }
1163
1164 return errorCount; /* total error count */
1165 }
1166
1167 /**
1168 * Display program invocation arguments
1169 */
1170
help(const char * argv0)1171 static void help ( const char *argv0 )
1172 {
1173 printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
1174 " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
1175 " [ /path/to/test ]\n",
1176 argv0);
1177 printf(" -l To get a list of test names\n");
1178 printf(" -e to do exhaustive testing\n");
1179 printf(" -verbose To turn ON verbosity\n");
1180 printf(" -v To turn ON verbosity(same as -verbose)\n");
1181 printf(" -x file.xml Write junit format output to file.xml\n");
1182 printf(" -h To print this message\n");
1183 printf(" -K to turn OFF suppressing known issues\n");
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"
1186 " user has reduced/changed the common set of ICU data \n");
1187 printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n");
1188 printf(" -no_err_msg (same as -n) \n");
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");
1193 printf(" eg: to run just the utility tests type: cintltest /tsutil) \n");
1194 }
1195
1196 int32_t T_CTEST_EXPORT2
getTestOption(int32_t testOption)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
setTestOption(int32_t testOption,int32_t value)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:
1236 ICU_TRACE = (UTraceLevel)value;
1237 break;
1238 default :
1239 break;
1240 }
1241 }
1242
1243
1244 /*
1245 * ================== JUnit support ================================
1246 */
1247
1248 int32_t
1249 T_CTEST_EXPORT2
ctest_xml_setFileName(const char * name)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
ctest_xml_init(const char * rootName)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 }
1266 while(*rootName&&!isalnum((int)*rootName)) {
1267 rootName++;
1268 }
1269 strcpy(XML_PREFIX,rootName);
1270 {
1271 char *p = XML_PREFIX+strlen(XML_PREFIX);
1272 for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) {
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
ctest_xml_fini(void)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
ctest_xml_testcase(const char * classname,const char * name,const char * timeSeconds,const char * failMsg)1297 ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) {
1298 if(!XML_FILE) return 0;
1299
1300 fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, timeSeconds);
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
1311