/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2003, International Business Machines Corporation and
+ * Copyright (c) 1997-2008, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
/* Modification History:
#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 "winnmtst.h"
#include <float.h>
#include <string.h>
-#include "digitlst.h"
+#include <stdlib.h>
+#include "cstring.h"
static const UChar EUR[] = {69,85,82,0}; // "EUR"
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(16,TestDigitList);
+ CASE(16,TestWhiteSpaceParsing);
+ CASE(17,TestComplexCurrency);
+ CASE(18,TestRegCurrency);
+ CASE(19,TestSymbolsWithBadLocale);
+ CASE(20,TestAdoptDecimalFormatSymbols);
+
+ CASE(21,TestScientific2);
+ CASE(22,TestScientificGrouping);
+ CASE(23,TestInt64);
+
+ CASE(24,TestPerMill);
+ CASE(25,TestIllegalPatterns);
+ CASE(26,TestCases);
+
+ CASE(27,TestCurrencyNames);
+ CASE(28,TestCurrencyAmount);
+ CASE(29,TestCurrencyUnit);
+ CASE(30,TestCoverage);
+ CASE(31,TestJB3832);
+ CASE(32,TestHost);
+ CASE(33,TestHostClone);
+ CASE(34,TestCurrencyFormat);
+ CASE(35,TestRounding);
+ CASE(36,TestNonpositiveMultiplier);
+
+ CASE(37,TestLenientParse);
+ CASE(38,TestSpaceParsing);
default: name = ""; break;
}
status = U_ZERO_ERROR;
}
+ result.remove();
+ int64_t ll = 12;
+ test->format(ll, result);
+ if (result != "12.00"){
+ errln("format int64_t error");
+ }
+
delete test;
}
+}
+class StubNumberForamt :public NumberFormat{
+public:
+ StubNumberForamt(){};
+ virtual UnicodeString& format(double ,UnicodeString& appendTo,FieldPosition& ) const {
+ return appendTo;
+ }
+ virtual UnicodeString& format(int32_t ,UnicodeString& appendTo,FieldPosition& ) const {
+ return appendTo.append((UChar)0x0033);
+ }
+ virtual UnicodeString& format(int64_t number,UnicodeString& appendTo,FieldPosition& pos) const {
+ return NumberFormat::format(number, appendTo, pos);
+ }
+ virtual UnicodeString& format(const Formattable& , UnicodeString& appendTo, FieldPosition& , UErrorCode& ) const {
+ return appendTo;
+ }
+ virtual void parse(const UnicodeString& ,
+ Formattable& ,
+ ParsePosition& ) const {}
+ virtual void parse( const UnicodeString& ,
+ Formattable& ,
+ UErrorCode& ) const {}
+ virtual UClassID getDynamicClassID(void) const {
+ static char classID = 0;
+ return (UClassID)&classID;
+ }
+ virtual Format* clone() const {return NULL;}
+};
-
+void
+NumberFormatTest::TestCoverage(void){
+ StubNumberForamt stub;
+ UnicodeString agent("agent");
+ FieldPosition pos;
+ int64_t num = 4;
+ if (stub.format(num, agent, pos) != UnicodeString("agent3")){
+ errln("NumberFormat::format(int64, UnicodString&, FieldPosition&) should delegate to (int32, ,)");
+ };
}
// Test various patterns
}
}
+/*
+icu_2_4::DigitList::operator== 0 0 2 icuuc24d.dll digitlst.cpp Doug
+icu_2_4::DigitList::append 0 0 4 icuin24d.dll digitlst.h Doug
+icu_2_4::DigitList::operator!= 0 0 1 icuuc24d.dll digitlst.h Doug
+*/
+/*
void
NumberFormatTest::TestDigitList(void)
{
// API coverage for DigitList
- /*
- icu_2_4::DigitList::operator== 0 0 2 icuuc24d.dll digitlst.cpp Doug
- icu_2_4::DigitList::append 0 0 4 icuin24d.dll digitlst.h Doug
- icu_2_4::DigitList::operator!= 0 0 1 icuuc24d.dll digitlst.h Doug
- */
DigitList list1;
list1.append('1');
list1.fDecimalAt = 1;
DigitList list2;
- list2.set(1);
+ list2.set((int32_t)1);
if (list1 != list2) {
errln("digitlist append, operator!= or set failed ");
}
errln("digitlist append, operator== or set failed ");
}
}
+*/
// -------------------------------------
ParsePosition pos(0);
Formattable af;
fmt.parse(s, af, pos);
- if (af.getType() == Formattable::kLong) {
- int32_t a = af.getLong();
+ 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);
}
}
+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
// -------------------------------------
-
+static const char* testCases[][2]= {
+ /* locale ID */ /* expected */
+ {"ca_ES_PREEURO", "1.150\\u00A0\\u20A7" },
+ {"de_LU_PREEURO", "1,150\\u00A0F" },
+ {"el_GR_PREEURO", "1.150,50\\u00A0\\u0394\\u03C1\\u03C7" },
+ {"en_BE_PREEURO", "1.150,50\\u00A0BF" },
+ {"es_ES_PREEURO", "\\u20A7\\u00A01.150" },
+ {"eu_ES_PREEURO", "1.150\\u00A0\\u20A7" },
+ {"gl_ES_PREEURO", "1.150\\u00A0\\u20A7" },
+ {"it_IT_PREEURO", "\\u20A4\\u00A01.150" },
+ {"pt_PT_PREEURO", "1,150$50\\u00A0Esc."},
+ {"en_US@currency=JPY", "\\u00A51,150"}
+};
/**
* Test localized currency patterns.
*/
{
UErrorCode status = U_ZERO_ERROR;
NumberFormat* currencyFmt = NumberFormat::createCurrencyInstance(Locale::getCanadaFrench(), status);
+ if (U_FAILURE(status)) {
+ dataerrln("Error calling NumberFormat::createCurrencyInstance()");
+ return;
+ }
+
UnicodeString s; currencyFmt->format(1.50, s);
logln((UnicodeString)"Un pauvre ici a..........." + s);
- if (!(s=="1,50 $"))
- errln((UnicodeString)"FAIL: Expected 1,50 $");
+ if (!(s==CharsToUnicodeString("1,50\\u00A0$")))
+ errln((UnicodeString)"FAIL: Expected 1,50<nbsp>$");
delete currencyFmt;
s.truncate(0);
- currencyFmt = NumberFormat::createCurrencyInstance(Locale("de_DE_PREEURO"),status);
+ 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");
+ if (!(s==CharsToUnicodeString("1,50\\u00A0DM")))
+ errln((UnicodeString)"FAIL: Expected 1,50<nbsp>DM");
delete currencyFmt;
s.truncate(0);
- currencyFmt = NumberFormat::createCurrencyInstance(Locale("fr_FR_PREEURO"), status);
+ 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");
+ if (!(s==CharsToUnicodeString("1,50\\u00A0F")))
+ errln((UnicodeString)"FAIL: Expected 1,50<nbsp>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], -1, US_INV);
+ 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;
+ }
}
// -------------------------------------
1234.56, CharsToUnicodeString("\\u00A51,235")); // Yen
expectCurrency(*fmt, Locale("fr", "CH", ""),
- 1234.56, "SwF1,234.55"); // 0.05 rounding
+ 1234.56, "Fr.1,234.55"); // 0.05 rounding
expectCurrency(*fmt, Locale::getUS(),
1234.56, "$1,234.56");
expectCurrency(*fmt, null, 1234.56, CharsToUnicodeString("1 234,56 \\u20AC"));
expectCurrency(*fmt, Locale::getJapan(),
- 1234.56, CharsToUnicodeString("1 235 \\u00A5")); // Yen
+ 1234.56, CharsToUnicodeString("1 235 \\u00A5JP")); // Yen
expectCurrency(*fmt, Locale("fr", "CH", ""),
1234.56, "1 234,55 sFr."); // 0.05 rounding
expectCurrency(*fmt, Locale::getUS(),
- 1234.56, "1 234,56 $");
+ 1234.56, "1 234,56 $US");
expectCurrency(*fmt, Locale::getFrance(),
1234.56, CharsToUnicodeString("1 234,56 \\u20AC")); // Euro
UnicodeString arg("0");
DecimalFormat* format = new DecimalFormat("00", status);
//try {
- Formattable n; format->parse(arg, n, status);
+ 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");
// -------------------------------------
+static const char *lenientAffixTestCases[] = {
+ "(1)",
+ "( 1)",
+ "(1 )",
+ "( 1 )"
+};
+
+static const char *lenientMinusTestCases[] = {
+ "-5",
+ "\\u22125",
+ "\\u20105"
+};
+
+static const char *lenientCurrencyTestCases[] = {
+ "$1,000",
+ "$ 1,000",
+ "$1000",
+ "$ 1000",
+ "$1 000.00",
+ "$ 1 000.00",
+ "$ 1\\u00A0000.00",
+ "1000.00"
+};
+
+static const char *lenientNegativeCurrencyTestCases[] = {
+ "($1,000)",
+ "($ 1,000)",
+ "($1000)",
+ "($ 1000)",
+ "($1 000.00)",
+ "($ 1 000.00)",
+ "( $ 1,000.00 )",
+ "($ 1\\u00A0000.00)",
+ "(1000.00)"
+};
+
+static const char *lenientPercentTestCases[] = {
+ "25%",
+ " 25%",
+ " 25 %",
+ "25 %",
+ "25\\u00A0%",
+ "25"
+};
+
+static const char *lenientNegativePercentTestCases[] = {
+ "-25%",
+ " -25%",
+ " - 25%",
+ "- 25 %",
+ " - 25 %",
+ "-25 %",
+ "-25\\u00A0%",
+ "-25",
+ "- 25"
+};
+
+static const char *strictFailureTestCases[] = {
+ " 1000",
+#if 0
+ "10,00",
+ "1,000,.0"
+#endif
+};
+
+#define ARRAY_SIZE(array) ((int32_t) (sizeof (array) / sizeof(array[0])))
+
+/**
+ * Test lenient parsing.
+ */
+void
+NumberFormatTest::TestLenientParse(void)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ DecimalFormat *format = new DecimalFormat("(#,##0)", status);
+ Formattable n;
+
+ format->setParseStrict(FALSE);
+ for (int32_t t = 0; t < ARRAY_SIZE (lenientAffixTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientAffixTestCases[t]);
+
+ format->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (U_FAILURE(status) || n.getType() != Formattable::kLong ||
+ n.getLong() != 1) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientAffixTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ delete format;
+
+ Locale en_US("en_US");
+ Locale sv_SE("sv_SE");
+
+ NumberFormat *mFormat = NumberFormat::createInstance(sv_SE, status);
+
+ mFormat->setParseStrict(FALSE);
+ for (int32_t t = 0; t < ARRAY_SIZE(lenientMinusTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientMinusTestCases[t]);
+
+ mFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (U_FAILURE(status) || n.getType() != Formattable::kLong || n.getLong() != -5) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientMinusTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ delete mFormat;
+
+ mFormat = NumberFormat::createInstance(en_US, status);
+
+ mFormat->setParseStrict(FALSE);
+ for (int32_t t = 0; t < ARRAY_SIZE(lenientMinusTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientMinusTestCases[t]);
+
+ mFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (U_FAILURE(status) || n.getType() != Formattable::kLong || n.getLong() != -5) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientMinusTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ delete mFormat;
+
+ NumberFormat *cFormat = NumberFormat::createCurrencyInstance(en_US, status);
+
+ cFormat->setParseStrict(FALSE);
+ for (int32_t t = 0; t < ARRAY_SIZE (lenientCurrencyTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientCurrencyTestCases[t]);
+
+ cFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (U_FAILURE(status) ||n.getType() != Formattable::kLong ||
+ n.getLong() != 1000) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientCurrencyTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ for (int32_t t = 0; t < ARRAY_SIZE (lenientNegativeCurrencyTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientNegativeCurrencyTestCases[t]);
+ ParsePosition pos(0);
+
+ cFormat->parse(testCase, n, /*status*/pos);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (/*U_FAILURE(status)*/pos.getErrorIndex() >= 0 ||n.getType() != Formattable::kLong ||
+ n.getLong() != -1000) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativeCurrencyTestCases[t] + (UnicodeString) "\" parse position = " +
+ pos.getIndex() + ", " + pos.getErrorIndex());
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ delete cFormat;
+
+ NumberFormat *pFormat = NumberFormat::createPercentInstance(en_US, status);
+
+ pFormat->setParseStrict(FALSE);
+ for (int32_t t = 0; t < ARRAY_SIZE (lenientPercentTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientPercentTestCases[t]);
+
+ pFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getDouble());
+
+ if (U_FAILURE(status) ||n.getType() != Formattable::kDouble ||
+ n.getDouble() != 0.25) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientPercentTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ for (int32_t t = 0; t < ARRAY_SIZE (lenientNegativePercentTestCases); t += 1) {
+ UnicodeString testCase = ctou(lenientNegativePercentTestCases[t]);
+
+ pFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getDouble());
+
+ if (U_FAILURE(status) ||n.getType() != Formattable::kDouble ||
+ n.getDouble() != -0.25) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativePercentTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ delete pFormat;
+
+ // Test cases that should fail with a strict parse and pass with a
+ // lenient parse.
+ NumberFormat *nFormat = NumberFormat::createInstance(en_US, status);
+
+ // first, make sure that they fail with a strict parse
+ for (int32_t t = 0; t < ARRAY_SIZE(strictFailureTestCases); t += 1) {
+ UnicodeString testCase = ctou(strictFailureTestCases[t]);
+
+ nFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (! U_FAILURE(status)) {
+ errln((UnicodeString)"Strict Parse succeeded for \"" + (UnicodeString) strictFailureTestCases[t] + (UnicodeString) "\"");
+ }
+
+ status = U_ZERO_ERROR;
+ }
+
+ // then, make sure that they pass with a lenient parse
+ nFormat->setParseStrict(FALSE);
+ for (int32_t t = 0; t < ARRAY_SIZE(strictFailureTestCases); t += 1) {
+ UnicodeString testCase = ctou(strictFailureTestCases[t]);
+
+ nFormat->parse(testCase, n, status);
+ logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong());
+
+ if (U_FAILURE(status) ||n.getType() != Formattable::kLong ||
+ n.getLong() != 1000) {
+ errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) strictFailureTestCases[t] + (UnicodeString) "\"");
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ delete nFormat;
+}
+
+// -------------------------------------
+
/**
* Test proper rounding by the format method.
*/
{
UErrorCode status = U_ZERO_ERROR;
NumberFormat *nf = NumberFormat::createInstance(status);
+ if (U_FAILURE(status)) {
+ dataerrln("Error calling NumberFormat::createInstance()");
+ return;
+ }
+
roundingTest(*nf, 0.00159999, 4, "0.0016");
roundingTest(*nf, 0.00995, 4, "0.01");
/**
* Test currencies whose display name is a ChoiceFormat.
+ * Less useful now that INR is no longer a choice format! See cldrbug 1961 !
*/
void NumberFormatTest::TestComplexCurrency() {
UErrorCode ec = U_ZERO_ERROR;
- Locale loc("en", "IN", "");
+ Locale loc("kn", "IN", "");
NumberFormat* fmt = NumberFormat::createCurrencyInstance(loc, ec);
if (U_SUCCESS(ec)) {
- expect2(*fmt, 1.0, "Re. 1.00");
+ expect2(*fmt, 1.0, CharsToUnicodeString("Rs.\\u00A01.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");
+ expect(*fmt, 1.00390625, CharsToUnicodeString("Rs.\\u00A01.00")); // tricky
+ expect2(*fmt, 12345678.0, CharsToUnicodeString("Rs.\\u00A01,23,45,678.00"));
+ expect2(*fmt, 0.5, CharsToUnicodeString("Rs.\\u00A00.50"));
+ expect2(*fmt, -1.0, CharsToUnicodeString("-Rs.\\u00A01.00"));
+ expect2(*fmt, -10.0, CharsToUnicodeString("-Rs.\\u00A010.00"));
} else {
- errln("FAIL: getCurrencyInstance(en_IN)");
+ errln("FAIL: getCurrencyInstance(kn_IN)");
}
delete fmt;
}
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),
+ 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),
+ 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),
+ 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),
+ 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
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),
+ 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),
+ 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),
+ 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),
+ 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, (UChar)0x30);
custom.setSymbol(DecimalFormatSymbols::kCurrencySymbol, "units of money");
custom.setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, "money separator");
- patternStr = "0.00 \\u00A4' in your bank account'";
+ patternStr = UNICODE_STRING_SIMPLE("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;
UErrorCode ec = U_ZERO_ERROR;
NumberFormat* nf = NumberFormat::createCurrencyInstance(locs[i], ec);
if (U_FAILURE(ec)) {
- errln("FAIL: Can't create NumberFormat");
+ errln("FAIL: Can't create NumberFormat(%s) - %s", locs[i].getName(), u_errorName(ec));
} else {
// Make sure currency formats do not have a variable number
// of fraction digits
}
void NumberFormatTest::TestRegCurrency(void) {
- UErrorCode status = U_ZERO_ERROR;
- const UChar* USD = ucurr_forLocale("en_US", &status);
- const UChar* YEN = ucurr_forLocale("ja_JP", &status);
- if(U_FAILURE(status)) {
- errln("Unable to get currency for locale, error %s", u_errorName(status));
- return;
- }
+#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");
+ }
- UCurrRegistryKey enkey = ucurr_register(YEN, "en_US", &status);
- UCurrRegistryKey enUSEUROkey = ucurr_register(EUR, "en_US_EURO", &status);
+ 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");
+ }
- if (u_strcmp(YEN, ucurr_forLocale("en_US", &status)) != 0) {
- errln("FAIL: didn't return YEN registered for en_US");
- }
+ 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
+}
- if (u_strcmp(EUR, ucurr_forLocale("en_US_EURO", &status)) != 0) {
- errln("FAIL: didn't return EUR for en_US_EURO");
- }
+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[] = {0x55, 0x53, 0x44, 0}; /*USD*/
+ static const UChar USX[] = {0x55, 0x53, 0x58, 0}; /*USX*/
+ static const UChar CAD[] = {0x43, 0x41, 0x44, 0}; /*CAD*/
+ static const UChar ITL[] = {0x49, 0x54, 0x4C, 0}; /*ITL*/
+ 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("$"),
+ 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)));
+ assertEquals("CAD.getName(SYMBOL_NAME)",
+ UnicodeString("CA$"),
+ UnicodeString(ucurr_getName(CAD, "en",
+ UCURR_SYMBOL_NAME,
+ &isChoiceFormat, &len, &ec)));
+ assertEquals("CAD.getName(SYMBOL_NAME)",
+ UnicodeString("$"),
+ UnicodeString(ucurr_getName(CAD, "en_CA",
+ UCURR_SYMBOL_NAME,
+ &isChoiceFormat, &len, &ec)));
+ assertEquals("USD.getName(SYMBOL_NAME)",
+ UnicodeString("US$"),
+ UnicodeString(ucurr_getName(USD, "en_AU",
+ UCURR_SYMBOL_NAME,
+ &isChoiceFormat, &len, &ec)));
+ assertEquals("CAD.getName(SYMBOL_NAME)",
+ UnicodeString("CA$"),
+ UnicodeString(ucurr_getName(CAD, "en_AU",
+ UCURR_SYMBOL_NAME,
+ &isChoiceFormat, &len, &ec)));
+ assertEquals("USX.getName(LONG_NAME)",
+ UnicodeString("USX"),
+ UnicodeString(ucurr_getName(USX, "en_US",
+ UCURR_LONG_NAME,
+ &isChoiceFormat, &len, &ec)));
+ assertSuccess("ucurr_getName", ec);
+
+ ec = U_ZERO_ERROR;
+
+ // Test that a default or fallback warning is being returned. JB 4239.
+ ucurr_getName(CAD, "es_ES", UCURR_LONG_NAME, &isChoiceFormat,
+ &len, &ec);
+ assertTrue("ucurr_getName (fallback)",
+ U_USING_FALLBACK_WARNING == ec, TRUE);
+
+ ucurr_getName(CAD, "zh_TW", UCURR_LONG_NAME, &isChoiceFormat,
+ &len, &ec);
+ assertTrue("ucurr_getName (fallback)",
+ U_USING_FALLBACK_WARNING == ec, TRUE);
+
+ ucurr_getName(CAD, "en_US", UCURR_LONG_NAME, &isChoiceFormat,
+ &len, &ec);
+ assertTrue("ucurr_getName (default)",
+ U_USING_DEFAULT_WARNING == ec, TRUE);
+
+ ucurr_getName(CAD, "vi", UCURR_LONG_NAME, &isChoiceFormat,
+ &len, &ec);
+ assertTrue("ucurr_getName (default)",
+ U_USING_DEFAULT_WARNING == ec, TRUE);
+
+ // Test that a default warning is being returned when falling back to root. JB 4536.
+ ucurr_getName(ITL, "cy", UCURR_LONG_NAME, &isChoiceFormat,
+ &len, &ec);
+ assertTrue("ucurr_getName (default to root)",
+ U_USING_DEFAULT_WARNING == ec, TRUE);
+
+ // TODO add more tests later
+}
- if (ucurr_forLocale("en_XX_BAR", &status) != NULL) {
- errln("FAIL: tried to fallback en_XX_BAR");
- }
- status = U_ZERO_ERROR; // reset
-
- if (!ucurr_unregister(enkey, &status)) {
- errln("FAIL: couldn't unregister enkey");
- }
+void NumberFormatTest::TestCurrencyUnit(void){
+ UErrorCode ec = U_ZERO_ERROR;
+ static const UChar USD[] = {85, 83, 68, 0}; /*USD*/
+ CurrencyUnit cu(USD, ec);
+ assertSuccess("CurrencyUnit", ec);
- if (u_strcmp(USD, ucurr_forLocale("en_US", &status)) != 0) {
- errln("FAIL: didn't return USD for en_US after unregister of en_US");
- }
- status = U_ZERO_ERROR; // reset
+ const UChar * r = cu.getISOCurrency(); // who is the buffer owner ?
+ assertEquals("getISOCurrency()", USD, r);
- if (u_strcmp(EUR, ucurr_forLocale("en_US_EURO", &status)) != 0) {
- errln("FAIL: didn't return EUR for en_US_EURO after unregister of en_US");
- }
+ CurrencyUnit cu2(cu);
+ if (!(cu2 == cu)){
+ errln("CurrencyUnit copy constructed object should be same");
+ }
- if (u_strcmp(USD, ucurr_forLocale("en_US_BLAH", &status)) != 0) {
- errln("FAIL: could not find USD for en_US_BLAH after unregister of en");
- }
- status = U_ZERO_ERROR; // reset
+ CurrencyUnit * cu3 = (CurrencyUnit *)cu.clone();
+ if (!(*cu3 == cu)){
+ errln("CurrencyUnit cloned object should be same");
+ }
+ delete cu3;
+}
- if (!ucurr_unregister(enUSEUROkey, &status)) {
- errln("FAIL: couldn't unregister enUSEUROkey");
- }
+void NumberFormatTest::TestCurrencyAmount(void){
+ UErrorCode ec = U_ZERO_ERROR;
+ static const UChar USD[] = {85, 83, 68, 0}; /*USD*/
+ CurrencyAmount ca(9, USD, ec);
+ assertSuccess("CurrencyAmount", ec);
- if (ucurr_forLocale("en_US_EURO", &status) != NULL) {
- errln("FAIL: didn't return NULL for en_US_EURO after unregister of en_US_EURO");
- }
- status = U_ZERO_ERROR; // reset
+ CurrencyAmount ca2(ca);
+ if (!(ca2 == ca)){
+ errln("CurrencyAmount copy constructed object should be same");
+ }
+
+ ca2=ca;
+ if (!(ca2 == ca)){
+ errln("CurrencyAmount assigned object should be same");
+ }
+
+ CurrencyAmount *ca3 = (CurrencyAmount *)ca.clone();
+ if (!(*ca3 == ca)){
+ errln("CurrencyAmount cloned object should be same");
+ }
+ delete ca3;
}
void NumberFormatTest::TestSymbolsWithBadLocale(void) {
if (strcmp(mySymbols.getLocale().getName(), locBad.getName()) != 0) {
errln("DecimalFormatSymbols does not have the right locale.");
}
- DecimalFormatSymbols::ENumberFormatSymbol symbolEnum;
- int *symbolEnumPtr = (int*)(&symbolEnum);
- for (symbolEnum = DecimalFormatSymbols::kDecimalSeparatorSymbol; symbolEnum < DecimalFormatSymbols::kFormatSymbolCount; (*symbolEnumPtr)++) {
- if (mySymbols.getSymbol(symbolEnum).length() == 0 && symbolEnum != DecimalFormatSymbols::kGroupingSeparatorSymbol) {
+ 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
+ && symbolEnum != (int)DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol)
+ {
errln("DecimalFormatSymbols has an empty string at index %d.", symbolEnum);
}
}
}
}
+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=", // <reference pattern to parse numbers>
+ /*1*/ "loc=", // <locale for formats>
+ /*2*/ "f:", // <pattern or '-'> <number> <exp. string>
+ /*3*/ "fp:", // <pattern or '-'> <number> <exp. string> <exp. number>
+ /*4*/ "rt:", // <pattern or '-'> <(exp.) number> <(exp.) string>
+ /*5*/ "p:", // <pattern or '-'> <string> <exp. number>
+ /*6*/ "perr:", // <pattern or '-'> <invalid string>
+ /*7*/ "pat:", // <pattern or '-'> <exp. toPattern or '-' or 'err'>
+ /*8*/ "fpc:", // <pattern or '-'> <curr.amt> <exp. string> <exp. curr.amt>
+ 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)) {
+ dataerrln("[DATA] 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= <reference pattern>
+ if (!tokens.next(tok, ec)) goto error;
+ delete ref;
+ ref = new DecimalFormat(tok,
+ new DecimalFormatSymbols(Locale::getUS(), ec), ec);
+ if (U_FAILURE(ec)) {
+ dataerrln("Error constructing DecimalFormat");
+ goto error;
+ }
+ break;
+ case 1:
+ // loc= <locale>
+ 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: <pattern or '-'> <number> <exp. string>
+ // fp: <pattern or '-'> <number> <exp. string> <exp. number>
+ // rt: <pattern or '-'> <number> <string>
+ 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: <pattern or '-'> <string to parse> <exp. number>
+ 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: <loc or '-'> <curr.amt> <exp. string> <exp. curr.amt>
+ 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: <pattern or '-'> <invalid string>
+ errln("FAIL: Under construction");
+ goto done;
+ case 7: {
+ // pat: <pattern> <exp. toPattern, or '-' or 'err'>
+ 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::kLong) {
+ if (b.getType() == Formattable::kInt64) {
return a.getLong() == b.getLong();
} else if (b.getType() == Formattable::kDouble) {
- return (double) a.getLong() == b.getDouble();
+ 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.getDouble() == b.getDouble();
+ return a.getInt64() == (int64_t)b.getDouble();
}
}
return FALSE;
UErrorCode ec = U_ZERO_ERROR;
DecimalFormat& fmt = * (DecimalFormat*) &nf;
const UChar DEFAULT_CURR[] = {45/*-*/,0};
- const UChar* curr = DEFAULT_CURR;
+ UChar curr[4];
+ u_strcpy(curr, DEFAULT_CURR);
if (*locale.getLanguage() != 0) {
- curr = ucurr_forLocale(locale.getName(), &ec);
- if (U_FAILURE(ec)) {
- errln("FAIL: UCurrency::forLocale");
- return;
- }
- fmt.setCurrency(curr);
+ ucurr_forLocale(locale.getName(), curr, 4, &ec);
+ assertSuccess("ucurr_forLocale", ec);
+ fmt.setCurrency(curr, ec);
+ assertSuccess("DecimalFormat::setCurrency", ec);
+ fmt.setCurrency(curr); //Deprecated variant, for coverage only
}
UnicodeString s;
fmt.format(value, s);
apadStr = pad;
}
if (apos == pos && awidth == width && apadStr == pad) {
- logln(UnicodeString("Ok \"") + pat + "\" pos=" + apos +
- ((pos == ILLEGAL) ? UnicodeString() :
- (UnicodeString(" width=") + awidth + " pad=" + apadStr)));
+ 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);
}
}
+void NumberFormatTest::TestJB3832(){
+ const char* localeID = "pt_PT@currency=PTE";
+ Locale loc(localeID);
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString expected(CharsToUnicodeString("1,150$50\\u00A0Esc."));
+ UnicodeString s;
+ NumberFormat* currencyFmt = NumberFormat::createCurrencyInstance(loc, status);
+ if(U_FAILURE(status)){
+ errln("Could not create currency formatter for locale %s", localeID);
+ return;
+ }
+ 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("FAIL: Status %s", u_errorName(status));
+ }
+ delete currencyFmt;
+}
+
+void NumberFormatTest::TestHost()
+{
+#ifdef U_WINDOWS
+ Win32NumberTest::testLocales(this);
+#endif
+}
+
+void NumberFormatTest::TestHostClone()
+{
+ /*
+ Verify that a cloned formatter gives the same results
+ and is useable after the original has been deleted.
+ */
+ // This is mainly important on Windows.
+ UErrorCode status = U_ZERO_ERROR;
+ Locale loc("en_US@compat=host");
+ UDate now = Calendar::getNow();
+ NumberFormat *full = NumberFormat::createInstance(loc, status);
+ if (full == NULL || U_FAILURE(status)) {
+ errln("FAIL: Can't create Relative date instance");
+ return;
+ }
+ UnicodeString result1;
+ full->format(now, result1, status);
+ Format *fullClone = full->clone();
+ delete full;
+ full = NULL;
+
+ UnicodeString result2;
+ fullClone->format(now, result2, status);
+ if (U_FAILURE(status)) {
+ errln("FAIL: format failure.");
+ }
+ if (result1 != result2) {
+ errln("FAIL: Clone returned different result from non-clone.");
+ }
+ delete fullClone;
+}
+
+void NumberFormatTest::TestCurrencyFormat()
+{
+ // This test is here to increase code coverage.
+ UErrorCode status = U_ZERO_ERROR;
+ MeasureFormat *cloneObj;
+ UnicodeString str;
+ Formattable toFormat, result;
+ static const UChar ISO_CODE[4] = {0x0047, 0x0042, 0x0050, 0};
+
+ Locale saveDefaultLocale = Locale::getDefault();
+ Locale::setDefault( Locale::getUK(), status );
+ if (U_FAILURE(status)) {
+ errln("couldn't set default Locale!");
+ return;
+ }
+
+ MeasureFormat *measureObj = MeasureFormat::createCurrencyFormat(status);
+ Locale::setDefault( saveDefaultLocale, status );
+ if (U_FAILURE(status)){
+ errln("FAIL: Status %s", u_errorName(status));
+ return;
+ }
+ cloneObj = (MeasureFormat *)measureObj->clone();
+ if (cloneObj == NULL) {
+ errln("Clone doesn't work");
+ return;
+ }
+ toFormat.adoptObject(new CurrencyAmount(1234.56, ISO_CODE, status));
+ measureObj->format(toFormat, str, status);
+ measureObj->parseObject(str, result, status);
+ if (U_FAILURE(status)){
+ errln("FAIL: Status %s", u_errorName(status));
+ }
+ if (result != toFormat) {
+ errln("measureObj does not round trip. Formatted string was \"" + str + "\" Got: " + toString(result) + " Expected: " + toString(toFormat));
+ }
+ status = U_ZERO_ERROR;
+ str.truncate(0);
+ cloneObj->format(toFormat, str, status);
+ cloneObj->parseObject(str, result, status);
+ if (U_FAILURE(status)){
+ errln("FAIL: Status %s", u_errorName(status));
+ }
+ if (result != toFormat) {
+ errln("Clone does not round trip. Formatted string was \"" + str + "\" Got: " + toString(result) + " Expected: " + toString(toFormat));
+ }
+ if (*measureObj != *cloneObj) {
+ errln("Cloned object is not equal to the original object");
+ }
+ delete measureObj;
+ delete cloneObj;
+
+ status = U_USELESS_COLLATOR_ERROR;
+ if (MeasureFormat::createCurrencyFormat(status) != NULL) {
+ errln("createCurrencyFormat should have returned NULL.");
+ }
+}
+
+/* Port of ICU4J rounding test. */
+void NumberFormatTest::TestRounding() {
+ UErrorCode status = U_ZERO_ERROR;
+ DecimalFormat *df = (DecimalFormat*)NumberFormat::createCurrencyInstance(Locale::getEnglish(), status);
+
+ if (U_FAILURE(status)) {
+ errln("Unable to create decimal formatter.");
+ return;
+ }
+
+ int roundingIncrements[]={1, 2, 5, 20, 50, 100};
+ int testValues[]={0, 300};
+
+ for (int j=0; j<2; j++) {
+ for (int mode=DecimalFormat::kRoundUp;mode<DecimalFormat::kRoundHalfEven;mode++) {
+ df->setRoundingMode((DecimalFormat::ERoundingMode)mode);
+ for (int increment=0; increment<6; increment++) {
+ double base=testValues[j];
+ double rInc=roundingIncrements[increment];
+ checkRounding(df, base, 20, rInc);
+ rInc=1.000000000/rInc;
+ checkRounding(df, base, 20, rInc);
+ }
+ }
+ }
+ delete df;
+}
+
+void NumberFormatTest::checkRounding(DecimalFormat* df, double base, int iterations, double increment) {
+ df->setRoundingIncrement(increment);
+ double lastParsed=INT32_MIN; //Intger.MIN_VALUE
+ for (int i=-iterations; i<=iterations;i++) {
+ double iValue=base+(increment*(i*0.1));
+ double smallIncrement=0.00000001;
+ if (iValue!=0) {
+ smallIncrement*=iValue;
+ }
+ //we not only test the value, but some values in a small range around it
+ lastParsed=checkRound(df, iValue-smallIncrement, lastParsed);
+ lastParsed=checkRound(df, iValue, lastParsed);
+ lastParsed=checkRound(df, iValue+smallIncrement, lastParsed);
+ }
+}
+
+double NumberFormatTest::checkRound(DecimalFormat* df, double iValue, double lastParsed) {
+ UErrorCode status=U_ZERO_ERROR;
+ UnicodeString formattedDecimal;
+ double parsed;
+ Formattable result;
+ df->format(iValue, formattedDecimal, status);
+
+ if (U_FAILURE(status)) {
+ errln("Error formatting number.");
+ }
+
+ df->parse(formattedDecimal, result, status);
+
+ if (U_FAILURE(status)) {
+ errln("Error parsing number.");
+ }
+
+ parsed=result.getDouble();
+
+ if (lastParsed>parsed) {
+ errln("Rounding wrong direction! %d > %d", lastParsed, parsed);
+ }
+
+ return lastParsed;
+}
+
+void NumberFormatTest::TestNonpositiveMultiplier() {
+ UErrorCode status = U_ZERO_ERROR;
+ DecimalFormatSymbols US(Locale::getUS(), status);
+ CHECK(status, "DecimalFormatSymbols constructor");
+ DecimalFormat df(UnicodeString("0"), US, status);
+ CHECK(status, "DecimalFormat(0)");
+
+ // test zero multiplier
+
+ int32_t mult = df.getMultiplier();
+ df.setMultiplier(0);
+ if (df.getMultiplier() != mult) {
+ errln("DecimalFormat.setMultiplier(0) did not ignore its zero input");
+ }
+
+ // test negative multiplier
+
+ df.setMultiplier(-1);
+ if (df.getMultiplier() != -1) {
+ errln("DecimalFormat.setMultiplier(-1) ignored its negative input");
+ return;
+ }
+
+ expect(df, "1122.123", -1122.123);
+ expect(df, "-1122.123", 1122.123);
+ expect(df, "1.2", -1.2);
+ expect(df, "-1.2", 1.2);
+
+ // TODO: change all the following int64_t tests once BigInteger is ported
+ // (right now the big numbers get turned into doubles and lose tons of accuracy)
+ static const char* posOutOfRange = "9223372036854780000";
+ static const char* negOutOfRange = "-9223372036854780000";
+
+ expect(df, U_INT64_MIN, posOutOfRange);
+ expect(df, U_INT64_MIN+1, "9223372036854775807");
+ expect(df, (int64_t)-123, "123");
+ expect(df, (int64_t)123, "-123");
+ expect(df, U_INT64_MAX-1, "-9223372036854775806");
+ expect(df, U_INT64_MAX, "-9223372036854775807");
+
+ df.setMultiplier(-2);
+ expect(df, -(U_INT64_MIN/2)-1, "-9223372036854775806");
+ expect(df, -(U_INT64_MIN/2), "-9223372036854775808");
+ expect(df, -(U_INT64_MIN/2)+1, negOutOfRange);
+
+ df.setMultiplier(-7);
+ expect(df, -(U_INT64_MAX/7)-1, posOutOfRange);
+ expect(df, -(U_INT64_MAX/7), "9223372036854775807");
+ expect(df, -(U_INT64_MAX/7)+1, "9223372036854775800");
+
+ // TODO: uncomment (and fix up) all the following int64_t tests once BigInteger is ported
+ // (right now the big numbers get turned into doubles and lose tons of accuracy)
+ //expect2(df, U_INT64_MAX, Int64ToUnicodeString(-U_INT64_MAX));
+ //expect2(df, U_INT64_MIN, UnicodeString(Int64ToUnicodeString(U_INT64_MIN), 1));
+ //expect2(df, U_INT64_MAX / 2, Int64ToUnicodeString(-(U_INT64_MAX / 2)));
+ //expect2(df, U_INT64_MIN / 2, Int64ToUnicodeString(-(U_INT64_MIN / 2)));
+
+ // TODO: uncomment (and fix up) once BigDecimal is ported and DecimalFormat can handle it
+ //expect2(df, BigDecimal.valueOf(Long.MAX_VALUE), BigDecimal.valueOf(Long.MAX_VALUE).negate().toString());
+ //expect2(df, BigDecimal.valueOf(Long.MIN_VALUE), BigDecimal.valueOf(Long.MIN_VALUE).negate().toString());
+ //expect2(df, java.math.BigDecimal.valueOf(Long.MAX_VALUE), java.math.BigDecimal.valueOf(Long.MAX_VALUE).negate().toString());
+ //expect2(df, java.math.BigDecimal.valueOf(Long.MIN_VALUE), java.math.BigDecimal.valueOf(Long.MIN_VALUE).negate().toString());
+}
+
+void
+NumberFormatTest::TestSpaceParsing() {
+ // the data are:
+ // the string to be parsed, parsed position, parsed error index
+ const char* DATA[][3] = {
+ {"$124", "4", "-1"},
+ {"$124 $124", "4", "-1"},
+ {"$124 ", "4", "-1"},
+ {"$ 124 ", "5", "-1"},
+ {"$\\u00A0124 ", "5", "-1"},
+ {" $ 124 ", "6", "-1"},
+ //{"124$", "4", "-1"}, // TODO: need to handle trailing currency correctly
+ {"124$", "3", "-1"},
+ //{"124 $", "5", "-1"}, // TODO: OK or not, need currency spacing rule
+ {"124 $", "3", "-1"},
+ };
+
+ UErrorCode status = U_ZERO_ERROR;
+ Locale locale("en_US");
+ NumberFormat* foo = NumberFormat::createCurrencyInstance(locale, status);
+ if (U_FAILURE(status)) {
+ delete foo;
+ return;
+ }
+
+ foo->setParseStrict(FALSE);
+ for (uint32_t i = 0; i < sizeof(DATA)/sizeof(DATA[0]); ++i) {
+ ParsePosition parsePosition(0);
+ UnicodeString stringToBeParsed = ctou(DATA[i][0]);
+ int parsedPosition = atoi(DATA[i][1]);
+ int errorIndex = atoi(DATA[i][2]);
+ Formattable result;
+ foo->parse(stringToBeParsed, result, parsePosition);
+ if (parsePosition.getIndex() != parsedPosition ||
+ parsePosition.getErrorIndex() != errorIndex) {
+ errln("FAILED parse " + stringToBeParsed + "; wrong position, expected: (" + parsedPosition + ", " + errorIndex + "); got (" + parsePosition.getIndex() + ", " + parsePosition.getErrorIndex() + ")");
+ }
+ if (parsePosition.getErrorIndex() == -1 &&
+ result.getType() == Formattable::kLong &&
+ result.getLong() != 124) {
+ errln("FAILED parse " + stringToBeParsed + "; wrong number, expect: 124, got " + result.getLong());
+ }
+ }
+ delete foo;
+}
#endif /* #if !UCONFIG_NO_FORMATTING */