summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/tools/ctestfw/ctest.c
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/tools/ctestfw/ctest.c')
-rw-r--r--intl/icu/source/tools/ctestfw/ctest.c1312
1 files changed, 1312 insertions, 0 deletions
diff --git a/intl/icu/source/tools/ctestfw/ctest.c b/intl/icu/source/tools/ctestfw/ctest.c
new file mode 100644
index 000000000..7d2a4470f
--- /dev/null
+++ b/intl/icu/source/tools/ctestfw/ctest.c
@@ -0,0 +1,1312 @@
+// Copyright (C) 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+********************************************************************************
+*
+* Copyright (C) 1996-2014, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+********************************************************************************
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "unicode/utrace.h"
+#include "unicode/uclean.h"
+#include "putilimp.h"
+#include "udbgutil.h"
+
+/* NOTES:
+ 3/20/1999 srl - strncpy called w/o setting nulls at the end
+ */
+
+#define MAXTESTNAME 128
+#define MAXTESTS 512
+#define MAX_TEST_LOG 4096
+
+/**
+ * How may columns to indent the 'OK' markers.
+ */
+#define FLAG_INDENT 45
+/**
+ * How many lines of scrollage can go by before we need to remind the user what the test is.
+ */
+#define PAGE_SIZE_LIMIT 25
+
+#ifndef SHOW_TIMES
+#define SHOW_TIMES 1
+#endif
+
+struct TestNode
+{
+ void (*test)(void);
+ struct TestNode* sibling;
+ struct TestNode* child;
+ char name[1]; /* This is dynamically allocated off the end with malloc. */
+};
+
+
+static const struct TestNode* currentTest;
+
+typedef enum { RUNTESTS, SHOWTESTS } TestMode;
+#define TEST_SEPARATOR '/'
+
+#ifndef C_TEST_IMPL
+#define C_TEST_IMPL
+#endif
+
+#include "unicode/ctest.h"
+
+static char ERROR_LOG[MAX_TEST_LOG][MAXTESTNAME];
+
+/* Local prototypes */
+static TestNode* addTestNode( TestNode *root, const char *name );
+
+static TestNode *createTestNode(const char* name, int32_t nameLen);
+
+static int strncmp_nullcheck( const char* s1,
+ const char* s2,
+ int n );
+
+static void getNextLevel( const char* name,
+ int* nameLen,
+ const char** nextName );
+
+static void iterateTestsWithLevel( const TestNode *root, int depth,
+ const TestNode** nodeList,
+ TestMode mode);
+
+static void help ( const char *argv0 );
+
+/**
+ * Do the work of logging an error. Doesn't increase the error count.
+ *
+ * @prefix optional prefix prepended to message, or NULL.
+ * @param pattern printf style pattern
+ * @param ap vprintf style arg list
+ */
+static void vlog_err(const char *prefix, const char *pattern, va_list ap);
+static void vlog_verbose(const char *prefix, const char *pattern, va_list ap);
+static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap);
+
+/**
+ * Log test structure, with indent
+ * @param pattern printf pattern
+ */
+static void log_testinfo_i(const char *pattern, ...);
+
+/**
+ * Log test structure, NO indent
+ * @param pattern printf pattern
+ */
+static void log_testinfo(const char *pattern, ...);
+
+/* If we need to make the framework multi-thread safe
+ we need to pass around the following vars
+*/
+static int ERRONEOUS_FUNCTION_COUNT = 0;
+static int ERROR_COUNT = 0; /* Count of errors from all tests. */
+static int ONE_ERROR = 0; /* were there any other errors? */
+static int DATA_ERROR_COUNT = 0; /* count of data related errors or warnings */
+static int INDENT_LEVEL = 0;
+static UBool NO_KNOWN = FALSE;
+static void *knownList = NULL;
+static char gTestName[1024] = "";
+static UBool ON_LINE = FALSE; /* are we on the top line with our test name? */
+static UBool HANGING_OUTPUT = FALSE; /* did the user leave us without a trailing \n ? */
+static int GLOBAL_PRINT_COUNT = 0; /* global count of printouts */
+int REPEAT_TESTS_INIT = 0; /* Was REPEAT_TESTS initialized? */
+int REPEAT_TESTS = 1; /* Number of times to run the test */
+int VERBOSITY = 0; /* be No-verbose by default */
+int ERR_MSG =1; /* error messages will be displayed by default*/
+int QUICK = 1; /* Skip some of the slower tests? */
+int WARN_ON_MISSING_DATA = 0; /* Reduce data errs to warnings? */
+UTraceLevel ICU_TRACE = UTRACE_OFF; /* ICU tracing level */
+size_t MINIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Minimum library memory allocation window that will fail. */
+size_t MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)-1; /* Maximum library memory allocation window that will fail. */
+static const char *ARGV_0 = "[ALL]";
+static const char *XML_FILE_NAME=NULL;
+static char XML_PREFIX[256];
+static const char *SUMMARY_FILE = NULL;
+FILE *XML_FILE = NULL;
+/*-------------------------------------------*/
+
+/* strncmp that also makes sure there's a \0 at s2[0] */
+static int strncmp_nullcheck( const char* s1,
+ const char* s2,
+ int n )
+{
+ if (((int)strlen(s2) >= n) && s2[n] != 0) {
+ return 3; /* null check fails */
+ }
+ else {
+ return strncmp ( s1, s2, n );
+ }
+}
+
+static void getNextLevel( const char* name,
+ int* nameLen,
+ const char** nextName )
+{
+ /* Get the next component of the name */
+ *nextName = strchr(name, TEST_SEPARATOR);
+
+ if( *nextName != 0 )
+ {
+ char n[255];
+ *nameLen = (int)((*nextName) - name);
+ (*nextName)++; /* skip '/' */
+ strncpy(n, name, *nameLen);
+ n[*nameLen] = 0;
+ /*printf("->%s-< [%d] -> [%s]\n", name, *nameLen, *nextName);*/
+ }
+ else {
+ *nameLen = (int)strlen(name);
+ }
+}
+
+static TestNode *createTestNode(const char* name, int32_t nameLen)
+{
+ TestNode *newNode;
+
+ newNode = (TestNode*)malloc(sizeof(TestNode) + (nameLen + 1));
+
+ newNode->test = NULL;
+ newNode->sibling = NULL;
+ newNode->child = NULL;
+
+ strncpy( newNode->name, name, nameLen );
+ newNode->name[nameLen] = 0;
+
+ return newNode;
+}
+
+void T_CTEST_EXPORT2
+cleanUpTestTree(TestNode *tn)
+{
+ if(tn->child != NULL) {
+ cleanUpTestTree(tn->child);
+ }
+ if(tn->sibling != NULL) {
+ cleanUpTestTree(tn->sibling);
+ }
+
+ free(tn);
+
+}
+
+
+void T_CTEST_EXPORT2
+addTest(TestNode** root,
+ TestFunctionPtr test,
+ const char* name )
+{
+ TestNode *newNode;
+
+ /*if this is the first Test created*/
+ if (*root == NULL)
+ *root = createTestNode("", 0);
+
+ newNode = addTestNode( *root, name );
+ assert(newNode != 0 );
+ /* printf("addTest: nreName = %s\n", newNode->name );*/
+
+ newNode->test = test;
+}
+
+/* non recursive insert function */
+static TestNode *addTestNode ( TestNode *root, const char *name )
+{
+ const char* nextName;
+ TestNode *nextNode, *curNode;
+ int nameLen; /* length of current 'name' */
+
+ /* remove leading slash */
+ if ( *name == TEST_SEPARATOR )
+ name++;
+
+ curNode = root;
+
+ for(;;)
+ {
+ /* Start with the next child */
+ nextNode = curNode->child;
+
+ getNextLevel ( name, &nameLen, &nextName );
+
+ /* printf("* %s\n", name );*/
+
+ /* if nextNode is already null, then curNode has no children
+ -- add them */
+ if( nextNode == NULL )
+ {
+ /* Add all children of the node */
+ do
+ {
+ /* Get the next component of the name */
+ getNextLevel(name, &nameLen, &nextName);
+
+ /* update curName to have the next name segment */
+ curNode->child = createTestNode(name, nameLen);
+ /* printf("*** added %s\n", curNode->child->name );*/
+ curNode = curNode->child;
+ name = nextName;
+ }
+ while( name != NULL );
+
+ return curNode;
+ }
+
+ /* Search across for the name */
+ while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 )
+ {
+ curNode = nextNode;
+ nextNode = nextNode -> sibling;
+
+ if ( nextNode == NULL )
+ {
+ /* Did not find 'name' on this level. */
+ nextNode = createTestNode(name, nameLen);
+ curNode->sibling = nextNode;
+ break;
+ }
+ }
+
+ /* nextNode matches 'name' */
+
+ if (nextName == NULL) /* end of the line */
+ {
+ return nextNode;
+ }
+
+ /* Loop again with the next item */
+ name = nextName;
+ curNode = nextNode;
+ }
+}
+
+/**
+ * Log the time taken. May not output anything.
+ * @param deltaTime change in time
+ */
+void T_CTEST_EXPORT2 str_timeDelta(char *str, UDate deltaTime) {
+ if (deltaTime > 110000.0 ) {
+ double mins = uprv_floor(deltaTime/60000.0);
+ sprintf(str, "[(%.0fm %.1fs)]", mins, (deltaTime-(mins*60000.0))/1000.0);
+ } else if (deltaTime > 1500.0) {
+ sprintf(str, "((%.1fs))", deltaTime/1000.0);
+ } else if(deltaTime>900.0) {
+ sprintf(str, "( %.2fs )", deltaTime/1000.0);
+ } else if(deltaTime > 5.0) {
+ sprintf(str, " (%.0fms) ", deltaTime);
+ } else {
+ str[0]=0; /* at least terminate it. */
+ }
+}
+
+static void print_timeDelta(UDate deltaTime) {
+ char str[256];
+ str_timeDelta(str, deltaTime);
+ if(str[0]) {
+ printf("%s", str);
+ }
+}
+
+/**
+ * Run or list tests (according to mode) in a subtree.
+ *
+ * @param root root of the subtree to operate on
+ * @param depth The depth of this tree (0=root)
+ * @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.
+ * @param mode what mode we are operating in.
+ */
+static void iterateTestsWithLevel ( const TestNode* root,
+ int depth,
+ const TestNode** nodeList,
+ TestMode mode)
+{
+ int i;
+
+ char pathToFunction[MAXTESTNAME] = "";
+ char separatorString[2] = { TEST_SEPARATOR, '\0'};
+#if SHOW_TIMES
+ UDate allStartTime = -1, allStopTime = -1;
+#endif
+
+ if(depth<2) {
+ allStartTime = uprv_getRawUTCtime();
+ }
+
+ if ( root == NULL )
+ return;
+
+ /* record the current root node, and increment depth. */
+ nodeList[depth++] = root;
+ /* depth is now the depth of root's children. */
+
+ /* Collect the 'path' to the current subtree. */
+ for ( i=0;i<(depth-1);i++ )
+ {
+ strcat(pathToFunction, nodeList[i]->name);
+ strcat(pathToFunction, separatorString);
+ }
+ strcat(pathToFunction, nodeList[i]->name); /* including 'root' */
+
+ /* print test name and space. */
+ INDENT_LEVEL = depth-1;
+ if(root->name[0]) {
+ log_testinfo_i("%s ", root->name);
+ } else {
+ log_testinfo_i("(%s) ", ARGV_0);
+ }
+ ON_LINE = TRUE; /* we are still on the line with the test name */
+
+
+ if ( (mode == RUNTESTS) &&
+ (root->test != NULL)) /* if root is a leaf node, run it */
+ {
+ int myERROR_COUNT = ERROR_COUNT;
+ int myGLOBAL_PRINT_COUNT = GLOBAL_PRINT_COUNT;
+#if SHOW_TIMES
+ UDate startTime, stopTime;
+ char timeDelta[256];
+ char timeSeconds[256];
+#else
+ const char timeDelta[] = "(unknown)";
+ const char timeSeconds[] = "0.000";
+#endif
+ currentTest = root;
+ INDENT_LEVEL = depth; /* depth of subitems */
+ ONE_ERROR=0;
+ HANGING_OUTPUT=FALSE;
+#if SHOW_TIMES
+ startTime = uprv_getRawUTCtime();
+#endif
+ strcpy(gTestName, pathToFunction);
+ root->test(); /* PERFORM THE TEST ************************/
+#if SHOW_TIMES
+ stopTime = uprv_getRawUTCtime();
+#endif
+ if(HANGING_OUTPUT) {
+ log_testinfo("\n");
+ HANGING_OUTPUT=FALSE;
+ }
+ INDENT_LEVEL = depth-1; /* depth of root */
+ currentTest = NULL;
+ if((ONE_ERROR>0)&&(ERROR_COUNT==0)) {
+ ERROR_COUNT++; /* There was an error without a newline */
+ }
+ ONE_ERROR=0;
+
+#if SHOW_TIMES
+ str_timeDelta(timeDelta, stopTime-startTime);
+ sprintf(timeSeconds, "%f", (stopTime-startTime)/1000.0);
+#endif
+ ctest_xml_testcase(pathToFunction, pathToFunction, timeSeconds, (myERROR_COUNT!=ERROR_COUNT)?"error":NULL);
+
+ if (myERROR_COUNT != ERROR_COUNT) {
+ log_testinfo_i("} ---[%d ERRORS in %s] ", ERROR_COUNT - myERROR_COUNT, pathToFunction);
+ strcpy(ERROR_LOG[ERRONEOUS_FUNCTION_COUNT++], pathToFunction);
+ } else {
+ if(!ON_LINE) { /* had some output */
+ int spaces = FLAG_INDENT-(depth-1);
+ log_testinfo_i("} %*s[OK] ", spaces, "---");
+ if((GLOBAL_PRINT_COUNT-myGLOBAL_PRINT_COUNT)>PAGE_SIZE_LIMIT) {
+ log_testinfo(" %s ", pathToFunction); /* in case they forgot. */
+ }
+ } else {
+ /* put -- out at 30 sp. */
+ int spaces = FLAG_INDENT-(strlen(root->name)+depth);
+ if(spaces<0) spaces=0;
+ log_testinfo(" %*s[OK] ", spaces,"---");
+ }
+ }
+
+#if SHOW_TIMES
+ if(timeDelta[0]) printf("%s", timeDelta);
+#endif
+
+ ON_LINE = TRUE; /* we are back on-line */
+ }
+
+ INDENT_LEVEL = depth-1; /* root */
+
+ /* we want these messages to be at 0 indent. so just push the indent level breifly. */
+ if(mode==SHOWTESTS) {
+ log_testinfo("---%s%c\n",pathToFunction, nodeList[i]->test?' ':TEST_SEPARATOR );
+ }
+
+ INDENT_LEVEL = depth;
+
+ if(root->child) {
+ int myERROR_COUNT = ERROR_COUNT;
+ int myGLOBAL_PRINT_COUNT = GLOBAL_PRINT_COUNT;
+ if(mode!=SHOWTESTS) {
+ INDENT_LEVEL=depth-1;
+ log_testinfo("{\n");
+ INDENT_LEVEL=depth;
+ }
+
+ iterateTestsWithLevel ( root->child, depth, nodeList, mode );
+
+ if(mode!=SHOWTESTS) {
+ INDENT_LEVEL=depth-1;
+ log_testinfo_i("} "); /* TODO: summarize subtests */
+ if((depth>1) && (ERROR_COUNT > myERROR_COUNT)) {
+ log_testinfo("[%d %s in %s] ", ERROR_COUNT-myERROR_COUNT, (ERROR_COUNT-myERROR_COUNT)==1?"error":"errors", pathToFunction);
+ } else if((GLOBAL_PRINT_COUNT-myGLOBAL_PRINT_COUNT)>PAGE_SIZE_LIMIT || (depth<1)) {
+ if(pathToFunction[0]) {
+ log_testinfo(" %s ", pathToFunction); /* in case they forgot. */
+ } else {
+ log_testinfo(" / (%s) ", ARGV_0);
+ }
+ }
+
+ ON_LINE=TRUE;
+ }
+ }
+ depth--;
+
+#if SHOW_TIMES
+ if(depth<2) {
+ allStopTime = uprv_getRawUTCtime();
+ print_timeDelta(allStopTime-allStartTime);
+ }
+#endif
+
+ if(mode!=SHOWTESTS && ON_LINE) {
+ log_testinfo("\n");
+ }
+
+ if ( depth != 0 ) { /* DO NOT iterate over siblings of the root. TODO: why not? */
+ iterateTestsWithLevel ( root->sibling, depth, nodeList, mode );
+ }
+}
+
+
+
+void T_CTEST_EXPORT2
+showTests ( const TestNode *root )
+{
+ /* make up one for them */
+ const TestNode *nodeList[MAXTESTS];
+
+ if (root == NULL)
+ log_err("TEST CAN'T BE FOUND!");
+
+ iterateTestsWithLevel ( root, 0, nodeList, SHOWTESTS );
+
+}
+
+void T_CTEST_EXPORT2
+runTests ( const TestNode *root )
+{
+ int i;
+ const TestNode *nodeList[MAXTESTS];
+ /* make up one for them */
+
+
+ if (root == NULL)
+ log_err("TEST CAN'T BE FOUND!\n");
+
+ ERRONEOUS_FUNCTION_COUNT = ERROR_COUNT = 0;
+ iterateTestsWithLevel ( root, 0, nodeList, RUNTESTS );
+
+ /*print out result summary*/
+
+ ON_LINE=FALSE; /* just in case */
+
+ if(knownList != NULL) {
+ if( udbg_knownIssue_print(knownList) ) {
+ fprintf(stdout, "(To run suppressed tests, use the -K option.) \n\n");
+ }
+ udbg_knownIssue_close(knownList);
+ knownList = NULL;
+ }
+
+ if (ERROR_COUNT)
+ {
+ fprintf(stdout,"\nSUMMARY:\n");
+ fflush(stdout);
+ fprintf(stdout,"******* [Total error count:\t%d]\n", ERROR_COUNT);
+ fflush(stdout);
+ fprintf(stdout, " Errors in\n");
+ for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++)
+ fprintf(stdout, "[%s]\n",ERROR_LOG[i]);
+ if(SUMMARY_FILE != NULL) {
+ FILE *summf = fopen(SUMMARY_FILE, "w");
+ if(summf!=NULL) {
+ for (i=0;i < ERRONEOUS_FUNCTION_COUNT; i++)
+ fprintf(summf, "%s\n",ERROR_LOG[i]);
+ fclose(summf);
+ }
+ }
+ }
+ else
+ {
+ log_testinfo("\n[All tests passed successfully...]\n");
+ }
+
+ if(DATA_ERROR_COUNT) {
+ if(WARN_ON_MISSING_DATA==0) {
+ log_testinfo("\t*Note* some errors are data-loading related. If the data used is not the \n"
+ "\tstock ICU data (i.e some have been added or removed), consider using\n"
+ "\tthe '-w' option to turn these errors into warnings.\n");
+ } else {
+ log_testinfo("\t*WARNING* some data-loading errors were ignored by the -w option.\n");
+ }
+ }
+}
+
+const char* T_CTEST_EXPORT2
+getTestName(void)
+{
+ if(currentTest != NULL) {
+ return currentTest->name;
+ } else {
+ return NULL;
+ }
+}
+
+const TestNode* T_CTEST_EXPORT2
+getTest(const TestNode* root, const char* name)
+{
+ const char* nextName;
+ TestNode *nextNode;
+ const TestNode* curNode;
+ int nameLen; /* length of current 'name' */
+
+ if (root == NULL) {
+ log_err("TEST CAN'T BE FOUND!\n");
+ return NULL;
+ }
+ /* remove leading slash */
+ if ( *name == TEST_SEPARATOR )
+ name++;
+
+ curNode = root;
+
+ for(;;)
+ {
+ /* Start with the next child */
+ nextNode = curNode->child;
+
+ getNextLevel ( name, &nameLen, &nextName );
+
+ /* printf("* %s\n", name );*/
+
+ /* if nextNode is already null, then curNode has no children
+ -- add them */
+ if( nextNode == NULL )
+ {
+ return NULL;
+ }
+
+ /* Search across for the name */
+ while (strncmp_nullcheck ( name, nextNode->name, nameLen) != 0 )
+ {
+ curNode = nextNode;
+ nextNode = nextNode -> sibling;
+
+ if ( nextNode == NULL )
+ {
+ /* Did not find 'name' on this level. */
+ return NULL;
+ }
+ }
+
+ /* nextNode matches 'name' */
+
+ if (nextName == NULL) /* end of the line */
+ {
+ return nextNode;
+ }
+
+ /* Loop again with the next item */
+ name = nextName;
+ curNode = nextNode;
+ }
+}
+
+/* =========== io functions ======== */
+
+static void go_offline_with_marker(const char *mrk) {
+ UBool wasON_LINE = ON_LINE;
+
+ if(ON_LINE) {
+ log_testinfo(" {\n");
+ ON_LINE=FALSE;
+ }
+
+ if(!HANGING_OUTPUT || wasON_LINE) {
+ if(mrk != NULL) {
+ fputs(mrk, stdout);
+ }
+ }
+}
+
+static void go_offline() {
+ go_offline_with_marker(NULL);
+}
+
+static void go_offline_err() {
+ go_offline();
+}
+
+static void first_line_verbose() {
+ go_offline_with_marker("v");
+}
+
+static void first_line_err() {
+ go_offline_with_marker("!");
+}
+
+static void first_line_info() {
+ go_offline_with_marker("\"");
+}
+
+static void first_line_test() {
+ fputs(" ", stdout);
+}
+
+
+static void vlog_err(const char *prefix, const char *pattern, va_list ap)
+{
+ if( ERR_MSG == FALSE){
+ return;
+ }
+ fputs("!", stdout); /* col 1 - bang */
+ fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
+ if(prefix) {
+ fputs(prefix, stdout);
+ }
+ vfprintf(stdout, pattern, ap);
+ fflush(stdout);
+ va_end(ap);
+ if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
+ HANGING_OUTPUT=1;
+ } else {
+ HANGING_OUTPUT=0;
+ }
+ GLOBAL_PRINT_COUNT++;
+}
+
+static UBool vlog_knownIssue(const char *ticket, const char *pattern, va_list ap)
+{
+ char buf[2048];
+ UBool firstForTicket;
+ UBool firstForWhere;
+
+ if(NO_KNOWN) return FALSE;
+ if(pattern==NULL) pattern="";
+
+ vsprintf(buf, pattern, ap);
+ knownList = udbg_knownIssue_open(knownList, ticket, gTestName, buf,
+ &firstForTicket, &firstForWhere);
+
+ if(firstForTicket || firstForWhere) {
+ log_info("(Known issue #%s) %s\n", ticket, buf);
+ } else {
+ log_verbose("(Known issue #%s) %s\n", ticket, buf);
+ }
+
+ return TRUE;
+}
+
+
+void T_CTEST_EXPORT2
+vlog_info(const char *prefix, const char *pattern, va_list ap)
+{
+ first_line_info();
+ fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
+ if(prefix) {
+ fputs(prefix, stdout);
+ }
+ vfprintf(stdout, pattern, ap);
+ fflush(stdout);
+ va_end(ap);
+ if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
+ HANGING_OUTPUT=1;
+ } else {
+ HANGING_OUTPUT=0;
+ }
+ GLOBAL_PRINT_COUNT++;
+}
+/**
+ * Log test structure, with indent
+ */
+static void log_testinfo_i(const char *pattern, ...)
+{
+ va_list ap;
+ first_line_test();
+ fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
+ va_start(ap, pattern);
+ vfprintf(stdout, pattern, ap);
+ fflush(stdout);
+ va_end(ap);
+ GLOBAL_PRINT_COUNT++;
+}
+/**
+ * Log test structure (no ident)
+ */
+static void log_testinfo(const char *pattern, ...)
+{
+ va_list ap;
+ va_start(ap, pattern);
+ first_line_test();
+ vfprintf(stdout, pattern, ap);
+ fflush(stdout);
+ va_end(ap);
+ GLOBAL_PRINT_COUNT++;
+}
+
+
+static void vlog_verbose(const char *prefix, const char *pattern, va_list ap)
+{
+ if ( VERBOSITY == FALSE )
+ return;
+
+ first_line_verbose();
+ fprintf(stdout, "%-*s", INDENT_LEVEL,"" );
+ if(prefix) {
+ fputs(prefix, stdout);
+ }
+ vfprintf(stdout, pattern, ap);
+ fflush(stdout);
+ va_end(ap);
+ GLOBAL_PRINT_COUNT++;
+ if((*pattern==0) || (pattern[strlen(pattern)-1]!='\n')) {
+ HANGING_OUTPUT=1;
+ } else {
+ HANGING_OUTPUT=0;
+ }
+}
+
+void T_CTEST_EXPORT2
+log_err(const char* pattern, ...)
+{
+ va_list ap;
+ first_line_err();
+ if(strchr(pattern, '\n') != NULL) {
+ /*
+ * Count errors only if there is a line feed in the pattern
+ * so that we do not exaggerate our error count.
+ */
+ ++ERROR_COUNT;
+ } else {
+ /* Count at least one error. */
+ ONE_ERROR=1;
+ }
+ va_start(ap, pattern);
+ vlog_err(NULL, pattern, ap);
+}
+
+UBool T_CTEST_EXPORT2
+log_knownIssue(const char *ticket, const char *pattern, ...) {
+ va_list ap;
+ va_start(ap, pattern);
+ return vlog_knownIssue(ticket, pattern, ap);
+}
+
+void T_CTEST_EXPORT2
+log_err_status(UErrorCode status, const char* pattern, ...)
+{
+ va_list ap;
+ va_start(ap, pattern);
+
+ if ((status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR)) {
+ ++DATA_ERROR_COUNT; /* for informational message at the end */
+
+ if (WARN_ON_MISSING_DATA == 0) {
+ first_line_err();
+ /* Fatal error. */
+ if (strchr(pattern, '\n') != NULL) {
+ ++ERROR_COUNT;
+ } else {
+ ++ONE_ERROR;
+ }
+ vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
+ } else {
+ vlog_info("[DATA] ", pattern, ap);
+ }
+ } else {
+ first_line_err();
+ /* Fatal error. */
+ if(strchr(pattern, '\n') != NULL) {
+ ++ERROR_COUNT;
+ } else {
+ ++ONE_ERROR;
+ }
+ vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
+ }
+}
+
+void T_CTEST_EXPORT2
+log_info(const char* pattern, ...)
+{
+ va_list ap;
+
+ va_start(ap, pattern);
+ vlog_info(NULL, pattern, ap);
+}
+
+void T_CTEST_EXPORT2
+log_verbose(const char* pattern, ...)
+{
+ va_list ap;
+
+ va_start(ap, pattern);
+ vlog_verbose(NULL, pattern, ap);
+}
+
+
+void T_CTEST_EXPORT2
+log_data_err(const char* pattern, ...)
+{
+ va_list ap;
+ va_start(ap, pattern);
+
+ go_offline_err();
+ ++DATA_ERROR_COUNT; /* for informational message at the end */
+
+ if(WARN_ON_MISSING_DATA == 0) {
+ /* Fatal error. */
+ if(strchr(pattern, '\n') != NULL) {
+ ++ERROR_COUNT;
+ }
+ vlog_err(NULL, pattern, ap); /* no need for prefix in default case */
+ } else {
+ vlog_info("[DATA] ", pattern, ap);
+ }
+}
+
+
+/*
+ * Tracing functions.
+ */
+static int traceFnNestingDepth = 0;
+U_CDECL_BEGIN
+static void U_CALLCONV TraceEntry(const void *context, int32_t fnNumber) {
+ char buf[500];
+ utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() enter.\n", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0;
+ fputs(buf, stdout);
+ traceFnNestingDepth++;
+}
+
+static void U_CALLCONV TraceExit(const void *context, int32_t fnNumber, const char *fmt, va_list args) { char buf[500];
+
+ if (traceFnNestingDepth>0) {
+ traceFnNestingDepth--;
+ }
+ utrace_format(buf, sizeof(buf), traceFnNestingDepth*3, "%s() ", utrace_functionName(fnNumber)); buf[sizeof(buf)-1]=0;
+ fputs(buf, stdout);
+ utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
+ buf[sizeof(buf)-1]=0;
+ fputs(buf, stdout);
+ putc('\n', stdout);
+}
+
+static void U_CALLCONV TraceData(const void *context, int32_t fnNumber,
+ int32_t level, const char *fmt, va_list args) {
+ char buf[500];
+ utrace_vformat(buf, sizeof(buf), traceFnNestingDepth*3, fmt, args);
+ buf[sizeof(buf)-1]=0;
+ fputs(buf, stdout);
+ putc('\n', stdout);
+}
+
+static void *U_CALLCONV ctest_libMalloc(const void *context, size_t size) {
+ /*if (VERBOSITY) {
+ printf("Allocated %ld\n", (long)size);
+ }*/
+ if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
+ return NULL;
+ }
+ return malloc(size);
+}
+static void *U_CALLCONV ctest_libRealloc(const void *context, void *mem, size_t size) {
+ /*if (VERBOSITY) {
+ printf("Reallocated %ld\n", (long)size);
+ }*/
+ if (MINIMUM_MEMORY_SIZE_FAILURE <= size && size <= MAXIMUM_MEMORY_SIZE_FAILURE) {
+ /*free(mem);*/ /* Realloc doesn't free on failure. */
+ return NULL;
+ }
+ return realloc(mem, size);
+}
+static void U_CALLCONV ctest_libFree(const void *context, void *mem) {
+ free(mem);
+}
+
+int T_CTEST_EXPORT2
+initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context)
+{
+ int i;
+ int argSkip = 0;
+
+ VERBOSITY = FALSE;
+ ERR_MSG = TRUE;
+
+ ARGV_0=argv[0];
+
+ for( i=1; i<argc; i++)
+ {
+ if ( argv[i][0] == '/' )
+ {
+ /* We don't run the tests here. */
+ continue;
+ }
+ else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0))
+ {
+ /* We don't run the tests here. */
+ continue;
+ }
+ else if (strcmp( argv[i], "-v" )==0 || strcmp( argv[i], "-verbose")==0)
+ {
+ VERBOSITY = TRUE;
+ }
+ else if (strcmp( argv[i], "-l" )==0 )
+ {
+ /* doList = TRUE; */
+ }
+ else if (strcmp( argv[i], "-e1") == 0)
+ {
+ QUICK = -1;
+ }
+ else if (strcmp( argv[i], "-e") ==0)
+ {
+ QUICK = 0;
+ }
+ else if (strcmp( argv[i], "-K") ==0)
+ {
+ NO_KNOWN = 1;
+ }
+ else if (strncmp( argv[i], "-E",2) ==0)
+ {
+ SUMMARY_FILE=argv[i]+2;
+ }
+ else if (strcmp( argv[i], "-w") ==0)
+ {
+ WARN_ON_MISSING_DATA = TRUE;
+ }
+ else if (strcmp( argv[i], "-m") ==0)
+ {
+ UErrorCode errorCode = U_ZERO_ERROR;
+ if (i+1 < argc) {
+ char *endPtr = NULL;
+ i++;
+ MINIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(argv[i], &endPtr, 10);
+ if (endPtr == argv[i]) {
+ printf("Can't parse %s\n", argv[i]);
+ help(argv[0]);
+ return 0;
+ }
+ if (*endPtr == '-') {
+ char *maxPtr = endPtr+1;
+ endPtr = NULL;
+ MAXIMUM_MEMORY_SIZE_FAILURE = (size_t)strtol(maxPtr, &endPtr, 10);
+ if (endPtr == argv[i]) {
+ printf("Can't parse %s\n", argv[i]);
+ help(argv[0]);
+ return 0;
+ }
+ }
+ }
+ /* Use the default value */
+ u_setMemoryFunctions(NULL, ctest_libMalloc, ctest_libRealloc, ctest_libFree, &errorCode);
+ if (U_FAILURE(errorCode)) {
+ printf("u_setMemoryFunctions returned %s\n", u_errorName(errorCode));
+ return 0;
+ }
+ }
+ else if(strcmp( argv[i], "-n") == 0 || strcmp( argv[i], "-no_err_msg") == 0)
+ {
+ ERR_MSG = FALSE;
+ }
+ else if (strcmp( argv[i], "-r") == 0)
+ {
+ if (!REPEAT_TESTS_INIT) {
+ REPEAT_TESTS++;
+ }
+ }
+ else if (strcmp( argv[i], "-x") == 0)
+ {
+ if(++i>=argc) {
+ printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n");
+ return 0;
+ }
+ if(ctest_xml_setFileName(argv[i])) { /* set the name */
+ return 0;
+ }
+ }
+ else if (strcmp( argv[i], "-t_info") == 0) {
+ ICU_TRACE = UTRACE_INFO;
+ }
+ else if (strcmp( argv[i], "-t_error") == 0) {
+ ICU_TRACE = UTRACE_ERROR;
+ }
+ else if (strcmp( argv[i], "-t_warn") == 0) {
+ ICU_TRACE = UTRACE_WARNING;
+ }
+ else if (strcmp( argv[i], "-t_verbose") == 0) {
+ ICU_TRACE = UTRACE_VERBOSE;
+ }
+ else if (strcmp( argv[i], "-t_oc") == 0) {
+ ICU_TRACE = UTRACE_OPEN_CLOSE;
+ }
+ else if (strcmp( argv[i], "-h" )==0 || strcmp( argv[i], "--help" )==0)
+ {
+ help( argv[0] );
+ return 0;
+ }
+ else if (argHandler != NULL && (argSkip = argHandler(i, argc, argv, context)) > 0)
+ {
+ i += argSkip - 1;
+ }
+ else
+ {
+ printf("* unknown option: %s\n", argv[i]);
+ help( argv[0] );
+ return 0;
+ }
+ }
+ if (ICU_TRACE != UTRACE_OFF) {
+ utrace_setFunctions(NULL, TraceEntry, TraceExit, TraceData);
+ utrace_setLevel(ICU_TRACE);
+ }
+
+ return 1; /* total error count */
+}
+
+int T_CTEST_EXPORT2
+runTestRequest(const TestNode* root,
+ int argc,
+ const char* const argv[])
+{
+ /**
+ * This main will parse the l, v, h, n, and path arguments
+ */
+ const TestNode* toRun;
+ int i;
+ int doList = FALSE;
+ int subtreeOptionSeen = FALSE;
+
+ int errorCount = 0;
+
+ toRun = root;
+
+ if(ctest_xml_init(ARGV_0)) {
+ return 1; /* couldn't fire up XML thing */
+ }
+
+ for( i=1; i<argc; i++)
+ {
+ if ( argv[i][0] == '/' )
+ {
+ printf("Selecting subtree '%s'\n", argv[i]);
+
+ if ( argv[i][1] == 0 )
+ toRun = root;
+ else
+ toRun = getTest(root, argv[i]);
+
+ if ( toRun == NULL )
+ {
+ printf("* Could not find any matching subtree\n");
+ return -1;
+ }
+
+ ON_LINE=FALSE; /* just in case */
+
+ if( doList == TRUE)
+ showTests(toRun);
+ else
+ runTests(toRun);
+
+ ON_LINE=FALSE; /* just in case */
+
+ errorCount += ERROR_COUNT;
+
+ subtreeOptionSeen = TRUE;
+ } else if ((strcmp( argv[i], "-a") == 0) || (strcmp(argv[i],"-all") == 0)) {
+ subtreeOptionSeen=FALSE;
+ } else if (strcmp( argv[i], "-l") == 0) {
+ doList = TRUE;
+ }
+ /* else option already handled by initArgs */
+ }
+
+ if( subtreeOptionSeen == FALSE) /* no other subtree given, run the default */
+ {
+ ON_LINE=FALSE; /* just in case */
+ if( doList == TRUE)
+ showTests(toRun);
+ else
+ runTests(toRun);
+ ON_LINE=FALSE; /* just in case */
+
+ errorCount += ERROR_COUNT;
+ }
+ else
+ {
+ if( ( doList == FALSE ) && ( errorCount > 0 ) )
+ printf(" Total errors: %d\n", errorCount );
+ }
+
+ REPEAT_TESTS_INIT = 1;
+
+ if(ctest_xml_fini()) {
+ errorCount++;
+ }
+
+ return errorCount; /* total error count */
+}
+
+/**
+ * Display program invocation arguments
+ */
+
+static void help ( const char *argv0 )
+{
+ printf("Usage: %s [ -l ] [ -v ] [ -verbose] [-a] [ -all] [-n] [ -no_err_msg]\n"
+ " [ -h ] [-t_info | -t_error | -t_warn | -t_oc | -t_verbose] [-m n[-q] ]\n"
+ " [ /path/to/test ]\n",
+ argv0);
+ printf(" -l To get a list of test names\n");
+ printf(" -e to do exhaustive testing\n");
+ printf(" -verbose To turn ON verbosity\n");
+ printf(" -v To turn ON verbosity(same as -verbose)\n");
+ printf(" -x file.xml Write junit format output to file.xml\n");
+ printf(" -h To print this message\n");
+ printf(" -K to turn OFF suppressing known issues\n");
+ printf(" -n To turn OFF printing error messages\n");
+ printf(" -w Don't fail on data-loading errs, just warn. Useful if\n"
+ " user has reduced/changed the common set of ICU data \n");
+ printf(" -t_info | -t_error | -t_warn | -t_oc | -t_verbose Enable ICU tracing\n");
+ printf(" -no_err_msg (same as -n) \n");
+ printf(" -m n[-q] Min-Max memory size that will cause an allocation failure.\n");
+ printf(" The default is the maximum value of size_t. Max is optional.\n");
+ printf(" -r Repeat tests after calling u_cleanup \n");
+ printf(" [/subtest] To run a subtest \n");
+ printf(" eg: to run just the utility tests type: cintltest /tsutil) \n");
+}
+
+int32_t T_CTEST_EXPORT2
+getTestOption ( int32_t testOption ) {
+ switch (testOption) {
+ case VERBOSITY_OPTION:
+ return VERBOSITY;
+ case WARN_ON_MISSING_DATA_OPTION:
+ return WARN_ON_MISSING_DATA;
+ case QUICK_OPTION:
+ return QUICK;
+ case REPEAT_TESTS_OPTION:
+ return REPEAT_TESTS;
+ case ERR_MSG_OPTION:
+ return ERR_MSG;
+ case ICU_TRACE_OPTION:
+ return ICU_TRACE;
+ default :
+ return 0;
+ }
+}
+
+void T_CTEST_EXPORT2
+setTestOption ( int32_t testOption, int32_t value) {
+ if (value == DECREMENT_OPTION_VALUE) {
+ value = getTestOption(testOption);
+ --value;
+ }
+ switch (testOption) {
+ case VERBOSITY_OPTION:
+ VERBOSITY = value;
+ break;
+ case WARN_ON_MISSING_DATA_OPTION:
+ WARN_ON_MISSING_DATA = value;
+ break;
+ case QUICK_OPTION:
+ QUICK = value;
+ break;
+ case REPEAT_TESTS_OPTION:
+ REPEAT_TESTS = value;
+ break;
+ case ICU_TRACE_OPTION:
+ ICU_TRACE = (UTraceLevel)value;
+ break;
+ default :
+ break;
+ }
+}
+
+
+/*
+ * ================== JUnit support ================================
+ */
+
+int32_t
+T_CTEST_EXPORT2
+ctest_xml_setFileName(const char *name) {
+ XML_FILE_NAME=name;
+ return 0;
+}
+
+
+int32_t
+T_CTEST_EXPORT2
+ctest_xml_init(const char *rootName) {
+ if(!XML_FILE_NAME) return 0;
+ XML_FILE = fopen(XML_FILE_NAME,"w");
+ if(!XML_FILE) {
+ perror("fopen");
+ fprintf(stderr," Error: couldn't open XML output file %s\n", XML_FILE_NAME);
+ return 1;
+ }
+ while(*rootName&&!isalnum((int)*rootName)) {
+ rootName++;
+ }
+ strcpy(XML_PREFIX,rootName);
+ {
+ char *p = XML_PREFIX+strlen(XML_PREFIX);
+ for(p--;*p&&p>XML_PREFIX&&!isalnum((int)*p);p--) {
+ *p=0;
+ }
+ }
+ /* write prefix */
+ fprintf(XML_FILE, "<testsuite name=\"%s\">\n", XML_PREFIX);
+
+ return 0;
+}
+
+int32_t
+T_CTEST_EXPORT2
+ctest_xml_fini(void) {
+ if(!XML_FILE) return 0;
+
+ fprintf(XML_FILE, "</testsuite>\n");
+ fclose(XML_FILE);
+ printf(" ( test results written to %s )\n", XML_FILE_NAME);
+ XML_FILE=0;
+ return 0;
+}
+
+
+int32_t
+T_CTEST_EXPORT2
+ctest_xml_testcase(const char *classname, const char *name, const char *timeSeconds, const char *failMsg) {
+ if(!XML_FILE) return 0;
+
+ fprintf(XML_FILE, "\t<testcase classname=\"%s:%s\" name=\"%s:%s\" time=\"%s\"", XML_PREFIX, classname, XML_PREFIX, name, timeSeconds);
+ if(failMsg) {
+ fprintf(XML_FILE, ">\n\t\t<failure type=\"err\" message=\"%s\"/>\n\t</testcase>\n", failMsg);
+ } else {
+ fprintf(XML_FILE, "/>\n");
+ }
+
+ return 0;
+}
+
+