/******************************************************************** * COPYRIGHT: * Copyright (c) 1997-2004, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ /* Modification History: * Date Name Description * 07/15/99 helena Ported to HPUX 10/11 CC. */ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #include "numfmtst.h" #include "unicode/dcfmtsym.h" #include "unicode/decimfmt.h" #include "unicode/ucurr.h" #include "unicode/ustring.h" #include "unicode/measfmt.h" #include "unicode/curramt.h" #include "digitlst.h" #include "textfile.h" #include "tokiter.h" #include "charstr.h" #include "putilimp.h" #include #include static const UChar EUR[] = {69,85,82,0}; // "EUR" // ***************************************************************************** // class NumberFormatTest // ***************************************************************************** #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break #define CHECK(status,str) if (U_FAILURE(status)) { errln(UnicodeString("FAIL: ") + str); return; } void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) { // if (exec) logln((UnicodeString)"TestSuite DateFormatTest"); switch (index) { CASE(0,TestCurrencySign); CASE(1,TestCurrency); CASE(2,TestParse); CASE(3,TestRounding487); CASE(4,TestQuotes); CASE(5,TestExponential); CASE(6,TestPatterns); // Upgrade to alphaWorks - liu 5/99 CASE(7,TestExponent); CASE(8,TestScientific); CASE(9,TestPad); CASE(10,TestPatterns2); CASE(11,TestSecondaryGrouping); CASE(12,TestSurrogateSupport); CASE(13,TestAPI); CASE(14,TestCurrencyObject); CASE(15,TestCurrencyPatterns); CASE(16,TestDigitList); CASE(17,TestWhiteSpaceParsing); CASE(18,TestComplexCurrency); CASE(19,TestRegCurrency); CASE(20,TestSymbolsWithBadLocale); CASE(21,TestAdoptDecimalFormatSymbols); CASE(22,TestScientific2); CASE(23,TestScientificGrouping); CASE(24,TestInt64); CASE(25,TestPerMill); CASE(26,TestIllegalPatterns); CASE(27,TestCases); CASE(28,TestCurrencyNames); default: name = ""; break; } } // ------------------------------------- // Test API (increase code coverage) void NumberFormatTest::TestAPI(void) { logln("Test API"); UErrorCode status = U_ZERO_ERROR; NumberFormat *test = NumberFormat::createInstance("root", status); if(U_FAILURE(status)) { errln("unable to create format object"); } if(test != NULL) { test->setMinimumIntegerDigits(10); test->setMaximumIntegerDigits(2); test->setMinimumFractionDigits(10); test->setMaximumFractionDigits(2); UnicodeString result; FieldPosition pos; Formattable bla("Paja Patak"); // Donald Duck for non Serbian speakers test->format(bla, result, pos, status); if(U_SUCCESS(status)) { errln("Yuck... Formatted a duck... As a number!"); } else { status = U_ZERO_ERROR; } delete test; } } // Test various patterns void NumberFormatTest::TestPatterns(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols sym(Locale::getUS(), status); if (U_FAILURE(status)) { errln("FAIL: Could not construct DecimalFormatSymbols"); return; } const char* pat[] = { "#.#", "#.", ".#", "#" }; int32_t pat_length = (int32_t)(sizeof(pat) / sizeof(pat[0])); const char* newpat[] = { "#0.#", "#0.", "#.0", "#" }; const char* num[] = { "0", "0.", ".0", "0" }; for (int32_t i=0; i 300 double val[] = { 0.01234, 123456789, 1.23e300, -3.141592653e-271 }; int32_t val_length = (int32_t)(sizeof(val) / sizeof(val[0])); const char* valFormat[] = { // 0.####E0 "1.234E-2", "1.2346E8", "1.23E300", "-3.1416E-271", // 00.000E00 "12.340E-03", "12.346E07", "12.300E299", "-31.416E-272", // ##0.######E000 "12.34E-003", "123.4568E006", "1.23E300", "-314.1593E-273", // 0.###E0;[0.###E0] "1.234E-2", "1.235E8", "1.23E300", "[3.142E-271]" }; double valParse[] = { 0.01234, 123460000, 1.23E300, -3.1416E-271, 0.01234, 123460000, 1.23E300, -3.1416E-271, 0.01234, 123456800, 1.23E300, -3.141593E-271, 0.01234, 123500000, 1.23E300, -3.142E-271, }; #elif DBL_MAX_10_EXP > 70 double val[] = { 0.01234, 123456789, 1.23e70, -3.141592653e-71 }; int32_t val_length = sizeof(val) / sizeof(val[0]); char* valFormat[] = { // 0.####E0 "1.234E-2", "1.2346E8", "1.23E70", "-3.1416E-71", // 00.000E00 "12.340E-03", "12.346E07", "12.300E69", "-31.416E-72", // ##0.######E000 "12.34E-003", "123.4568E006", "12.3E069", "-31.41593E-072", // 0.###E0;[0.###E0] "1.234E-2", "1.235E8", "1.23E70", "[3.142E-71]" }; double valParse[] = { 0.01234, 123460000, 1.23E70, -3.1416E-71, 0.01234, 123460000, 1.23E70, -3.1416E-71, 0.01234, 123456800, 1.23E70, -3.141593E-71, 0.01234, 123500000, 1.23E70, -3.142E-71, }; #else // Don't test double conversion double* val = 0; int32_t val_length = 0; char** valFormat = 0; double* valParse = 0; logln("Warning: Skipping double conversion tests"); #endif int32_t lval[] = { 0, -1, 1, 123456789 }; int32_t lval_length = (int32_t)(sizeof(lval) / sizeof(lval[0])); const char* lvalFormat[] = { // 0.####E0 "0E0", "-1E0", "1E0", "1.2346E8", // 00.000E00 "00.000E00", "-10.000E-01", "10.000E-01", "12.346E07", // ##0.######E000 "0E000", "-1E000", "1E000", "123.4568E006", // 0.###E0;[0.###E0] "0E0", "[1E0]", "1E0", "1.235E8" }; int32_t lvalParse[] = { 0, -1, 1, 123460000, 0, -1, 1, 123460000, 0, -1, 1, 123456800, 0, -1, 1, 123500000, }; int32_t ival = 0, ilval = 0; for (int32_t p=0; p \"" + fmt.toPattern(pattern) + "\""); int32_t v; for (v=0; v " + s); if (s != valFormat[v+ival]) errln((UnicodeString)"FAIL: Expected " + valFormat[v+ival]); ParsePosition pos(0); Formattable af; fmt.parse(s, af, pos); double a; UBool useEpsilon = FALSE; if (af.getType() == Formattable::kLong) a = af.getLong(); else if (af.getType() == Formattable::kDouble) { a = af.getDouble(); #if defined(OS390) || defined(OS400) // S/390 will show a failure like this: //| -3.141592652999999e-271 -format-> -3.1416E-271 //| -parse-> -3.1416e-271 //| FAIL: Expected -3.141599999999999e-271 // To compensate, we use an epsilon-based equality // test on S/390 only. We don't want to do this in // general because it's less exacting. useEpsilon = TRUE; #endif } else { errln((UnicodeString)"FAIL: Non-numeric Formattable returned"); continue; } if (pos.getIndex() == s.length()) { logln((UnicodeString)" -parse-> " + a); // Use epsilon comparison as necessary if ((useEpsilon && (uprv_fabs(a - valParse[v+ival]) / a > (2*DBL_EPSILON))) || (!useEpsilon && a != valParse[v+ival])) { errln((UnicodeString)"FAIL: Expected " + valParse[v+ival]); } } else { errln((UnicodeString)"FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + a); errln((UnicodeString)" should be (" + s.length() + " chars) -> " + valParse[v+ival]); } } for (v=0; v " + s); if (s != lvalFormat[v+ilval]) errln((UnicodeString)"ERROR: Expected " + lvalFormat[v+ilval] + " Got: " + s); ParsePosition pos(0); Formattable af; fmt.parse(s, af, pos); if (af.getType() == Formattable::kLong || af.getType() == Formattable::kInt64) { UErrorCode status = U_ZERO_ERROR; int32_t a = af.getLong(status); if (pos.getIndex() == s.length()) { logln((UnicodeString)" -parse-> " + a); if (a != lvalParse[v+ilval]) errln((UnicodeString)"FAIL: Expected " + lvalParse[v+ilval]); } else errln((UnicodeString)"FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + a); } else errln((UnicodeString)"FAIL: Non-long Formattable returned for " + s + " Double: " + af.getDouble() + ", Long: " + af.getLong()); } ival += val_length; ilval += lval_length; } } void NumberFormatTest::TestScientific2() { // jb 2552 UErrorCode status = U_ZERO_ERROR; DecimalFormat* fmt = (DecimalFormat*)NumberFormat::createCurrencyInstance("en_US", status); if (U_SUCCESS(status)) { double num = 12.34; expect(*fmt, num, "$12.34"); fmt->setScientificNotation(TRUE); expect(*fmt, num, "$1.23E1"); fmt->setScientificNotation(FALSE); expect(*fmt, num, "$12.34"); } delete fmt; } void NumberFormatTest::TestScientificGrouping() { // jb 2552 UErrorCode status = U_ZERO_ERROR; DecimalFormat fmt("##0.00E0",status); if (U_SUCCESS(status)) { expect(fmt, .01234, "12.3E-3"); expect(fmt, .1234, "123E-3"); expect(fmt, 1.234, "1.23E0"); expect(fmt, 12.34, "12.3E0"); expect(fmt, 123.4, "123E0"); expect(fmt, 1234., "1.23E3"); } } static void setFromString(DigitList& dl, const char* str) { char c; UBool decimalSet = FALSE; dl.clear(); while ((c = *str++)) { if (c == '-') { dl.fIsPositive = FALSE; } else if (c == '+') { dl.fIsPositive = TRUE; } else if (c == '.') { dl.fDecimalAt = dl.fCount; decimalSet = TRUE; } else { dl.append(c); } } if (!decimalSet) { dl.fDecimalAt = dl.fCount; } } void NumberFormatTest::TestInt64() { UErrorCode status = U_ZERO_ERROR; DecimalFormat fmt("#.#E0",status); fmt.setMaximumFractionDigits(20); if (U_SUCCESS(status)) { expect(fmt, (Formattable)(int64_t)0, "0E0"); expect(fmt, (Formattable)(int64_t)-1, "-1E0"); expect(fmt, (Formattable)(int64_t)1, "1E0"); expect(fmt, (Formattable)(int64_t)2147483647, "2.147483647E9"); expect(fmt, (Formattable)((int64_t)-2147483647-1), "-2.147483648E9"); expect(fmt, (Formattable)(int64_t)U_INT64_MAX, "9.223372036854775807E18"); expect(fmt, (Formattable)(int64_t)U_INT64_MIN, "-9.223372036854775808E18"); } // also test digitlist int64_t int64max = U_INT64_MAX; int64_t int64min = U_INT64_MIN; const char* int64maxstr = "9223372036854775807"; const char* int64minstr = "-9223372036854775808"; UnicodeString fail("fail: "); // test max int64 value DigitList dl; setFromString(dl, int64maxstr); { if (!dl.fitsIntoInt64(FALSE)) { errln(fail + int64maxstr + " didn't fit"); } int64_t int64Value = dl.getInt64(); if (int64Value != int64max) { errln(fail + int64maxstr); } dl.set(int64Value); int64Value = dl.getInt64(); if (int64Value != int64max) { errln(fail + int64maxstr); } } // test negative of max int64 value (1 shy of min int64 value) dl.fIsPositive = FALSE; { if (!dl.fitsIntoInt64(FALSE)) { errln(fail + "-" + int64maxstr + " didn't fit"); } int64_t int64Value = dl.getInt64(); if (int64Value != -int64max) { errln(fail + "-" + int64maxstr); } dl.set(int64Value); int64Value = dl.getInt64(); if (int64Value != -int64max) { errln(fail + "-" + int64maxstr); } } // test min int64 value setFromString(dl, int64minstr); { if (!dl.fitsIntoInt64(FALSE)) { errln(fail + "-" + int64minstr + " didn't fit"); } int64_t int64Value = dl.getInt64(); if (int64Value != int64min) { errln(fail + int64minstr); } dl.set(int64Value); int64Value = dl.getInt64(); if (int64Value != int64min) { errln(fail + int64minstr); } } // test negative of min int 64 value (1 more than max int64 value) dl.fIsPositive = TRUE; // won't fit { if (dl.fitsIntoInt64(FALSE)) { errln(fail + "-(" + int64minstr + ") didn't fit"); } } } // ------------------------------------- // Test the handling of quotes void NumberFormatTest::TestQuotes(void) { UErrorCode status = U_ZERO_ERROR; UnicodeString *pat; DecimalFormatSymbols *sym = new DecimalFormatSymbols(Locale::getUS(), status); pat = new UnicodeString("a'fo''o'b#"); DecimalFormat *fmt = new DecimalFormat(*pat, *sym, status); UnicodeString s; ((NumberFormat*)fmt)->format((int32_t)123, s); logln((UnicodeString)"Pattern \"" + *pat + "\""); logln((UnicodeString)" Format 123 -> " + escape(s)); if (!(s=="afo'ob123")) errln((UnicodeString)"FAIL: Expected afo'ob123"); s.truncate(0); delete fmt; delete pat; pat = new UnicodeString("a''b#"); fmt = new DecimalFormat(*pat, *sym, status); ((NumberFormat*)fmt)->format((int32_t)123, s); logln((UnicodeString)"Pattern \"" + *pat + "\""); logln((UnicodeString)" Format 123 -> " + escape(s)); if (!(s=="a'b123")) errln((UnicodeString)"FAIL: Expected a'b123"); delete fmt; delete pat; delete sym; } /** * Test the handling of the currency symbol in patterns. */ void NumberFormatTest::TestCurrencySign(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols* sym = new DecimalFormatSymbols(Locale::getUS(), status); UnicodeString pat; UChar currency = 0x00A4; // "\xA4#,##0.00;-\xA4#,##0.00" pat.append(currency).append("#,##0.00;-"). append(currency).append("#,##0.00"); DecimalFormat *fmt = new DecimalFormat(pat, *sym, status); UnicodeString s; ((NumberFormat*)fmt)->format(1234.56, s); pat.truncate(0); logln((UnicodeString)"Pattern \"" + fmt->toPattern(pat) + "\""); logln((UnicodeString)" Format " + 1234.56 + " -> " + escape(s)); if (s != "$1,234.56") errln((UnicodeString)"FAIL: Expected $1,234.56"); s.truncate(0); ((NumberFormat*)fmt)->format(- 1234.56, s); logln((UnicodeString)" Format " + (-1234.56) + " -> " + escape(s)); if (s != "-$1,234.56") errln((UnicodeString)"FAIL: Expected -$1,234.56"); delete fmt; pat.truncate(0); // "\xA4\xA4 #,##0.00;\xA4\xA4 -#,##0.00" pat.append(currency).append(currency). append(" #,##0.00;"). append(currency).append(currency). append(" -#,##0.00"); fmt = new DecimalFormat(pat, *sym, status); s.truncate(0); ((NumberFormat*)fmt)->format(1234.56, s); logln((UnicodeString)"Pattern \"" + fmt->toPattern(pat) + "\""); logln((UnicodeString)" Format " + 1234.56 + " -> " + escape(s)); if (s != "USD 1,234.56") errln((UnicodeString)"FAIL: Expected USD 1,234.56"); s.truncate(0); ((NumberFormat*)fmt)->format(-1234.56, s); logln((UnicodeString)" Format " + (-1234.56) + " -> " + escape(s)); if (s != "USD -1,234.56") errln((UnicodeString)"FAIL: Expected USD -1,234.56"); delete fmt; delete sym; if (U_FAILURE(status)) errln((UnicodeString)"FAIL: Status " + (int32_t)status); } // ------------------------------------- static UChar toHexString(int32_t i) { return (UChar)(i + (i < 10 ? 0x30 : (0x41 - 10))); } UnicodeString& NumberFormatTest::escape(UnicodeString& s) { UnicodeString buf; for (int32_t i=0; i> 12); buf += toHexString((c & 0x0F00) >> 8); buf += toHexString((c & 0x00F0) >> 4); buf += toHexString(c & 0x000F); } } return (s = buf); } // ------------------------------------- static const char* testCases[][2]= { /* locale ID */ /* expected */ {"ca_ES_PREEURO", "\\u20A7 1.150" }, {"de_LU_PREEURO", "1,150 F" }, {"el_GR_PREEURO", "1.150,50 \\u0394\\u03C1\\u03C7" }, {"en_BE_PREEURO", "1.150,50 BF" }, {"es_ES_PREEURO", "1.150 \\u20A7" }, {"eu_ES_PREEURO", "\\u20A7 1.150" }, {"gl_ES_PREEURO", "\\u20A7 1.150" }, {"it_IT_PREEURO", "\\u20A4 1.150" }, {"pt_PT_PREEURO", "1,150$50 Esc."} }; /** * Test localized currency patterns. */ void NumberFormatTest::TestCurrency(void) { UErrorCode status = U_ZERO_ERROR; NumberFormat* currencyFmt = NumberFormat::createCurrencyInstance(Locale::getCanadaFrench(), status); UnicodeString s; currencyFmt->format(1.50, s); logln((UnicodeString)"Un pauvre ici a..........." + s); if (!(s=="1,50 $")) errln((UnicodeString)"FAIL: Expected 1,50 $"); delete currencyFmt; s.truncate(0); char loc[256]={0}; int len = uloc_canonicalize("de_DE_PREEURO", loc, 256, &status); currencyFmt = NumberFormat::createCurrencyInstance(Locale(loc),status); currencyFmt->format(1.50, s); logln((UnicodeString)"Un pauvre en Allemagne a.." + s); if (!(s=="1,50 DM")) errln((UnicodeString)"FAIL: Expected 1,50 DM"); delete currencyFmt; s.truncate(0); len = uloc_canonicalize("fr_FR_PREEURO", loc, 256, &status); currencyFmt = NumberFormat::createCurrencyInstance(Locale(loc), status); currencyFmt->format(1.50, s); logln((UnicodeString)"Un pauvre en France a....." + s); if (!(s=="1,50 F")) errln((UnicodeString)"FAIL: Expected 1,50 F"); delete currencyFmt; if (U_FAILURE(status)) errln((UnicodeString)"FAIL: Status " + (int32_t)status); for(int i=0; i < (int)(sizeof(testCases)/sizeof(testCases[i])); i++){ status = U_ZERO_ERROR; const char *localeID = testCases[i][0]; UnicodeString expected(testCases[i][1]); expected = expected.unescape(); s.truncate(0); char loc[256]={0}; uloc_canonicalize(localeID, loc, 256, &status); currencyFmt = NumberFormat::createCurrencyInstance(Locale(loc), status); if(U_FAILURE(status)){ errln("Could not create currency formatter for locale %s",localeID); continue; } currencyFmt->format(1150.50, s); if(s!=expected){ errln(UnicodeString("FAIL: Expected: ")+expected + UnicodeString(" Got: ") + s + UnicodeString( " for locale: ")+ UnicodeString(localeID) ); } if (U_FAILURE(status)){ errln((UnicodeString)"FAIL: Status " + (int32_t)status); } delete currencyFmt; } } // ------------------------------------- /** * Test the Currency object handling, new as of ICU 2.2. */ void NumberFormatTest::TestCurrencyObject() { UErrorCode ec = U_ZERO_ERROR; NumberFormat* fmt = NumberFormat::createCurrencyInstance(Locale::getUS(), ec); if (U_FAILURE(ec)) { errln("FAIL: getCurrencyInstance(US)"); delete fmt; return; } Locale null("", "", ""); expectCurrency(*fmt, null, 1234.56, "$1,234.56"); expectCurrency(*fmt, Locale::getFrance(), 1234.56, CharsToUnicodeString("\\u20AC1,234.56")); // Euro expectCurrency(*fmt, Locale::getJapan(), 1234.56, CharsToUnicodeString("\\u00A51,235")); // Yen expectCurrency(*fmt, Locale("fr", "CH", ""), 1234.56, "SwF1,234.55"); // 0.05 rounding expectCurrency(*fmt, Locale::getUS(), 1234.56, "$1,234.56"); delete fmt; fmt = NumberFormat::createCurrencyInstance(Locale::getFrance(), ec); if (U_FAILURE(ec)) { errln("FAIL: getCurrencyInstance(FRANCE)"); delete fmt; return; } expectCurrency(*fmt, null, 1234.56, CharsToUnicodeString("1 234,56 \\u20AC")); expectCurrency(*fmt, Locale::getJapan(), 1234.56, CharsToUnicodeString("1 235 \\u00A5")); // Yen expectCurrency(*fmt, Locale("fr", "CH", ""), 1234.56, "1 234,55 sFr."); // 0.05 rounding expectCurrency(*fmt, Locale::getUS(), 1234.56, "1 234,56 $"); expectCurrency(*fmt, Locale::getFrance(), 1234.56, CharsToUnicodeString("1 234,56 \\u20AC")); // Euro delete fmt; } // ------------------------------------- /** * Do rudimentary testing of parsing. */ void NumberFormatTest::TestParse(void) { UErrorCode status = U_ZERO_ERROR; UnicodeString arg("0"); DecimalFormat* format = new DecimalFormat("00", status); //try { Formattable n; format->parse(arg, n, status); logln((UnicodeString)"parse(" + arg + ") = " + n.getLong()); if (n.getType() != Formattable::kLong || n.getLong() != 0) errln((UnicodeString)"FAIL: Expected 0"); delete format; if (U_FAILURE(status)) errln((UnicodeString)"FAIL: Status " + (int32_t)status); //} //catch(Exception e) { // errln((UnicodeString)"Exception caught: " + e); //} } // ------------------------------------- /** * Test proper rounding by the format method. */ void NumberFormatTest::TestRounding487(void) { UErrorCode status = U_ZERO_ERROR; NumberFormat *nf = NumberFormat::createInstance(status); roundingTest(*nf, 0.00159999, 4, "0.0016"); roundingTest(*nf, 0.00995, 4, "0.01"); roundingTest(*nf, 12.3995, 3, "12.4"); roundingTest(*nf, 12.4999, 0, "12"); roundingTest(*nf, - 19.5, 0, "-20"); delete nf; if (U_FAILURE(status)) errln((UnicodeString)"FAIL: Status " + (int32_t)status); } /** * Test the functioning of the secondary grouping value. */ void NumberFormatTest::TestSecondaryGrouping(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols US(Locale::getUS(), status); CHECK(status, "DecimalFormatSymbols ct"); DecimalFormat f("#,##,###", US, status); CHECK(status, "DecimalFormat ct"); expect2(f, (int32_t)123456789L, "12,34,56,789"); expectPat(f, "#,##,###"); f.applyPattern("#,###", status); CHECK(status, "applyPattern"); f.setSecondaryGroupingSize(4); expect2(f, (int32_t)123456789L, "12,3456,789"); expectPat(f, "#,####,###"); NumberFormat *g = NumberFormat::createInstance(Locale("hi", "IN"), status); CHECK(status, "createInstance(hi_IN)"); UnicodeString out; int32_t l = (int32_t)1876543210L; g->format(l, out); delete g; // expect "1,87,65,43,210", but with Hindi digits // 01234567890123 UBool ok = TRUE; if (out.length() != 14) { ok = FALSE; } else { for (int32_t i=0; i \"1,87,65,43,210\" (with Hindi digits), got \"" + escape(out) + "\""); } else { logln((UnicodeString)"Ok " + l + " x hi_IN -> \"" + escape(out) + "\""); } } void NumberFormatTest::TestWhiteSpaceParsing(void) { UErrorCode ec = U_ZERO_ERROR; DecimalFormatSymbols US(Locale::getUS(), ec); DecimalFormat fmt("a b#0c ", US, ec); if (U_FAILURE(ec)) { errln("FAIL: Constructor"); return; } int32_t n = 1234; expect(fmt, "a b1234c ", n); expect(fmt, "a b1234c ", n); } /** * Test currencies whose display name is a ChoiceFormat. */ void NumberFormatTest::TestComplexCurrency() { UErrorCode ec = U_ZERO_ERROR; Locale loc("en", "IN", ""); NumberFormat* fmt = NumberFormat::createCurrencyInstance(loc, ec); if (U_SUCCESS(ec)) { expect2(*fmt, 1.0, "Re. 1.00"); // Use .00392625 because that's 2^-8. Any value less than 0.005 is fine. expect(*fmt, 1.00390625, "Re. 1.00"); // tricky expect2(*fmt, 12345678.0, "Rs. 1,23,45,678.00"); expect2(*fmt, 0.5, "Rs. 0.50"); expect2(*fmt, -1.0, "-Re. 1.00"); expect2(*fmt, -10.0, "-Rs. 10.00"); } else { errln("FAIL: getCurrencyInstance(en_IN)"); } delete fmt; } // ------------------------------------- void NumberFormatTest::roundingTest(NumberFormat& nf, double x, int32_t maxFractionDigits, const char* expected) { nf.setMaximumFractionDigits(maxFractionDigits); UnicodeString out; nf.format(x, out); logln((UnicodeString)"" + x + " formats with " + maxFractionDigits + " fractional digits to " + out); if (!(out==expected)) errln((UnicodeString)"FAIL: Expected " + expected); } /** * Upgrade to alphaWorks */ void NumberFormatTest::TestExponent(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols US(Locale::getUS(), status); CHECK(status, "DecimalFormatSymbols constructor"); DecimalFormat fmt1(UnicodeString("0.###E0"), US, status); CHECK(status, "DecimalFormat(0.###E0)"); DecimalFormat fmt2(UnicodeString("0.###E+0"), US, status); CHECK(status, "DecimalFormat(0.###E+0)"); int32_t n = 1234; expect2(fmt1, n, "1.234E3"); expect2(fmt2, n, "1.234E+3"); expect(fmt1, "1.234E+3", n); // Either format should parse "E+3" } /** * Upgrade to alphaWorks */ void NumberFormatTest::TestScientific(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols US(Locale::getUS(), status); CHECK(status, "DecimalFormatSymbols constructor"); // Test pattern round-trip const char* PAT[] = { "#E0", "0.####E0", "00.000E00", "##0.####E000", "0.###E0;[0.###E0]" }; int32_t PAT_length = (int32_t)(sizeof(PAT) / sizeof(PAT[0])); int32_t DIGITS[] = { // min int, max int, min frac, max frac 0, 1, 0, 0, // "#E0" 1, 1, 0, 4, // "0.####E0" 2, 2, 3, 3, // "00.000E00" 1, 3, 0, 4, // "##0.####E000" 1, 1, 0, 3, // "0.###E0;[0.###E0]" }; for (int32_t i=0; i \"" + pat2 + "\""); } else { errln(UnicodeString("FAIL Pattern rt \"") + pat + "\" -> \"" + pat2 + "\""); } // Make sure digit counts match what we expect if (df.getMinimumIntegerDigits() != DIGITS[4*i] || df.getMaximumIntegerDigits() != DIGITS[4*i+1] || df.getMinimumFractionDigits() != DIGITS[4*i+2] || df.getMaximumFractionDigits() != DIGITS[4*i+3]) { errln(UnicodeString("FAIL \"" + pat + "\" min/max int; min/max frac = ") + df.getMinimumIntegerDigits() + "/" + df.getMaximumIntegerDigits() + ";" + df.getMinimumFractionDigits() + "/" + df.getMaximumFractionDigits() + ", expect " + DIGITS[4*i] + "/" + DIGITS[4*i+1] + ";" + DIGITS[4*i+2] + "/" + DIGITS[4*i+3]); } } // Test the constructor for default locale. We have to // manually set the default locale, as there is no // guarantee that the default locale has the same // scientific format. Locale def = Locale::getDefault(); Locale::setDefault(Locale::getUS(), status); expect2(NumberFormat::createScientificInstance(status), 12345.678901, "1.2345678901E4", status); Locale::setDefault(def, status); expect2(new DecimalFormat("#E0", US, status), 12345.0, "1.2345E4", status); expect(new DecimalFormat("0E0", US, status), 12345.0, "1E4", status); expect2(NumberFormat::createScientificInstance(Locale::getUS(), status), 12345.678901, "1.2345678901E4", status); expect(new DecimalFormat("##0.###E0", US, status), 12345.0, "12.34E3", status); expect(new DecimalFormat("##0.###E0", US, status), 12345.00001, "12.35E3", status); expect2(new DecimalFormat("##0.####E0", US, status), (int32_t) 12345, "12.345E3", status); expect2(NumberFormat::createScientificInstance(Locale::getFrance(), status), 12345.678901, "1,2345678901E4", status); expect(new DecimalFormat("##0.####E0", US, status), 789.12345e-9, "789.12E-9", status); expect2(new DecimalFormat("##0.####E0", US, status), 780.e-9, "780E-9", status); expect(new DecimalFormat(".###E0", US, status), 45678.0, ".457E5", status); expect2(new DecimalFormat(".###E0", US, status), (int32_t) 0, ".0E0", status); /* expect(new DecimalFormat[] { new DecimalFormat("#E0", US), new DecimalFormat("##E0", US), new DecimalFormat("####E0", US), new DecimalFormat("0E0", US), new DecimalFormat("00E0", US), new DecimalFormat("000E0", US), }, new Long(45678000), new String[] { "4.5678E7", "45.678E6", "4567.8E4", "5E7", "46E6", "457E5", } ); ! ! Unroll this test into individual tests below... ! */ expect2(new DecimalFormat("#E0", US, status), (int32_t) 45678000, "4.5678E7", status); expect2(new DecimalFormat("##E0", US, status), (int32_t) 45678000, "45.678E6", status); expect2(new DecimalFormat("####E0", US, status), (int32_t) 45678000, "4567.8E4", status); expect(new DecimalFormat("0E0", US, status), (int32_t) 45678000, "5E7", status); expect(new DecimalFormat("00E0", US, status), (int32_t) 45678000, "46E6", status); expect(new DecimalFormat("000E0", US, status), (int32_t) 45678000, "457E5", status); /* expect(new DecimalFormat("###E0", US, status), new Object[] { new Double(0.0000123), "12.3E-6", new Double(0.000123), "123E-6", new Double(0.00123), "1.23E-3", new Double(0.0123), "12.3E-3", new Double(0.123), "123E-3", new Double(1.23), "1.23E0", new Double(12.3), "12.3E0", new Double(123), "123E0", new Double(1230), "1.23E3", }); ! ! Unroll this test into individual tests below... ! */ expect2(new DecimalFormat("###E0", US, status), 0.0000123, "12.3E-6", status); expect2(new DecimalFormat("###E0", US, status), 0.000123, "123E-6", status); expect2(new DecimalFormat("###E0", US, status), 0.00123, "1.23E-3", status); expect2(new DecimalFormat("###E0", US, status), 0.0123, "12.3E-3", status); expect2(new DecimalFormat("###E0", US, status), 0.123, "123E-3", status); expect2(new DecimalFormat("###E0", US, status), 1.23, "1.23E0", status); expect2(new DecimalFormat("###E0", US, status), 12.3, "12.3E0", status); expect2(new DecimalFormat("###E0", US, status), 123.0, "123E0", status); expect2(new DecimalFormat("###E0", US, status), 1230.0, "1.23E3", status); /* expect(new DecimalFormat("0.#E+00", US, status), new Object[] { new Double(0.00012), "1.2E-04", new Long(12000), "1.2E+04", }); ! ! Unroll this test into individual tests below... ! */ expect2(new DecimalFormat("0.#E+00", US, status), 0.00012, "1.2E-04", status); expect2(new DecimalFormat("0.#E+00", US, status), (int32_t) 12000, "1.2E+04", status); } /** * Upgrade to alphaWorks */ void NumberFormatTest::TestPad(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols US(Locale::getUS(), status); CHECK(status, "DecimalFormatSymbols constructor"); expect2(new DecimalFormat("*^##.##", US, status), int32_t(0), "^^^^0", status); expect2(new DecimalFormat("*^##.##", US, status), -1.3, "^-1.3", status); expect2(new DecimalFormat("##0.0####E0*_ 'g-m/s^2'", US, status), int32_t(0), "0.0E0______ g-m/s^2", status); expect(new DecimalFormat("##0.0####E0*_ 'g-m/s^2'", US, status), 1.0/3, "333.333E-3_ g-m/s^2", status); expect2(new DecimalFormat("##0.0####*_ 'g-m/s^2'", US, status), int32_t(0), "0.0______ g-m/s^2", status); expect(new DecimalFormat("##0.0####*_ 'g-m/s^2'", US, status), 1.0/3, "0.33333__ g-m/s^2", status); // Test padding before a sign const char *formatStr = "*x#,###,###,##0.0#;*x(###,###,##0.0#)"; expect2(new DecimalFormat(formatStr, US, status), int32_t(-10), "xxxxxxxxxx(10.0)", status); expect2(new DecimalFormat(formatStr, US, status), int32_t(-1000),"xxxxxxx(1,000.0)", status); expect2(new DecimalFormat(formatStr, US, status), int32_t(-1000000),"xxx(1,000,000.0)", status); expect2(new DecimalFormat(formatStr, US, status), -100.37, "xxxxxxxx(100.37)", status); expect2(new DecimalFormat(formatStr, US, status), -10456.37, "xxxxx(10,456.37)", status); expect2(new DecimalFormat(formatStr, US, status), -1120456.37, "xx(1,120,456.37)", status); expect2(new DecimalFormat(formatStr, US, status), -112045600.37, "(112,045,600.37)", status); expect2(new DecimalFormat(formatStr, US, status), -1252045600.37,"(1,252,045,600.37)", status); expect2(new DecimalFormat(formatStr, US, status), int32_t(10), "xxxxxxxxxxxx10.0", status); expect2(new DecimalFormat(formatStr, US, status), int32_t(1000),"xxxxxxxxx1,000.0", status); expect2(new DecimalFormat(formatStr, US, status), int32_t(1000000),"xxxxx1,000,000.0", status); expect2(new DecimalFormat(formatStr, US, status), 100.37, "xxxxxxxxxx100.37", status); expect2(new DecimalFormat(formatStr, US, status), 10456.37, "xxxxxxx10,456.37", status); expect2(new DecimalFormat(formatStr, US, status), 1120456.37, "xxxx1,120,456.37", status); expect2(new DecimalFormat(formatStr, US, status), 112045600.37, "xx112,045,600.37", status); expect2(new DecimalFormat(formatStr, US, status), 10252045600.37,"10,252,045,600.37", status); // Test padding between a sign and a number const char *formatStr2 = "#,###,###,##0.0#*x;(###,###,##0.0#*x)"; expect2(new DecimalFormat(formatStr2, US, status), int32_t(-10), "(10.0xxxxxxxxxx)", status); expect2(new DecimalFormat(formatStr2, US, status), int32_t(-1000),"(1,000.0xxxxxxx)", status); expect2(new DecimalFormat(formatStr2, US, status), int32_t(-1000000),"(1,000,000.0xxx)", status); expect2(new DecimalFormat(formatStr2, US, status), -100.37, "(100.37xxxxxxxx)", status); expect2(new DecimalFormat(formatStr2, US, status), -10456.37, "(10,456.37xxxxx)", status); expect2(new DecimalFormat(formatStr2, US, status), -1120456.37, "(1,120,456.37xx)", status); expect2(new DecimalFormat(formatStr2, US, status), -112045600.37, "(112,045,600.37)", status); expect2(new DecimalFormat(formatStr2, US, status), -1252045600.37,"(1,252,045,600.37)", status); expect2(new DecimalFormat(formatStr2, US, status), int32_t(10), "10.0xxxxxxxxxxxx", status); expect2(new DecimalFormat(formatStr2, US, status), int32_t(1000),"1,000.0xxxxxxxxx", status); expect2(new DecimalFormat(formatStr2, US, status), int32_t(1000000),"1,000,000.0xxxxx", status); expect2(new DecimalFormat(formatStr2, US, status), 100.37, "100.37xxxxxxxxxx", status); expect2(new DecimalFormat(formatStr2, US, status), 10456.37, "10,456.37xxxxxxx", status); expect2(new DecimalFormat(formatStr2, US, status), 1120456.37, "1,120,456.37xxxx", status); expect2(new DecimalFormat(formatStr2, US, status), 112045600.37, "112,045,600.37xx", status); expect2(new DecimalFormat(formatStr2, US, status), 10252045600.37,"10,252,045,600.37", status); //testing the setPadCharacter(UnicodeString) and getPadCharacterString() DecimalFormat fmt("#", US, status); CHECK(status, "DecimalFormat constructor"); UnicodeString padString("P"); fmt.setPadCharacter(padString); expectPad(fmt, "*P##.##", DecimalFormat::kPadBeforePrefix, 5, padString); fmt.setPadCharacter((UnicodeString)"^"); expectPad(fmt, "*^#", DecimalFormat::kPadBeforePrefix, 1, (UnicodeString)"^"); //commented untill implementation is complete /* fmt.setPadCharacter((UnicodeString)"^^^"); expectPad(fmt, "*^^^#", DecimalFormat::kPadBeforePrefix, 3, (UnicodeString)"^^^"); padString.remove(); padString.append((UChar)0x0061); padString.append((UChar)0x0302); fmt.setPadCharacter(padString); UChar patternChars[]={0x002a, 0x0061, 0x0302, 0x0061, 0x0302, 0x0023, 0x0000}; UnicodeString pattern(patternChars); expectPad(fmt, pattern , DecimalFormat::kPadBeforePrefix, 4, padString); */ } /** * Upgrade to alphaWorks */ void NumberFormatTest::TestPatterns2(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols US(Locale::getUS(), status); CHECK(status, "DecimalFormatSymbols constructor"); DecimalFormat fmt("#", US, status); CHECK(status, "DecimalFormat constructor"); UChar hat = 0x005E; /*^*/ expectPad(fmt, "*^#", DecimalFormat::kPadBeforePrefix, 1, hat); expectPad(fmt, "$*^#", DecimalFormat::kPadAfterPrefix, 2, hat); expectPad(fmt, "#*^", DecimalFormat::kPadBeforeSuffix, 1, hat); expectPad(fmt, "#$*^", DecimalFormat::kPadAfterSuffix, 2, hat); expectPad(fmt, "$*^$#", ILLEGAL); expectPad(fmt, "#$*^$", ILLEGAL); expectPad(fmt, "'pre'#,##0*x'post'", DecimalFormat::kPadBeforeSuffix, 12, (UChar)0x0078 /*x*/); expectPad(fmt, "''#0*x", DecimalFormat::kPadBeforeSuffix, 3, (UChar)0x0078 /*x*/); expectPad(fmt, "'I''ll'*a###.##", DecimalFormat::kPadAfterPrefix, 10, (UChar)0x0061 /*a*/); fmt.applyPattern("AA#,##0.00ZZ", status); CHECK(status, "applyPattern"); fmt.setPadCharacter(hat); fmt.setFormatWidth(10); fmt.setPadPosition(DecimalFormat::kPadBeforePrefix); expectPat(fmt, "*^AA#,##0.00ZZ"); fmt.setPadPosition(DecimalFormat::kPadBeforeSuffix); expectPat(fmt, "AA#,##0.00*^ZZ"); fmt.setPadPosition(DecimalFormat::kPadAfterSuffix); expectPat(fmt, "AA#,##0.00ZZ*^"); // 12 3456789012 UnicodeString exp("AA*^#,##0.00ZZ", ""); fmt.setFormatWidth(12); fmt.setPadPosition(DecimalFormat::kPadAfterPrefix); expectPat(fmt, exp); fmt.setFormatWidth(13); // 12 34567890123 expectPat(fmt, "AA*^##,##0.00ZZ"); fmt.setFormatWidth(14); // 12 345678901234 expectPat(fmt, "AA*^###,##0.00ZZ"); fmt.setFormatWidth(15); // 12 3456789012345 expectPat(fmt, "AA*^####,##0.00ZZ"); // This is the interesting case fmt.setFormatWidth(16); // 12 34567890123456 expectPat(fmt, "AA*^#,###,##0.00ZZ"); } void NumberFormatTest::TestSurrogateSupport(void) { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols custom(Locale::getUS(), status); CHECK(status, "DecimalFormatSymbols constructor"); custom.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, "decimal"); custom.setSymbol(DecimalFormatSymbols::kPlusSignSymbol, "plus"); custom.setSymbol(DecimalFormatSymbols::kMinusSignSymbol, " minus "); custom.setSymbol(DecimalFormatSymbols::kExponentialSymbol, "exponent"); UnicodeString patternStr("*\\U00010000##.##", ""); patternStr = patternStr.unescape(); UnicodeString expStr("\\U00010000\\U00010000\\U00010000\\U000100000", ""); expStr = expStr.unescape(); expect2(new DecimalFormat(patternStr, custom, status), int32_t(0), expStr, status); status = U_ZERO_ERROR; expect2(new DecimalFormat("*^##.##", custom, status), int32_t(0), "^^^^0", status); status = U_ZERO_ERROR; expect2(new DecimalFormat("##.##", custom, status), -1.3, " minus 1decimal3", status); status = U_ZERO_ERROR; expect2(new DecimalFormat("##0.0####E0 'g-m/s^2'", custom, status), int32_t(0), "0decimal0exponent0 g-m/s^2", status); status = U_ZERO_ERROR; expect(new DecimalFormat("##0.0####E0 'g-m/s^2'", custom, status), 1.0/3, "333decimal333exponent minus 3 g-m/s^2", status); status = U_ZERO_ERROR; expect2(new DecimalFormat("##0.0#### 'g-m/s^2'", custom, status), int32_t(0), "0decimal0 g-m/s^2", status); status = U_ZERO_ERROR; expect(new DecimalFormat("##0.0#### 'g-m/s^2'", custom, status), 1.0/3, "0decimal33333 g-m/s^2", status); UnicodeString zero((UChar32)0x10000); custom.setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, zero); expStr = UnicodeString("\\U00010001decimal\\U00010002\\U00010005\\U00010000", ""); expStr = expStr.unescape(); status = U_ZERO_ERROR; expect2(new DecimalFormat("##0.000", custom, status), 1.25, expStr, status); custom.setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, (UChar)0x30); custom.setSymbol(DecimalFormatSymbols::kCurrencySymbol, "units of money"); custom.setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, "money separator"); patternStr = "0.00 \\u00A4' in your bank account'"; patternStr = patternStr.unescape(); expStr = UnicodeString(" minus 20money separator00 units of money in your bank account", ""); status = U_ZERO_ERROR; expect2(new DecimalFormat(patternStr, custom, status), int32_t(-20), expStr, status); custom.setSymbol(DecimalFormatSymbols::kPercentSymbol, "percent"); patternStr = "'You''ve lost ' -0.00 %' of your money today'"; patternStr = patternStr.unescape(); expStr = UnicodeString(" minus You've lost minus 2000decimal00 percent of your money today", ""); status = U_ZERO_ERROR; expect2(new DecimalFormat(patternStr, custom, status), int32_t(-20), expStr, status); } void NumberFormatTest::TestCurrencyPatterns(void) { int32_t i, locCount; const Locale* locs = NumberFormat::getAvailableLocales(locCount); for (i=0; igetMinimumFractionDigits(); int32_t max = nf->getMaximumFractionDigits(); if (min != max) { UnicodeString a, b; nf->format(1.0, a); nf->format(1.125, b); errln((UnicodeString)"FAIL: " + locs[i].getName() + " min fraction digits != max fraction digits; " "x 1.0 => " + escape(a) + "; x 1.125 => " + escape(b)); } // Make sure EURO currency formats have exactly 2 fraction digits if (nf->getDynamicClassID() == DecimalFormat::getStaticClassID()) { DecimalFormat* df = (DecimalFormat*) nf; if (u_strcmp(EUR, df->getCurrency()) == 0) { if (min != 2 || max != 2) { UnicodeString a; nf->format(1.0, a); errln((UnicodeString)"FAIL: " + locs[i].getName() + " is a EURO format but it does not have 2 fraction digits; " "x 1.0 => " + escape(a)); } } } } delete nf; } } void NumberFormatTest::TestRegCurrency(void) { #if !UCONFIG_NO_SERVICE UErrorCode status = U_ZERO_ERROR; UChar USD[4]; ucurr_forLocale("en_US", USD, 4, &status); UChar YEN[4]; ucurr_forLocale("ja_JP", YEN, 4, &status); UChar TMP[4]; static const UChar QQQ[] = {0x51, 0x51, 0x51, 0}; if(U_FAILURE(status)) { errln("Unable to get currency for locale, error %s", u_errorName(status)); return; } UCurrRegistryKey enkey = ucurr_register(YEN, "en_US", &status); UCurrRegistryKey enUSEUROkey = ucurr_register(QQQ, "en_US_EURO", &status); ucurr_forLocale("en_US", TMP, 4, &status); if (u_strcmp(YEN, TMP) != 0) { errln("FAIL: didn't return YEN registered for en_US"); } ucurr_forLocale("en_US_EURO", TMP, 4, &status); if (u_strcmp(QQQ, TMP) != 0) { errln("FAIL: didn't return QQQ for en_US_EURO"); } int32_t fallbackLen = ucurr_forLocale("en_XX_BAR", TMP, 4, &status); if (fallbackLen) { errln("FAIL: tried to fallback en_XX_BAR"); } status = U_ZERO_ERROR; // reset if (!ucurr_unregister(enkey, &status)) { errln("FAIL: couldn't unregister enkey"); } ucurr_forLocale("en_US", TMP, 4, &status); if (u_strcmp(USD, TMP) != 0) { errln("FAIL: didn't return USD for en_US after unregister of en_US"); } status = U_ZERO_ERROR; // reset ucurr_forLocale("en_US_EURO", TMP, 4, &status); if (u_strcmp(QQQ, TMP) != 0) { errln("FAIL: didn't return QQQ for en_US_EURO after unregister of en_US"); } ucurr_forLocale("en_US_BLAH", TMP, 4, &status); if (u_strcmp(USD, TMP) != 0) { errln("FAIL: could not find USD for en_US_BLAH after unregister of en"); } status = U_ZERO_ERROR; // reset if (!ucurr_unregister(enUSEUROkey, &status)) { errln("FAIL: couldn't unregister enUSEUROkey"); } ucurr_forLocale("en_US_EURO", TMP, 4, &status); if (u_strcmp(EUR, TMP) != 0) { errln("FAIL: didn't return EUR for en_US_EURO after unregister of en_US_EURO"); } status = U_ZERO_ERROR; // reset #endif } void NumberFormatTest::TestCurrencyNames(void) { // Do a basic check of getName() // USD { "US$", "US Dollar" } // 04/04/1792- UErrorCode ec = U_ZERO_ERROR; static const UChar USD[] = {85, 83, 68, 0}; /*USD*/ UBool isChoiceFormat; int32_t len; // Warning: HARD-CODED LOCALE DATA in this test. If it fails, CHECK // THE LOCALE DATA before diving into the code. assertEquals("USD.getName(SYMBOL_NAME)", UnicodeString("US$"), UnicodeString(ucurr_getName(USD, "en", UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec))); assertEquals("USD.getName(LONG_NAME)", UnicodeString("US Dollar"), UnicodeString(ucurr_getName(USD, "en", UCURR_LONG_NAME, &isChoiceFormat, &len, &ec))); assertSuccess("ucurr_getName", ec); // TODO add more tests later } void NumberFormatTest::TestSymbolsWithBadLocale(void) { Locale locDefault; Locale locBad("x-crazy_ZZ_MY_SPECIAL_ADMINISTRATION_REGION_NEEDS_A_SPECIAL_VARIANT_WITH_A_REALLY_REALLY_REALLY_REALLY_REALLY_REALLY_REALLY_LONG_NAME"); UErrorCode status = U_ZERO_ERROR; UnicodeString intlCurrencySymbol((UChar)0xa4); intlCurrencySymbol.append((UChar)0xa4); logln("Current locale is %s", Locale::getDefault().getName()); Locale::setDefault(locBad, status); logln("Current locale is %s", Locale::getDefault().getName()); DecimalFormatSymbols mySymbols(status); if (status != U_USING_FALLBACK_WARNING) { errln("DecimalFormatSymbols should returned U_USING_FALLBACK_WARNING."); } if (strcmp(mySymbols.getLocale().getName(), locBad.getName()) != 0) { errln("DecimalFormatSymbols does not have the right locale."); } int symbolEnum = (int)DecimalFormatSymbols::kDecimalSeparatorSymbol; for (; symbolEnum < (int)DecimalFormatSymbols::kFormatSymbolCount; symbolEnum++) { logln(UnicodeString("DecimalFormatSymbols[") + symbolEnum + UnicodeString("] = ") + prettify(mySymbols.getSymbol((DecimalFormatSymbols::ENumberFormatSymbol)symbolEnum))); if (mySymbols.getSymbol((DecimalFormatSymbols::ENumberFormatSymbol)symbolEnum).length() == 0 && symbolEnum != (int)DecimalFormatSymbols::kGroupingSeparatorSymbol) { errln("DecimalFormatSymbols has an empty string at index %d.", symbolEnum); } } status = U_ZERO_ERROR; Locale::setDefault(locDefault, status); logln("Current locale is %s", Locale::getDefault().getName()); } /** * Check that adoptDecimalFormatSymbols and setDecimalFormatSymbols * behave the same, except for memory ownership semantics. (No * version of this test on Java, since Java has only one method.) */ void NumberFormatTest::TestAdoptDecimalFormatSymbols(void) { UErrorCode ec = U_ZERO_ERROR; DecimalFormatSymbols *sym = new DecimalFormatSymbols(Locale::getUS(), ec); if (U_FAILURE(ec)) { errln("Fail: DecimalFormatSymbols constructor"); delete sym; return; } UnicodeString pat(" #,##0.00"); pat.insert(0, (UChar)0x00A4); DecimalFormat fmt(pat, sym, ec); if (U_FAILURE(ec)) { errln("Fail: DecimalFormat constructor"); return; } UnicodeString str; fmt.format(2350.75, str); if (str == "$ 2,350.75") { logln(str); } else { errln("Fail: " + str + ", expected $ 2,350.75"); } sym = new DecimalFormatSymbols(Locale::getUS(), ec); if (U_FAILURE(ec)) { errln("Fail: DecimalFormatSymbols constructor"); delete sym; return; } sym->setSymbol(DecimalFormatSymbols::kCurrencySymbol, "Q"); fmt.adoptDecimalFormatSymbols(sym); str.truncate(0); fmt.format(2350.75, str); if (str == "Q 2,350.75") { logln(str); } else { errln("Fail: adoptDecimalFormatSymbols -> " + str + ", expected Q 2,350.75"); } sym = new DecimalFormatSymbols(Locale::getUS(), ec); if (U_FAILURE(ec)) { errln("Fail: DecimalFormatSymbols constructor"); delete sym; return; } DecimalFormat fmt2(pat, sym, ec); if (U_FAILURE(ec)) { errln("Fail: DecimalFormat constructor"); return; } DecimalFormatSymbols sym2(Locale::getUS(), ec); if (U_FAILURE(ec)) { errln("Fail: DecimalFormatSymbols constructor"); return; } sym2.setSymbol(DecimalFormatSymbols::kCurrencySymbol, "Q"); fmt2.setDecimalFormatSymbols(sym2); str.truncate(0); fmt2.format(2350.75, str); if (str == "Q 2,350.75") { logln(str); } else { errln("Fail: setDecimalFormatSymbols -> " + str + ", expected Q 2,350.75"); } } void NumberFormatTest::TestPerMill() { UErrorCode ec = U_ZERO_ERROR; UnicodeString str; DecimalFormat fmt(ctou("###.###\\u2030"), ec); if (!assertSuccess("DecimalFormat ct", ec)) return; assertEquals("0.4857 x ###.###\\u2030", ctou("485.7\\u2030"), fmt.format(0.4857, str)); DecimalFormatSymbols sym(Locale::getUS(), ec); sym.setSymbol(DecimalFormatSymbols::kPerMillSymbol, ctou("m")); DecimalFormat fmt2("", sym, ec); fmt2.applyLocalizedPattern("###.###m", ec); if (!assertSuccess("setup", ec)) return; str.truncate(0); assertEquals("0.4857 x ###.###m", "485.7m", fmt2.format(0.4857, str)); } /** * Generic test for patterns that should be legal/illegal. */ void NumberFormatTest::TestIllegalPatterns() { // Test cases: // Prefix with "-:" for illegal patterns // Prefix with "+:" for legal patterns const char* DATA[] = { // Unquoted special characters in the suffix are illegal "-:000.000|###", "+:000.000'|###'", 0 }; for (int32_t i=0; DATA[i]; ++i) { const char* pat=DATA[i]; UBool valid = (*pat) == '+'; pat += 2; UErrorCode ec = U_ZERO_ERROR; DecimalFormat fmt(pat, ec); // locale doesn't matter here if (U_SUCCESS(ec) == valid) { logln("Ok: pattern \"%s\": %s", pat, u_errorName(ec)); } else { errln("FAIL: pattern \"%s\" should have %s; got %s", pat, (valid?"succeeded":"failed"), u_errorName(ec)); } } } //---------------------------------------------------------------------- static const char* KEYWORDS[] = { /*0*/ "ref=", // /*1*/ "loc=", // /*2*/ "f:", // /*3*/ "fp:", // /*4*/ "rt:", // <(exp.) number> <(exp.) string> /*5*/ "p:", // /*6*/ "perr:", // /*7*/ "pat:", // /*8*/ "fpc:", // 0 }; /** * Return an integer representing the next token from this * iterator. The integer will be an index into the given list, or * -1 if there are no more tokens, or -2 if the token is not on * the list. */ static int32_t keywordIndex(const UnicodeString& tok) { for (int32_t i=0; KEYWORDS[i]!=0; ++i) { if (tok==KEYWORDS[i]) { return i; } } return -1; } /** * Parse a CurrencyAmount using the given NumberFormat, with * the 'delim' character separating the number and the currency. */ static void parseCurrencyAmount(const UnicodeString& str, const NumberFormat& fmt, UChar delim, Formattable& result, UErrorCode& ec) { UnicodeString num, cur; int32_t i = str.indexOf(delim); str.extractBetween(0, i, num); str.extractBetween(i+1, INT32_MAX, cur); Formattable n; fmt.parse(num, n, ec); result.adoptObject(new CurrencyAmount(n, cur.getTerminatedBuffer(), ec)); } void NumberFormatTest::TestCases() { UErrorCode ec = U_ZERO_ERROR; TextFile reader("NumberFormatTestCases.txt", "UTF8", ec); if (U_FAILURE(ec)) { errln("FAIL: Couldn't open NumberFormatTestCases.txt"); return; } TokenIterator tokens(&reader); Locale loc("en", "US", ""); DecimalFormat *ref = 0, *fmt = 0; MeasureFormat *mfmt = 0; UnicodeString pat, tok, mloc, str, out, where, currAmt; Formattable n; for (;;) { ec = U_ZERO_ERROR; if (!tokens.next(tok, ec)) { break; } where = UnicodeString("(") + tokens.getLineNumber() + ") "; int32_t cmd = keywordIndex(tok); switch (cmd) { case 0: // ref= if (!tokens.next(tok, ec)) goto error; delete ref; ref = new DecimalFormat(tok, new DecimalFormatSymbols(Locale::getUS(), ec), ec); break; case 1: // loc= if (!tokens.next(tok, ec)) goto error; loc = Locale::createFromName(CharString(tok)); break; case 2: // f: case 3: // fp: case 4: // rt: case 5: // p: if (!tokens.next(tok, ec)) goto error; if (tok != "-") { pat = tok; delete fmt; fmt = new DecimalFormat(pat, new DecimalFormatSymbols(loc, ec), ec); if (U_FAILURE(ec)) { errln("FAIL: " + where + "Pattern \"" + pat + "\": " + u_errorName(ec)); ec = U_ZERO_ERROR; if (!tokens.next(tok, ec)) goto error; if (!tokens.next(tok, ec)) goto error; if (cmd == 3) { if (!tokens.next(tok, ec)) goto error; } continue; } } if (cmd == 2 || cmd == 3 || cmd == 4) { // f: // fp: // rt: UnicodeString num; if (!tokens.next(num, ec)) goto error; if (!tokens.next(str, ec)) goto error; ref->parse(num, n, ec); assertSuccess("parse", ec); assertEquals(where + "\"" + pat + "\".format(" + num + ")", str, fmt->format(n, out.remove(), ec)); assertSuccess("format", ec); if (cmd == 3) { // fp: if (!tokens.next(num, ec)) goto error; ref->parse(num, n, ec); assertSuccess("parse", ec); } if (cmd != 2) { // != f: Formattable m; fmt->parse(str, m, ec); assertSuccess("parse", ec); assertEquals(where + "\"" + pat + "\".parse(\"" + str + "\")", n, m); } } // p: else { UnicodeString expstr; if (!tokens.next(str, ec)) goto error; if (!tokens.next(expstr, ec)) goto error; Formattable exp, n; ref->parse(expstr, exp, ec); assertSuccess("parse", ec); fmt->parse(str, n, ec); assertSuccess("parse", ec); assertEquals(where + "\"" + pat + "\".parse(\"" + str + "\")", exp, n); } break; case 8: // fpc: if (!tokens.next(tok, ec)) goto error; if (tok != "-") { mloc = tok; delete mfmt; mfmt = MeasureFormat::createCurrencyFormat( Locale::createFromName(CharString(mloc)), ec); if (U_FAILURE(ec)) { errln("FAIL: " + where + "Loc \"" + mloc + "\": " + u_errorName(ec)); ec = U_ZERO_ERROR; if (!tokens.next(tok, ec)) goto error; if (!tokens.next(tok, ec)) goto error; if (!tokens.next(tok, ec)) goto error; continue; } } // fpc: if (!tokens.next(currAmt, ec)) goto error; if (!tokens.next(str, ec)) goto error; parseCurrencyAmount(currAmt, *ref, (UChar)0x2F/*'/'*/, n, ec); if (assertSuccess("parseCurrencyAmount", ec)) { assertEquals(where + "getCurrencyFormat(" + mloc + ").format(" + currAmt + ")", str, mfmt->format(n, out.remove(), ec)); assertSuccess("format", ec); } if (!tokens.next(currAmt, ec)) goto error; parseCurrencyAmount(currAmt, *ref, (UChar)0x2F/*'/'*/, n, ec); if (assertSuccess("parseCurrencyAmount", ec)) { Formattable m; mfmt->parseObject(str, m, ec); if (assertSuccess("parseCurrency", ec)) { assertEquals(where + "getCurrencyFormat(" + mloc + ").parse(\"" + str + "\")", n, m); } } break; case 6: // perr: errln("FAIL: Under construction"); goto done; case 7: { // pat: UnicodeString testpat; UnicodeString exppat; if (!tokens.next(testpat, ec)) goto error; if (!tokens.next(exppat, ec)) goto error; UBool err = exppat == "err"; UBool existingPat = FALSE; if (testpat == "-") { if (err) { errln("FAIL: " + where + "Invalid command \"pat: - err\""); continue; } existingPat = TRUE; testpat = pat; } if (exppat == "-") exppat = testpat; DecimalFormat* f = 0; UErrorCode ec2 = U_ZERO_ERROR; if (existingPat) { f = fmt; } else { f = new DecimalFormat(testpat, ec2); } if (U_SUCCESS(ec2)) { if (err) { errln("FAIL: " + where + "Invalid pattern \"" + testpat + "\" was accepted"); } else { UnicodeString pat2; assertEquals(where + "\"" + testpat + "\".toPattern()", exppat, f->toPattern(pat2)); } } else { if (err) { logln("Ok: " + where + "Invalid pattern \"" + testpat + "\" failed: " + u_errorName(ec2)); } else { errln("FAIL: " + where + "Valid pattern \"" + testpat + "\" failed: " + u_errorName(ec2)); } } if (!existingPat) delete f; } break; case -1: errln("FAIL: " + where + "Unknown command \"" + tok + "\""); goto done; } } goto done; error: if (U_SUCCESS(ec)) { errln("FAIL: Unexpected EOF"); } else { errln("FAIL: " + where + "Unexpected " + u_errorName(ec)); } done: delete mfmt; delete fmt; delete ref; } //---------------------------------------------------------------------- // Support methods //---------------------------------------------------------------------- UBool NumberFormatTest::equalValue(const Formattable& a, const Formattable& b) { if (a.getType() == b.getType()) { return a == b; } if (a.getType() == Formattable::kLong) { if (b.getType() == Formattable::kInt64) { return a.getLong() == b.getLong(); } else if (b.getType() == Formattable::kDouble) { return (double) a.getLong() == b.getDouble(); // TODO check use of double instead of long } } else if (a.getType() == Formattable::kDouble) { if (b.getType() == Formattable::kLong) { return a.getDouble() == (double) b.getLong(); } else if (b.getType() == Formattable::kInt64) { return a.getDouble() == (double)b.getInt64(); } } else if (a.getType() == Formattable::kInt64) { if (b.getType() == Formattable::kLong) { return a.getInt64() == (int64_t)b.getLong(); } else if (b.getType() == Formattable::kDouble) { return a.getInt64() == (int64_t)b.getDouble(); } } return FALSE; } void NumberFormatTest::expect2(NumberFormat& fmt, const Formattable& n, const UnicodeString& str) { // Don't round-trip format test, since we explicitly do it expect(fmt, n, str, FALSE); expect(fmt, str, n); } void NumberFormatTest::expect2(NumberFormat* fmt, const Formattable& n, const UnicodeString& exp, UErrorCode status) { if (U_FAILURE(status)) { errln("FAIL: NumberFormat constructor"); } else { expect2(*fmt, n, exp); } delete fmt; } void NumberFormatTest::expect(NumberFormat& fmt, const UnicodeString& str, const Formattable& n) { UErrorCode status = U_ZERO_ERROR; Formattable num; fmt.parse(str, num, status); if (U_FAILURE(status)) { errln(UnicodeString("FAIL: Parse failed for \"") + str + "\""); return; } UnicodeString pat; ((DecimalFormat*) &fmt)->toPattern(pat); if (equalValue(num, n)) { logln(UnicodeString("Ok \"") + str + "\" x " + pat + " = " + toString(num)); } else { errln(UnicodeString("FAIL \"") + str + "\" x " + pat + " = " + toString(num) + ", expected " + toString(n)); } } void NumberFormatTest::expect(NumberFormat& fmt, const Formattable& n, const UnicodeString& exp, UBool rt) { UnicodeString saw; FieldPosition pos; UErrorCode status = U_ZERO_ERROR; fmt.format(n, saw, pos, status); CHECK(status, "NumberFormat::format"); UnicodeString pat; ((DecimalFormat*) &fmt)->toPattern(pat); if (saw == exp) { logln(UnicodeString("Ok ") + toString(n) + " x " + escape(pat) + " = \"" + escape(saw) + "\""); // We should be able to round-trip the formatted string => // number => string (but not the other way around: number // => string => number2, might have number2 != number): if (rt) { Formattable n2; fmt.parse(exp, n2, status); if (U_FAILURE(status)) { errln(UnicodeString("FAIL: Parse failed for \"") + exp + "\""); return; } UnicodeString saw2; fmt.format(n2, saw2, pos, status); CHECK(status, "NumberFormat::format"); if (saw2 != exp) { errln((UnicodeString)"FAIL \"" + exp + "\" => " + toString(n2) + " => \"" + saw2 + "\""); } } } else { errln(UnicodeString("FAIL ") + toString(n) + " x " + escape(pat) + " = \"" + escape(saw) + "\", expected \"" + exp + "\""); } } void NumberFormatTest::expect(NumberFormat* fmt, const Formattable& n, const UnicodeString& exp, UErrorCode status) { if (U_FAILURE(status)) { errln("FAIL: NumberFormat constructor"); } else { expect(*fmt, n, exp); } delete fmt; } void NumberFormatTest::expectCurrency(NumberFormat& nf, const Locale& locale, double value, const UnicodeString& string) { UErrorCode ec = U_ZERO_ERROR; DecimalFormat& fmt = * (DecimalFormat*) &nf; const UChar DEFAULT_CURR[] = {45/*-*/,0}; UChar curr[4]; u_strcpy(curr, DEFAULT_CURR); if (*locale.getLanguage() != 0) { ucurr_forLocale(locale.getName(), curr, 4, &ec); assertSuccess("ucurr_forLocale", ec); fmt.setCurrency(curr, ec); assertSuccess("DecimalFormat::setCurrency", ec); } UnicodeString s; fmt.format(value, s); s.findAndReplace((UChar32)0x00A0, (UChar32)0x0020); // Default display of the number yields "1234.5599999999999" // instead of "1234.56". Use a formatter to fix this. NumberFormat* f = NumberFormat::createInstance(Locale::getUS(), ec); UnicodeString v; if (U_FAILURE(ec)) { // Oops; bad formatter. Use default op+= display. v = (UnicodeString)"" + value; } else { f->setMaximumFractionDigits(4); f->setGroupingUsed(FALSE); f->format(value, v); } delete f; if (s == string) { logln((UnicodeString)"Ok: " + v + " x " + curr + " => " + prettify(s)); } else { errln((UnicodeString)"FAIL: " + v + " x " + curr + " => " + prettify(s) + ", expected " + prettify(string)); } } void NumberFormatTest::expectPat(DecimalFormat& fmt, const UnicodeString& exp) { UnicodeString pat; fmt.toPattern(pat); if (pat == exp) { logln(UnicodeString("Ok \"") + pat + "\""); } else { errln(UnicodeString("FAIL \"") + pat + "\", expected \"" + exp + "\""); } } void NumberFormatTest::expectPad(DecimalFormat& fmt, const UnicodeString& pat, int32_t pos) { expectPad(fmt, pat, pos, 0, (UnicodeString)""); } void NumberFormatTest::expectPad(DecimalFormat& fmt, const UnicodeString& pat, int32_t pos, int32_t width, UChar pad) { expectPad(fmt, pat, pos, width, UnicodeString(pad)); } void NumberFormatTest::expectPad(DecimalFormat& fmt, const UnicodeString& pat, int32_t pos, int32_t width, const UnicodeString& pad) { int32_t apos = 0, awidth = 0; UnicodeString apadStr; UErrorCode status = U_ZERO_ERROR; fmt.applyPattern(pat, status); if (U_SUCCESS(status)) { apos = fmt.getPadPosition(); awidth = fmt.getFormatWidth(); apadStr=fmt.getPadCharacterString(); } else { apos = -1; awidth = width; apadStr = pad; } if (apos == pos && awidth == width && apadStr == pad) { UnicodeString infoStr; if (pos == ILLEGAL) { infoStr = UnicodeString(" width=", "") + awidth + UnicodeString(" pad=", "") + apadStr; } logln(UnicodeString("Ok \"") + pat + "\" pos=" + apos + infoStr); } else { errln(UnicodeString("FAIL \"") + pat + "\" pos=" + apos + " width=" + awidth + " pad=" + apadStr + ", expected " + pos + " " + width + " " + pad); } } #endif /* #if !UCONFIG_NO_FORMATTING */