Index: icu46/source/test/intltest/tsnmfmt.cpp |
=================================================================== |
--- icu46/source/test/intltest/tsnmfmt.cpp (revision 0) |
+++ icu46/source/test/intltest/tsnmfmt.cpp (revision 0) |
@@ -0,0 +1,441 @@ |
+/*********************************************************************** |
+ * COPYRIGHT: |
+ * Copyright (c) 1997-2009, International Business Machines Corporation |
+ * and others. All Rights Reserved. |
+ ***********************************************************************/ |
+ |
+#include "unicode/utypes.h" |
+ |
+#if !UCONFIG_NO_FORMATTING |
+ |
+#include "unicode/decimfmt.h" |
+#include "tsnmfmt.h" |
+#include "putilimp.h" |
+#include <float.h> |
+#include <stdlib.h> |
+ |
+IntlTestNumberFormat::~IntlTestNumberFormat() {} |
+ |
+static const char * formattableTypeName(Formattable::Type t) |
+{ |
+ switch(t) { |
+ case Formattable::kDate: return "kDate"; |
+ case Formattable::kDouble: return "kDouble"; |
+ case Formattable::kLong: return "kLong"; |
+ case Formattable::kString: return "kString"; |
+ case Formattable::kArray: return "kArray"; |
+ case Formattable::kInt64: return "kInt64"; |
+ default: return "??unknown??"; |
+ } |
+} |
+ |
+/** |
+ * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of |
+ * NumberFormat. |
+ */ |
+void IntlTestNumberFormat::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) |
+{ |
+ |
+ if (exec) logln((UnicodeString)"TestSuite NumberFormat"); |
+ switch (index) { |
+ case 0: name = "createInstance"; |
+ if (exec) |
+ { |
+ logln(name); |
+ fStatus = U_ZERO_ERROR; |
+ fFormat = NumberFormat::createInstance(fStatus); |
+ testFormat(/*par*/); |
+ } |
+ break; |
+ |
+ case 1: name = "DefaultLocale"; |
+ if (exec) testLocale(/*par, */Locale::getDefault(), name); |
+ break; |
+ |
+ case 2: name = "testAvailableLocales"; |
+ if (exec) { |
+ logln(name); |
+ testAvailableLocales(/*par*/); |
+ } |
+ break; |
+ |
+ case 3: name = "monsterTest"; |
+ if (exec) { |
+ logln(name); |
+ monsterTest(/*par*/); |
+ } |
+ break; |
+ |
+ default: name = ""; break; |
+ } |
+} |
+ |
+void |
+IntlTestNumberFormat::testLocale(/* char* par, */const Locale& locale, const UnicodeString& localeName) |
+{ |
+ const char* name; |
+ |
+ fLocale = locale; |
+ name = "Number test"; |
+ logln((UnicodeString)name + " (" + localeName + ")"); |
+ fStatus = U_ZERO_ERROR; |
+ fFormat = NumberFormat::createInstance(locale, fStatus); |
+ testFormat(/* par */); |
+ |
+ name = "Currency test"; |
+ logln((UnicodeString)name + " (" + localeName + ")"); |
+ fStatus = U_ZERO_ERROR; |
+ fFormat = NumberFormat::createCurrencyInstance(locale, fStatus); |
+ testFormat(/* par */); |
+ |
+ name = "Percent test"; |
+ logln((UnicodeString)name + " (" + localeName + ")"); |
+ fStatus = U_ZERO_ERROR; |
+ fFormat = NumberFormat::createPercentInstance(locale, fStatus); |
+ testFormat(/* par */); |
+} |
+ |
+double IntlTestNumberFormat::randDouble() |
+{ |
+ // Assume 8-bit (or larger) rand values. Also assume |
+ // that the system rand() function is very poor, which it always is. |
+ // Call srand(currentTime) in intltest to make it truly random. |
+ double d; |
+ uint32_t i; |
+ char* poke = (char*)&d; |
+ do { |
+ for (i=0; i < sizeof(double); ++i) |
+ { |
+ poke[i] = (char)(rand() & 0xFF); |
+ } |
+ } while (uprv_isNaN(d) || uprv_isInfinite(d) |
+ || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d))); |
+ |
+ return d; |
+} |
+ |
+/* |
+ * Return a random uint32_t |
+ **/ |
+uint32_t IntlTestNumberFormat::randLong() |
+{ |
+ // Assume 8-bit (or larger) rand values. Also assume |
+ // that the system rand() function is very poor, which it always is. |
+ // Call srand(currentTime) in intltest to make it truly random. |
+ uint32_t d; |
+ uint32_t i; |
+ char* poke = (char*)&d; |
+ for (i=0; i < sizeof(uint32_t); ++i) |
+ { |
+ poke[i] = (char)(rand() & 0xFF); |
+ } |
+ return d; |
+} |
+ |
+ |
+/* Make sure that we don't get something too large and multiply into infinity. |
+ @param smallerThanMax the requested maximum value smaller than DBL_MAX */ |
+double IntlTestNumberFormat::getSafeDouble(double smallerThanMax) { |
+ double it; |
+ double high = (DBL_MAX/smallerThanMax)/10.0; |
+ double low = -high; |
+ do { |
+ it = randDouble(); |
+ } while (low > it || it > high); |
+ return it; |
+} |
+ |
+void |
+IntlTestNumberFormat::testFormat(/* char* par */) |
+{ |
+ if (U_FAILURE(fStatus)) |
+ { |
+ dataerrln((UnicodeString)"**** FAIL: createXxxInstance failed. - " + u_errorName(fStatus)); |
+ if (fFormat != 0) |
+ errln("**** FAIL: Non-null format returned by createXxxInstance upon failure."); |
+ delete fFormat; |
+ fFormat = 0; |
+ return; |
+ } |
+ |
+ if (fFormat == 0) |
+ { |
+ errln((UnicodeString)"**** FAIL: Null format returned by createXxxInstance."); |
+ return; |
+ } |
+ |
+ UnicodeString str; |
+ |
+ // Assume it's a DecimalFormat and get some info |
+ DecimalFormat *s = (DecimalFormat*)fFormat; |
+ logln((UnicodeString)" Pattern " + s->toPattern(str)); |
+ |
+#if defined(OS390) || defined(OS400) |
+ tryIt(-2.02147304840132e-68); |
+ tryIt(3.88057859588817e-68); // Test rounding when only some digits are shown because exponent is close to -maxfrac |
+ tryIt(-2.64651110485945e+65); // Overflows to +INF when shown as a percent |
+ tryIt(9.29526819488338e+64); // Ok -- used to fail? |
+#else |
+ tryIt(-2.02147304840132e-100); |
+ tryIt(3.88057859588817e-096); // Test rounding when only some digits are shown because exponent is close to -maxfrac |
+ tryIt(-2.64651110485945e+306); // Overflows to +INF when shown as a percent |
+ tryIt(9.29526819488338e+250); // Ok -- used to fail? |
+#endif |
+ |
+ // These PASS now, with the sprintf/atof based format-parse. |
+ |
+ // These fail due to round-off |
+ // The least significant digit drops by one during each format-parse cycle. |
+ // Both numbers DON'T have a round-off problem when multiplied by 100! (shown as %) |
+#ifdef OS390 |
+ tryIt(-9.18228054496402e+64); |
+ tryIt(-9.69413034454191e+64); |
+#else |
+ tryIt(-9.18228054496402e+255); |
+ tryIt(-9.69413034454191e+273); |
+#endif |
+ |
+#ifndef OS390 |
+ tryIt(1.234e-200); |
+ tryIt(-2.3e-168); |
+ |
+ tryIt(uprv_getNaN()); |
+ tryIt(uprv_getInfinity()); |
+ tryIt(-uprv_getInfinity()); |
+#endif |
+ |
+ tryIt((int32_t)251887531); |
+ tryIt(5e-20 / 9); |
+ tryIt(5e20 / 9); |
+ tryIt(1.234e-50); |
+ tryIt(9.99999999999996); |
+ tryIt(9.999999999999996); |
+ |
+ tryIt((int32_t)INT32_MIN); |
+ tryIt((int32_t)INT32_MAX); |
+ tryIt((double)INT32_MIN); |
+ tryIt((double)INT32_MAX); |
+ tryIt((double)INT32_MIN - 1.0); |
+ tryIt((double)INT32_MAX + 1.0); |
+ |
+ tryIt(5.0 / 9.0 * 1e-20); |
+ tryIt(4.0 / 9.0 * 1e-20); |
+ tryIt(5.0 / 9.0 * 1e+20); |
+ tryIt(4.0 / 9.0 * 1e+20); |
+ |
+ tryIt(2147483647.); |
+ tryIt((int32_t)0); |
+ tryIt(0.0); |
+ tryIt((int32_t)1); |
+ tryIt((int32_t)10); |
+ tryIt((int32_t)100); |
+ tryIt((int32_t)-1); |
+ tryIt((int32_t)-10); |
+ tryIt((int32_t)-100); |
+ tryIt((int32_t)-1913860352); |
+ |
+ for (int32_t z=0; z<10; ++z) |
+ { |
+ double d = randFraction() * 2e10 - 1e10; |
+ tryIt(d); |
+ } |
+ |
+ double it = getSafeDouble(100000.0); |
+ |
+ tryIt(0.0); |
+ tryIt(it); |
+ tryIt((int32_t)0); |
+ tryIt(uprv_floor(it)); |
+ tryIt((int32_t)randLong()); |
+ |
+ // try again |
+ it = getSafeDouble(100.0); |
+ tryIt(it); |
+ tryIt(uprv_floor(it)); |
+ tryIt((int32_t)randLong()); |
+ |
+ // try again with very large numbers |
+ it = getSafeDouble(100000000000.0); |
+ tryIt(it); |
+ |
+ // try again with very large numbers |
+ // and without going outside of the int32_t range |
+ it = randFraction() * INT32_MAX; |
+ tryIt(it); |
+ tryIt((int32_t)uprv_floor(it)); |
+ |
+ delete fFormat; |
+} |
+ |
+void |
+IntlTestNumberFormat::tryIt(double aNumber) |
+{ |
+ const int32_t DEPTH = 10; |
+ Formattable number[DEPTH]; |
+ UnicodeString string[DEPTH]; |
+ |
+ int32_t numberMatch = 0; |
+ int32_t stringMatch = 0; |
+ UnicodeString errMsg; |
+ int32_t i; |
+ for (i=0; i<DEPTH; ++i) |
+ { |
+ errMsg.truncate(0); // if non-empty, we failed this iteration |
+ UErrorCode status = U_ZERO_ERROR; |
+ string[i] = "(n/a)"; // "format was never done" value |
+ if (i == 0) { |
+ number[i].setDouble(aNumber); |
+ } else { |
+ fFormat->parse(string[i-1], number[i], status); |
+ if (U_FAILURE(status)) { |
+ number[i].setDouble(1234.5); // "parse failed" value |
+ errMsg = "**** FAIL: Parse of " + prettify(string[i-1]) + " failed."; |
+ --i; // don't show empty last line: "1234.5 F> (n/a) P>" |
+ break; |
+ } |
+ } |
+ // Convert from long to double |
+ if (number[i].getType() == Formattable::kLong) |
+ number[i].setDouble(number[i].getLong()); |
+ else if (number[i].getType() == Formattable::kInt64) |
+ number[i].setDouble((double)number[i].getInt64()); |
+ else if (number[i].getType() != Formattable::kDouble) |
+ { |
+ errMsg = ("**** FAIL: Parse of " + prettify(string[i-1]) |
+ + " returned non-numeric Formattable, type " + UnicodeString(formattableTypeName(number[i].getType())) |
+ + ", Locale=" + UnicodeString(fLocale.getName()) |
+ + ", longValue=" + number[i].getLong()); |
+ break; |
+ } |
+ string[i].truncate(0); |
+ fFormat->format(number[i].getDouble(), string[i]); |
+ if (i > 0) |
+ { |
+ if (numberMatch == 0 && number[i] == number[i-1]) |
+ numberMatch = i; |
+ else if (numberMatch > 0 && number[i] != number[i-1]) |
+ { |
+ errMsg = ("**** FAIL: Numeric mismatch after match."); |
+ break; |
+ } |
+ if (stringMatch == 0 && string[i] == string[i-1]) |
+ stringMatch = i; |
+ else if (stringMatch > 0 && string[i] != string[i-1]) |
+ { |
+ errMsg = ("**** FAIL: String mismatch after match."); |
+ break; |
+ } |
+ } |
+ if (numberMatch > 0 && stringMatch > 0) |
+ break; |
+ } |
+ if (i == DEPTH) |
+ --i; |
+ |
+ if (stringMatch > 2 || numberMatch > 2) |
+ { |
+ errMsg = ("**** FAIL: No string and/or number match within 2 iterations."); |
+ } |
+ |
+ if (errMsg.length() != 0) |
+ { |
+ for (int32_t k=0; k<=i; ++k) |
+ { |
+ logln((UnicodeString)"" + k + ": " + number[k].getDouble() + " F> " + |
+ prettify(string[k]) + " P> "); |
+ } |
+ errln(errMsg); |
+ } |
+} |
+ |
+void |
+IntlTestNumberFormat::tryIt(int32_t aNumber) |
+{ |
+ Formattable number(aNumber); |
+ UnicodeString stringNum; |
+ UErrorCode status = U_ZERO_ERROR; |
+ |
+ fFormat->format(number, stringNum, status); |
+ if (U_FAILURE(status)) |
+ { |
+ errln("**** FAIL: Formatting " + aNumber); |
+ return; |
+ } |
+ fFormat->parse(stringNum, number, status); |
+ if (U_FAILURE(status)) |
+ { |
+ errln("**** FAIL: Parse of " + prettify(stringNum) + " failed."); |
+ return; |
+ } |
+ if (number.getType() != Formattable::kLong) |
+ { |
+ errln("**** FAIL: Parse of " + prettify(stringNum) |
+ + " returned non-long Formattable, type " + UnicodeString(formattableTypeName(number.getType())) |
+ + ", Locale=" + UnicodeString(fLocale.getName()) |
+ + ", doubleValue=" + number.getDouble() |
+ + ", longValue=" + number.getLong() |
+ + ", origValue=" + aNumber |
+ ); |
+ } |
+ if (number.getLong() != aNumber) { |
+ errln("**** FAIL: Parse of " + prettify(stringNum) + " failed. Got:" + number.getLong() |
+ + " Expected:" + aNumber); |
+ } |
+} |
+ |
+void IntlTestNumberFormat::testAvailableLocales(/* char* par */) |
+{ |
+ int32_t count = 0; |
+ const Locale* locales = NumberFormat::getAvailableLocales(count); |
+ logln((UnicodeString)"" + count + " available locales"); |
+ if (locales && count) |
+ { |
+ UnicodeString name; |
+ UnicodeString all; |
+ for (int32_t i=0; i<count; ++i) |
+ { |
+ if (i!=0) |
+ all += ", "; |
+ all += locales[i].getName(); |
+ } |
+ logln(all); |
+ } |
+ else |
+ dataerrln((UnicodeString)"**** FAIL: Zero available locales or null array pointer"); |
+} |
+ |
+void IntlTestNumberFormat::monsterTest(/* char* par */) |
+{ |
+ const char *SEP = "============================================================\n"; |
+ int32_t count; |
+ const Locale* allLocales = NumberFormat::getAvailableLocales(count); |
+ Locale* locales = (Locale*)allLocales; |
+ Locale quickLocales[6]; |
+ if (allLocales && count) |
+ { |
+ if (quick && count > 6) { |
+ logln("quick test: testing just 6 locales!"); |
+ count = 6; |
+ locales = quickLocales; |
+ locales[0] = allLocales[0]; |
+ locales[1] = allLocales[1]; |
+ locales[2] = allLocales[2]; |
+ // In a quick test, make sure we test locales that use |
+ // currency prefix, currency suffix, and choice currency |
+ // logic. Otherwise bugs in these areas can slip through. |
+ locales[3] = Locale("ar", "AE", ""); |
+ locales[4] = Locale("cs", "CZ", ""); |
+ locales[5] = Locale("en", "IN", ""); |
+ } |
+ for (int32_t i=0; i<count; ++i) |
+ { |
+ UnicodeString name(locales[i].getName(), ""); |
+ logln(SEP); |
+ testLocale(/* par, */locales[i], name); |
+ } |
+ } |
+ |
+ logln(SEP); |
+} |
+ |
+#endif /* #if !UCONFIG_NO_FORMATTING */ |
Property changes on: icu46/source/test/intltest/tsnmfmt.cpp |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |