diff options
Diffstat (limited to 'intl/icu/source/tools/ctestfw')
18 files changed, 4143 insertions, 0 deletions
diff --git a/intl/icu/source/tools/ctestfw/Makefile.in b/intl/icu/source/tools/ctestfw/Makefile.in new file mode 100644 index 0000000000..2ad1fbe579 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/Makefile.in @@ -0,0 +1,149 @@ +# Copyright (C) 2016 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +#****************************************************************************** +# +# Copyright (C) 1999-2011, International Business Machines +# Corporation and others. All Rights Reserved. +# +#****************************************************************************** +## Makefile.in for ICU - tools/ctestfw +## Stephen F. Booth + +## Source directory information +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ + +top_builddir = ../.. + +## All the flags and other definitions are included here. +include $(top_builddir)/icudefs.mk + +## Build directory information +subdir = tools/ctestfw + +## Extra files to remove for 'make clean' +CLEANFILES = *~ $(DEPS) $(IMPORT_LIB) $(MIDDLE_IMPORT_LIB) $(FINAL_IMPORT_LIB) + +## Target information + +TARGET_STUBNAME=$(CTESTFW_STUBNAME) + +ifneq ($(ENABLE_STATIC),) +TARGET = $(LIBSICU)$(TARGET_STUBNAME)$(ICULIBSUFFIX).$(A) +endif + +ifneq ($(ENABLE_SHARED),) +SO_TARGET = $(LIBICU)$(TARGET_STUBNAME)$(ICULIBSUFFIX).$(SO) +ALL_SO_TARGETS = $(SO_TARGET) $(MIDDLE_SO_TARGET) $(FINAL_SO_TARGET) $(SHARED_OBJECT) +endif + +ALL_TARGETS = $(TARGET) $(ALL_SO_TARGETS) + +DYNAMICCPPFLAGS = $(SHAREDLIBCPPFLAGS) +DYNAMICCFLAGS = $(SHAREDLIBCFLAGS) +DYNAMICCXXFLAGS = $(SHAREDLIBCXXFLAGS) +CFLAGS += $(LIBCFLAGS) +CXXFLAGS += $(LIBCXXFLAGS) + +CPPFLAGS += -I$(top_srcdir)/common -I$(top_srcdir)/i18n -I$(srcdir)/../toolutil -I$(srcdir) $(LIBCPPFLAGS) $(CPPFLAGSCTESTFW) +DEFS += -DT_CTEST_IMPLEMENTATION +LDFLAGS += $(LDFLAGSCTESTFW) +LIBS = $(LIBICUTOOLUTIL) $(LIBICUI18N) $(LIBICUUC) $(DEFAULT_LIBS) + +SOURCES = $(shell cat $(srcdir)/sources.txt) +OBJECTS = $(patsubst %.cpp,%.o,$(patsubst %.c,%.o, $(SOURCES))) + +STATIC_OBJECTS = $(OBJECTS:.o=.$(STATIC_O)) + +DEPS = $(OBJECTS:.o=.d) + +-include Makefile.local + +## List of phony targets +.PHONY : all all-local install install-local clean clean-local \ +distclean distclean-local dist dist-local check check-local + +## Clear suffix list +.SUFFIXES : + +## List of standard targets +all: all-local +install: install-local +clean: clean-local +distclean : distclean-local +dist: dist-local +check: all check-local + +all-local: $(ALL_TARGETS) + +install-local: install-library + +install-library: all-local + $(MKINSTALLDIRS) $(DESTDIR)$(libdir) +ifneq ($(ENABLE_STATIC),) + $(INSTALL-L) $(TARGET) $(DESTDIR)$(libdir) +endif +ifneq ($(ENABLE_SHARED),) +# For MinGW, do we want the DLL to go in the bin location? +ifeq ($(MINGW_MOVEDLLSTOBINDIR),YES) + $(MKINSTALLDIRS) $(DESTDIR)$(bindir) + $(INSTALL-L) $(FINAL_SO_TARGET) $(DESTDIR)$(bindir) +else + $(INSTALL-L) $(FINAL_SO_TARGET) $(DESTDIR)$(libdir) +ifneq ($(FINAL_SO_TARGET),$(SO_TARGET)) + cd $(DESTDIR)$(libdir) && $(RM) $(notdir $(SO_TARGET)) && ln -s $(notdir $(FINAL_SO_TARGET)) $(notdir $(SO_TARGET)) +ifneq ($(FINAL_SO_TARGET),$(MIDDLE_SO_TARGET)) + cd $(DESTDIR)$(libdir) && $(RM) $(notdir $(MIDDLE_SO_TARGET)) && ln -s $(notdir $(FINAL_SO_TARGET)) $(notdir $(MIDDLE_SO_TARGET)) +endif +endif +endif +ifneq ($(IMPORT_LIB_EXT),) + $(INSTALL-L) $(FINAL_IMPORT_LIB) $(DESTDIR)$(libdir) +ifneq ($(IMPORT_LIB),$(FINAL_IMPORT_LIB)) + cd $(DESTDIR)$(libdir) && $(RM) $(notdir $(IMPORT_LIB)) && ln -s $(notdir $(FINAL_IMPORT_LIB)) $(notdir $(IMPORT_LIB)) +endif +ifneq ($(MIDDLE_IMPORT_LIB),$(FINAL_IMPORT_LIB)) + cd $(DESTDIR)$(libdir) && $(RM) $(notdir $(MIDDLE_IMPORT_LIB)) && ln -s $(notdir $(FINAL_IMPORT_LIB)) $(notdir $(MIDDLE_IMPORT_LIB)) +endif +endif +endif + +dist-local: + +clean-local: + test -z "$(CLEANFILES)" || $(RMV) $(CLEANFILES) + $(RMV) $(OBJECTS) $(STATIC_OBJECTS) $(ALL_TARGETS) + +distclean-local: clean-local + $(RMV) Makefile + +check-local: all-local + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +ifneq ($(ENABLE_STATIC),) +$(TARGET): $(STATIC_OBJECTS) + $(AR) $(ARFLAGS) $(AR_OUTOPT)$@ $^ + $(RANLIB) $@ +endif + +ifneq ($(ENABLE_SHARED),) +$(SHARED_OBJECT): $(OBJECTS) + $(SHLIB.cc) $(LD_SONAME) $(OUTOPT)$@ $^ $(LIBS) +ifeq ($(ENABLE_RPATH),YES) +ifneq ($(wildcard $(libdir)/$(MIDDLE_SO_TARGET)),) + $(warning RPATH warning: --enable-rpath means test programs may use existing $(libdir)/$(MIDDLE_SO_TARGET)) +endif +endif +endif + +ifeq (,$(MAKECMDGOALS)) +-include $(DEPS) +else +ifneq ($(patsubst %clean,,$(MAKECMDGOALS)),) +-include $(DEPS) +endif +endif + diff --git a/intl/icu/source/tools/ctestfw/ctest.c b/intl/icu/source/tools/ctestfw/ctest.c new file mode 100644 index 0000000000..99f9789d3f --- /dev/null +++ b/intl/icu/source/tools/ctestfw/ctest.c @@ -0,0 +1,1333 @@ +// © 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 <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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 */ +int WRITE_GOLDEN_DATA = 0; /* Overwrite golden data files? */ +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 - ((int)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 briefly. */ + 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) { + (void)context; // suppress compiler warnings about unused variable + 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) { + (void)context; // suppress compiler warnings about unused variable + 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) { + // suppress compiler warnings about unused variables + (void)context; + (void)fnNumber; + (void)level; + 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) { + (void)context; // suppress compiler warnings about unused variable + /*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) { + (void)context; // suppress compiler warnings about unused variable + /*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) { + (void)context; // suppress compiler warnings about unused variable + 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], "-G") == 0) { + WRITE_GOLDEN_DATA = 1; + } + 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(" -G Write golden data files \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; + case WRITE_GOLDEN_DATA_OPTION: + return WRITE_GOLDEN_DATA; + 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; + case WRITE_GOLDEN_DATA_OPTION: + WRITE_GOLDEN_DATA = value; + 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; +} + + diff --git a/intl/icu/source/tools/ctestfw/ctestfw.vcxproj b/intl/icu/source/tools/ctestfw/ctestfw.vcxproj new file mode 100644 index 0000000000..f55c3f0b00 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/ctestfw.vcxproj @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup Label="Globals"> + <ProjectGuid>{ECA6B435-B4FA-4F9F-BF95-F451D078FC47}</ProjectGuid> + </PropertyGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseOfMfc>false</UseOfMfc> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <!-- The following import will include the 'default' configuration options for VS projects. --> + <Import Project="..\..\allinone\Build.Windows.ProjectConfiguration.props" /> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <OutDir>.\$(Platform)\$(Configuration)\</OutDir> + <IntDir>.\$(Platform)\$(Configuration)\</IntDir> + <!-- The ICU projects use "Win32" to mean "x86", so we need to special case it. --> + <OutDir Condition="'$(Platform)'=='Win32'">.\x86\$(Configuration)\</OutDir> + <IntDir Condition="'$(Platform)'=='Win32'">.\x86\$(Configuration)\</IntDir> + <!-- Disable Incremental Linking for Release builds as it prevents Link-time Code Generation --> + <LinkIncremental Condition="'$(Configuration)'=='Debug'">true</LinkIncremental> + <LinkIncremental Condition="'$(Configuration)'=='Release'">false</LinkIncremental> + </PropertyGroup> + <!-- Options that are common to *all* project configurations --> + <ItemDefinitionGroup> + <Midl> + <TypeLibraryName>$(OutDir)/icutest.tlb</TypeLibraryName> + </Midl> + <ClCompile> + <AdditionalIncludeDirectories>..\..\..\include;..\..\common;..\toolutil;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>T_CTEST_IMPLEMENTATION;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableLanguageExtensions>true</DisableLanguageExtensions> + <WarningLevel>Level3</WarningLevel> + <CompileAs>Default</CompileAs> + <PrecompiledHeaderOutputFile>$(OutDir)/icutest.pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>$(OutDir)/</AssemblerListingLocation> + <ObjectFileName>$(OutDir)/</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)/icutest.pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalLibraryDirectories>..\..\..\$(IcuLibOutputDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + </ItemDefinitionGroup> + <!-- Options that are common to all 'Debug' project configurations --> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> + <ClCompile> + <BrowseInformation>true</BrowseInformation> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + </ClCompile> + <Link> + <OutputFile>..\..\..\$(IcuBinOutputDir)\icutest$(IcuMajorVersion)d.exe</OutputFile> + <ProgramDatabaseFile>.\..\..\..\$(IcuLibOutputDir)\icutestd.pdb</ProgramDatabaseFile> + <ImportLibrary>.\..\..\..\$(IcuLibOutputDir)\icutestd.lib</ImportLibrary> + <AdditionalDependencies>icuucd.lib;icutud.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <!-- Options that are common to all 'Release' project configurations --> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + </ClCompile> + <Link> + <OutputFile>..\..\..\$(IcuBinOutputDir)\icutest$(IcuMajorVersion).exe</OutputFile> + <ProgramDatabaseFile>.\..\..\..\$(IcuLibOutputDir)\icutest.pdb</ProgramDatabaseFile> + <ImportLibrary>.\..\..\..\$(IcuLibOutputDir)\icutest.lib</ImportLibrary> + <AdditionalDependencies>icuuc.lib;icutu.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="ctest.c" /> + <ClCompile Include="datamap.cpp" /> + <ClCompile Include="testdata.cpp"> + <DisableLanguageExtensions>false</DisableLanguageExtensions> + </ClCompile> + <ClCompile Include="tstdtmod.cpp"> + <DisableLanguageExtensions>false</DisableLanguageExtensions> + </ClCompile> + <ClCompile Include="ucln_ct.c"> + <DisableLanguageExtensions>false</DisableLanguageExtensions> + </ClCompile> + <ClCompile Include="uperf.cpp"> + <DisableLanguageExtensions>false</DisableLanguageExtensions> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="unicode\ctest.h" /> + <ClInclude Include="unicode\datamap.h" /> + <ClInclude Include="unicode\testdata.h" /> + <ClInclude Include="unicode\testlog.h" /> + <ClInclude Include="unicode\testtype.h" /> + <ClInclude Include="unicode\tstdtmod.h" /> + <ClInclude Include="unicode\uperf.h" /> + <ClInclude Include="unicode\utimer.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/intl/icu/source/tools/ctestfw/ctestfw.vcxproj.filters b/intl/icu/source/tools/ctestfw/ctestfw.vcxproj.filters new file mode 100644 index 0000000000..31da517dd1 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/ctestfw.vcxproj.filters @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{852ed8c9-5bc0-4d29-8eb6-be22c01226a8}</UniqueIdentifier> + <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{b2dfb7a8-10dc-4668-bc01-42b2b3403944}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{76918e76-1025-421a-9363-11071191fbbc}</UniqueIdentifier> + <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="ctest.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="datamap.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="testdata.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="tstdtmod.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ucln_ct.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="uperf.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="unicode\ctest.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\datamap.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\testdata.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\testlog.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\testtype.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\tstdtmod.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\uperf.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="unicode\utimer.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/intl/icu/source/tools/ctestfw/datamap.cpp b/intl/icu/source/tools/ctestfw/datamap.cpp new file mode 100644 index 0000000000..0dd86f4f5b --- /dev/null +++ b/intl/icu/source/tools/ctestfw/datamap.cpp @@ -0,0 +1,224 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2006, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by weiv 05/09/2002 */ + +#include "unicode/datamap.h" +#include "unicode/resbund.h" +#include "unicode/unistr.h" +#include "hash.h" +#include <stdlib.h> + +DataMap::~DataMap() {} +DataMap::DataMap() {} + +int32_t +DataMap::utoi(const UnicodeString &s) const +{ + char ch[256]; + const char16_t *u = toUCharPtr(s.getBuffer()); + int32_t len = s.length(); + u_UCharsToChars(u, ch, len); + ch[len] = 0; /* include terminating \0 */ + return atoi(ch); +} + +U_CDECL_BEGIN +void U_CALLCONV +deleteResBund(void *obj) { + delete (ResourceBundle *)obj; +} +U_CDECL_END + + +RBDataMap::~RBDataMap() +{ + delete fData; +} + +RBDataMap::RBDataMap() +{ + UErrorCode status = U_ZERO_ERROR; + fData = new Hashtable(true, status); + fData->setValueDeleter(deleteResBund); +} + +// init from table resource +// will put stuff in hashtable according to +// keys. +RBDataMap::RBDataMap(UResourceBundle *data, UErrorCode &status) +{ + fData = new Hashtable(true, status); + fData->setValueDeleter(deleteResBund); + init(data, status); +} + +// init from headers and resource +// with checking the whether the size of resource matches +// header size +RBDataMap::RBDataMap(UResourceBundle *headers, UResourceBundle *data, UErrorCode &status) +{ + fData = new Hashtable(true, status); + fData->setValueDeleter(deleteResBund); + init(headers, data, status); +} + + +void RBDataMap::init(UResourceBundle *data, UErrorCode &status) { + int32_t i = 0; + fData->removeAll(); + UResourceBundle *t = nullptr; + for(i = 0; i < ures_getSize(data); i++) { + t = ures_getByIndex(data, i, t, &status); + fData->put(UnicodeString(ures_getKey(t), -1, US_INV), new ResourceBundle(t, status), status); + } + ures_close(t); +} + +void RBDataMap::init(UResourceBundle *headers, UResourceBundle *data, UErrorCode &status) +{ + int32_t i = 0; + fData->removeAll(); + UResourceBundle *t = nullptr; + const char16_t *key = nullptr; + int32_t keyLen = 0; + if(ures_getSize(headers) == ures_getSize(data)) { + for(i = 0; i < ures_getSize(data); i++) { + t = ures_getByIndex(data, i, t, &status); + key = ures_getStringByIndex(headers, i, &keyLen, &status); + fData->put(UnicodeString(key, keyLen), new ResourceBundle(t, status), status); + } + } else { + // error + status = U_INVALID_FORMAT_ERROR; + } + ures_close(t); +} + +const ResourceBundle *RBDataMap::getItem(const char* key, UErrorCode &status) const +{ + if(U_FAILURE(status)) { + return nullptr; + } + + UnicodeString hashKey(key, -1, US_INV); + const ResourceBundle *r = (ResourceBundle *)fData->get(hashKey); + if(r != nullptr) { + return r; + } else { + status = U_MISSING_RESOURCE_ERROR; + return nullptr; + } +} + +const UnicodeString RBDataMap::getString(const char* key, UErrorCode &status) const +{ + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + return r->getString(status); + } else { + return UnicodeString(); + } +} + +int32_t +RBDataMap::getInt28(const char* key, UErrorCode &status) const +{ + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + return r->getInt(status); + } else { + return 0; + } +} + +uint32_t +RBDataMap::getUInt28(const char* key, UErrorCode &status) const +{ + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + return r->getUInt(status); + } else { + return 0; + } +} + +const int32_t * +RBDataMap::getIntVector(int32_t &length, const char *key, UErrorCode &status) const { + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + return r->getIntVector(length, status); + } else { + return nullptr; + } +} + +const uint8_t * +RBDataMap::getBinary(int32_t &length, const char *key, UErrorCode &status) const { + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + return r->getBinary(length, status); + } else { + return nullptr; + } +} + +int32_t RBDataMap::getInt(const char* key, UErrorCode &status) const +{ + UnicodeString r = this->getString(key, status); + if(U_SUCCESS(status)) { + return utoi(r); + } else { + return 0; + } +} + +const UnicodeString* RBDataMap::getStringArray(int32_t& count, const char* key, UErrorCode &status) const +{ + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + int32_t i = 0; + + count = r->getSize(); + if(count <= 0) { + return nullptr; + } + + UnicodeString *result = new UnicodeString[count]; + for(i = 0; i<count; i++) { + result[i] = r->getStringEx(i, status); + } + return result; + } else { + return nullptr; + } +} + +const int32_t* RBDataMap::getIntArray(int32_t& count, const char* key, UErrorCode &status) const +{ + const ResourceBundle *r = getItem(key, status); + if(U_SUCCESS(status)) { + int32_t i = 0; + + count = r->getSize(); + if(count <= 0) { + return nullptr; + } + + int32_t *result = new int32_t[count]; + UnicodeString stringRes; + for(i = 0; i<count; i++) { + stringRes = r->getStringEx(i, status); + result[i] = utoi(stringRes); + } + return result; + } else { + return nullptr; + } +} + diff --git a/intl/icu/source/tools/ctestfw/sources.txt b/intl/icu/source/tools/ctestfw/sources.txt new file mode 100644 index 0000000000..30103db4a1 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/sources.txt @@ -0,0 +1,6 @@ +ctest.c +datamap.cpp +testdata.cpp +tstdtmod.cpp +ucln_ct.c +uperf.cpp diff --git a/intl/icu/source/tools/ctestfw/testdata.cpp b/intl/icu/source/tools/ctestfw/testdata.cpp new file mode 100644 index 0000000000..2fb93381dc --- /dev/null +++ b/intl/icu/source/tools/ctestfw/testdata.cpp @@ -0,0 +1,144 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2005, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by weiv 05/09/2002 */ + +#include "unicode/testdata.h" + + +TestData::TestData(const char* testName) +: name(testName), +fInfo(nullptr), +fCurrSettings(nullptr), +fCurrCase(nullptr), +fSettingsSize(0), +fCasesSize(0), +fCurrentSettings(0), +fCurrentCase(0) + +{ +} + +TestData::~TestData() { + if(fInfo != nullptr) { + delete fInfo; + } + if(fCurrSettings != nullptr) { + delete fCurrSettings; + } + if(fCurrCase != nullptr) { + delete fCurrCase; + } +} + +const char * TestData::getName() const +{ + return name; +} + + + +RBTestData::RBTestData(const char* testName) +: TestData(testName), +fData(nullptr), +fHeaders(nullptr), +fSettings(nullptr), +fCases(nullptr) +{ +} + +RBTestData::RBTestData(UResourceBundle *data, UResourceBundle *headers, UErrorCode& status) +: TestData(ures_getKey(data)), +fData(data), +fHeaders(headers), +fSettings(nullptr), +fCases(nullptr) +{ + UErrorCode intStatus = U_ZERO_ERROR; + UResourceBundle *currHeaders = ures_getByKey(data, "Headers", nullptr, &intStatus); + if(intStatus == U_ZERO_ERROR) { + ures_close(fHeaders); + fHeaders = currHeaders; + } else { + intStatus = U_ZERO_ERROR; + } + fSettings = ures_getByKey(data, "Settings", nullptr, &intStatus); + fSettingsSize = ures_getSize(fSettings); + UResourceBundle *info = ures_getByKey(data, "Info", nullptr, &intStatus); + if(U_SUCCESS(intStatus)) { + fInfo = new RBDataMap(info, status); + } else { + intStatus = U_ZERO_ERROR; + } + fCases = ures_getByKey(data, "Cases", nullptr, &status); + fCasesSize = ures_getSize(fCases); + + ures_close(info); +} + + +RBTestData::~RBTestData() +{ + ures_close(fData); + ures_close(fHeaders); + ures_close(fSettings); + ures_close(fCases); +} + +UBool RBTestData::getInfo(const DataMap *& info, UErrorCode &/*status*/) const +{ + if(fInfo) { + info = fInfo; + return true; + } else { + info = nullptr; + return false; + } +} + +UBool RBTestData::nextSettings(const DataMap *& settings, UErrorCode &status) +{ + UErrorCode intStatus = U_ZERO_ERROR; + UResourceBundle *data = ures_getByIndex(fSettings, fCurrentSettings++, nullptr, &intStatus); + if(U_SUCCESS(intStatus)) { + // reset the cases iterator + fCurrentCase = 0; + if(fCurrSettings == nullptr) { + fCurrSettings = new RBDataMap(data, status); + } else { + ((RBDataMap *)fCurrSettings)->init(data, status); + } + ures_close(data); + settings = fCurrSettings; + return true; + } else { + settings = nullptr; + return false; + } +} + +UBool RBTestData::nextCase(const DataMap *& nextCase, UErrorCode &status) +{ + UErrorCode intStatus = U_ZERO_ERROR; + UResourceBundle *currCase = ures_getByIndex(fCases, fCurrentCase++, nullptr, &intStatus); + if(U_SUCCESS(intStatus)) { + if(fCurrCase == nullptr) { + fCurrCase = new RBDataMap(fHeaders, currCase, status); + } else { + ((RBDataMap *)fCurrCase)->init(fHeaders, currCase, status); + } + ures_close(currCase); + nextCase = fCurrCase; + return true; + } else { + nextCase = nullptr; + return false; + } +} + + diff --git a/intl/icu/source/tools/ctestfw/tstdtmod.cpp b/intl/icu/source/tools/ctestfw/tstdtmod.cpp new file mode 100644 index 0000000000..3ebe22466e --- /dev/null +++ b/intl/icu/source/tools/ctestfw/tstdtmod.cpp @@ -0,0 +1,293 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by weiv 05/09/2002 */ + +#include <stdarg.h> + +#include "unicode/tstdtmod.h" +#include "cmemory.h" +#include <stdio.h> +#include "cstr.h" +#include "cstring.h" + +TestLog::~TestLog() {} + +IcuTestErrorCode::~IcuTestErrorCode() { + // Safe because our errlog() does not throw exceptions. + if(isFailure()) { + errlog(false, u"destructor: expected success", nullptr); + } +} + +UBool IcuTestErrorCode::errIfFailureAndReset() { + if(isFailure()) { + errlog(false, u"expected success", nullptr); + reset(); + return true; + } else { + reset(); + return false; + } +} + +UBool IcuTestErrorCode::errIfFailureAndReset(const char *fmt, ...) { + if(isFailure()) { + char buffer[4000]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + errlog(false, u"expected success", buffer); + reset(); + return true; + } else { + reset(); + return false; + } +} + +UBool IcuTestErrorCode::errDataIfFailureAndReset() { + if(isFailure()) { + errlog(true, u"data: expected success", nullptr); + reset(); + return true; + } else { + reset(); + return false; + } +} + +UBool IcuTestErrorCode::errDataIfFailureAndReset(const char *fmt, ...) { + if(isFailure()) { + char buffer[4000]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + errlog(true, u"data: expected success", buffer); + reset(); + return true; + } else { + reset(); + return false; + } +} + +UBool IcuTestErrorCode::expectErrorAndReset(UErrorCode expectedError) { + if(get() != expectedError) { + errlog(false, UnicodeString(u"expected: ") + u_errorName(expectedError), nullptr); + } + UBool retval = isFailure(); + reset(); + return retval; +} + +UBool IcuTestErrorCode::expectErrorAndReset(UErrorCode expectedError, const char *fmt, ...) { + if(get() != expectedError) { + char buffer[4000]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + errlog(false, UnicodeString(u"expected: ") + u_errorName(expectedError), buffer); + } + UBool retval = isFailure(); + reset(); + return retval; +} + +void IcuTestErrorCode::setScope(const char* message) { + scopeMessage.remove().append({ message, -1, US_INV }); +} + +void IcuTestErrorCode::setScope(const UnicodeString& message) { + scopeMessage = message; +} + +void IcuTestErrorCode::handleFailure() const { + errlog(false, u"(handleFailure)", nullptr); +} + +void IcuTestErrorCode::errlog(UBool dataErr, const UnicodeString& mainMessage, const char* extraMessage) const { + UnicodeString msg(testName, -1, US_INV); + msg.append(u' ').append(mainMessage); + msg.append(u" but got error: ").append(UnicodeString(errorName(), -1, US_INV)); + + if (!scopeMessage.isEmpty()) { + msg.append(u" scope: ").append(scopeMessage); + } + + if (extraMessage != nullptr) { + msg.append(u" - ").append(UnicodeString(extraMessage, -1, US_INV)); + } + + if (dataErr || get() == U_MISSING_RESOURCE_ERROR || get() == U_FILE_ACCESS_ERROR) { + testClass.dataerrln(msg); + } else { + testClass.errln(msg); + } +} + +TestDataModule *TestDataModule::getTestDataModule(const char* name, TestLog& log, UErrorCode &status) +{ + if(U_FAILURE(status)) { + return nullptr; + } + TestDataModule *result = nullptr; + + // TODO: probe for resource bundle and then for XML. + // According to that, construct an appropriate driver object + + result = new RBTestDataModule(name, log, status); + if(U_SUCCESS(status)) { + return result; + } else { + delete result; + return nullptr; + } +} + +TestDataModule::TestDataModule(const char* name, TestLog& log, UErrorCode& /*status*/) +: testName(name), +fInfo(nullptr), +fLog(log) +{ +} + +TestDataModule::~TestDataModule() { + if(fInfo != nullptr) { + delete fInfo; + } +} + +const char * TestDataModule::getName() const +{ + return testName; +} + + + +RBTestDataModule::~RBTestDataModule() +{ + ures_close(fTestData); + ures_close(fModuleBundle); + ures_close(fInfoRB); + uprv_free(tdpath); +} + +RBTestDataModule::RBTestDataModule(const char* name, TestLog& log, UErrorCode& status) +: TestDataModule(name, log, status), + fModuleBundle(nullptr), + fTestData(nullptr), + fInfoRB(nullptr), + tdpath(nullptr) +{ + fNumberOfTests = 0; + fDataTestValid = true; + fModuleBundle = getTestBundle(name, status); + if(fDataTestValid) { + fTestData = ures_getByKey(fModuleBundle, "TestData", nullptr, &status); + fNumberOfTests = ures_getSize(fTestData); + fInfoRB = ures_getByKey(fModuleBundle, "Info", nullptr, &status); + if(status != U_ZERO_ERROR) { + log.errln(UNICODE_STRING_SIMPLE("Unable to initialize test data - missing mandatory description resources!")); + fDataTestValid = false; + } else { + fInfo = new RBDataMap(fInfoRB, status); + } + } +} + +UBool RBTestDataModule::getInfo(const DataMap *& info, UErrorCode &/*status*/) const +{ + info = fInfo; + if(fInfo) { + return true; + } else { + return false; + } +} + +TestData* RBTestDataModule::createTestData(int32_t index, UErrorCode &status) const +{ + TestData *result = nullptr; + UErrorCode intStatus = U_ZERO_ERROR; + + if(fDataTestValid == true) { + // Both of these resources get adopted by a TestData object. + UResourceBundle *DataFillIn = ures_getByIndex(fTestData, index, nullptr, &status); + UResourceBundle *headers = ures_getByKey(fInfoRB, "Headers", nullptr, &intStatus); + + if(U_SUCCESS(status)) { + result = new RBTestData(DataFillIn, headers, status); + + if(U_SUCCESS(status)) { + return result; + } else { + delete result; + } + } else { + ures_close(DataFillIn); + ures_close(headers); + } + } else { + status = U_MISSING_RESOURCE_ERROR; + } + return nullptr; +} + +TestData* RBTestDataModule::createTestData(const char* name, UErrorCode &status) const +{ + TestData *result = nullptr; + UErrorCode intStatus = U_ZERO_ERROR; + + if(fDataTestValid == true) { + // Both of these resources get adopted by a TestData object. + UResourceBundle *DataFillIn = ures_getByKey(fTestData, name, nullptr, &status); + UResourceBundle *headers = ures_getByKey(fInfoRB, "Headers", nullptr, &intStatus); + + if(U_SUCCESS(status)) { + result = new RBTestData(DataFillIn, headers, status); + if(U_SUCCESS(status)) { + return result; + } else { + delete result; + } + } else { + ures_close(DataFillIn); + ures_close(headers); + } + } else { + status = U_MISSING_RESOURCE_ERROR; + } + return nullptr; +} + + + +//Get test data from ResourceBundles +UResourceBundle* +RBTestDataModule::getTestBundle(const char* bundleName, UErrorCode &status) +{ + if(U_SUCCESS(status)) { + UResourceBundle *testBundle = nullptr; + const char* icu_data = fLog.getTestDataPath(status); + if (testBundle == nullptr) { + testBundle = ures_openDirect(icu_data, bundleName, &status); + if (status != U_ZERO_ERROR) { + fLog.dataerrln(UNICODE_STRING_SIMPLE("Could not load test data from resourcebundle: ") + UnicodeString(bundleName, -1, US_INV)); + fDataTestValid = false; + } + } + return testBundle; + } else { + return nullptr; + } +} + diff --git a/intl/icu/source/tools/ctestfw/ucln_ct.c b/intl/icu/source/tools/ctestfw/ucln_ct.c new file mode 100644 index 0000000000..a4d1ce86e8 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/ucln_ct.c @@ -0,0 +1,19 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2007-2013, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + + +/** Auto-client **/ +#define UCLN_TYPE UCLN_CTESTFW +#include "ucln_imp.h" + +int uprv_dummyFunction_CT(void); +int uprv_dummyFunction_CT(void) +{ + /* this is here to prevent the compiler from complaining about an empty file */ + return 0; +} diff --git a/intl/icu/source/tools/ctestfw/unicode/ctest.h b/intl/icu/source/tools/ctestfw/unicode/ctest.h new file mode 100644 index 0000000000..da75be55b2 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/ctest.h @@ -0,0 +1,321 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************** + * + * Copyright (C) 1996-2013, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************** + */ + +#ifndef CTEST_H +#define CTEST_H + +#include "unicode/testtype.h" +#include "unicode/utrace.h" + + +/* prototypes *********************************/ + +U_CDECL_BEGIN +typedef void (U_CALLCONV *TestFunctionPtr)(void); +typedef int (U_CALLCONV *ArgHandlerPtr)(int arg, int argc, const char* const argv[], void *context); +typedef struct TestNode TestNode; +U_CDECL_END + +/** + * This is use to set or get the option value for REPEAT_TESTS. + * Use with set/getTestOption(). + * + * @internal + */ +#define REPEAT_TESTS_OPTION 1 + +/** + * This is use to set or get the option value for VERBOSITY. + * When option is set to zero to disable log_verbose() messages. + * Otherwise nonzero to see log_verbose() messages. + * Use with set/getTestOption(). + * + * @internal + */ +#define VERBOSITY_OPTION 2 + +/** + * This is use to set or get the option value for ERR_MSG. + * Use with set/getTestOption(). + * + * @internal + */ +#define ERR_MSG_OPTION 3 + +/** + * This is use to set or get the option value for QUICK. + * When option is zero, disable some of the slower tests. + * Otherwise nonzero to run the slower tests. + * Use with set/getTestOption(). + * + * @internal + */ +#define QUICK_OPTION 4 + +/** + * This is use to set or get the option value for WARN_ON_MISSING_DATA. + * When option is nonzero, warn on missing data. + * Otherwise, errors are propagated when data is not available. + * Affects the behavior of log_dataerr. + * Use with set/getTestOption(). + * + * @see log_data_err + * @internal + */ +#define WARN_ON_MISSING_DATA_OPTION 5 + +/** + * This is use to set or get the option value for ICU_TRACE. + * ICU tracing level, is set by command line option. + * Use with set/getTestOption(). + * + * @internal + */ +#define ICU_TRACE_OPTION 6 + +/** + * This is used to set or get the option value for WRITE_GOLDEN_DATA. + * Set to 1 to overwrite golden data files, such as those in testdata/ucptrie. + * Use with set/getTestOption(). + */ +#define WRITE_GOLDEN_DATA_OPTION 7 + +/** + * Maximum amount of memory uprv_malloc should allocate before returning NULL. + * + * @internal + */ +extern T_CTEST_EXPORT_API size_t MAX_MEMORY_ALLOCATION; + +/** + * If memory tracing was enabled, contains the number of unfreed allocations. + * + * @internal + */ +extern T_CTEST_EXPORT_API int32_t ALLOCATION_COUNT; + +/** + * Pass to setTestOption to decrement the test option value. + * + * @internal + */ +#define DECREMENT_OPTION_VALUE -99 + +/** + * Gets the test option set on commandline. + * + * @param testOption macro definition for the individual test option + * @return value of test option, zero if option is not set or off + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API int32_t T_CTEST_EXPORT2 +getTestOption ( int32_t testOption ); + +/** + * Sets the test option with value given on commandline. + * + * @param testOption macro definition for the individual test option + * @param value to set the test option to + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +setTestOption ( int32_t testOption, int32_t value); + +/** + * Show the names of all nodes. + * + * @param root Subtree of tests. + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +showTests ( const TestNode *root); + +/** + * Run a subtree of tests. + * + * @param root Subtree of tests. + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +runTests ( const TestNode* root); + +/** + * Add a test to the subtree. + * Example usage: + * <PRE> + * TestNode* root=NULL; + * addTest(&root, &mytest, "/a/b/mytest" ); + * </PRE> + * @param root Pointer to the root pointer. + * @param test Pointer to 'void function(void)' for actual test. + * @param path Path from root under which test will be placed. Ex. '/a/b/mytest' + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +addTest(TestNode** root, + TestFunctionPtr test, + const char *path); + +/** + * Clean up any allocated memory. + * Conditions for calling this function are the same as u_cleanup(). + * @see u_cleanup + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +cleanUpTestTree(TestNode *tn); + +/** + * Retrieve a specific subtest. (subtree). + * + * @param root Pointer to the root. + * @param path Path relative to the root, Ex. '/a/b' + * @return The subtest, or NULL on failure. + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API const TestNode* T_CTEST_EXPORT2 +getTest(const TestNode* root, + const char *path); + + +/** + * Log an error message. (printf style) + * @param pattern printf-style format string + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +log_err(const char* pattern, ...); + +T_CTEST_API void T_CTEST_EXPORT2 +log_err_status(UErrorCode status, const char* pattern, ...); +/** + * Log an informational message. (printf style) + * @param pattern printf-style format string + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +log_info(const char* pattern, ...); + +/** + * Log an informational message. (vprintf style) + * @param prefix a string that is output before the pattern and without formatting + * @param pattern printf-style format string + * @param ap variable-arguments list + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +vlog_info(const char *prefix, const char *pattern, va_list ap); + +/** + * Log a verbose informational message. (printf style) + * This message will only appear if the global VERBOSITY is nonzero + * @param pattern printf-style format string + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +log_verbose(const char* pattern, ...); + +/** + * Log an error message concerning missing data. (printf style) + * If WARN_ON_MISSING_DATA is nonzero, this will case a log_info (warning) to be + * printed, but if it is zero this will produce an error (log_err). + * @param pattern printf-style format string + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API void T_CTEST_EXPORT2 +log_data_err(const char *pattern, ...); + +/** + * Log a known issue. + * @param ticket ticket number such as "ICU-12345" for ICU tickets or "CLDR-6636" for CLDR tickets. + * @param fmt ... sprintf-style format, optional message. can be NULL. + * @return true if known issue test should be skipped, false if it should be run + */ +T_CTEST_API UBool +T_CTEST_EXPORT2 +log_knownIssue(const char *ticket, const char *fmt, ...); + +/** + * Initialize the variables above. This allows the test to set up accordingly + * before running the tests. + * This must be called before runTests. + */ +T_CTEST_API int T_CTEST_EXPORT2 +initArgs( int argc, const char* const argv[], ArgHandlerPtr argHandler, void *context); + +/** + * Processes the command line arguments. + * This is a sample implementation + * <PRE>Usage: %s [ -l ] [ -v ] [ -? ] [ /path/to/test ] + * -l List only, do not run\ + * -v turn OFF verbosity + * -? print this message</PRE> + * @param root Testnode root with tests already attached to it + * @param argv argument list from main (stdio.h) + * @param argc argument list count from main (stdio.h) + * @return positive for error count, 0 for success, negative for illegal argument + * @internal Internal APIs for testing purpose only + */ +T_CTEST_API int T_CTEST_EXPORT2 +runTestRequest(const TestNode* root, + int argc, + const char* const argv[]); + + +T_CTEST_API const char* T_CTEST_EXPORT2 +getTestName(void); + +/** + * Append a time delta to str if it is significant (>5 ms) otherwise no change + * @param delta a delta in millis + * @param str a string to append to. + */ +T_CTEST_API void T_CTEST_EXPORT2 +str_timeDelta(char *str, UDate delta); + + +/* ======== XML (JUnit output) ========= */ + +/** + * Set the filename for the XML output. + * @param fileName file name. Caller must retain storage. + * @return 0 on success, 1 on failure. + */ +T_CTEST_API int32_t T_CTEST_EXPORT2 +ctest_xml_setFileName(const char *fileName); + + +/** + * Init XML subsystem. Call ctest_xml_setFileName first + * @param rootName the test root name to be written + * @return 0 on success, 1 on failure. + */ +T_CTEST_API int32_t T_CTEST_EXPORT2 +ctest_xml_init(const char *rootName); + + +/** + * Set the filename for the XML output. Caller must retain storage. + * @return 0 on success, 1 on failure. + */ +T_CTEST_API int32_t T_CTEST_EXPORT2 +ctest_xml_fini(void); + + +/** + * report a test case + * @return 0 on success, 1 on failure. + */ +T_CTEST_API int32_t +T_CTEST_EXPORT2 +ctest_xml_testcase(const char *classname, const char *name, const char *time, const char *failMsg); + +#endif diff --git a/intl/icu/source/tools/ctestfw/unicode/datamap.h b/intl/icu/source/tools/ctestfw/unicode/datamap.h new file mode 100644 index 0000000000..b4f7f82fd6 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/datamap.h @@ -0,0 +1,140 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2006, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by weiv 05/09/2002 */ + +#ifndef U_TESTFW_DATAMAP +#define U_TESTFW_DATAMAP + +#include "unicode/resbund.h" +#include "unicode/testtype.h" + + + +U_NAMESPACE_BEGIN +class Hashtable; +U_NAMESPACE_END + +/** Holder of test data and settings. Allows addressing of items by name. + * For test cases, names are defined in the "Headers" section. For settings + * and info data, names are keys in data. Currently, we return scalar strings + * and integers and arrays of strings and integers. Arrays should be deposited + * of by the user. + */ +class T_CTEST_EXPORT_API DataMap { +public: + virtual ~DataMap(); + +protected: + DataMap(); + int32_t utoi(const UnicodeString &s) const; + + +public: + /** get the string from the DataMap. Addressed by name + * @param key name of the data field. + * @return a string containing the data + */ + virtual const UnicodeString getString(const char* key, UErrorCode &status) const = 0; + + /** get the string from the DataMap. Addressed by name + * parses a bundle string into an integer + * @param key name of the data field. + * @return an integer containing the data + */ + virtual int32_t getInt(const char* key, UErrorCode &status) const = 0; + + /** + * Get a signed integer without runtime parsing. + * @param key name of the data field. + * @param status UErrorCode in/out parameter + * @return the integer + */ + virtual int32_t getInt28(const char* key, UErrorCode &status) const = 0; + + /** + * Get an unsigned integer without runtime parsing. + * @param key name of the data field. + * @param status UErrorCode in/out parameter + * @return the integer + */ + virtual uint32_t getUInt28(const char* key, UErrorCode &status) const = 0; + + /** + * Get a vector of integers without runtime parsing. + * @param length output parameter for the length of the vector + * @param key name of the data field. + * @param status UErrorCode in/out parameter + * @return the integer vector, do not delete + */ + virtual const int32_t *getIntVector(int32_t &length, const char *key, UErrorCode &status) const = 0; + + /** + * Get binary data without runtime parsing. + * @param length output parameter for the length of the data + * @param key name of the data field. + * @param status UErrorCode in/out parameter + * @return the binary data, do not delete + */ + virtual const uint8_t *getBinary(int32_t &length, const char *key, UErrorCode &status) const = 0; + + /** get an array of strings from the DataMap. Addressed by name. + * The user must dispose of it after usage, using delete. + * @param key name of the data field. + * @return a string array containing the data + */ + virtual const UnicodeString* getStringArray(int32_t& count, const char* key, UErrorCode &status) const = 0; + + /** get an array of integers from the DataMap. Addressed by name. + * The user must dispose of it after usage, using delete. + * @param key name of the data field. + * @return an integer array containing the data + */ + virtual const int32_t* getIntArray(int32_t& count, const char* key, UErrorCode &status) const = 0; + + // ... etc ... +}; + +// This one is already concrete - it is going to be instantiated from +// concrete data by TestData children... +class T_CTEST_EXPORT_API RBDataMap : public DataMap{ +private: + Hashtable *fData; + +public: + virtual ~RBDataMap(); + +public: + RBDataMap(); + + RBDataMap(UResourceBundle *data, UErrorCode &status); + RBDataMap(UResourceBundle *headers, UResourceBundle *data, UErrorCode &status); + +public: + void init(UResourceBundle *data, UErrorCode &status); + void init(UResourceBundle *headers, UResourceBundle *data, UErrorCode &status); + + virtual const ResourceBundle *getItem(const char* key, UErrorCode &status) const; + + virtual const UnicodeString getString(const char* key, UErrorCode &status) const override; + virtual int32_t getInt28(const char* key, UErrorCode &status) const override; + virtual uint32_t getUInt28(const char* key, UErrorCode &status) const override; + virtual const int32_t *getIntVector(int32_t &length, const char *key, UErrorCode &status) const override; + virtual const uint8_t *getBinary(int32_t &length, const char *key, UErrorCode &status) const override; + + virtual int32_t getInt(const char* key, UErrorCode &status) const override; + + virtual const UnicodeString* getStringArray(int32_t& count, const char* key, UErrorCode &status) const override; + virtual const int32_t* getIntArray(int32_t& count, const char* key, UErrorCode &status) const override; + + // ... etc ... +}; + + +#endif + diff --git a/intl/icu/source/tools/ctestfw/unicode/testdata.h b/intl/icu/source/tools/ctestfw/unicode/testdata.h new file mode 100644 index 0000000000..77db9ceaf1 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/testdata.h @@ -0,0 +1,113 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2006, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by weiv 05/09/2002 */ + +/* Base class for data driven tests */ + +#ifndef U_TESTFW_TESTDATA +#define U_TESTFW_TESTDATA + +#include "unicode/tstdtmod.h" +#include "unicode/datamap.h" + + + /** This is the class that abstracts one of the tests in a data file + * It is usually instantiated using TestDataModule::CreateTestData method + * This class provides two important methods: nextSettings and nextCase + * Usually, one walks through all settings and executes all cases for + * each setting. Each call to nextSettings resets the cases iterator. + * Individual test cases have to have the same number of fields as the + * number of entries in headers. Default headers can be specified in + * the TestDataModule info section. The default headers will be overridden + * by per-test headers. + * Example: + * DataMap *settings = nullptr; + * DataMap *cases = nullptr; + * while(nextSettings(settings, status)) { + * // set settings for the subtest + * while(nextCase(cases, status) { + * // process testcase + * } + * } + */ + +class T_CTEST_EXPORT_API TestData { + const char* name; + +protected: + DataMap *fInfo; + DataMap *fCurrSettings; + DataMap *fCurrCase; + int32_t fSettingsSize; + int32_t fCasesSize; + int32_t fCurrentSettings; + int32_t fCurrentCase; + /** constructor - don't use */ + TestData(const char* name); + +public: + virtual ~TestData(); + + const char* getName() const; + + /** Get a pointer to an object owned DataMap that contains more information on this + * TestData object. + * Usual fields is "Description". + * @param info pass in a const DataMap pointer. If no info, it will be set to nullptr + */ + virtual UBool getInfo(const DataMap *& info, UErrorCode &status) const = 0; + + /** Gets the next set of settings for the test. Resets the cases iterator. + * DataMap is owned by the object and should not be deleted. + * @param settings a DataMap pointer provided by the user. Will be nullptr if + * no more settings are available. + * @param status for reporting unexpected errors. + * @return A boolean, true if there are settings, false if there is no more + * settings. + */ + virtual UBool nextSettings(const DataMap *& settings, UErrorCode &status) = 0; + + /** Gets the next test case. + * DataMap is owned by the object and should not be deleted. + * @param data a DataMap pointer provided by the user. Will be nullptr if + * no more cases are available. + * @param status for reporting unexpected errors. + * @return A boolean, true if there are cases, false if there is no more + * cases. + */ + virtual UBool nextCase(const DataMap *& data, UErrorCode &status) = 0; +}; + +// implementation of TestData that uses resource bundles + +class T_CTEST_EXPORT_API RBTestData : public TestData { + UResourceBundle *fData; + UResourceBundle *fHeaders; + UResourceBundle *fSettings; + UResourceBundle *fCases; + +public: + RBTestData(const char* name); + RBTestData(UResourceBundle *data, UResourceBundle *headers, UErrorCode& status); +private: +// RBTestData() {}; +// RBTestData(const RBTestData& original) {}; + RBTestData& operator=(const RBTestData& /*original*/); + +public: + virtual ~RBTestData(); + + virtual UBool getInfo(const DataMap *& info, UErrorCode &status) const override; + + virtual UBool nextSettings(const DataMap *& settings, UErrorCode &status) override; + virtual UBool nextCase(const DataMap *& nextCase, UErrorCode &status) override; +}; + +#endif + diff --git a/intl/icu/source/tools/ctestfw/unicode/testlog.h b/intl/icu/source/tools/ctestfw/unicode/testlog.h new file mode 100644 index 0000000000..a7ffbc6084 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/testlog.h @@ -0,0 +1,62 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2004-2010, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by grhoten 03/17/2004 */ + +/* Base class for data driven tests */ + +#ifndef U_TESTFW_TESTLOG +#define U_TESTFW_TESTLOG + +#include "unicode/errorcode.h" +#include "unicode/unistr.h" +#include "unicode/testtype.h" + +/** Facilitates internal logging of data driven test service + * It would be interesting to develop this into a full + * fledged control system as in Java. + */ +class T_CTEST_EXPORT_API TestLog { +public: + virtual ~TestLog(); + virtual void errln( const UnicodeString &message ) = 0; + virtual void logln( const UnicodeString &message ) = 0; + virtual void dataerrln( const UnicodeString &message ) = 0; + virtual const char* getTestDataPath(UErrorCode& err) = 0; +}; + +class T_CTEST_EXPORT_API IcuTestErrorCode : public ErrorCode { +public: + IcuTestErrorCode(TestLog &callingTestClass, const char *callingTestName) + : testClass(callingTestClass), testName(callingTestName), scopeMessage() {} + virtual ~IcuTestErrorCode(); + + // Returns true if isFailure(). + UBool errIfFailureAndReset(); + UBool errIfFailureAndReset(const char *fmt, ...); + UBool errDataIfFailureAndReset(); + UBool errDataIfFailureAndReset(const char *fmt, ...); + UBool expectErrorAndReset(UErrorCode expectedError); + UBool expectErrorAndReset(UErrorCode expectedError, const char *fmt, ...); + + /** Sets an additional message string to be appended to failure output. */ + void setScope(const char* message); + void setScope(const UnicodeString& message); + +protected: + virtual void handleFailure() const override; + +private: + TestLog &testClass; + const char *const testName; + UnicodeString scopeMessage; + + void errlog(UBool dataErr, const UnicodeString& mainMessage, const char* extraMessage) const; +}; + +#endif diff --git a/intl/icu/source/tools/ctestfw/unicode/testtype.h b/intl/icu/source/tools/ctestfw/unicode/testtype.h new file mode 100644 index 0000000000..a5c70d577a --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/testtype.h @@ -0,0 +1,40 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ***************************************************************************************** + * Copyright (C) 2004-2011, International Business Machines + * Corporation and others. All Rights Reserved. + ***************************************************************************************** + */ + +#include "unicode/utypes.h" + +/*Deals with imports and exports of the dynamic library*/ +#if !defined(U_STATIC_IMPLEMENTATION) + #define T_CTEST_EXPORT U_EXPORT + #define T_CTEST_IMPORT U_IMPORT +#else + #define T_CTEST_EXPORT + #define T_CTEST_IMPORT +#endif + +#if defined(_MSC_VER) +#define T_CTEST_EXPORT2 __cdecl +#else +#define T_CTEST_EXPORT2 +#endif + +#ifdef __cplusplus + #define C_CTEST_API extern "C" + U_NAMESPACE_USE +#else + #define C_CTEST_API +#endif + +#ifdef T_CTEST_IMPLEMENTATION + #define T_CTEST_API C_CTEST_API T_CTEST_EXPORT + #define T_CTEST_EXPORT_API T_CTEST_EXPORT +#else + #define T_CTEST_API C_CTEST_API T_CTEST_IMPORT + #define T_CTEST_EXPORT_API T_CTEST_IMPORT +#endif diff --git a/intl/icu/source/tools/ctestfw/unicode/tstdtmod.h b/intl/icu/source/tools/ctestfw/unicode/tstdtmod.h new file mode 100644 index 0000000000..fb2f19631d --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/tstdtmod.h @@ -0,0 +1,117 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2005, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/* Created by weiv 05/09/2002 */ + +/* Base class for data driven tests */ + +#ifndef U_TESTFW_TESTMODULE +#define U_TESTFW_TESTMODULE + +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "unicode/testtype.h" +#include "unicode/testdata.h" +#include "unicode/datamap.h" +#include "unicode/testlog.h" + + +/* This class abstracts the actual organization of the + * data for data driven tests + */ + + +class DataMap; +class TestData; + + +/** Main data driven test class. Corresponds to one named data + * unit (such as a resource bundle. It is instantiated using + * a factory method getTestDataModule + */ +class T_CTEST_EXPORT_API TestDataModule { + const char* testName; + +protected: + DataMap *fInfo; + TestLog& fLog; + +public: + /** Factory method. + * @param name name of the test module. Usually name of a resource bundle or a XML file + * @param log a logging class, used for internal error reporting. + * @param status if something goes wrong, status will be set + * @return a TestDataModule object. Use it to get test data from it + */ + static TestDataModule *getTestDataModule(const char* name, TestLog& log, UErrorCode &status); + virtual ~TestDataModule(); + +protected: + TestDataModule(const char* name, TestLog& log, UErrorCode& status); + +public: + /** Name of this TestData module. + * @return a name + */ + const char * getName() const; + + /** Get a pointer to an object owned DataMap that contains more information on this module + * Usual fields are "Description", "LongDescription", "Settings". Also, if containing a + * field "Headers" these will be used as the default headers, so that you don't have to + * to specify per test headers. + * @param info pass in a const DataMap pointer. If no info, it will be set to nullptr + */ + virtual UBool getInfo(const DataMap *& info, UErrorCode &status) const = 0; + + /** Create a test data object from an index. Helpful for integrating tests with current + * intltest framework which addresses the tests by index. + * @param index index of the test to be instantiated + * @return an instantiated TestData object, ready to provide settings and cases for + * the tests. + */ + virtual TestData* createTestData(int32_t index, UErrorCode &status) const = 0; + + /** Create a test data object from a name. + * @param name name of the test to be instantiated + * @return an instantiated TestData object, ready to provide settings and cases for + * the tests. + */ + virtual TestData* createTestData(const char* name, UErrorCode &status) const = 0; +}; + +class T_CTEST_EXPORT_API RBTestDataModule : public TestDataModule { +public: + virtual ~RBTestDataModule(); + +public: + RBTestDataModule(const char* name, TestLog& log, UErrorCode& status); + +public: + virtual UBool getInfo(const DataMap *& info, UErrorCode &status) const override; + + virtual TestData* createTestData(int32_t index, UErrorCode &status) const override; + virtual TestData* createTestData(const char* name, UErrorCode &status) const override; + +private: + UResourceBundle *getTestBundle(const char* bundleName, UErrorCode &status); + +private: + UResourceBundle *fModuleBundle; + UResourceBundle *fTestData; + UResourceBundle *fInfoRB; + UBool fDataTestValid; + char *tdpath; + +/* const char* fTestName;*/ /* See name */ + int32_t fNumberOfTests; + +}; + + +#endif + diff --git a/intl/icu/source/tools/ctestfw/unicode/uperf.h b/intl/icu/source/tools/ctestfw/unicode/uperf.h new file mode 100644 index 0000000000..e578c46694 --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/uperf.h @@ -0,0 +1,200 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ +#ifndef _UPERF_H +#define _UPERF_H + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/ustring.h" + +#include "unicode/testtype.h" +#include "unicode/utimer.h" +#include "ucbuf.h" + +// Forward declarations from uoptions.h. +struct UOption; +typedef struct UOption UOption; + +#if !UCONFIG_NO_CONVERSION + +U_NAMESPACE_USE +// Use the TESTCASE macro in subclasses of UPerfTest. Define the +// runIndexedTest method in this fashion: +// +//| void MyTest::runIndexedTest(int32_t index, UBool exec, +//| const char* &name, char* /*par*/) { +//| switch (index) { +//| TESTCASE(0,TestSomething); +//| TESTCASE(1,TestSomethingElse); +//| TESTCASE(2,TestAnotherThing); +//| default: +//| name = ""; +//| break; +//| } +//| return nullptr; +//| } +#define TESTCASE(id,test) \ + case id: \ + name = #test; \ + if (exec) { \ + return test(); \ + } \ + break + +// More convenient macros. These allow easy reordering of the test cases. +// Copied from intltest.h, and adjusted to not logln() but return a UPerfFunction. +// +//| void MyTest::runIndexedTest(int32_t index, UBool exec, +//| const char* &name, char* /*par*/) { +//| TESTCASE_AUTO_BEGIN; +//| TESTCASE_AUTO(TestSomething); +//| TESTCASE_AUTO(TestSomethingElse); +//| TESTCASE_AUTO(TestAnotherThing); +//| TESTCASE_AUTO_END; +//| return nullptr; +//| } +#define TESTCASE_AUTO_BEGIN \ + for(;;) { \ + int32_t testCaseAutoNumber = 0 + +#define TESTCASE_AUTO(test) \ + if (index == testCaseAutoNumber++) { \ + name = #test; \ + if (exec) { \ + return test(); \ + } \ + break; \ + } + +#define TESTCASE_AUTO_END \ + name = ""; \ + break; \ + } + +/** + * Subclasses of PerfTest will need to create subclasses of + * Function that define a call() method which contains the code to + * be timed. They then call setTestFunction() in their "Test..." + * method to establish this as the current test functor. + */ +class T_CTEST_EXPORT_API UPerfFunction { +public: + /** + * destructor + */ + virtual ~UPerfFunction(); + + /** + * Subclasses must implement this method to do the action to be + * measured. + */ + virtual void call(UErrorCode* status)=0; + + /** + * Subclasses must implement this method to return positive + * integer indicating the number of operations in a single + * call to this object's call() method. + */ + virtual long getOperationsPerIteration()=0; + /** + * Subclasses should override this method to return either positive + * or negative integer indicating the number of events in a single + * call to this object's call() method, if applicable + * e.g: Number of breaks / iterations for break iterator + */ + virtual long getEventsPerIteration(){ + return -1; + } + /** + * Call call() n times in a tight loop and return the elapsed + * milliseconds. If n is small and call() is fast the return + * result may be zero. Small return values have limited + * meaningfulness, depending on the underlying CPU and OS. + */ + virtual double time(int32_t n, UErrorCode* status) { + UTimer start, stop; + utimer_getTime(&start); + while (n-- > 0) { + call(status); + } + utimer_getTime(&stop); + return utimer_getDeltaSeconds(&start,&stop); // ms + } + +}; + + +class T_CTEST_EXPORT_API UPerfTest { +public: + UBool run(); + UBool runTest( char* name = nullptr, char* par = nullptr ); // not to be overridden + + virtual void usage() ; + + virtual ~UPerfTest(); + + void setCaller( UPerfTest* callingTest ); // for internal use only + + void setPath( char* path ); // for internal use only + + ULine* getLines(UErrorCode& status); + + const char16_t* getBuffer(int32_t& len,UErrorCode& status); + +protected: + UPerfTest(int32_t argc, const char* argv[], UErrorCode& status); + + UPerfTest(int32_t argc, const char* argv[], + UOption addOptions[], int32_t addOptionsCount, + const char *addUsage, + UErrorCode& status); + + void init(UOption addOptions[], int32_t addOptionsCount, + UErrorCode& status); + + virtual UPerfFunction* runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = nullptr ); // override ! + + virtual UBool runTestLoop( char* testname, char* par ); + + virtual UBool callTest( UPerfTest& testToBeCalled, char* par ); + + int32_t _argc; + const char** _argv; + const char * _addUsage; + char* resolvedFileName; + UCHARBUF* ucharBuf; + const char* encoding; + UBool uselen; + const char* fileName; + const char* sourceDir; + int32_t _remainingArgc; + ULine* lines; + int32_t numLines; + UBool line_mode; + char16_t* buffer; + int32_t bufferLen; + UBool verbose; + UBool bulk_mode; + int32_t passes; + int32_t iterations; + int32_t time; + const char* locale; +private: + UPerfTest* caller; + char* path; // specifies subtests + +// static members +public: + static UPerfTest* gTest; + static const char gUsageString[]; +}; + +#endif +#endif + diff --git a/intl/icu/source/tools/ctestfw/unicode/utimer.h b/intl/icu/source/tools/ctestfw/unicode/utimer.h new file mode 100644 index 0000000000..10f80833cb --- /dev/null +++ b/intl/icu/source/tools/ctestfw/unicode/utimer.h @@ -0,0 +1,282 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +************************************************************************ +* Copyright (c) 1997-2012, International Business Machines +* Corporation and others. All Rights Reserved. +************************************************************************ +*/ + +#ifndef _UTIMER_H +#define _UTIMER_H + +#include "unicode/utypes.h" + +#if U_PLATFORM_USES_ONLY_WIN32_API +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else +# if U_PLATFORM == U_PF_OS390 && !defined(__UU) +# define __UU /* Universal Unix - for struct timeval */ +# endif +# include <time.h> +# include <sys/time.h> +# include <unistd.h> +#endif + +/** + * This API provides functions for performing performance measurement + * There are 3 main usage scenarios. + * i) Loop until a threshold time is reached: + * Example: + * <code> + * typedef Params Params; + * struct Params{ + * char16_t* target; + * int32_t targetLen; + * const char16_t* source; + * int32_t sourceLen; + * UNormalizationMode mode; + * } + * void NormFn( void* param){ + * Params* parameters = ( Params*) param; + * UErrorCode error = U_ZERO_ERROR; + * unorm_normalize(parameters->source, parameters->sourceLen, parameters->mode, 0, parameters->target, parameters->targetLen, &error); + * if(U_FAILURE(error)){ + * printf("Normalization failed\n"); + * } + * } + * + * int main(){ + * // time the normalization function + * double timeTaken = 0; + * Params param; + * param.source // set up the source buffer + * param.target // set up the target buffer + * .... so on ... + * UTimer timer; + * // time the loop for 10 seconds at least and find out the loop count and time taken + * timeTaken = utimer_loopUntilDone((double)10,(void*) param, NormFn, &loopCount); + * } + * </code> + * + * ii) Measure the time taken + * Example: + * <code> + * double perfNormalization(NormFn fn,const char* mode,Line* fileLines,int32_t loopCount){ + * int line; + * int loops; + * UErrorCode error = U_ZERO_ERROR; + * char16_t* dest=nullptr; + * int32_t destCapacity=0; + * int len =-1; + * double elapsedTime = 0; + * int retVal=0; + * + * char16_t arr[5000]; + * dest=arr; + * destCapacity = 5000; + * UTimer start; + * + * // Initialize cache and ensure the data is loaded. + * // This loop checks for errors in Normalization. Once we pass the initialization + * // without errors we can safelly assume that there are no errors while timing the + * // function + * for (loops=0; loops<10; loops++) { + * for (line=0; line < gNumFileLines; line++) { + * if (opt_uselen) { + * len = fileLines[line].len; + * } + * + * retVal= fn(fileLines[line].name,len,dest,destCapacity,&error); + * #if U_PLATFORM_HAS_WIN32_API + * if(retVal==0 ){ + * fprintf(stderr,"Normalization of string in Windows API failed for mode %s. ErrorNo: %i at line number %i\n",mode,GetLastError(),line); + * return 0; + * } + * #endif + * if(U_FAILURE(error)){ + * fprintf(stderr,"Normalization of string in ICU API failed for mode %s. Error: %s at line number %i\n",mode,u_errorName(error),line); + * return 0; + * } + * + * } + * } + * + * //compute the time + * + * utimer_getTime(&start); + * for (loops=0; loops<loopCount; loops++) { + * for (line=0; line < gNumFileLines; line++) { + * if (opt_uselen) { + * len = fileLines[line].len; + * } + * + * retVal= fn(fileLines[line].name,len,dest,destCapacity,&error); + * + * } + * } + * + * return utimer_getElapsedSeconds(&start); + * } + * </code> + * + * iii) Let a higher level function do the calculation of confidence levels etc. + * Example: + * <code> + * void perf(UTimer* timer, char16_t* source, int32_t sourceLen, char16_t* target, int32_t targetLen, int32_t loopCount,UNormalizationMode mode, UErrorCode* error){ + * int32_t loops; + * for (loops=0; loops<loopCount; loops++) { + * unorm_normalize(source,sourceLen,target, targetLen,mode,error); + * } + * utimer_getTime(timer); + * } + * void main(const char* argsc, int argv){ + * // read the file and setup the data + * // set up options + * UTimer start,timer1, timer2, timer3, timer4; + * double NFDTimeTaken, NFCTimeTaken, FCDTimeTaken; + * switch(opt){ + * case 0: + * utimer_getTime(start); + * perf(timer1, source,sourceLen, target, targetLen,loopCount,UNORM_NFD,&error); + * NFDTimeTaken = utimer_getDeltaSeconds(start,timer1); + * case 1: + * timer_getTime(start); + * perf(timer2,source,sourceLen,target,targetLen,loopCount,UNORM_NFC,&error); + * NFCTimeTaken = utimer_getDeltaSeconds(start,timer2); + * perf(timer3, source, sourceLen, target,targetLen, loopCount, UNORM_FCD,&error); + * // ........so on ............. + * } + * // calculate confidence levels etc and print + * + * } + * + * </code> + * + */ + +typedef struct UTimer UTimer; + +typedef void FunctionToBeTimed(void* param); + + +#if U_PLATFORM_USES_ONLY_WIN32_API + + struct UTimer{ + LARGE_INTEGER start; + LARGE_INTEGER placeHolder; + }; + +static int uprv_initFrequency(UTimer* timer) + { + return QueryPerformanceFrequency(&timer->placeHolder); + } +static void uprv_start(UTimer* timer) + { + QueryPerformanceCounter(&timer->start); + } +static double uprv_delta(UTimer* timer1, UTimer* timer2){ + return ((double)(timer2->start.QuadPart - timer1->start.QuadPart))/((double)timer1->placeHolder.QuadPart); + } +static UBool uprv_compareFrequency(UTimer* timer1, UTimer* timer2){ + return (timer1->placeHolder.QuadPart == timer2->placeHolder.QuadPart); + } + +#else + + struct UTimer{ + struct timeval start; + struct timeval placeHolder; + }; + +static int32_t uprv_initFrequency(UTimer* /*timer*/) + { + return 0; + } +static void uprv_start(UTimer* timer) + { + gettimeofday(&timer->start, 0); + } +static double uprv_delta(UTimer* timer1, UTimer* timer2){ + double t1, t2; + + t1 = (double)timer1->start.tv_sec + (double)timer1->start.tv_usec/(1000*1000); + t2 = (double)timer2->start.tv_sec + (double)timer2->start.tv_usec/(1000*1000); + return (t2-t1); + } +static UBool uprv_compareFrequency(UTimer* /*timer1*/, UTimer* /*timer2*/){ + return true; + } + +#endif +/** + * Initializes the timer with the current time + * + * @param timer A pointer to UTimer struct to receive the current time + */ +static inline void U_EXPORT2 +utimer_getTime(UTimer* timer){ + uprv_initFrequency(timer); + uprv_start(timer); +} + +/** + * Returns the difference in times between timer1 and timer2 by subtracting + * timer1's time from timer2's time + * + * @param timer1 A pointer to UTimer struct to be used as starting time + * @param timer2 A pointer to UTimer struct to be used as end time + * @return Time in seconds + */ +static inline double U_EXPORT2 +utimer_getDeltaSeconds(UTimer* timer1, UTimer* timer2){ + if(uprv_compareFrequency(timer1,timer2)){ + return uprv_delta(timer1,timer2); + } + /* got error return -1 */ + return -1; +} + +/** + * Returns the time elapsed from the starting time represented by the + * UTimer struct pointer passed + * @param timer A pointer to UTimer struct to be used as starting time + * @return Time elapsed in seconds + */ +static inline double U_EXPORT2 +utimer_getElapsedSeconds(UTimer* timer){ + UTimer temp; + utimer_getTime(&temp); + return uprv_delta(timer,&temp); +} + +/** + * Executes the function pointed to for a given time and returns exact time + * taken and number of iterations of the loop + * @param thresholTimeVal + * @param loopCount output param to receive the number of iterations + * @param fn The function to be executed + * @param param Parameters to be passed to the fn + * @return the time elapsed in seconds + */ +static inline double U_EXPORT2 +utimer_loopUntilDone(double thresholdTimeVal, + int32_t* loopCount, + FunctionToBeTimed fn, + void* param){ + UTimer timer; + double currentVal=0; + *loopCount = 0; + utimer_getTime(&timer); + for(;currentVal<thresholdTimeVal;){ + fn(param); + currentVal = utimer_getElapsedSeconds(&timer); + (*loopCount)++; + } + return currentVal; +} + +#endif + diff --git a/intl/icu/source/tools/ctestfw/uperf.cpp b/intl/icu/source/tools/ctestfw/uperf.cpp new file mode 100644 index 0000000000..9e92b7714b --- /dev/null +++ b/intl/icu/source/tools/ctestfw/uperf.cpp @@ -0,0 +1,533 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2002-2012, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +// Defines _XOPEN_SOURCE for access to POSIX functions. +// Must be before any other #includes. +#include "uposixdefs.h" + +#include "unicode/uperf.h" +#include "uoptions.h" +#include "cmemory.h" +#include <stdio.h> +#include <stdlib.h> + +#if !UCONFIG_NO_CONVERSION + +UPerfFunction::~UPerfFunction() {} + +static const char delim = '/'; +static int32_t execCount = 0; +UPerfTest* UPerfTest::gTest = nullptr; +static const int MAXLINES = 40000; +const char UPerfTest::gUsageString[] = + "Usage: %s [OPTIONS] [FILES]\n" + "\tReads the input file and prints out time taken in seconds\n" + "Options:\n" + "\t-h or -? or --help this usage text\n" + "\t-v or --verbose print extra information when processing files\n" + "\t-s or --sourcedir source directory for files followed by path\n" + "\t followed by path\n" + "\t-e or --encoding encoding of source files\n" + "\t-u or --uselen perform timing analysis on non-null terminated buffer using length\n" + "\t-f or --file-name file to be used as input data\n" + "\t-p or --passes Number of passes to be performed. Requires Numeric argument.\n" + "\t Cannot be used with --time\n" + "\t-i or --iterations Number of iterations to be performed. Requires Numeric argument\n" + "\t-t or --time Threshold time for looping until in seconds. Requires Numeric argument.\n" + "\t Cannot be used with --iterations\n" + "\t-l or --line-mode The data file should be processed in line mode\n" + "\t-b or --bulk-mode The data file should be processed in file based.\n" + "\t Cannot be used with --line-mode\n" + "\t-L or --locale Locale for the test\n"; + +enum +{ + HELP1, + HELP2, + VERBOSE, + SOURCEDIR, + ENCODING, + USELEN, + FILE_NAME, + PASSES, + ITERATIONS, + TIME, + LINE_MODE, + BULK_MODE, + LOCALE, + OPTIONS_COUNT +}; + + +static UOption options[OPTIONS_COUNT+20]={ + UOPTION_HELP_H, + UOPTION_HELP_QUESTION_MARK, + UOPTION_VERBOSE, + UOPTION_SOURCEDIR, + UOPTION_ENCODING, + UOPTION_DEF( "uselen", 'u', UOPT_NO_ARG), + UOPTION_DEF( "file-name", 'f', UOPT_REQUIRES_ARG), + UOPTION_DEF( "passes", 'p', UOPT_REQUIRES_ARG), + UOPTION_DEF( "iterations", 'i', UOPT_REQUIRES_ARG), + UOPTION_DEF( "time", 't', UOPT_REQUIRES_ARG), + UOPTION_DEF( "line-mode", 'l', UOPT_NO_ARG), + UOPTION_DEF( "bulk-mode", 'b', UOPT_NO_ARG), + UOPTION_DEF( "locale", 'L', UOPT_REQUIRES_ARG) +}; + +UPerfTest::UPerfTest(int32_t argc, const char* argv[], UErrorCode& status) + : _argc(argc), _argv(argv), _addUsage(nullptr), + ucharBuf(nullptr), encoding(""), + uselen(false), + fileName(nullptr), sourceDir("."), + lines(nullptr), numLines(0), line_mode(true), + buffer(nullptr), bufferLen(0), + verbose(false), bulk_mode(false), + passes(1), iterations(0), time(0), + locale(nullptr) { + init(nullptr, 0, status); +} + +UPerfTest::UPerfTest(int32_t argc, const char* argv[], + UOption addOptions[], int32_t addOptionsCount, + const char *addUsage, + UErrorCode& status) + : _argc(argc), _argv(argv), _addUsage(addUsage), + ucharBuf(nullptr), encoding(""), + uselen(false), + fileName(nullptr), sourceDir("."), + lines(nullptr), numLines(0), line_mode(true), + buffer(nullptr), bufferLen(0), + verbose(false), bulk_mode(false), + passes(1), iterations(0), time(0), + locale(nullptr) { + init(addOptions, addOptionsCount, status); +} + +void UPerfTest::init(UOption addOptions[], int32_t addOptionsCount, + UErrorCode& status) { + //initialize the argument list + U_MAIN_INIT_ARGS(_argc, _argv); + + resolvedFileName = nullptr; + + // add specific options + int32_t optionsCount = OPTIONS_COUNT; + if (addOptionsCount > 0) { + memcpy(options+optionsCount, addOptions, addOptionsCount*sizeof(UOption)); + optionsCount += addOptionsCount; + } + + //parse the arguments + _remainingArgc = u_parseArgs(_argc, (char**)_argv, optionsCount, options); + + // copy back values for additional options + if (addOptionsCount > 0) { + memcpy(addOptions, options+OPTIONS_COUNT, addOptionsCount*sizeof(UOption)); + } + + // Now setup the arguments + if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if(options[VERBOSE].doesOccur) { + verbose = true; + } + + if(options[SOURCEDIR].doesOccur) { + sourceDir = options[SOURCEDIR].value; + } + + if(options[ENCODING].doesOccur) { + encoding = options[ENCODING].value; + } + + if(options[USELEN].doesOccur) { + uselen = true; + } + + if(options[FILE_NAME].doesOccur){ + fileName = options[FILE_NAME].value; + } + + if(options[PASSES].doesOccur) { + passes = atoi(options[PASSES].value); + } + if(options[ITERATIONS].doesOccur) { + iterations = atoi(options[ITERATIONS].value); + if(options[TIME].doesOccur) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + } else if(options[TIME].doesOccur) { + time = atoi(options[TIME].value); + } else { + iterations = 1000; // some default + } + + if(options[LINE_MODE].doesOccur) { + line_mode = true; + bulk_mode = false; + } + + if(options[BULK_MODE].doesOccur) { + bulk_mode = true; + line_mode = false; + } + + if(options[LOCALE].doesOccur) { + locale = options[LOCALE].value; + } + + int32_t len = 0; + if(fileName!=nullptr){ + //pre-flight + ucbuf_resolveFileName(sourceDir, fileName, nullptr, &len, &status); + resolvedFileName = (char*) uprv_malloc(len); + if(resolvedFileName==nullptr){ + status= U_MEMORY_ALLOCATION_ERROR; + return; + } + if(status == U_BUFFER_OVERFLOW_ERROR){ + status = U_ZERO_ERROR; + } + ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &status); + ucharBuf = ucbuf_open(resolvedFileName,&encoding,true,false,&status); + + if(U_FAILURE(status)){ + printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status)); + return; + } + } +} + +ULine* UPerfTest::getLines(UErrorCode& status){ + if (U_FAILURE(status)) { + return nullptr; + } + if (lines != nullptr) { + return lines; // don't do it again + } + lines = new ULine[MAXLINES]; + int maxLines = MAXLINES; + numLines=0; + const char16_t* line=nullptr; + int32_t len =0; + for (;;) { + line = ucbuf_readline(ucharBuf,&len,&status); + if(line == nullptr || U_FAILURE(status)){ + break; + } + lines[numLines].name = new char16_t[len]; + lines[numLines].len = len; + memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR); + + numLines++; + len = 0; + if (numLines >= maxLines) { + maxLines += MAXLINES; + ULine *newLines = new ULine[maxLines]; + if(newLines == nullptr) { + fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines); + status= U_MEMORY_ALLOCATION_ERROR; + delete []lines; + return nullptr; + } + + memcpy(newLines, lines, numLines*sizeof(ULine)); + delete []lines; + lines = newLines; + } + } + return lines; +} +const char16_t* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){ + if (U_FAILURE(status)) { + return nullptr; + } + len = ucbuf_size(ucharBuf); + buffer = (char16_t*) uprv_malloc(U_SIZEOF_UCHAR * (len+1)); + u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len); + buffer[len]=0; + len = bufferLen; + return buffer; +} +UBool UPerfTest::run(){ + if(_remainingArgc==1){ + // Testing all methods + return runTest(); + } + UBool res=false; + // Test only the specified function + for (int i = 1; i < _remainingArgc; ++i) { + if (_argv[i][0] != '-') { + char* name = (char*) _argv[i]; + if(verbose==true){ + //fprintf(stdout, "\n=== Handling test: %s: ===\n", name); + //fprintf(stdout, "\n%s:\n", name); + } + char* parameter = strchr( name, '@' ); + if (parameter) { + *parameter = 0; + parameter += 1; + } + execCount = 0; + res = runTest( name, parameter ); + if (!res || (execCount <= 0)) { + fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name); + return false; + } + } + } + return res; +} +UBool UPerfTest::runTest(char* name, char* par ){ + UBool rval; + char* pos = nullptr; + + if (name) + pos = strchr( name, delim ); // check if name contains path (by looking for '/') + if (pos) { + path = pos+1; // store subpath for calling subtest + *pos = 0; // split into two strings + }else{ + path = nullptr; + } + + if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) { + rval = runTestLoop( nullptr, nullptr ); + + }else if (strcmp( name, "LIST" ) == 0) { + this->usage(); + rval = true; + + }else{ + rval = runTestLoop( name, par ); + } + + if (pos) + *pos = delim; // restore original value at pos + return rval; +} + + +void UPerfTest::setPath( char* pathVal ) +{ + this->path = pathVal; +} + +// call individual tests, to be overridden to call implementations +UPerfFunction* UPerfTest::runIndexedTest( int32_t /*index*/, UBool /*exec*/, const char* & /*name*/, char* /*par*/ ) +{ + // to be overridden by a method like: + /* + switch (index) { + case 0: name = "First Test"; if (exec) FirstTest( par ); break; + case 1: name = "Second Test"; if (exec) SecondTest( par ); break; + default: name = ""; break; + } + */ + fprintf(stderr,"*** runIndexedTest needs to be overridden! ***"); + return nullptr; +} + + +UBool UPerfTest::runTestLoop( char* testname, char* par ) +{ + int32_t index = 0; + const char* name; + UBool run_this_test; + UBool rval = false; + UErrorCode status = U_ZERO_ERROR; + UPerfTest* saveTest = gTest; + gTest = this; + int32_t loops = 0; + double t=0; + int32_t n = 1; + long ops; + do { + this->runIndexedTest( index, false, name ); + if (!name || (name[0] == 0)) + break; + if (!testname) { + run_this_test = true; + }else{ + run_this_test = (UBool) (strcmp( name, testname ) == 0); + } + if (run_this_test) { + UPerfFunction* testFunction = this->runIndexedTest( index, true, name, par ); + execCount++; + rval=true; + if(testFunction==nullptr){ + fprintf(stderr,"%s function returned nullptr", name); + return false; + } + ops = testFunction->getOperationsPerIteration(); + if (ops < 1) { + fprintf(stderr, "%s returned an illegal operations/iteration()\n", name); + return false; + } + if(iterations == 0) { + n = time; + // Run for specified duration in seconds + if(verbose==true){ + fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n); + } + + //n *= 1000; // s => ms + //System.out.println("# " + meth.getName() + " " + n + " sec"); + int32_t failsafe = 1; // last resort for very fast methods + t = 0; + while (t < (int)(n * 0.9)) { // 90% is close enough + if (loops == 0 || t == 0) { + loops = failsafe; + failsafe *= 10; + } else { + //System.out.println("# " + meth.getName() + " x " + loops + " = " + t); + loops = (int)((double)n / t * loops + 0.5); + if (loops == 0) { + fprintf(stderr,"Unable to converge on desired duration"); + return false; + } + } + //System.out.println("# " + meth.getName() + " x " + loops); + t = testFunction->time(loops,&status); + if(U_FAILURE(status)){ + printf("Performance test failed with error: %s \n", u_errorName(status)); + break; + } + } + } else { + loops = iterations; + } + + double min_t=1000000.0, sum_t=0.0; + long events = -1; + + for(int32_t ps =0; ps < passes; ps++){ + if(verbose==true){ + fprintf(stdout,"= %s begin " ,name); + if(iterations > 0) { + fprintf(stdout, "%i\n", (int)loops); + } else { + fprintf(stdout, "%i\n", (int)n); + } + } + t = testFunction->time(loops, &status); + if(U_FAILURE(status)){ + printf("Performance test failed with error: %s \n", u_errorName(status)); + break; + } + sum_t+=t; + if(t<min_t) { + min_t=t; + } + events = testFunction->getEventsPerIteration(); + //print info only in verbose mode + if(verbose==true){ + if(events == -1){ + fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops); + }else{ + fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events); + } + } + } + if(verbose && U_SUCCESS(status)) { + double avg_t = sum_t/passes; + if (loops == 0 || ops == 0) { + fprintf(stderr, "%s did not run\n", name); + } + else if(events == -1) { + fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n", + name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops)); + fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n", + name, min_t, (int)loops, (min_t*1E9)/(loops*ops)); + } + else { + fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n", + name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events)); + fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n", + name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events)); + } + } + else if(U_SUCCESS(status)) { + // Print results in ndjson format for GHA Benchmark to process. + fprintf(stdout, + "{\"biggerIsBetter\":false,\"name\":\"%s\",\"unit\":\"ns/iter\",\"value\":%.4f}\n", + name, (min_t*1E9)/(loops*ops)); + } + delete testFunction; + } + index++; + }while(name); + + gTest = saveTest; + return rval; +} + +/** +* Print a usage message for this test class. +*/ +void UPerfTest::usage() +{ + puts(gUsageString); + if (_addUsage != nullptr) { + puts(_addUsage); + } + + UBool save_verbose = verbose; + verbose = true; + fprintf(stdout,"Test names:\n"); + fprintf(stdout,"-----------\n"); + + int32_t index = 0; + const char* name = nullptr; + do{ + this->runIndexedTest( index, false, name ); + if (!name) + break; + fprintf(stdout, "%s\n", name); + index++; + }while (name && (name[0] != 0)); + verbose = save_verbose; +} + + + + +void UPerfTest::setCaller( UPerfTest* callingTest ) +{ + caller = callingTest; + if (caller) { + verbose = caller->verbose; + } +} + +UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par ) +{ + execCount--; // correct a previously assumed test-exec, as this only calls a subtest + testToBeCalled.setCaller( this ); + return testToBeCalled.runTest( path, par ); +} + +UPerfTest::~UPerfTest(){ + if(lines!=nullptr){ + delete[] lines; + } + if(buffer!=nullptr){ + uprv_free(buffer); + } + if(resolvedFileName!=nullptr){ + uprv_free(resolvedFileName); + } + ucbuf_close(ucharBuf); +} + +#endif |