X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/b75a7d8f3b4adbae880cab104ce2c6a50eee4db2..2be6500137966bba13903b24204a44804f9e133a:/icuSources/test/intltest/intltest.cpp diff --git a/icuSources/test/intltest/intltest.cpp b/icuSources/test/intltest/intltest.cpp index 5251c66c..6d08af15 100644 --- a/icuSources/test/intltest/intltest.cpp +++ b/icuSources/test/intltest/intltest.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2003, International Business Machines Corporation and + * Copyright (c) 1997-2013, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -22,13 +22,22 @@ #include "unicode/smpdtfmt.h" #include "unicode/ucnv.h" #include "unicode/uclean.h" +#include "unicode/timezone.h" +#include "unicode/curramt.h" +#include "unicode/putil.h" #include "intltest.h" #include "caltztst.h" #include "itmajor.h" -#include "tsmutex.h" - +#include "cstring.h" #include "umutex.h" +#include "uassert.h" +#include "cmemory.h" +#include "uoptions.h" + +#include "putilimp.h" // for uprv_getRawUTCtime() +#include "unicode/locid.h" +#include "unicode/ctest.h" // for str_timeDelta #ifdef XP_MAC_CONSOLE #include @@ -50,16 +59,6 @@ UnicodeString UCharToUnicodeString(UChar c) { return UnicodeString(c); } -// [LIU] Just to get things working -UnicodeString -operator+(const UnicodeString& left, - const UnicodeString& right) -{ - UnicodeString str(left); - str += right; - return str; -} - // [rtg] Just to get things working UnicodeString operator+(const UnicodeString& left, @@ -87,6 +86,22 @@ operator+(const UnicodeString& left, return left + buffer; } +UnicodeString +Int64ToUnicodeString(int64_t num) +{ + char buffer[64]; // nos changed from 10 to 64 + char danger = 'p'; // guard against overrunning the buffer (rtg) + +#if defined(_MSC_VER) + sprintf(buffer, "%I64d", num); +#else + sprintf(buffer, "%lld", (long long)num); +#endif + assert(danger == 'p'); + + return buffer; +} + // [LIU] Just to get things working UnicodeString operator+(const UnicodeString& left, @@ -95,7 +110,11 @@ operator+(const UnicodeString& left, char buffer[64]; // was 32, made it arbitrarily bigger (rtg) char danger = 'p'; // guard against overrunning the buffer (rtg) - sprintf(buffer, "%.30g", num); // nos changed from 99 to 30 + // IEEE floating point has 52 bits of mantissa, plus one assumed bit + // 53*log(2)/log(10) = 15.95 + // so there is no need to show more than 16 digits. [alan] + + sprintf(buffer, "%.17g", num); assert(danger == 'p'); return left + buffer; @@ -104,10 +123,9 @@ operator+(const UnicodeString& left, #if !UCONFIG_NO_FORMATTING /** - * Originally coded this as operator+, but that makes the expression - * + char* ambiguous. - liu + * Return a string display for this, without surrounding braces. */ -UnicodeString toString(const Formattable& f) { +UnicodeString _toString(const Formattable& f) { UnicodeString s; switch (f.getType()) { case Formattable::kDate: @@ -117,45 +135,75 @@ UnicodeString toString(const Formattable& f) { if (U_SUCCESS(status)) { FieldPosition pos; fmt.format(f.getDate(), s, pos); - s.insert(0, "[Date:"); - s.insert(s.length(), (UChar)0x005d); + s.insert(0, "Date:"); } else { - s = UnicodeString("[Error creating date format]"); + s = UnicodeString("Error creating date format]"); } } break; case Formattable::kDouble: - s = UnicodeString("[Double:") + f.getDouble() + "]"; + s = UnicodeString("double:") + f.getDouble(); break; case Formattable::kLong: - s = UnicodeString("[Long:") + f.getLong() + "]"; + s = UnicodeString("long:") + f.getLong(); break; + + case Formattable::kInt64: + s = UnicodeString("int64:") + Int64ToUnicodeString(f.getInt64()); + break; + case Formattable::kString: f.getString(s); - s.insert(0, "[String:"); - s.insert(s.length(), (UChar)0x005d); + s.insert(0, "String:"); break; case Formattable::kArray: { int32_t i, n; const Formattable* array = f.getArray(n); - s.insert(0, UnicodeString("[Array:")); + s.insert(0, UnicodeString("Array:")); UnicodeString delim(", "); for (i=0; i 0) { s.append(delim); } - s = s + toString(array[i]); + s = s + _toString(array[i]); } - s.append(UChar(0x005d)); } break; + case Formattable::kObject: { + const CurrencyAmount* c = dynamic_cast(f.getObject()); + if (c != NULL) { + s = _toString(c->getNumber()) + " " + UnicodeString(c->getISOCurrency()); + } else { + s = UnicodeString("Unknown UObject"); + } + break; + } + default: + s = UnicodeString("Unknown Formattable type=") + (int32_t)f.getType(); + break; } return s; } +/** + * Originally coded this as operator+, but that makes the expression + * + char* ambiguous. - liu + */ +UnicodeString toString(const Formattable& f) { + UnicodeString s((UChar)91/*[*/); + s.append(_toString(f)); + s.append((UChar)0x5d/*]*/); + return s; +} + #endif +// useful when operator+ won't cooperate +UnicodeString toString(int32_t n) { + return UnicodeString() + (long)n; +} + // stephen - cleaned up 05/05/99 UnicodeString operator+(const UnicodeString& left, char num) { return left + (long)num; } @@ -185,6 +233,14 @@ IntlTest::appendHex(uint32_t number, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0 }; /* "0123456789ABCDEF" */ + if (digits < 0) { // auto-digits + digits = 2; + uint32_t max = 0xff; + while (number > max) { + digits += 2; + max = (max << 8) | 0xff; + } + } switch (digits) { case 8: @@ -210,6 +266,17 @@ IntlTest::appendHex(uint32_t number, return target; } +UnicodeString +IntlTest::toHex(uint32_t number, int32_t digits) { + UnicodeString result; + appendHex(number, digits, result); + return result; +} + +static inline UBool isPrintable(UChar32 c) { + return c <= 0x7E && (c >= 0x20 || c == 9 || c == 0xA || c == 0xD); +} + // Replace nonprintable characters with unicode escapes UnicodeString& IntlTest::prettify(const UnicodeString &source, @@ -223,9 +290,9 @@ IntlTest::prettify(const UnicodeString &source, for (i = 0; i < source.length(); ) { UChar32 ch = source.char32At(i); - i += UTF_CHAR_LENGTH(ch); + i += U16_LENGTH(ch); - if (ch < 0x09 || (ch > 0x0A && ch < 0x20)|| ch > 0x7E) + if (!isPrintable(ch)) { if (ch <= 0xFFFF) { target += "\\u"; @@ -258,9 +325,9 @@ IntlTest::prettify(const UnicodeString &source, UBool parseBackslash) for (i = 0; i < source.length();) { UChar32 ch = source.char32At(i); - i += UTF_CHAR_LENGTH(ch); + i += U16_LENGTH(ch); - if (ch < 0x09 || (ch > 0x0A && ch < 0x20)|| ch > 0x7E) + if (!isPrintable(ch)) { if (parseBackslash) { // If we are preceded by an odd number of backslashes, @@ -297,68 +364,6 @@ IntlTest::prettify(const UnicodeString &source, UBool parseBackslash) return target; } -#if defined(_WIN32) || defined(WIN32) || defined(__OS2__) || defined(OS2) -#define PREV_DIR ".." -#else -#define PREV_DIR "/../" -#endif - -void -IntlTest::pathnameInContext( char* fullname, int32_t maxsize, const char* relPath ) //nosmac -{ - const char* mainDir; - char sepChar; - const char inpSepChar = '|'; - - // So what's going on is that ICU_DATA during tests points to: - // ICU | source | data - //and we want ICU | source | - // - // We'll add | test | testdata - // - // So, just add a .. here - back up one level - - mainDir = u_getDataDirectory(); - sepChar = U_FILE_SEP_CHAR; - char sepString[] = U_FILE_SEP_STRING; - -#if defined(XP_MAC) - Str255 volName; - int16_t volNum; - OSErr err = GetVol( volName, &volNum ); - if (err != noErr) - volName[0] = 0; - mainDir = (char*) &(volName[1]); - mainDir[volName[0]] = 0; -#else - char mainDirBuffer[255]; - if(mainDir!=NULL) { - strcpy(mainDirBuffer, mainDir); - strcat(mainDirBuffer, PREV_DIR); - } else { - mainDirBuffer[0]='\0'; - } - mainDir=mainDirBuffer; -#endif - - if (relPath[0] == '|') - relPath++; - int32_t lenMainDir = strlen(mainDir); - int32_t lenRelPath = strlen(relPath); - if (maxsize < lenMainDir + lenRelPath + 2) { - fullname[0] = 0; - return; - } - strcpy(fullname, mainDir); - strcat(fullname, sepString); - strcat(fullname, relPath); - char* tmp = strchr(fullname, inpSepChar); - while (tmp) { - *tmp = sepChar; - tmp = strchr(tmp+1, inpSepChar); - } -} - /* IntlTest::setICU_DATA - if the ICU_DATA environment variable is not already * set, try to deduce the directory in which ICU was built, * and set ICU_DATA to "icu/source/data" in that location. @@ -381,7 +386,7 @@ void IntlTest::setICU_DATA() { // may not be the same as the source directory, depending on // the configure options used. At any rate, // set the data path to the built data from this directory. - // The value is complete with quotes, so it can be used + // The value is complete with quotes, so it can be used // as-is as a string constant. #if defined (U_TOPBUILDDIR) @@ -498,71 +503,52 @@ void it_errln( UnicodeString message ) IntlTest::gTest->errln( message ); } -IntlTest& operator<<(IntlTest& test, const UnicodeString& string) -{ -/* NULL shouldn't happen */ -// if (&test == NULL) -// return *((IntlTest*) NULL); - test.log( string ); - return test; -} - -IntlTest& operator<<(IntlTest& test, const char* string) -{ -/* NULL shouldn't happen */ -// if (&test == NULL) -// return *((IntlTest*) NULL); - test.log( string ); - return test; -} - -IntlTest& operator<<(IntlTest& test, const int32_t num) -{ -/* NULL shouldn't happen */ -// if (&test == NULL) -// return *((IntlTest*) NULL); - char convert[20]; - sprintf(convert, "%li", (long)num); - test.log(convert); - return test; -} - -IntlTest& endl( IntlTest& test ) +void it_dataerr( UnicodeString message ) { - test.logln(); - return test; + if (IntlTest::gTest) + IntlTest::gTest->dataerr( message ); } -IntlTest& operator<<(IntlTest& test, IntlTest& ( * _f)(IntlTest&)) +void it_dataerrln( UnicodeString message ) { - (*_f)(test); - return test; + if (IntlTest::gTest) + IntlTest::gTest->dataerrln( message ); } - IntlTest::IntlTest() { caller = NULL; - path = NULL; + testPath = NULL; LL_linestart = TRUE; errorCount = 0; + dataErrorCount = 0; verbose = FALSE; + no_time = FALSE; no_err_msg = FALSE; + warn_on_missing_data = FALSE; quick = FALSE; leaks = FALSE; + threadCount = 1; testoutfp = stdout; LL_indentlevel = indentLevel_offset; + numProps = 0; + strcpy(basePath, "/"); } void IntlTest::setCaller( IntlTest* callingTest ) { caller = callingTest; if (caller) { + warn_on_missing_data = caller->warn_on_missing_data; verbose = caller->verbose; no_err_msg = caller->no_err_msg; quick = caller->quick; testoutfp = caller->testoutfp; LL_indentlevel = caller->LL_indentlevel + indentLevel_offset; + numProps = caller->numProps; + for (int32_t i = 0; i < numProps; i++) { + proplines[i] = caller->proplines[i]; + } } } @@ -570,12 +556,15 @@ UBool IntlTest::callTest( IntlTest& testToBeCalled, char* par ) { execCount--; // correct a previously assumed test-exec, as this only calls a subtest testToBeCalled.setCaller( this ); - return testToBeCalled.runTest( path, par ); + strcpy(testToBeCalled.basePath, this->basePath ); + UBool result = testToBeCalled.runTest( testPath, par, testToBeCalled.basePath ); + strcpy(testToBeCalled.basePath, this->basePath ); // reset it. + return result; } void IntlTest::setPath( char* pathVal ) { - this->path = pathVal; + this->testPath = pathVal; } UBool IntlTest::setVerbose( UBool verboseVal ) @@ -585,6 +574,20 @@ UBool IntlTest::setVerbose( UBool verboseVal ) return rval; } +UBool IntlTest::setNotime( UBool no_time ) +{ + UBool rval = this->no_time; + this->no_time = no_time; + return rval; +} + +UBool IntlTest::setWarnOnMissingData( UBool warn_on_missing_dataVal ) +{ + UBool rval = this->warn_on_missing_data; + this->warn_on_missing_data = warn_on_missing_dataVal; + return rval; +} + UBool IntlTest::setNoErrMsg( UBool no_err_msgVal ) { UBool rval = this->no_err_msg; @@ -606,43 +609,66 @@ UBool IntlTest::setLeaks( UBool leaksVal ) return rval; } +int32_t IntlTest::setThreadCount( int32_t count ) +{ + int32_t rval = this->threadCount; + this->threadCount = count; + return rval; +} + int32_t IntlTest::getErrors( void ) { return errorCount; } -UBool IntlTest::runTest( char* name, char* par ) +int32_t IntlTest::getDataErrors( void ) +{ + return dataErrorCount; +} + +UBool IntlTest::runTest( char* name, char* par, char *baseName ) { UBool rval; char* pos = NULL; + char* baseNameBuffer = NULL; + + if(baseName == NULL) { + baseNameBuffer = (char*)malloc(1024); + baseName=baseNameBuffer; + strcpy(baseName, "/"); + } + if (name) pos = strchr( name, delim ); // check if name contains path (by looking for '/') if (pos) { - path = pos+1; // store subpath for calling subtest + testPath = pos+1; // store subpath for calling subtest *pos = 0; // split into two strings }else{ - path = NULL; + testPath = NULL; } if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) { - rval = runTestLoop( NULL, NULL ); + rval = runTestLoop( NULL, par, baseName ); }else if (strcmp( name, "LIST" ) == 0) { this->usage(); rval = TRUE; }else{ - rval = runTestLoop( name, par ); + rval = runTestLoop( name, par, baseName ); } if (pos) *pos = delim; // restore original value at pos + if(baseNameBuffer!=NULL) { + free(baseNameBuffer); + } return rval; } // call individual tests, to be overriden to call implementations -void IntlTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par ) +void IntlTest::runIndexedTest( int32_t /*index*/, UBool /*exec*/, const char* & /*name*/, char* /*par*/ ) { // to be overriden by a method like: /* @@ -653,11 +679,10 @@ void IntlTest::runIndexedTest( int32_t index, UBool exec, const char* &name, cha } */ this->errln("*** runIndexedTest needs to be overriden! ***"); - name = ""; exec = exec; index = index; par = par; } -UBool IntlTest::runTestLoop( char* testname, char* par ) +UBool IntlTest::runTestLoop( char* testname, char* par, char *baseName ) { int32_t index = 0; const char* name; @@ -666,28 +691,69 @@ UBool IntlTest::runTestLoop( char* testname, char* par ) UBool rval = FALSE; UBool lastTestFailed; + if(baseName == NULL) { + printf("ERROR: baseName can't be null.\n"); + return FALSE; + } else { + if ((char *)this->basePath != baseName) { + strcpy(this->basePath, baseName); + } + } + + char * saveBaseLoc = baseName+strlen(baseName); + IntlTest* saveTest = gTest; gTest = this; 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); + this->runIndexedTest( index, FALSE, name, par ); + if (strcmp(name,"skip") == 0) { + run_this_test = FALSE; + } else { + 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) { lastErrorCount = errorCount; execCount++; + char msg[256]; + sprintf(msg, "%s {", name); + LL_message(msg, TRUE); + UDate timeStart = uprv_getRawUTCtime(); + strcpy(saveBaseLoc,name); + strcat(saveBaseLoc,"/"); + this->runIndexedTest( index, TRUE, name, par ); + + UDate timeStop = uprv_getRawUTCtime(); rval = TRUE; // at least one test has been called - char msg[256]; + char secs[256]; + if(!no_time) { + sprintf(secs, "%f", (timeStop-timeStart)/1000.0); + } else { + secs[0]=0; + } + + + strcpy(saveBaseLoc,name); + + + ctest_xml_testcase(baseName, name, secs, (lastErrorCount!=errorCount)?"err":NULL); + + + saveBaseLoc[0]=0; /* reset path */ + if (lastErrorCount == errorCount) { - sprintf( msg, "---OK: %s", name ); + sprintf( msg, " } OK: %s ", name ); + if(!no_time) str_timeDelta(msg+strlen(msg),timeStop-timeStart); lastTestFailed = FALSE; }else{ - sprintf(msg, "---ERRORS (%li) in %s", (long)(errorCount-lastErrorCount), name); + sprintf(msg, " } ERRORS (%li) in %s", (long)(errorCount-lastErrorCount), name); + if(!no_time) str_timeDelta(msg+strlen(msg),timeStop-timeStart); for(int i=0;iIncDataErrorCount(); + return dataErrorCount; +} + +void IntlTest::err() +{ IncErrorCount(); } @@ -787,10 +863,43 @@ void IntlTest::errln( const UnicodeString &message ) if (!no_err_msg) LL_message( message, TRUE ); } +void IntlTest::dataerr( const UnicodeString &message ) +{ + IncDataErrorCount(); + + if (!warn_on_missing_data) { + IncErrorCount(); + } + + if (!no_err_msg) LL_message( message, FALSE ); +} + +void IntlTest::dataerrln( const UnicodeString &message ) +{ + IncDataErrorCount(); + UnicodeString msg; + if (!warn_on_missing_data) { + IncErrorCount(); + msg = message; + } else { + msg = UnicodeString("[DATA] " + message); + } + + if (!no_err_msg) LL_message( msg + " - (Are you missing data?)", TRUE ); +} + +void IntlTest::errcheckln(UErrorCode status, const UnicodeString &message ) { + if (status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR) { + dataerrln(message); + } else { + errln(message); + } +} + /* convenience functions that include sprintf formatting */ void IntlTest::log(const char *fmt, ...) { - char buffer[512]; + char buffer[4000]; va_list ap; va_start(ap, fmt); @@ -804,7 +913,7 @@ void IntlTest::log(const char *fmt, ...) void IntlTest::logln(const char *fmt, ...) { - char buffer[512]; + char buffer[4000]; va_list ap; va_start(ap, fmt); @@ -819,7 +928,7 @@ void IntlTest::logln(const char *fmt, ...) /* convenience functions that include sprintf formatting */ void IntlTest::info(const char *fmt, ...) { - char buffer[512]; + char buffer[4000]; va_list ap; va_start(ap, fmt); @@ -831,7 +940,7 @@ void IntlTest::info(const char *fmt, ...) void IntlTest::infoln(const char *fmt, ...) { - char buffer[512]; + char buffer[4000]; va_list ap; va_start(ap, fmt); @@ -843,7 +952,7 @@ void IntlTest::infoln(const char *fmt, ...) void IntlTest::err(const char *fmt, ...) { - char buffer[512]; + char buffer[4000]; va_list ap; va_start(ap, fmt); @@ -854,7 +963,7 @@ void IntlTest::err(const char *fmt, ...) void IntlTest::errln(const char *fmt, ...) { - char buffer[512]; + char buffer[4000]; va_list ap; va_start(ap, fmt); @@ -863,6 +972,33 @@ void IntlTest::errln(const char *fmt, ...) errln(UnicodeString(buffer, "")); } +void IntlTest::dataerrln(const char *fmt, ...) +{ + char buffer[4000]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + dataerrln(UnicodeString(buffer, "")); +} + +void IntlTest::errcheckln(UErrorCode status, const char *fmt, ...) +{ + char buffer[4000]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + if (status == U_FILE_ACCESS_ERROR || status == U_MISSING_RESOURCE_ERROR) { + dataerrln(UnicodeString(buffer, "")); + } else { + errln(UnicodeString(buffer, "")); + } +} + void IntlTest::printErrors() { IntlTest::LL_message(errorList, TRUE); @@ -893,7 +1029,7 @@ void IntlTest::LL_message( UnicodeString message, UBool newline ) // stream out the indentation string first if necessary length = indent.extract(1, indent.length(), buffer, sizeof(buffer)); if (length > 0) { - fwrite(buffer, sizeof(*buffer), length, testoutfp); + fwrite(buffer, sizeof(*buffer), length, (FILE *)testoutfp); } // replace each LineFeed by the indentation string @@ -902,17 +1038,18 @@ void IntlTest::LL_message( UnicodeString message, UBool newline ) // stream out the message length = message.extract(0, message.length(), buffer, sizeof(buffer)); if (length > 0) { - fwrite(buffer, sizeof(*buffer), length, testoutfp); + length = length > 10000 ? 10000 : length; + fwrite(buffer, sizeof(*buffer), length, (FILE *)testoutfp); } if (newline) { char newLine = '\n'; - fwrite(&newLine, sizeof(newLine), 1, testoutfp); + fwrite(&newLine, sizeof(newLine), 1, (FILE *)testoutfp); } // A newline usually flushes the buffer, but // flush the message just in case of a core dump. - fflush(testoutfp); + fflush((FILE *)testoutfp); } /** @@ -959,83 +1096,95 @@ int main(int argc, char* argv[]) { UBool syntax = FALSE; - UBool all = TRUE; + UBool all = FALSE; UBool verbose = FALSE; UBool no_err_msg = FALSE; + UBool no_time = FALSE; UBool quick = TRUE; UBool name = FALSE; UBool leaks = FALSE; UBool warnOnMissingData = FALSE; + UBool defaultDataFound = FALSE; + int32_t threadCount = 1; UErrorCode errorCode = U_ZERO_ERROR; UConverter *cnv = NULL; - const char *warnOrErr = "Failure"; - - /* This must be tested before using anything! */ - MutexTest::gMutexInitialized = umtx_isInitialized(NULL); - -#ifdef XP_MAC_CONSOLE - argc = ccommand( &argv ); -#endif + const char *warnOrErr = "Failure"; + UDate startTime, endTime; + int32_t diffTime; + const char *props[IntlTest::kMaxProps]; + int32_t nProps = 0; - /* try opening the data from dll instead of the dat file */ - cnv = ucnv_open(TRY_CNV_1, &errorCode); - if(cnv != 0) { - /* ok */ - ucnv_close(cnv); - } else { - fprintf(stderr, - "#### WARNING! The converter for " TRY_CNV_1 " cannot be loaded from data dll/so." - "Proceeding to load data from dat file.\n"); - errorCode = U_ZERO_ERROR; + U_MAIN_INIT_ARGS(argc, argv); - } - // If user didn't set ICU_DATA, attempt to generate one. - IntlTest::setICU_DATA(); + startTime = uprv_getRawUTCtime(); for (int i = 1; i < argc; ++i) { if (argv[i][0] == '-') { const char* str = argv[i] + 1; - if (strcmp("verbose", str) == 0) + if (strcmp("verbose", str) == 0 || + strcmp("v", str) == 0) verbose = TRUE; - else if (strcmp("v", str) == 0) - verbose = TRUE; - else if (strcmp("noerrormsg", str) == 0) - no_err_msg = TRUE; - else if (strcmp("n", str) == 0) + else if (strcmp("noerrormsg", str) == 0 || + strcmp("n", str) == 0) no_err_msg = TRUE; - else if (strcmp("exhaustive", str) == 0) - quick = FALSE; - else if (strcmp("e", str) == 0) + else if (strcmp("exhaustive", str) == 0 || + strcmp("e", str) == 0) quick = FALSE; - else if (strcmp("all", str) == 0) + else if (strcmp("all", str) == 0 || + strcmp("a", str) == 0) all = TRUE; - else if (strcmp("a", str) == 0) - all = TRUE; - else if (strcmp("leaks", str) == 0) - leaks = TRUE; - else if (strcmp("l", str) == 0) + else if (strcmp("leaks", str) == 0 || + strcmp("l", str) == 0) leaks = TRUE; - else if (strcmp("w", str) == 0) { + else if (strcmp("notime", str) == 0 || + strcmp("T", str) == 0) + no_time = TRUE; + else if (strcmp("x", str)==0) { + if(++i>=argc) { + printf("* Error: '-x' option requires an argument. usage: '-x outfile.xml'.\n"); + syntax = TRUE; + } + if(ctest_xml_setFileName(argv[i])) { /* set the name */ + return 1; /* error */ + } + } else if (strcmp("w", str) == 0) { warnOnMissingData = TRUE; warnOrErr = "WARNING"; - } else { + } + else if (strncmp("threads:", str, 8) == 0) { + threadCount = atoi(str + 8); + } + else if (strncmp("prop:", str, 5) == 0) { + if (nProps < IntlTest::kMaxProps) { + props[nProps] = str + 5; + } + nProps++; + } + else { syntax = TRUE; } }else{ name = TRUE; - all = FALSE; } } - if (all && name) syntax = TRUE; - if (!all && !name) syntax = TRUE; + if (!all && !name) { + all = TRUE; + } else if (all && name) { + syntax = TRUE; + } if (syntax) { fprintf(stdout, "### Syntax:\n" "### IntlTest [-option1 -option2 ...] [testname1 testname2 ...] \n" - "### where options are: verbose (v), all (a), noerrormsg (n), \n" - "### exhaustive (e) and leaks (l). \n" + "### \n" + "### Options are: verbose (v), all (a), noerrormsg (n), \n" + "### exhaustive (e), leaks (l), -x xmlfile.xml, prop:=, \n" + "### notime (T), \n" + "### threads: (Mulithreading must first be \n" + "### enabled otherwise this will be ignored. \n" + "### The default thread count is 1.),\n" "### (Specify either -all (shortcut -a) or a test name). \n" "### -all will run all of the tests.\n" "### \n" @@ -1051,37 +1200,97 @@ main(int argc, char* argv[]) return 1; } + if (nProps > IntlTest::kMaxProps) { + fprintf(stdout, "### Too many properties. Exiting.\n"); + } + UBool all_tests_exist = TRUE; MajorTestLevel major; major.setVerbose( verbose ); major.setNoErrMsg( no_err_msg ); major.setQuick( quick ); major.setLeaks( leaks ); + major.setThreadCount( threadCount ); + major.setWarnOnMissingData( warnOnMissingData ); + major.setNotime (no_time); + for (int32_t i = 0; i < nProps; i++) { + major.setProperty(props[i]); + } + + fprintf(stdout, "-----------------------------------------------\n"); fprintf(stdout, " IntlTest (C++) Test Suite for \n"); fprintf(stdout, " International Components for Unicode %s\n", U_ICU_VERSION); + + + { + const char *charsetFamily = "Unknown"; + int32_t voidSize = (int32_t)sizeof(void*); + int32_t bits = voidSize * 8; + if(U_CHARSET_FAMILY==U_ASCII_FAMILY) { + charsetFamily="ASCII"; + } else if(U_CHARSET_FAMILY==U_EBCDIC_FAMILY) { + charsetFamily="EBCDIC"; + } + fprintf(stdout, + " Bits: %d, Byte order: %s, Chars: %s\n", + bits, U_IS_BIG_ENDIAN?"Big endian":"Little endian", + charsetFamily); + } fprintf(stdout, "-----------------------------------------------\n"); fprintf(stdout, " Options: \n"); - fprintf(stdout, " all (a) : %s\n", (all? "On" : "Off")); - fprintf(stdout, " Verbose (v) : %s\n", (verbose? "On" : "Off")); - fprintf(stdout, " No error messages (n) : %s\n", (no_err_msg? "On" : "Off")); - fprintf(stdout, " Exhaustive (e) : %s\n", (!quick? "On" : "Off")); - fprintf(stdout, " Leaks (l) : %s\n", (leaks? "On" : "Off")); + fprintf(stdout, " all (a) : %s\n", (all? "On" : "Off")); + fprintf(stdout, " Verbose (v) : %s\n", (verbose? "On" : "Off")); + fprintf(stdout, " No error messages (n) : %s\n", (no_err_msg? "On" : "Off")); + fprintf(stdout, " Exhaustive (e) : %s\n", (!quick? "On" : "Off")); + fprintf(stdout, " Leaks (l) : %s\n", (leaks? "On" : "Off")); + fprintf(stdout, " notime (T) : %s\n", (no_time? "On" : "Off")); + fprintf(stdout, " Warn on missing data (w) : %s\n", (warnOnMissingData? "On" : "Off")); +#if (ICU_USE_THREADS==0) + fprintf(stdout, " Threads : Disabled\n"); +#else + fprintf(stdout, " Threads : %d\n", threadCount); +#endif + for (int32_t i = 0; i < nProps; i++) { + fprintf(stdout, " Custom property (prop:) : %s\n", props[i]); + } fprintf(stdout, "-----------------------------------------------\n"); - // Check that u_init() works + /* Check whether ICU will initialize without forcing the build data directory into + * the ICU_DATA path. Success here means either the data dll contains data, or that + * this test program was run with ICU_DATA set externally. Failure of this check + * is normal when ICU data is not packaged into a shared library. + * + * Whether or not this test succeeds, we want to cleanup and reinitialize + * with a data path so that data loading from individual files can be tested. + */ + u_init(&errorCode); + if (U_FAILURE(errorCode)) { + fprintf(stderr, + "#### Note: ICU Init without build-specific setDataDirectory() failed.\n"); + defaultDataFound = FALSE; + } + else { + defaultDataFound = TRUE; + } + u_cleanup(); errorCode = U_ZERO_ERROR; + + /* Initialize ICU */ + if (!defaultDataFound) { + IntlTest::setICU_DATA(); // Must set data directory before u_init() is called. + } u_init(&errorCode); if (U_FAILURE(errorCode)) { - fprintf(stdout, - "*** u_init() failed with error code = %s\n" - "*** Check the ICU_DATA environment variable and\n" - "*** check that the data files are present.\n", - u_errorName(errorCode)); - if(!warnOnMissingData) { - fprintf(stdout, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); - return 1; - } + fprintf(stderr, + "#### ERROR! %s: u_init() failed with status = \"%s\".\n" + "*** Check the ICU_DATA environment variable and \n" + "*** check that the data files are present.\n", argv[0], u_errorName(errorCode)); + if(warnOnMissingData == 0) { + fprintf(stderr, "*** Exiting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); + u_cleanup(); + return 1; + } } @@ -1098,7 +1307,7 @@ main(int argc, char* argv[]) "*** check that the data files are present.\n", warnOrErr, ucnv_getDefaultName()); if(!warnOnMissingData) { - fprintf(stdout, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); + fprintf(stdout, "*** Exiting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); return 1; } } @@ -1114,7 +1323,7 @@ main(int argc, char* argv[]) "*** Check the ICU_DATA environment variable and \n" "*** check that the data files are present.\n", warnOrErr); if(!warnOnMissingData) { - fprintf(stdout, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); + fprintf(stdout, "*** Exiting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); return 1; } } @@ -1127,11 +1336,17 @@ main(int argc, char* argv[]) "*** Check the ICU_DATA environment variable and \n" "*** check that the data files are present.\n", warnOrErr); if(!warnOnMissingData) { - fprintf(stdout, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); + fprintf(stdout, "*** Exiting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n"); return 1; } } + Locale originalLocale; // Save the default locale for comparison later on. + + if(ctest_xml_init("intltest")) + return 1; + + /* TODO: Add option to call u_cleanup and rerun tests. */ if (all) { major.runTest(); @@ -1143,13 +1358,17 @@ main(int argc, char* argv[]) if (argv[i][0] != '-') { char* name = argv[i]; fprintf(stdout, "\n=== Handling test: %s: ===\n", name); + + char baseName[1024]; + sprintf(baseName, "/%s/", name); + char* parameter = strchr( name, '@' ); if (parameter) { *parameter = 0; parameter += 1; } execCount = 0; - UBool res = major.runTest( name, parameter ); + UBool res = major.runTest( name, parameter, baseName ); if (leaks && res) { major.run_phase2( name, parameter ); } @@ -1157,10 +1376,13 @@ main(int argc, char* argv[]) fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name); all_tests_exist = FALSE; } + } else if(!strcmp(argv[i],"-x")) { + i++; } } } + #if !UCONFIG_NO_FORMATTING CalendarTimeZoneTest::cleanup(); #endif @@ -1168,39 +1390,56 @@ main(int argc, char* argv[]) free(_testDataPath); _testDataPath = 0; + Locale lastDefaultLocale; + if (originalLocale != lastDefaultLocale) { + major.errln("FAILURE: A test changed the default locale without resetting it."); + } + fprintf(stdout, "\n--------------------------------------\n"); if (major.getErrors() == 0) { /* Call it twice to make sure that the defaults were reset. */ /* Call it before the OK message to verify proper cleanup. */ u_cleanup(); - u_cleanup(); + u_cleanup(); fprintf(stdout, "OK: All tests passed without error.\n"); + + if (major.getDataErrors() != 0) { + fprintf(stdout, "\t*WARNING* some data-loading errors were ignored by the -w option.\n"); + } }else{ fprintf(stdout, "Errors in total: %ld.\n", (long)major.getErrors()); major.printErrors(); + + if (major.getDataErrors() != 0) { + fprintf(stdout, "\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"); + } + /* Call afterwards to display errors. */ u_cleanup(); } fprintf(stdout, "--------------------------------------\n"); - if (!MutexTest::gMutexInitialized) { - fprintf(stderr, - "#### WARNING!\n" - " The global mutex was not initialized during C++ static initialization.\n" - " You must use an ICU API in a single thread, like ucnv_open() or\n" - " uloc_countAvailable(), before using ICU in multiple threads.\n" - " Most ICU API functions will initialize the global mutex for you.\n" - " If you are using ICU in a single threaded application, please ignore this\n" - " warning.\n" - "#### WARNING!\n" - ); - } if (execCount <= 0) { fprintf(stdout, "***** Not all called tests actually exist! *****\n"); } + if(!no_time) { + endTime = uprv_getRawUTCtime(); + diffTime = (int32_t)(endTime - startTime); + printf("Elapsed Time: %02d:%02d:%02d.%03d\n", + (int)((diffTime%U_MILLIS_PER_DAY)/U_MILLIS_PER_HOUR), + (int)((diffTime%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), + (int)((diffTime%U_MILLIS_PER_MINUTE)/U_MILLIS_PER_SECOND), + (int)(diffTime%U_MILLIS_PER_SECOND)); + } + + if(ctest_xml_fini()) + return 1; + return major.getErrors(); } @@ -1212,10 +1451,10 @@ const char* IntlTest::loadTestData(UErrorCode& err){ const char* tdrelativepath; #if defined (U_TOPBUILDDIR) - tdrelativepath = "test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING; + tdrelativepath = "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING; directory = U_TOPBUILDDIR; #else - tdrelativepath = ".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING; + tdrelativepath = ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING; directory = pathToDataDirectory(); #endif @@ -1225,7 +1464,7 @@ const char* IntlTest::loadTestData(UErrorCode& err){ /* u_getDataDirectory shoul return \source\data ... set the * directory to ..\source\data\..\test\testdata\out\testdata */ - strcpy(tdpath, directory); + strcpy(tdpath, directory); strcat(tdpath, tdrelativepath); strcat(tdpath,"testdata"); @@ -1233,7 +1472,7 @@ const char* IntlTest::loadTestData(UErrorCode& err){ if(U_FAILURE(err)){ err = U_FILE_ACCESS_ERROR; - it_errln((UnicodeString)"Could not load testtypes.res in testdata bundle with path " + tdpath + (UnicodeString)" - " + u_errorName(err)); + it_dataerrln((UnicodeString)"Could not load testtypes.res in testdata bundle with path " + tdpath + (UnicodeString)" - " + u_errorName(err)); return ""; } ures_close(test); @@ -1243,6 +1482,30 @@ const char* IntlTest::loadTestData(UErrorCode& err){ return _testDataPath; } +const char* IntlTest::getTestDataPath(UErrorCode& err) { + return loadTestData(err); +} + +/* Returns the path to icu/source/test/testdata/ */ +const char *IntlTest::getSourceTestData(UErrorCode& /*err*/) { + const char *srcDataDir = NULL; +#ifdef U_TOPSRCDIR + srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING"test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING; +#else + srcDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING; + FILE *f = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING "rbbitst.txt", "r"); + if (f) { + /* We're in icu/source/test/intltest/ */ + fclose(f); + } + else { + /* We're in icu/source/test/intltest/Platform/(Debug|Release) */ + srcDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata"U_FILE_SEP_STRING; + } +#endif + return srcDataDir; +} + const char* IntlTest::fgDataDir = NULL; /* returns the path to icu/source/data */ @@ -1295,14 +1558,14 @@ const char * IntlTest::pathToDataDirectory() } else { /* __FILE__ on MSVC7 does not contain the directory */ - FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r"); - if (file) { - fclose(file); - fgDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING; - } - else { - fgDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING; - } + FILE *file = fopen(".." U_FILE_SEP_STRING ".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r"); + if (file) { + fclose(file); + fgDataDir = ".." U_FILE_SEP_STRING ".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING; + } + else { + fgDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING; + } } } #endif @@ -1313,13 +1576,281 @@ const char * IntlTest::pathToDataDirectory() /* * This is a variant of cintltst/ccolltst.c:CharsToUChars(). - * It converts a character string into a UnicodeString, with + * It converts an invariant-character string into a UnicodeString, with * unescaping \u sequences. */ -UnicodeString CharsToUnicodeString(const char* chars) -{ - UnicodeString str(chars, ""); // Invariant conversion - return str.unescape(); +UnicodeString CharsToUnicodeString(const char* chars){ + return UnicodeString(chars, -1, US_INV).unescape(); +} + +UnicodeString ctou(const char* chars) { + return CharsToUnicodeString(chars); +} + +#define RAND_M (714025) +#define RAND_IA (1366) +#define RAND_IC (150889) + +static int32_t RAND_SEED; + +/** + * Returns a uniform random value x, with 0.0 <= x < 1.0. Use + * with care: Does not return all possible values; returns one of + * 714,025 values, uniformly spaced. However, the period is + * effectively infinite. See: Numerical Recipes, section 7.1. + * + * @param seedp pointer to seed. Set *seedp to any negative value + * to restart the sequence. + */ +float IntlTest::random(int32_t* seedp) { + static int32_t iy, ir[98]; + static UBool first=TRUE; + int32_t j; + if (*seedp < 0 || first) { + first = FALSE; + if ((*seedp=(RAND_IC-(*seedp)) % RAND_M) < 0) *seedp = -(*seedp); + for (j=1;j<=97;++j) { + *seedp=(RAND_IA*(*seedp)+RAND_IC) % RAND_M; + ir[j]=(*seedp); + } + *seedp=(RAND_IA*(*seedp)+RAND_IC) % RAND_M; + iy=(*seedp); + } + j=(int32_t)(1 + 97.0*iy/RAND_M); + U_ASSERT(j>=1 && j<=97); + iy=ir[j]; + *seedp=(RAND_IA*(*seedp)+RAND_IC) % RAND_M; + ir[j]=(*seedp); + return (float) iy/RAND_M; +} + +/** + * Convenience method using a global seed. + */ +float IntlTest::random() { + return random(&RAND_SEED); +} + +static inline UChar toHex(int32_t i) { + return (UChar)(i + (i < 10 ? 0x30 : (0x41 - 10))); +} + +static UnicodeString& escape(const UnicodeString& s, UnicodeString& result) { + for (int32_t i=0; i> 12) & 0xF); + result += toHex((c >> 8) & 0xF); + result += toHex((c >> 4) & 0xF); + result += toHex( c & 0xF); + } + } + return result; +} + +#define VERBOSE_ASSERTIONS + +UBool IntlTest::assertTrue(const char* message, UBool condition, UBool quiet, UBool possibleDataError, const char *file, int line) { + if (file != NULL) { + if (!condition) { + if (possibleDataError) { + dataerrln("%s:%d: FAIL: assertTrue() failed: %s", file, line, message); + } else { + errln("%s:%d: FAIL: assertTrue() failed: %s", file, line, message); + } + } else if (!quiet) { + logln("%s:%d: Ok: %s", file, line, message); + } + } else { + if (!condition) { + if (possibleDataError) { + dataerrln("FAIL: assertTrue() failed: %s", message); + } else { + errln("FAIL: assertTrue() failed: %s", message); + } + } else if (!quiet) { + logln("Ok: %s", message); + } + + } + return condition; +} + +UBool IntlTest::assertFalse(const char* message, UBool condition, UBool quiet) { + if (condition) { + errln("FAIL: assertFalse() failed: %s", message); + } else if (!quiet) { + logln("Ok: %s", message); + } + return !condition; +} + +UBool IntlTest::assertSuccess(const char* message, UErrorCode ec, UBool possibleDataError) { + if (U_FAILURE(ec)) { + if (possibleDataError) { + dataerrln("FAIL: %s (%s)", message, u_errorName(ec)); + } else { + errcheckln(ec, "FAIL: %s (%s)", message, u_errorName(ec)); + } + + return FALSE; + } + return TRUE; +} + +UBool IntlTest::assertEquals(const char* message, + const UnicodeString& expected, + const UnicodeString& actual, + UBool possibleDataError) { + if (expected != actual) { + if (possibleDataError) { + dataerrln((UnicodeString)"FAIL: " + message + "; got " + + prettify(actual) + + "; expected " + prettify(expected)); + } else { + errln((UnicodeString)"FAIL: " + message + "; got " + + prettify(actual) + + "; expected " + prettify(expected)); + } + return FALSE; + } +#ifdef VERBOSE_ASSERTIONS + else { + logln((UnicodeString)"Ok: " + message + "; got " + prettify(actual)); + } +#endif + return TRUE; +} + +UBool IntlTest::assertEquals(const char* message, + const char* expected, + const char* actual) { + if (uprv_strcmp(expected, actual) != 0) { + errln((UnicodeString)"FAIL: " + message + "; got \"" + + actual + + "\"; expected \"" + expected + "\""); + return FALSE; + } +#ifdef VERBOSE_ASSERTIONS + else { + logln((UnicodeString)"Ok: " + message + "; got \"" + actual + "\""); + } +#endif + return TRUE; +} + +UBool IntlTest::assertEquals(const char* message, + int32_t expected, + int32_t actual) { + if (expected != actual) { + errln((UnicodeString)"FAIL: " + message + "; got " + + actual + "=0x" + toHex(actual) + + "; expected " + expected + "=0x" + toHex(expected)); + return FALSE; + } +#ifdef VERBOSE_ASSERTIONS + else { + logln((UnicodeString)"Ok: " + message + "; got " + actual + "=0x" + toHex(actual)); + } +#endif + return TRUE; +} + +#if !UCONFIG_NO_FORMATTING +UBool IntlTest::assertEquals(const char* message, + const Formattable& expected, + const Formattable& actual) { + if (expected != actual) { + errln((UnicodeString)"FAIL: " + message + "; got " + + toString(actual) + + "; expected " + toString(expected)); + return FALSE; + } +#ifdef VERBOSE_ASSERTIONS + else { + logln((UnicodeString)"Ok: " + message + "; got " + toString(actual)); + } +#endif + return TRUE; +} +#endif + +static char ASSERT_BUF[256]; + +static const char* extractToAssertBuf(const UnicodeString& message) { + UnicodeString buf; + escape(message, buf); + buf.extract(0, 0x7FFFFFFF, ASSERT_BUF, sizeof(ASSERT_BUF)-1, 0); + ASSERT_BUF[sizeof(ASSERT_BUF)-1] = 0; + return ASSERT_BUF; +} + +UBool IntlTest::assertTrue(const UnicodeString& message, UBool condition, UBool quiet) { + return assertTrue(extractToAssertBuf(message), condition, quiet); +} + +UBool IntlTest::assertFalse(const UnicodeString& message, UBool condition, UBool quiet) { + return assertFalse(extractToAssertBuf(message), condition, quiet); +} + +UBool IntlTest::assertSuccess(const UnicodeString& message, UErrorCode ec) { + return assertSuccess(extractToAssertBuf(message), ec); +} + +UBool IntlTest::assertEquals(const UnicodeString& message, + const UnicodeString& expected, + const UnicodeString& actual) { + return assertEquals(extractToAssertBuf(message), expected, actual); +} + +UBool IntlTest::assertEquals(const UnicodeString& message, + const char* expected, + const char* actual) { + return assertEquals(extractToAssertBuf(message), expected, actual); +} +//-------------------------------------------------------------------- +// Time bomb - allows temporary behavior that expires at a given +// release +//-------------------------------------------------------------------- + +UBool IntlTest::isICUVersionBefore(int major, int minor, int milli) { + UVersionInfo iv; + UVersionInfo ov = { (uint8_t)major, (uint8_t)minor, (uint8_t)milli, 0 }; + u_getVersion(iv); + return uprv_memcmp(iv, ov, U_MAX_VERSION_LENGTH) < 0; +} + +#if !UCONFIG_NO_FORMATTING +UBool IntlTest::assertEquals(const UnicodeString& message, + const Formattable& expected, + const Formattable& actual) { + return assertEquals(extractToAssertBuf(message), expected, actual); +} +#endif + +void IntlTest::setProperty(const char* propline) { + if (numProps < kMaxProps) { + proplines[numProps] = propline; + } + numProps++; +} + +const char* IntlTest::getProperty(const char* prop) { + const char* val = NULL; + for (int32_t i = 0; i < numProps; i++) { + int32_t plen = uprv_strlen(prop); + if ((int32_t)uprv_strlen(proplines[i]) > plen + 1 + && proplines[i][plen] == '=' + && uprv_strncmp(proplines[i], prop, plen) == 0) { + val = &(proplines[i][plen+1]); + break; + } + } + return val; } /* @@ -1330,4 +1861,3 @@ UnicodeString CharsToUnicodeString(const char* chars) * End: * */ -