summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/tools/ctestfw
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /intl/icu/source/tools/ctestfw
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/icu/source/tools/ctestfw')
-rw-r--r--intl/icu/source/tools/ctestfw/Makefile.in149
-rw-r--r--intl/icu/source/tools/ctestfw/ctest.c1333
-rw-r--r--intl/icu/source/tools/ctestfw/ctestfw.vcxproj104
-rw-r--r--intl/icu/source/tools/ctestfw/ctestfw.vcxproj.filters63
-rw-r--r--intl/icu/source/tools/ctestfw/datamap.cpp224
-rw-r--r--intl/icu/source/tools/ctestfw/sources.txt6
-rw-r--r--intl/icu/source/tools/ctestfw/testdata.cpp144
-rw-r--r--intl/icu/source/tools/ctestfw/tstdtmod.cpp293
-rw-r--r--intl/icu/source/tools/ctestfw/ucln_ct.c19
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/ctest.h321
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/datamap.h140
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/testdata.h113
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/testlog.h62
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/testtype.h40
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/tstdtmod.h117
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/uperf.h200
-rw-r--r--intl/icu/source/tools/ctestfw/unicode/utimer.h282
-rw-r--r--intl/icu/source/tools/ctestfw/uperf.cpp533
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