X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/2ca993e82fb37b597a3c73ecd1586a139a6579c5..249c4c5ea9376c24572daf9c2effa7484a282f14:/icuSources/test/intltest/numfmtst.cpp?ds=sidebyside diff --git a/icuSources/test/intltest/numfmtst.cpp b/icuSources/test/intltest/numfmtst.cpp index d877f3e4..86c25e6b 100644 --- a/icuSources/test/intltest/numfmtst.cpp +++ b/icuSources/test/intltest/numfmtst.cpp @@ -1,3 +1,5 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /******************************************************************** * COPYRIGHT: * Copyright (c) 1997-2016, International Business Machines Corporation and @@ -13,6 +15,7 @@ #if !UCONFIG_NO_FORMATTING #include "numfmtst.h" +#include "unicode/currpinf.h" #include "unicode/dcfmtsym.h" #include "unicode/decimfmt.h" #include "unicode/localpointer.h" @@ -20,12 +23,14 @@ #include "unicode/ustring.h" #include "unicode/measfmt.h" #include "unicode/curramt.h" -#include "digitlst.h" +#include "unicode/strenum.h" #include "textfile.h" #include "tokiter.h" #include "charstr.h" +#include "cstr.h" #include "putilimp.h" #include "winnmtst.h" +#include #include #include #include @@ -34,434 +39,33 @@ #include "unicode/numsys.h" #include "fmtableimp.h" #include "numberformattesttuple.h" -#include "datadrivennumberformattestsuite.h" #include "unicode/msgfmt.h" - -class NumberFormatTestDataDriven : public DataDrivenNumberFormatTestSuite { -protected: -UBool isFormatPass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status); -UBool isToPatternPass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status); -UBool isParsePass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status); -UBool isParseCurrencyPass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status); -}; - -static DigitList &strToDigitList( - const UnicodeString &str, - DigitList &digitList, - UErrorCode &status) { - if (U_FAILURE(status)) { - return digitList; - } - if (str == "NaN") { - digitList.set(uprv_getNaN()); - return digitList; - } - if (str == "-Inf") { - digitList.set(-1*uprv_getInfinity()); - return digitList; - } - if (str == "Inf") { - digitList.set(uprv_getInfinity()); - return digitList; - } - CharString formatValue; - formatValue.appendInvariantChars(str, status); - digitList.set(StringPiece(formatValue.data()), status, 0); - return digitList; -} - -static UnicodeString &format( - const DecimalFormat &fmt, - const DigitList &digitList, - UnicodeString &appendTo, - UErrorCode &status) { - if (U_FAILURE(status)) { - return appendTo; - } - FieldPosition fpos(FieldPosition::DONT_CARE); - return fmt.format(digitList, appendTo, fpos, status); -} - -template -static UnicodeString &format( - const DecimalFormat &fmt, - T value, - UnicodeString &appendTo, - UErrorCode &status) { - if (U_FAILURE(status)) { - return appendTo; - } - FieldPosition fpos(FieldPosition::DONT_CARE); - return fmt.format(value, appendTo, fpos, status); +#include "number_decimalquantity.h" +#include "unicode/numberformatter.h" + +#if (U_PLATFORM == U_PF_AIX) || (U_PLATFORM == U_PF_OS390) +// These should not be macros. If they are, +// replace them with std::isnan and std::isinf +#if defined(isnan) +#undef isnan +namespace std { + bool isnan(double x) { + return _isnan(x); + } } - -static void adjustDecimalFormat( - const NumberFormatTestTuple &tuple, - DecimalFormat &fmt, - UnicodeString &appendErrorMessage) { - if (tuple.minIntegerDigitsFlag) { - fmt.setMinimumIntegerDigits(tuple.minIntegerDigits); - } - if (tuple.maxIntegerDigitsFlag) { - fmt.setMaximumIntegerDigits(tuple.maxIntegerDigits); - } - if (tuple.minFractionDigitsFlag) { - fmt.setMinimumFractionDigits(tuple.minFractionDigits); - } - if (tuple.maxFractionDigitsFlag) { - fmt.setMaximumFractionDigits(tuple.maxFractionDigits); - } - if (tuple.currencyFlag) { - UErrorCode status = U_ZERO_ERROR; - UnicodeString currency(tuple.currency); - const UChar *terminatedCurrency = currency.getTerminatedBuffer(); - fmt.setCurrency(terminatedCurrency, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error setting currency."); - } - } - if (tuple.minGroupingDigitsFlag) { - fmt.setMinimumGroupingDigits(tuple.minGroupingDigits); - } - if (tuple.useSigDigitsFlag) { - fmt.setSignificantDigitsUsed(tuple.useSigDigits != 0); - } - if (tuple.minSigDigitsFlag) { - fmt.setMinimumSignificantDigits(tuple.minSigDigits); - } - if (tuple.maxSigDigitsFlag) { - fmt.setMaximumSignificantDigits(tuple.maxSigDigits); - } - if (tuple.useGroupingFlag) { - fmt.setGroupingUsed(tuple.useGrouping != 0); - } - if (tuple.multiplierFlag) { - fmt.setMultiplier(tuple.multiplier); - } - if (tuple.roundingIncrementFlag) { - fmt.setRoundingIncrement(tuple.roundingIncrement); - } - if (tuple.formatWidthFlag) { - fmt.setFormatWidth(tuple.formatWidth); - } - if (tuple.padCharacterFlag) { - fmt.setPadCharacter(tuple.padCharacter); - } - if (tuple.useScientificFlag) { - fmt.setScientificNotation(tuple.useScientific != 0); - } - if (tuple.groupingFlag) { - fmt.setGroupingSize(tuple.grouping); - } - if (tuple.grouping2Flag) { - fmt.setSecondaryGroupingSize(tuple.grouping2); - } - if (tuple.roundingModeFlag) { - fmt.setRoundingMode(tuple.roundingMode); - } - if (tuple.currencyUsageFlag) { - UErrorCode status = U_ZERO_ERROR; - fmt.setCurrencyUsage(tuple.currencyUsage, &status); - if (U_FAILURE(status)) { - appendErrorMessage.append("CurrencyUsage: error setting."); - } - } - if (tuple.minimumExponentDigitsFlag) { - fmt.setMinimumExponentDigits(tuple.minimumExponentDigits); - } - if (tuple.exponentSignAlwaysShownFlag) { - fmt.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0); - } - if (tuple.decimalSeparatorAlwaysShownFlag) { - fmt.setDecimalSeparatorAlwaysShown( - tuple.decimalSeparatorAlwaysShown != 0); - } - if (tuple.padPositionFlag) { - fmt.setPadPosition(tuple.padPosition); - } - if (tuple.positivePrefixFlag) { - fmt.setPositivePrefix(tuple.positivePrefix); - } - if (tuple.positiveSuffixFlag) { - fmt.setPositiveSuffix(tuple.positiveSuffix); - } - if (tuple.negativePrefixFlag) { - fmt.setNegativePrefix(tuple.negativePrefix); - } - if (tuple.negativeSuffixFlag) { - fmt.setNegativeSuffix(tuple.negativeSuffix); - } - if (tuple.localizedPatternFlag) { - UErrorCode status = U_ZERO_ERROR; - fmt.applyLocalizedPattern(tuple.localizedPattern, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error setting localized pattern."); - } - } - fmt.setLenient(NFTT_GET_FIELD(tuple, lenient, 1) != 0); - if (tuple.parseIntegerOnlyFlag) { - fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0); - } - if (tuple.decimalPatternMatchRequiredFlag) { - fmt.setDecimalPatternMatchRequired( - tuple.decimalPatternMatchRequired != 0); - } - if (tuple.parseNoExponentFlag) { - UErrorCode status = U_ZERO_ERROR; - fmt.setAttribute( - UNUM_PARSE_NO_EXPONENT, - tuple.parseNoExponent, - status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error setting parse no exponent flag."); - } - } -} - -static DecimalFormat *newDecimalFormat( - const Locale &locale, - const UnicodeString &pattern, - UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - LocalPointer symbols( - new DecimalFormatSymbols(locale, status), status); - if (U_FAILURE(status)) { - return NULL; - } - UParseError perror; - LocalPointer result(new DecimalFormat( - pattern, symbols.getAlias(), perror, status), status); - if (!result.isNull()) { - symbols.orphan(); - } - if (U_FAILURE(status)) { - return NULL; - } - return result.orphan(); -} - -static DecimalFormat *newDecimalFormat( - const NumberFormatTestTuple &tuple, - UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - Locale en("en"); - return newDecimalFormat( - NFTT_GET_FIELD(tuple, locale, en), - NFTT_GET_FIELD(tuple, pattern, "0"), - status); -} - -UBool NumberFormatTestDataDriven::isFormatPass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status) { - if (U_FAILURE(status)) { - return FALSE; - } - LocalPointer fmtPtr(newDecimalFormat(tuple, status)); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error creating DecimalFormat."); - return FALSE; - } - adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage); - if (appendErrorMessage.length() > 0) { - return FALSE; - } - DigitList digitList; - strToDigitList(tuple.format, digitList, status); - { - UnicodeString appendTo; - format(*fmtPtr, digitList, appendTo, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error formatting."); - return FALSE; - } - if (appendTo != tuple.output) { - appendErrorMessage.append( - UnicodeString("Expected: ") + tuple.output + ", got: " + appendTo); - return FALSE; - } - } - double doubleVal = digitList.getDouble(); - { - UnicodeString appendTo; - format(*fmtPtr, doubleVal, appendTo, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error formatting."); - return FALSE; - } - if (appendTo != tuple.output) { - appendErrorMessage.append( - UnicodeString("double Expected: ") + tuple.output + ", got: " + appendTo); - return FALSE; - } - } - if (!uprv_isNaN(doubleVal) && !uprv_isInfinite(doubleVal) && doubleVal == uprv_floor(doubleVal)) { - int64_t intVal = digitList.getInt64(); - { - UnicodeString appendTo; - format(*fmtPtr, intVal, appendTo, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error formatting."); - return FALSE; - } - if (appendTo != tuple.output) { - appendErrorMessage.append( - UnicodeString("int64 Expected: ") + tuple.output + ", got: " + appendTo); - return FALSE; - } - } - } - return TRUE; -} - -UBool NumberFormatTestDataDriven::isToPatternPass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status) { - if (U_FAILURE(status)) { - return FALSE; - } - LocalPointer fmtPtr(newDecimalFormat(tuple, status)); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error creating DecimalFormat."); - return FALSE; - } - adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage); - if (appendErrorMessage.length() > 0) { - return FALSE; - } - if (tuple.toPatternFlag) { - UnicodeString actual; - fmtPtr->toPattern(actual); - if (actual != tuple.toPattern) { - appendErrorMessage.append( - UnicodeString("Expected: ") + tuple.toPattern + ", got: " + actual + ". "); - } - } - if (tuple.toLocalizedPatternFlag) { - UnicodeString actual; - fmtPtr->toLocalizedPattern(actual); - if (actual != tuple.toLocalizedPattern) { - appendErrorMessage.append( - UnicodeString("Expected: ") + tuple.toLocalizedPattern + ", got: " + actual + ". "); - } - } - return appendErrorMessage.length() == 0; -} - -UBool NumberFormatTestDataDriven::isParsePass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status) { - if (U_FAILURE(status)) { - return FALSE; - } - LocalPointer fmtPtr(newDecimalFormat(tuple, status)); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error creating DecimalFormat."); - return FALSE; - } - adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage); - if (appendErrorMessage.length() > 0) { - return FALSE; - } - Formattable result; - ParsePosition ppos; - fmtPtr->parse(tuple.parse, result, ppos); - if (ppos.getIndex() == 0) { - if (tuple.output != "fail") { - appendErrorMessage.append("Parse failed but was expected to succeed."); - return FALSE; - } - return TRUE; - } - UnicodeString resultStr(UnicodeString::fromUTF8(result.getDecimalNumber(status))); - if (tuple.output == "fail") { - appendErrorMessage.append(UnicodeString("Parse succeeded: ") + resultStr + ", but was expected to fail."); - return FALSE; - } - DigitList expected; - strToDigitList(tuple.output, expected, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error parsing."); - return FALSE; - } - if (expected != *result.getDigitList()) { - appendErrorMessage.append( - UnicodeString("Expected: ") + tuple.output + ", got: " + resultStr + ". "); - return FALSE; - } - return TRUE; +#endif +#if defined(isinf) +#undef isinf +namespace std { + bool isinf(double x) { + return _isinf(x); + } } +#endif +#endif -UBool NumberFormatTestDataDriven::isParseCurrencyPass( - const NumberFormatTestTuple &tuple, - UnicodeString &appendErrorMessage, - UErrorCode &status) { - if (U_FAILURE(status)) { - return FALSE; - } - LocalPointer fmtPtr(newDecimalFormat(tuple, status)); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error creating DecimalFormat."); - return FALSE; - } - adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage); - if (appendErrorMessage.length() > 0) { - return FALSE; - } - ParsePosition ppos; - LocalPointer currAmt( - fmtPtr->parseCurrency(tuple.parse, ppos)); - if (ppos.getIndex() == 0) { - if (tuple.output != "fail") { - appendErrorMessage.append("Parse failed but was expected to succeed."); - return FALSE; - } - return TRUE; - } - UnicodeString currStr(currAmt->getISOCurrency()); - Formattable resultFormattable(currAmt->getNumber()); - UnicodeString resultStr(UnicodeString::fromUTF8(resultFormattable.getDecimalNumber(status))); - if (tuple.output == "fail") { - appendErrorMessage.append(UnicodeString("Parse succeeded: ") + resultStr + ", but was expected to fail."); - return FALSE; - } - DigitList expected; - strToDigitList(tuple.output, expected, status); - if (U_FAILURE(status)) { - appendErrorMessage.append("Error parsing."); - return FALSE; - } - if (expected != *currAmt->getNumber().getDigitList()) { - appendErrorMessage.append( - UnicodeString("Expected: ") + tuple.output + ", got: " + resultStr + ". "); - return FALSE; - } - if (currStr != tuple.outputCurrency) { - appendErrorMessage.append(UnicodeString( - "Expected currency: ") + tuple.outputCurrency + ", got: " + currStr + ". "); - return FALSE; - } - return TRUE; -} +using icu::number::impl::DecimalQuantity; +using namespace icu::number; //#define NUMFMTST_CACHE_DEBUG 1 #include "stdio.h" /* for sprintf */ @@ -521,6 +125,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestCurrencyAmount); TESTCASE_AUTO(TestCurrencyUnit); TESTCASE_AUTO(TestCoverage); + //TESTCASE_AUTO(TestLocalizedPatternSymbolCoverage); TESTCASE_AUTO(TestJB3832); TESTCASE_AUTO(TestHost); TESTCASE_AUTO(TestHostClone); @@ -531,6 +136,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestSpaceParsing); TESTCASE_AUTO(TestMultiCurrencySign); TESTCASE_AUTO(TestCurrencyFormatForMixParsing); + //TESTCASE_AUTO(TestMismatchedCurrencyFormatFail); TESTCASE_AUTO(TestDecimalFormatCurrencyParse); TESTCASE_AUTO(TestCurrencyIsoPluralFormat); TESTCASE_AUTO(TestCurrencyParsing); @@ -539,8 +145,8 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestFieldPositionIterator); TESTCASE_AUTO(TestDecimal); TESTCASE_AUTO(TestCurrencyFractionDigits); - TESTCASE_AUTO(TestExponentParse); - TESTCASE_AUTO(TestExplicitParents); + TESTCASE_AUTO(TestExponentParse); + TESTCASE_AUTO(TestExplicitParents); TESTCASE_AUTO(TestLenientParse); TESTCASE_AUTO(TestAvailableNumberingSystems); TESTCASE_AUTO(TestRoundingPattern); @@ -563,10 +169,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestAccountingCurrency); TESTCASE_AUTO(TestEquality); TESTCASE_AUTO(TestCurrencyUsage); - TESTCASE_AUTO(TestNumberFormatTestTuple); - TESTCASE_AUTO(TestDataDriven); TESTCASE_AUTO(TestDoubleLimit11439); - TESTCASE_AUTO(TestFastPathConsistent11524); TESTCASE_AUTO(TestGetAffixes); TESTCASE_AUTO(TestToPatternScientific11648); TESTCASE_AUTO(TestBenchmark); @@ -574,11 +177,44 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestFractionalDigitsForCurrency); TESTCASE_AUTO(TestFormatCurrencyPlural); TESTCASE_AUTO(Test11868); + TESTCASE_AUTO(Test11739_ParseLongCurrency); + //TESTCASE_AUTO(Test13035_MultiCodePointPaddingInPattern); + TESTCASE_AUTO(Test13737_ParseScientificStrict); TESTCASE_AUTO(Test10727_RoundingZero); TESTCASE_AUTO(Test11376_getAndSetPositivePrefix); TESTCASE_AUTO(Test11475_signRecognition); TESTCASE_AUTO(Test11640_getAffixes); TESTCASE_AUTO(Test11649_toPatternWithMultiCurrency); + TESTCASE_AUTO(Test13327_numberingSystemBufferOverflow); + TESTCASE_AUTO(Test13391_chakmaParsing); + TESTCASE_AUTO(Test11735_ExceptionIssue); + TESTCASE_AUTO(Test11035_FormatCurrencyAmount); + TESTCASE_AUTO(Test11318_DoubleConversion); + TESTCASE_AUTO(TestParsePercentRegression); + TESTCASE_AUTO(TestMultiplierWithScale); + TESTCASE_AUTO(TestFastFormatInt32); + TESTCASE_AUTO(Test11646_Equality); + TESTCASE_AUTO(TestParseNaN); + TESTCASE_AUTO(Test11897_LocalizedPatternSeparator); + TESTCASE_AUTO(Test13055_PercentageRounding); + TESTCASE_AUTO(Test11839); + TESTCASE_AUTO(Test10354); + TESTCASE_AUTO(Test11645_ApplyPatternEquality); + TESTCASE_AUTO(Test12567); + TESTCASE_AUTO(Test11626_CustomizeCurrencyPluralInfo); + TESTCASE_AUTO(Test13056_GroupingSize); + TESTCASE_AUTO(Test11025_CurrencyPadding); + TESTCASE_AUTO(Test11648_ExpDecFormatMalPattern); + //TESTCASE_AUTO(Test11649_DecFmtCurrencies); + TESTCASE_AUTO(Test13148_ParseGroupingSeparators); + TESTCASE_AUTO(Test12753_PatternDecimalPoint); + TESTCASE_AUTO(Test11647_PatternCurrencySymbols); + TESTCASE_AUTO(Test11913_BigDecimal); + TESTCASE_AUTO(Test11020_RoundingInScientificNotation); + TESTCASE_AUTO(Test11640_TripleCurrencySymbol); + TESTCASE_AUTO(Test13763_FieldPositionIteratorOffset); + TESTCASE_AUTO(Test13777_ParseLongNameNonCurrencyMode); + TESTCASE_AUTO(Test13804_EmptyStringsWhenParsing); TESTCASE_AUTO_END; } @@ -596,10 +232,10 @@ NumberFormatTest::TestAPI(void) } if(test != NULL) { test->setMinimumIntegerDigits(10); - test->setMaximumIntegerDigits(2); + test->setMaximumIntegerDigits(1); test->setMinimumFractionDigits(10); - test->setMaximumFractionDigits(2); + test->setMaximumFractionDigits(1); UnicodeString result; FieldPosition pos; @@ -614,9 +250,14 @@ NumberFormatTest::TestAPI(void) result.remove(); int64_t ll = 12; test->format(ll, result); - if (result != "12.00"){ - errln("format int64_t error"); - } + assertEquals("format int64_t error", u"2.0", result); + + test->setMinimumIntegerDigits(4); + test->setMinimumFractionDigits(4); + + result.remove(); + test->format(ll, result); + assertEquals("format int64_t error", u"0,012.0000", result); ParsePosition ppos; LocalPointer currAmt(test->parseCurrency("",ppos)); @@ -668,6 +309,58 @@ NumberFormatTest::TestCoverage(void){ }; } +void NumberFormatTest::TestLocalizedPatternSymbolCoverage() { + IcuTestErrorCode errorCode(*this, "TestLocalizedPatternSymbolCoverage"); + // Ticket #12961: DecimalFormat::toLocalizedPattern() is not working as designed. + DecimalFormatSymbols dfs(errorCode); + dfs.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, u'⁖'); + dfs.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, u'⁘'); + dfs.setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, u'⁙'); + dfs.setSymbol(DecimalFormatSymbols::kDigitSymbol, u'▰'); + dfs.setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, u'໐'); + dfs.setSymbol(DecimalFormatSymbols::kSignificantDigitSymbol, u'⁕'); + dfs.setSymbol(DecimalFormatSymbols::kPlusSignSymbol, u'†'); + dfs.setSymbol(DecimalFormatSymbols::kMinusSignSymbol, u'‡'); + dfs.setSymbol(DecimalFormatSymbols::kPercentSymbol, u'⁜'); + dfs.setSymbol(DecimalFormatSymbols::kPerMillSymbol, u'‱'); + dfs.setSymbol(DecimalFormatSymbols::kExponentialSymbol, u"⁑⁑"); // tests multi-char sequence + dfs.setSymbol(DecimalFormatSymbols::kPadEscapeSymbol, u'⁂'); + + { + UnicodeString standardPattern(u"#,##0.05+%;#,##0.05-%"); + UnicodeString localizedPattern(u"▰⁖▰▰໐⁘໐໕†⁜⁙▰⁖▰▰໐⁘໐໕‡⁜"); + + DecimalFormat df1("#", new DecimalFormatSymbols(dfs), errorCode); + df1.applyPattern(standardPattern, errorCode); + DecimalFormat df2("#", new DecimalFormatSymbols(dfs), errorCode); + df2.applyLocalizedPattern(localizedPattern, errorCode); + assertTrue("DecimalFormat instances should be equal", df1 == df2); + UnicodeString p2; + assertEquals("toPattern should match on localizedPattern instance", + standardPattern, df2.toPattern(p2)); + UnicodeString lp1; + assertEquals("toLocalizedPattern should match on standardPattern instance", + localizedPattern, df1.toLocalizedPattern(lp1)); + } + + { + UnicodeString standardPattern(u"* @@@E0‰"); + UnicodeString localizedPattern(u"⁂ ⁕⁕⁕⁑⁑໐‱"); + + DecimalFormat df1("#", new DecimalFormatSymbols(dfs), errorCode); + df1.applyPattern(standardPattern, errorCode); + DecimalFormat df2("#", new DecimalFormatSymbols(dfs), errorCode); + df2.applyLocalizedPattern(localizedPattern, errorCode); + assertTrue("DecimalFormat instances should be equal", df1 == df2); + UnicodeString p2; + assertEquals("toPattern should match on localizedPattern instance", + standardPattern, df2.toPattern(p2)); + UnicodeString lp1; + assertEquals("toLocalizedPattern should match on standardPattern instance", + localizedPattern, df1.toLocalizedPattern(lp1)); + } +} + // Test various patterns void NumberFormatTest::TestPatterns(void) @@ -678,7 +371,7 @@ NumberFormatTest::TestPatterns(void) const char* pat[] = { "#.#", "#.", ".#", "#" }; int32_t pat_length = UPRV_LENGTHOF(pat); - const char* newpat[] = { "#0.#", "#0.", "#.0", "#" }; + const char* newpat[] = { "#0.#", "#0.", "#.0", "#" }; // use ICU 61 behavior const char* num[] = { "0", "0.", ".0", "0" }; for (int32_t i=0; i (2*DBL_EPSILON))) || (!useEpsilon && a != valParse[v+ival])) { - errln((UnicodeString)"FAIL: Expected " + valParse[v+ival]); + errln((UnicodeString)"FAIL: Expected " + valParse[v+ival] + " but got " + a + + " on input " + s); } } else { @@ -890,7 +584,7 @@ NumberFormatTest::TestExponential(void) { logln((UnicodeString)" -parse-> " + a); if (a != lvalParse[v+ilval]) - errln((UnicodeString)"FAIL: Expected " + lvalParse[v+ilval]); + errln((UnicodeString)"FAIL: Expected " + lvalParse[v+ilval] + " but got " + a); } else errln((UnicodeString)"FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + a); @@ -926,7 +620,7 @@ NumberFormatTest::TestScientificGrouping() { // jb 2552 UErrorCode status = U_ZERO_ERROR; DecimalFormat fmt("##0.00E0",status); - if (U_SUCCESS(status)) { + if (assertSuccess("", status, true, __FILE__, __LINE__)) { expect(fmt, .01234, "12.3E-3"); expect(fmt, .1234, "123E-3"); expect(fmt, 1.234, "1.23E0"); @@ -1186,7 +880,7 @@ NumberFormatTest::TestCurrency(void) UnicodeString s; currencyFmt->format(1.50, s); logln((UnicodeString)"Un pauvre ici a..........." + s); if (!(s==CharsToUnicodeString("1,50\\u00A0$"))) - errln((UnicodeString)"FAIL: Expected 1,50$"); + errln((UnicodeString)"FAIL: Expected 1,50$ but got " + s); delete currencyFmt; s.truncate(0); char loc[256]={0}; @@ -1196,7 +890,7 @@ NumberFormatTest::TestCurrency(void) currencyFmt->format(1.50, s); logln((UnicodeString)"Un pauvre en Allemagne a.." + s); if (!(s==CharsToUnicodeString("1,50\\u00A0DM"))) - errln((UnicodeString)"FAIL: Expected 1,50DM"); + errln((UnicodeString)"FAIL: Expected 1,50DM but got " + s); delete currencyFmt; s.truncate(0); len = uloc_canonicalize("fr_FR_PREEURO", loc, 256, &status); @@ -1262,7 +956,7 @@ void NumberFormatTest::TestCurrencyObject() { 1234.56, CharsToUnicodeString("\\u00A51,235")); // Yen expectCurrency(*fmt, Locale("fr", "CH", ""), - 1234.56, "CHF1,234.56"); // no more 0.05 rounding here, see cldrbug 5548 + 1234.56, "CHF1,234.56"); // no more 0.05 rounding here, see cldrbug 5548 // use ICU 61 behavior expectCurrency(*fmt, Locale::getUS(), 1234.56, "$1,234.56"); @@ -1329,7 +1023,7 @@ static const char *lenientAffixTestCases[] = { static const char *lenientMinusTestCases[] = { "-5", "\\u22125", - "\\u20105" + "\\u27965" }; static const char *lenientCurrencyTestCases[] = { @@ -1405,7 +1099,8 @@ NumberFormatTest::TestLenientParse(void) if (U_FAILURE(status) || n.getType() != Formattable::kLong || n.getLong() != 1) { - errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientAffixTestCases[t] + (UnicodeString) "\""); + dataerrln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientAffixTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); status = U_ZERO_ERROR; } } @@ -1414,47 +1109,49 @@ NumberFormatTest::TestLenientParse(void) Locale en_US("en_US"); Locale sv_SE("sv_SE"); - + NumberFormat *mFormat = NumberFormat::createInstance(sv_SE, UNUM_DECIMAL, status); - + if (mFormat == NULL || U_FAILURE(status)) { dataerrln("Unable to create NumberFormat (sv_SE, UNUM_DECIMAL) - %s", u_errorName(status)); } else { mFormat->setLenient(TRUE); for (int32_t t = 0; t < UPRV_LENGTHOF(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) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientMinusTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); status = U_ZERO_ERROR; } } delete mFormat; } - + mFormat = NumberFormat::createInstance(en_US, UNUM_DECIMAL, status); - + if (mFormat == NULL || U_FAILURE(status)) { dataerrln("Unable to create NumberFormat (en_US, UNUM_DECIMAL) - %s", u_errorName(status)); } else { mFormat->setLenient(TRUE); for (int32_t t = 0; t < UPRV_LENGTHOF(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) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientMinusTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); status = U_ZERO_ERROR; } } delete mFormat; } - + NumberFormat *cFormat = NumberFormat::createInstance(en_US, UNUM_CURRENCY, status); if (cFormat == NULL || U_FAILURE(status)) { @@ -1469,7 +1166,8 @@ NumberFormatTest::TestLenientParse(void) if (U_FAILURE(status) ||n.getType() != Formattable::kLong || n.getLong() != 1000) { - errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientCurrencyTestCases[t] + (UnicodeString) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientCurrencyTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); status = U_ZERO_ERROR; } } @@ -1482,7 +1180,8 @@ NumberFormatTest::TestLenientParse(void) if (U_FAILURE(status) ||n.getType() != Formattable::kLong || n.getLong() != -1000) { - errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativeCurrencyTestCases[t] + (UnicodeString) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativeCurrencyTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); status = U_ZERO_ERROR; } } @@ -1504,7 +1203,9 @@ NumberFormatTest::TestLenientParse(void) if (U_FAILURE(status) ||n.getType() != Formattable::kDouble || n.getDouble() != 0.25) { - errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientPercentTestCases[t] + (UnicodeString) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientPercentTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status) + + "; got: " + n.getDouble(status)); status = U_ZERO_ERROR; } } @@ -1517,7 +1218,9 @@ NumberFormatTest::TestLenientParse(void) if (U_FAILURE(status) ||n.getType() != Formattable::kDouble || n.getDouble() != -0.25) { - errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativePercentTestCases[t] + (UnicodeString) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativePercentTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status) + + "; got: " + n.getDouble(status)); status = U_ZERO_ERROR; } } @@ -1528,10 +1231,10 @@ NumberFormatTest::TestLenientParse(void) // Test cases that should fail with a strict parse and pass with a // lenient parse. NumberFormat *nFormat = NumberFormat::createInstance(en_US, status); - + if (nFormat == NULL || U_FAILURE(status)) { dataerrln("Unable to create NumberFormat (en_US) - %s", u_errorName(status)); - } else { + } else { // first, make sure that they fail with a strict parse for (int32_t t = 0; t < UPRV_LENGTHOF(strictFailureTestCases); t += 1) { UnicodeString testCase = ctou(strictFailureTestCases[t]); @@ -1540,7 +1243,8 @@ NumberFormatTest::TestLenientParse(void) logln((UnicodeString)"parse(" + testCase + ") = " + n.getLong()); if (! U_FAILURE(status)) { - errln((UnicodeString)"Strict Parse succeeded for \"" + (UnicodeString) strictFailureTestCases[t] + (UnicodeString) "\""); + errln((UnicodeString)"Strict Parse succeeded for \"" + (UnicodeString) strictFailureTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); } status = U_ZERO_ERROR; @@ -1556,7 +1260,8 @@ NumberFormatTest::TestLenientParse(void) if (U_FAILURE(status) ||n.getType() != Formattable::kLong || n.getLong() != 1000) { - errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) strictFailureTestCases[t] + (UnicodeString) "\""); + errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) strictFailureTestCases[t] + + (UnicodeString) "\"; error code = " + u_errorName(status)); status = U_ZERO_ERROR; } } @@ -1603,13 +1308,13 @@ void NumberFormatTest::TestSecondaryGrouping(void) { CHECK(status, "DecimalFormat ct"); expect2(f, (int32_t)123456789L, "12,34,56,789"); - expectPat(f, "#,##,###"); + expectPat(f, "#,##,###"); // use ICU 61 behavior f.applyPattern("#,###", status); CHECK(status, "applyPattern"); f.setSecondaryGroupingSize(4); expect2(f, (int32_t)123456789L, "12,3456,789"); - expectPat(f, "#,####,###"); + expectPat(f, "#,####,###"); // use ICU 61 behavior NumberFormat *g = NumberFormat::createInstance(Locale("hi", "IN"), status); CHECK_DATA(status, "createInstance(hi_IN)"); @@ -1661,6 +1366,8 @@ void NumberFormatTest::TestWhiteSpaceParsing(void) { errcheckln(ec, "FAIL: Constructor - %s", u_errorName(ec)); return; } + // From ICU 62, flexible whitespace needs lenient mode + fmt.setLenient(TRUE); int32_t n = 1234; expect(fmt, "a b1234c ", n); expect(fmt, "a b1234c ", n); @@ -1731,7 +1438,7 @@ void NumberFormatTest::TestScientific(void) { int32_t PAT_length = UPRV_LENGTHOF(PAT); int32_t DIGITS[] = { // min int, max int, min frac, max frac - 0, 1, 0, 0, // "#E0" + 0, 1, 0, 0, // "#E0" // expect ICU 61 behavior 1, 1, 0, 4, // "0.####E0" 2, 2, 3, 3, // "00.000E00" 1, 3, 0, 4, // "##0.####E000" @@ -2074,7 +1781,7 @@ void NumberFormatTest::TestPatterns2(void) { fmt.setFormatWidth(16); // 12 34567890123456 - expectPat(fmt, "AA*^#,###,##0.00ZZ"); + expectPat(fmt, "AA*^#,###,##0.00ZZ"); // use ICU 61 behavior } void NumberFormatTest::TestSurrogateSupport(void) { @@ -2267,30 +1974,54 @@ void NumberFormatTest::TestCurrencyNames(void) { const UBool possibleDataError = TRUE; // 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)", + assertEquals("USD.getName(SYMBOL_NAME, en)", UnicodeString("$"), UnicodeString(ucurr_getName(USD, "en", UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec)), possibleDataError); - assertEquals("USD.getName(LONG_NAME)", + assertEquals("USD.getName(NARROW_SYMBOL_NAME, en)", + UnicodeString("$"), + UnicodeString(ucurr_getName(USD, "en", + UCURR_NARROW_SYMBOL_NAME, + &isChoiceFormat, &len, &ec)), + possibleDataError); + assertEquals("USD.getName(LONG_NAME, en)", UnicodeString("US Dollar"), UnicodeString(ucurr_getName(USD, "en", UCURR_LONG_NAME, &isChoiceFormat, &len, &ec)), possibleDataError); - assertEquals("CAD.getName(SYMBOL_NAME)", + assertEquals("CAD.getName(SYMBOL_NAME, en)", UnicodeString("CA$"), UnicodeString(ucurr_getName(CAD, "en", UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec)), possibleDataError); - assertEquals("CAD.getName(SYMBOL_NAME)", + assertEquals("CAD.getName(NARROW_SYMBOL_NAME, en)", + UnicodeString("$"), + UnicodeString(ucurr_getName(CAD, "en", + UCURR_NARROW_SYMBOL_NAME, + &isChoiceFormat, &len, &ec)), + possibleDataError); + assertEquals("CAD.getName(SYMBOL_NAME, en_CA)", UnicodeString("$"), UnicodeString(ucurr_getName(CAD, "en_CA", UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec)), possibleDataError); + assertEquals("USD.getName(SYMBOL_NAME, en_CA)", + UnicodeString("US$"), + UnicodeString(ucurr_getName(USD, "en_CA", + UCURR_SYMBOL_NAME, + &isChoiceFormat, &len, &ec)), + possibleDataError); + assertEquals("USD.getName(NARROW_SYMBOL_NAME, en_CA)", + UnicodeString("$"), + UnicodeString(ucurr_getName(USD, "en_CA", + UCURR_NARROW_SYMBOL_NAME, + &isChoiceFormat, &len, &ec)), + possibleDataError); assertEquals("USD.getName(SYMBOL_NAME) in en_NZ", UnicodeString("US$"), UnicodeString(ucurr_getName(USD, "en_NZ", @@ -2303,6 +2034,18 @@ void NumberFormatTest::TestCurrencyNames(void) { UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec)), possibleDataError); + assertEquals("USX.getName(SYMBOL_NAME)", + UnicodeString("USX"), + UnicodeString(ucurr_getName(USX, "en_US", + UCURR_SYMBOL_NAME, + &isChoiceFormat, &len, &ec)), + possibleDataError); + assertEquals("USX.getName(NARROW_SYMBOL_NAME)", + UnicodeString("USX"), + UnicodeString(ucurr_getName(USX, "en_US", + UCURR_NARROW_SYMBOL_NAME, + &isChoiceFormat, &len, &ec)), + possibleDataError); assertEquals("USX.getName(LONG_NAME)", UnicodeString("USX"), UnicodeString(ucurr_getName(USX, "en_US", @@ -2345,14 +2088,17 @@ void NumberFormatTest::TestCurrencyNames(void) { void NumberFormatTest::TestCurrencyUnit(void){ UErrorCode ec = U_ZERO_ERROR; - static const UChar USD[] = {85, 83, 68, 0}; /*USD*/ - static const UChar BAD[] = {63, 63, 63, 0}; /*???*/ - static const UChar BAD2[] = {63, 63, 65, 0}; /*???*/ + static const UChar USD[] = u"USD"; + static const char USD8[] = "USD"; + static const UChar BAD[] = u"???"; + static const UChar BAD2[] = u"??A"; + static const UChar XXX[] = u"XXX"; + static const char XXX8[] = "XXX"; CurrencyUnit cu(USD, ec); assertSuccess("CurrencyUnit", ec); - const UChar * r = cu.getISOCurrency(); // who is the buffer owner ? - assertEquals("getISOCurrency()", USD, r); + assertEquals("getISOCurrency()", USD, cu.getISOCurrency()); + assertEquals("getSubtype()", USD8, cu.getSubtype()); CurrencyUnit cu2(cu); if (!(cu2 == cu)){ @@ -2381,6 +2127,31 @@ void NumberFormatTest::TestCurrencyUnit(void){ errln("Currency unit assignment should be the same."); } delete cu3; + + // Test default constructor + CurrencyUnit def; + assertEquals("Default currency", XXX, def.getISOCurrency()); + assertEquals("Default currency as subtype", XXX8, def.getSubtype()); + + // Test slicing + MeasureUnit sliced1 = cu; + MeasureUnit sliced2 = cu; + assertEquals("Subtype after slicing 1", USD8, sliced1.getSubtype()); + assertEquals("Subtype after slicing 2", USD8, sliced2.getSubtype()); + CurrencyUnit restored1(sliced1, ec); + CurrencyUnit restored2(sliced2, ec); + assertSuccess("Restoring from MeasureUnit", ec); + assertEquals("Subtype after restoring 1", USD8, restored1.getSubtype()); + assertEquals("Subtype after restoring 2", USD8, restored2.getSubtype()); + assertEquals("ISO Code after restoring 1", USD, restored1.getISOCurrency()); + assertEquals("ISO Code after restoring 2", USD, restored2.getISOCurrency()); + + // Test copy constructor failure + LocalPointer meter(MeasureUnit::createMeter(ec)); + assertSuccess("Creating meter", ec); + CurrencyUnit failure(*meter, ec); + assertEquals("Copying from meter should fail", ec, U_ILLEGAL_ARGUMENT_ERROR); + assertEquals("Copying should not give uninitialized ISO code", u"", failure.getISOCurrency()); } void NumberFormatTest::TestCurrencyAmount(void){ @@ -2420,6 +2191,7 @@ void NumberFormatTest::TestSymbolsWithBadLocale(void) { for (i = 0; i < UPRV_LENGTHOF(badLocales); i++) { const char *localeName = badLocales[i]; Locale locBad(localeName); + TEST_ASSERT_TRUE(!locBad.isBogus()); UErrorCode status = U_ZERO_ERROR; UnicodeString intlCurrencySymbol((UChar)0xa4); @@ -2534,11 +2306,17 @@ void NumberFormatTest::TestPerMill() { 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)); + ctou("485.7\\u2030"), fmt.format(0.4857, str), true); DecimalFormatSymbols sym(Locale::getUS(), ec); + if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { + return; + } sym.setSymbol(DecimalFormatSymbols::kPerMillSymbol, ctou("m")); DecimalFormat fmt2("", sym, ec); + if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { + return; + } fmt2.applyLocalizedPattern("###.###m", ec); if (!assertSuccess("setup", ec)) return; str.truncate(0); @@ -3135,7 +2913,7 @@ void NumberFormatTest::TestCompatibleCurrencies() { expectParseCurrency(*fmtJP, JPY, 1235, "\\u00A51,235"); logln("%s:%d - testing parse of fullwidth yen sign in JP\n", __FILE__, __LINE__); expectParseCurrency(*fmtJP, JPY, 1235, "\\uFFE51,235"); - + // more.. */ } @@ -3155,7 +2933,7 @@ void NumberFormatTest::expectParseCurrency(const NumberFormat &fmt, const UChar* fmt.getLocale(ULOC_ACTUAL_LOCALE, status).getBaseName(), text); u_austrcpy(theInfo+uprv_strlen(theInfo), currency); - + char theOperation[100]; uprv_strcpy(theOperation, theInfo); @@ -3166,7 +2944,7 @@ void NumberFormatTest::expectParseCurrency(const NumberFormat &fmt, const UChar* uprv_strcat(theOperation, ", check currency:"); assertEquals(theOperation, currency, currencyAmount->getISOCurrency()); } - + void NumberFormatTest::TestJB3832(){ const char* localeID = "pt_PT@currency=PTE"; @@ -3241,7 +3019,7 @@ void NumberFormatTest::TestHostClone() UDate now = Calendar::getNow(); NumberFormat *full = NumberFormat::createInstance(loc, status); if (full == NULL || U_FAILURE(status)) { - dataerrln("FAIL: Can't create Relative date instance - %s", u_errorName(status)); + dataerrln("FAIL: Can't create NumberFormat date instance - %s", u_errorName(status)); return; } UnicodeString result1; @@ -3278,9 +3056,13 @@ void NumberFormatTest::TestCurrencyFormat() } MeasureFormat *measureObj = MeasureFormat::createCurrencyFormat(status); + if (U_FAILURE(status)){ + dataerrln("FAIL: MeasureFormat::createCurrencyFormat status %s", u_errorName(status)); + return; + } Locale::setDefault( saveDefaultLocale, status ); if (U_FAILURE(status)){ - dataerrln("FAIL: Status %s", u_errorName(status)); + dataerrln("FAIL: Locale::setDefault status %s", u_errorName(status)); return; } cloneObj = (MeasureFormat *)measureObj->clone(); @@ -3541,6 +3323,7 @@ NumberFormatTest::TestSpaceParsing() { foo->setLenient(DATA[i].lenient); Formattable result; foo->parse(stringToBeParsed, result, parsePosition); + logln("Parsing: " + stringToBeParsed); if (parsePosition.getIndex() != parsedPosition || parsePosition.getErrorIndex() != errorIndex) { errln("FAILED parse " + stringToBeParsed + "; lenient: " + DATA[i].lenient + "; wrong position, expected: (" + parsedPosition + ", " + errorIndex + "); got (" + parsePosition.getIndex() + ", " + parsePosition.getErrorIndex() + ")"); @@ -3599,7 +3382,7 @@ void NumberFormatTest::TestNumberingSystems() { NumberFormat *fmt = (NumberFormat *) origFmt->clone(); delete origFmt; - + if (item->isRBNF) { expect3(*fmt,item->value,CharsToUnicodeString(item->expectedResult)); } else { @@ -3657,9 +3440,9 @@ NumberFormatTest::TestMultiCurrencySign() { {"en_US", "\\u00A4#,##0.00;-\\u00A4#,##0.00", "-1234.56", "-$1,234.56", "-USD1,234.56", "-US dollars1,234.56"}, {"en_US", "\\u00A4#,##0.00;-\\u00A4#,##0.00", "1", "$1.00", "USD1.00", "US dollars1.00"}, // for CHINA locale - {"zh_CN", "\\u00A4#,##0.00;(\\u00A4#,##0.00)", "1234.56", "\\uFFE51,234.56", "CNY1,234.56", "\\u4EBA\\u6C11\\u5E011,234.56"}, - {"zh_CN", "\\u00A4#,##0.00;(\\u00A4#,##0.00)", "-1234.56", "(\\uFFE51,234.56)", "(CNY1,234.56)", "(\\u4EBA\\u6C11\\u5E011,234.56)"}, - {"zh_CN", "\\u00A4#,##0.00;(\\u00A4#,##0.00)", "1", "\\uFFE51.00", "CNY1.00", "\\u4EBA\\u6C11\\u5E011.00"} + {"zh_CN", "\\u00A4#,##0.00;(\\u00A4#,##0.00)", "1234.56", "\\u00A51,234.56", "CNY1,234.56", "\\u4EBA\\u6C11\\u5E011,234.56"}, + {"zh_CN", "\\u00A4#,##0.00;(\\u00A4#,##0.00)", "-1234.56", "(\\u00A51,234.56)", "(CNY1,234.56)", "(\\u4EBA\\u6C11\\u5E011,234.56)"}, + {"zh_CN", "\\u00A4#,##0.00;(\\u00A4#,##0.00)", "1", "\\u00A51.00", "CNY1.00", "\\u4EBA\\u6C11\\u5E011.00"} }; const UChar doubleCurrencySign[] = {0xA4, 0xA4, 0}; @@ -3745,7 +3528,7 @@ NumberFormatTest::TestCurrencyFormatForMixParsing() { "$1,234.56", // string to be parsed "USD1,234.56", "US dollars1,234.56", - "1,234.56 US dollars" + // "1,234.56 US dollars" // Fails in 62 because currency format is not compatible with pattern. }; const CurrencyAmount* curramt = NULL; for (uint32_t i = 0; i < UPRV_LENGTHOF(formats); ++i) { @@ -3774,6 +3557,40 @@ NumberFormatTest::TestCurrencyFormatForMixParsing() { } +/** Starting in ICU 62, strict mode is actually strict with currency formats. */ +void NumberFormatTest::TestMismatchedCurrencyFormatFail() { + IcuTestErrorCode status(*this, "TestMismatchedCurrencyFormatFail"); + LocalPointer df( + dynamic_cast(DecimalFormat::createCurrencyInstance("en", status)), status); + if (!assertSuccess("createCurrencyInstance() failed.", status, true, __FILE__, __LINE__)) {return;} + UnicodeString pattern; + assertEquals("Test assumes that currency sign is at the beginning", + u"\u00A4#,##0.00", + df->toPattern(pattern)); + // Should round-trip on the correct currency format: + expect2(*df, 1.23, u"XXX\u00A01.23"); + df->setCurrency(u"EUR", status); + expect2(*df, 1.23, u"\u20AC1.23"); + // Should parse with currency in the wrong place in lenient mode + df->setLenient(TRUE); + expect(*df, u"1.23\u20AC", 1.23); + expectParseCurrency(*df, u"EUR", 1.23, "1.23\\u20AC"); + // Should NOT parse with currency in the wrong place in STRICT mode + df->setLenient(FALSE); + { + Formattable result; + ErrorCode failStatus; + df->parse(u"1.23\u20AC", result, failStatus); + assertEquals("Should fail to parse", U_INVALID_FORMAT_ERROR, failStatus); + } + { + ParsePosition ppos; + df->parseCurrency(u"1.23\u20AC", ppos); + assertEquals("Should fail to parse currency", 0, ppos.getIndex()); + } +} + + void NumberFormatTest::TestDecimalFormatCurrencyParse() { // Locale.US @@ -3803,12 +3620,15 @@ NumberFormatTest::TestDecimalFormatCurrencyParse() { {"USD1,234.56", "1234.56"}, {"1,234.56 US dollar", "1234.56"}, }; + // NOTE: ICU 62 requires that the currency format match the pattern in strict mode. + fmt->setLenient(TRUE); for (uint32_t i = 0; i < UPRV_LENGTHOF(DATA); ++i) { UnicodeString stringToBeParsed = ctou(DATA[i][0]); double parsedResult = atof(DATA[i][1]); UErrorCode status = U_ZERO_ERROR; Formattable result; fmt->parse(stringToBeParsed, result, status); + logln((UnicodeString)"Input: " + stringToBeParsed + "; output: " + result.getDouble(status)); if (U_FAILURE(status) || (result.getType() == Formattable::kDouble && result.getDouble() != parsedResult) || @@ -3837,8 +3657,8 @@ NumberFormatTest::TestCurrencyIsoPluralFormat() { {"en_US", "-1234.56", "USD", "-$1,234.56", "-USD1,234.56", "-1,234.56 US dollars"}, {"zh_CN", "1", "USD", "US$1.00", "USD1.00", "1.00\\u7F8E\\u5143"}, {"zh_CN", "1234.56", "USD", "US$1,234.56", "USD1,234.56", "1,234.56\\u7F8E\\u5143"}, - {"zh_CN", "1", "CNY", "\\uFFE51.00", "CNY1.00", "1.00\\u4EBA\\u6C11\\u5E01"}, - {"zh_CN", "1234.56", "CNY", "\\uFFE51,234.56", "CNY1,234.56", "1,234.56\\u4EBA\\u6C11\\u5E01"}, + {"zh_CN", "1", "CNY", "\\u00A51.00", "CNY1.00", "1.00\\u4EBA\\u6C11\\u5E01"}, + {"zh_CN", "1234.56", "CNY", "\\u00A51,234.56", "CNY1,234.56", "1,234.56\\u4EBA\\u6C11\\u5E01"}, {"ru_RU", "1", "RUB", "1,00\\u00A0\\u20BD", "1,00\\u00A0RUB", "1,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"}, {"ru_RU", "2", "RUB", "2,00\\u00A0\\u20BD", "2,00\\u00A0RUB", "2,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"}, {"ru_RU", "5", "RUB", "5,00\\u00A0\\u20BD", "5,00\\u00A0RUB", "5,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"}, @@ -3854,12 +3674,14 @@ NumberFormatTest::TestCurrencyIsoPluralFormat() { }; for (int32_t i=0; isetLenient(TRUE); for (int j = 3; j < 6; ++j) { // DATA[i][3] is the currency format result using // CURRENCYSTYLE formatter. @@ -3926,24 +3750,25 @@ NumberFormatTest::TestCurrencyParsing() { // format result using CURRENCYSTYLE, // format result using ISOCURRENCYSTYLE, // format result using PLURALCURRENCYSTYLE, - {"en_US", "1", "USD", "$1.00", "USD1.00", "1.00 US dollar"}, + {"en_US", "1", "USD", "$1.00", "USD\\u00A01.00", "1.00 US dollars"}, {"pa_IN", "1", "USD", "US$\\u00A01.00", "USD\\u00A01.00", "1.00 \\u0a2f\\u0a42.\\u0a10\\u0a38. \\u0a21\\u0a3e\\u0a32\\u0a30"}, {"es_AR", "1", "USD", "US$\\u00A01,00", "USD\\u00A01,00", "1,00 d\\u00f3lar estadounidense"}, {"ar_EG", "1", "USD", "\\u0661\\u066b\\u0660\\u0660\\u00a0US$", "\\u0661\\u066b\\u0660\\u0660\\u00a0USD", "\\u0661\\u066b\\u0660\\u0660 \\u062f\\u0648\\u0644\\u0627\\u0631 \\u0623\\u0645\\u0631\\u064a\\u0643\\u064a"}, - {"fa_CA", "1", "USD", "\\u200e$\\u06f1\\u066b\\u06f0\\u06f0", "\\u200eUSD\\u06f1\\u066b\\u06f0\\u06f0", "\\u200e\\u062f\\u0644\\u0627\\u0631 \\u0627\\u0645\\u0631\\u06cc\\u06a9\\u0627\\u06f1\\u066b\\u06f0\\u06f0"}, - {"he_IL", "1", "USD", "1.00\\u00a0$", "1.00\\u00a0USD", "1.00 \\u05d3\\u05d5\\u05dc\\u05e8 \\u05d0\\u05de\\u05e8\\u05d9\\u05e7\\u05d0\\u05d9"}, - {"hr_HR", "1", "USD", "1,00\\u00a0USD", "1,00\\u00a0USD", "1,00 Ameri\\u010dki dolar"}, - {"id_ID", "1", "USD", "US$1,00", "USD1,00", "1,00 Dolar Amerika Serikat"}, - {"it_IT", "1", "USD", "1,00\\u00a0US$", "1,00\\u00a0USD", "1,00 Dollaro Statunitense"}, - {"ko_KR", "1", "USD", "US$1.00", "USD1.00", "1.00 \\ubbf8\\uad6d \\ub2ec\\ub7ec"}, - {"ja_JP", "1", "USD", "$1.00", "USD1.00", "1.00\\u7c73\\u30c9\\u30eb"}, - {"zh_CN", "1", "CNY", "\\uFFE51.00", "CNY01.00", "1.00\\u4EBA\\u6C11\\u5E01"}, - {"zh_TW", "1", "CNY", "CN\\u00A51.00", "CNY1.00", "1.00 \\u4eba\\u6c11\\u5e63"}, - {"zh_Hant", "1", "CNY", "CN\\u00A51.00", "CNY1.00", "1.00 \\u4eba\\u6c11\\u5e63"}, - {"zh_Hant", "1", "JPY", "\\u00A51.00", "JPY1.00", "1.00 \\u65e5\\u5713"}, - {"ja_JP", "1", "JPY", "\\uFFE51.00", "JPY1.00", "1.00\\u65e5\\u672c\\u5186"}, - {"ja_JP", "1", "JPY", "\\u00A51.00", "JPY1.00", "1.00\\u65e5\\u672c\\u5186"}, - {"ru_RU", "1", "RUB", "1,00\\u00A0\\u20BD", "1,00\\u00A0RUB", "1,00 \\u0420\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u0438\\u0439 \\u0440\\u0443\\u0431\\u043B\\u044C"} + {"fa_CA", "1", "USD", "\\u200e$\\u06f1\\u066b\\u06f0\\u06f0", "\\u200eUSD\\u06f1\\u066b\\u06f0\\u06f0", "\\u06f1\\u066b\\u06f0\\u06f0 \\u062f\\u0644\\u0627\\u0631 \\u0627\\u0645\\u0631\\u06cc\\u06a9\\u0627"}, + {"he_IL", "1", "USD", "\\u200f1.00\\u00a0$", "\\u200f1.00\\u00a0USD", "1.00 \\u05d3\\u05d5\\u05dc\\u05e8 \\u05d0\\u05de\\u05e8\\u05d9\\u05e7\\u05d0\\u05d9"}, + {"hr_HR", "1", "USD", "1,00\\u00a0USD", "1,00\\u00a0USD", "1,00 ameri\\u010Dkih dolara"}, + {"id_ID", "1", "USD", "US$\\u00A01,00", "USD\\u00A01,00", "1,00 Dolar Amerika Serikat"}, + {"it_IT", "1", "USD", "1,00\\u00a0USD", "1,00\\u00a0USD", "1,00 dollari statunitensi"}, + {"ko_KR", "1", "USD", "US$\\u00A01.00", "USD\\u00A01.00", "1.00 \\ubbf8\\uad6d \\ub2ec\\ub7ec"}, + {"ja_JP", "1", "USD", "$1.00", "USD\\u00A01.00", "1.00\\u7c73\\u30c9\\u30eb"}, + {"zh_CN", "1", "CNY", "\\uFFE51.00", "CNY\\u00A001.00", "1.00\\u4EBA\\u6C11\\u5E01"}, + {"zh_TW", "1", "CNY", "CN\\u00A51.00", "CNY\\u00A01.00", "1.00 \\u4eba\\u6c11\\u5e63"}, + {"zh_Hant", "1", "CNY", "CN\\u00A51.00", "CNY\\u00A01.00", "1.00 \\u4eba\\u6c11\\u5e63"}, + {"zh_Hant", "1", "JPY", "\\u00A51.00", "JPY\\u00A01.00", "1 \\u65E5\\u5713"}, + {"ja_JP", "1", "JPY", "\\uFFE51.00", "JPY\\u00A01.00", "1\\u5186"}, + // ICU 62 requires #parseCurrency() to recognize variants when parsing + // {"ja_JP", "1", "JPY", "\\u00A51.00", "JPY\\u00A01.00", "1\\u00A0\\u5186"}, + {"ru_RU", "1", "RUB", "1,00\\u00A0\\u00A0\\u20BD", "1,00\\u00A0\\u00A0RUB", "1,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"} }; static const UNumberFormatStyle currencyStyles[] = { UNUM_CURRENCY, @@ -3971,7 +3796,7 @@ for (;;) { UErrorCode status = U_ZERO_ERROR; NumberFormat* numFmt = NumberFormat::createInstance(locale, k, status); logln("#%d NumberFormat(%s, %s) Currency=%s\n", - i, localeString, currencyStyleNames[kIndex], + i, localeString, currencyStyleNames[kIndex], currencyISOCode); if (U_FAILURE(status)) { @@ -3990,7 +3815,6 @@ for (;;) { UnicodeString strBuf; numFmt->format(numberToBeFormat, strBuf); - /* int resultDataIndex = 3 + kIndex; // DATA[i][resultDataIndex] is the currency format result // using 'k' currency style. @@ -3998,8 +3822,9 @@ for (;;) { if (strBuf.compare(formatResult)) { errln("FAIL: Expected " + formatResult + " actual: " + strBuf); } - */ // test parsing, and test parsing for all currency formats. + // NOTE: ICU 62 requires that the currency format match the pattern in strict mode. + numFmt->setLenient(TRUE); for (int j = 3; j < 6; ++j) { // DATA[i][3] is the currency format result using // CURRENCYSTYLE formatter. @@ -4182,10 +4007,10 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Barbadian Dollar1.00", "Barbadian dollar1.00", "Barbadian dollars1.00", - "Belarusian New Ruble (1994\\u20131999)1.00", + "Belarusian Ruble (1994\\u20131999)1.00", "Belarusian Ruble1.00", - "Belarusian new ruble (1994\\u20131999)1.00", - "Belarusian new rubles (1994\\u20131999)1.00", + "Belarusian ruble (1994\\u20131999)1.00", + "Belarusian rubles (1994\\u20131999)1.00", "Belarusian ruble1.00", "Belarusian rubles1.00", "Belgian Franc (convertible)1.00", @@ -4340,9 +4165,9 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Cypriot Pound1.00", "Cypriot pound1.00", "Cypriot pounds1.00", - "Czech Republic Koruna1.00", - "Czech Republic koruna1.00", - "Czech Republic korunas1.00", + "Czech Koruna1.00", + "Czech koruna1.00", + "Czech korunas1.00", "Czechoslovak Hard Koruna1.00", "Czechoslovak hard koruna1.00", "Czechoslovak hard korunas1.00", @@ -4569,7 +4394,7 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Irish pound1.00", "Irish pounds1.00", "Israeli Pound1.00", - "Israeli new sheqel1.00", + "Israeli new shekel1.00", "Israeli pound1.00", "Israeli pounds1.00", "Italian Lira1.00", @@ -4796,8 +4621,8 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Dutch Guilder1.00", "Dutch guilder1.00", "Dutch guilders1.00", - "Israeli New Sheqel1.00", - "Israeli New Sheqels1.00", + "Israeli New Shekel1.00", + "Israeli New Shekels1.00", "New Zealand Dollar1.00", "New Zealand dollar1.00", "New Zealand dollars1.00", @@ -4870,17 +4695,17 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Paraguayan guarani1.00", "Paraguayan guaranis1.00", "Peruvian Inti1.00", - "Peruvian Nuevo Sol1.00", + "Peruvian Sol1.00", "Peruvian Sol (1863\\u20131965)1.00", "Peruvian inti1.00", "Peruvian intis1.00", - "Peruvian nuevo sol1.00", - "Peruvian nuevos soles1.00", + "Peruvian sol1.00", + "Peruvian soles1.00", "Peruvian sol (1863\\u20131965)1.00", "Peruvian soles (1863\\u20131965)1.00", - "Philippine Peso1.00", - "Philippine peso1.00", - "Philippine pesos1.00", + "Philippine Piso1.00", + "Philippine piso1.00", + "Philippine pisos1.00", "Platinum1.00", "Platinum1.00", "Polish Zloty (1950\\u20131995)1.00", @@ -5341,10 +5166,10 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "1.00 Barbadian Dollar random", "1.00 Barbadian dollar random", "1.00 Barbadian dollars random", - "1.00 Belarusian New Ruble (1994\\u20131999) random", + "1.00 Belarusian Ruble (1994\\u20131999) random", "1.00 Belarusian Ruble random", - "1.00 Belarusian new ruble (1994\\u20131999) random", - "1.00 Belarusian new rubles (1994\\u20131999) random", + "1.00 Belarusian ruble (1994\\u20131999) random", + "1.00 Belarusian rubles (1994\\u20131999) random", "1.00 Belarusian ruble random", "1.00 Belarusian rubles random", "1.00 Belgian Franc (convertible) random", @@ -5463,9 +5288,9 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "1.00 Cypriot Pound random", "1.00 Cypriot pound random", "1.00 Cypriot pounds random", - "1.00 Czech Republic Koruna random", - "1.00 Czech Republic koruna random", - "1.00 Czech Republic korunas random", + "1.00 Czech Koruna random", + "1.00 Czech koruna random", + "1.00 Czech korunas random", "1.00 Czechoslovak Hard Koruna random", "1.00 Czechoslovak hard koruna random", "1.00 Czechoslovak hard korunas random", @@ -5612,7 +5437,7 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "1.00 Irish pound random", "1.00 Irish pounds random", "1.00 Israeli Pound random", - "1.00 Israeli new sheqel random", + "1.00 Israeli new shekel random", "1.00 Israeli pound random", "1.00 Israeli pounds random", "1.00 Italian Lira random", @@ -5752,8 +5577,8 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "1.00 Dutch Guilder random", "1.00 Dutch guilder random", "1.00 Dutch guilders random", - "1.00 Israeli New Sheqel random", - "1.00 Israeli new sheqels random", + "1.00 Israeli New Shekel random", + "1.00 Israeli new shekels random", "1.00 New Zealand Dollar random", "1.00 New Zealand dollar random", "1.00 New Zealand dollars random", @@ -5807,17 +5632,17 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "1.00 Paraguayan guarani random", "1.00 Paraguayan guaranis random", "1.00 Peruvian Inti random", - "1.00 Peruvian Nuevo Sol random", + "1.00 Peruvian Sol random", "1.00 Peruvian Sol (1863\\u20131965) random", "1.00 Peruvian inti random", "1.00 Peruvian intis random", - "1.00 Peruvian nuevo sol random", - "1.00 Peruvian nuevos soles random", + "1.00 Peruvian sol random", + "1.00 Peruvian soles random", "1.00 Peruvian sol (1863\\u20131965) random", "1.00 Peruvian soles (1863\\u20131965) random", - "1.00 Philippine Peso random", - "1.00 Philippine peso random", - "1.00 Philippine pesos random", + "1.00 Philippine Piso random", + "1.00 Philippine piso random", + "1.00 Philippine pisos random", "1.00 Platinum random", "1.00 Platinum random", "1.00 Polish Zloty (1950\\u20131995) random", @@ -6133,7 +5958,7 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Bangladeshi Tak1.00", "Barbadian Dolla1.00", "Bds1.00", - "Belarusian New Ruble (1994\\u201319991.00", + "Belarusian Ruble (1994\\u201319991.00", "Belarusian Rubl1.00", "Belgian Fran1.00", "Belgian Franc (convertible1.00", @@ -6583,26 +6408,24 @@ NumberFormatTest::TestParseCurrencyInUCurr() { Locale locale("en_US"); for (uint32_t i=0; i currAmt(numFmt->parseCurrency(formatted, parsePos)); - if (parsePos.getIndex() > 0) { - double doubleVal = currAmt->getNumber().getDouble(status); - if ( doubleVal != 1.0 ) { - errln("Parsed as currency value other than 1.0: " + formatted + " -> " + doubleVal); - } - } else { - errln("Failed to parse as currency: " + formatted); - } - } else { - dataerrln("Unable to create NumberFormat. - %s", u_errorName(status)); - delete numFmt; - break; - } - delete numFmt; + UnicodeString formatted = ctou(DATA[i]); + UErrorCode status = U_ZERO_ERROR; + LocalPointer numFmt(NumberFormat::createInstance(locale, UNUM_CURRENCY, status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { + return; + } + // NOTE: ICU 62 requires that the currency format match the pattern in strict mode. + numFmt->setLenient(TRUE); + ParsePosition parsePos; + LocalPointer currAmt(numFmt->parseCurrency(formatted, parsePos)); + if (parsePos.getIndex() > 0) { + double doubleVal = currAmt->getNumber().getDouble(status); + if ( doubleVal != 1.0 ) { + errln("Parsed as currency value other than 1.0: " + formatted + " -> " + doubleVal); + } + } else { + errln("Failed to parse as currency: " + formatted); + } } for (uint32_t i=0; iparse(input, result, status); ASSERT_SUCCESS(status); - ASSERT_EQUALS(0, strcmp("0.0184", result.getDecimalNumber(status).data())); + ASSERT_EQUALS("0.0184", result.getDecimalNumber(status).data()); //std::cout << result.getDecimalNumber(status).data(); delete fmtr; } } - + #if U_PLATFORM != U_PF_CYGWIN || defined(CYGWINMSVC) /* * This test fails on Cygwin (1.7.16) using GCC because of a rounding issue with strtod(). @@ -6995,42 +6814,42 @@ void NumberFormatTest::TestCurrencyFractionDigits() { errln((UnicodeString)"NumberFormat::format() should return the same result - text1=" + text1 + " text2=" + text2); } - delete fmt; } + delete fmt; } -void NumberFormatTest::TestExponentParse() { - - UErrorCode status = U_ZERO_ERROR; - Formattable result; - ParsePosition parsePos(0); - - // set the exponent symbol - status = U_ZERO_ERROR; - DecimalFormatSymbols *symbols = new DecimalFormatSymbols(Locale::getDefault(), status); - if(U_FAILURE(status)) { - dataerrln((UnicodeString)"ERROR: Could not create DecimalFormatSymbols (Default)"); - return; - } - - // create format instance - status = U_ZERO_ERROR; - DecimalFormat fmt("#####", symbols, status); - if(U_FAILURE(status)) { - errln((UnicodeString)"ERROR: Could not create DecimalFormat (pattern, symbols*)"); - } - - // parse the text - fmt.parse("5.06e-27", result, parsePos); - if(result.getType() != Formattable::kDouble && - result.getDouble() != 5.06E-27 && - parsePos.getIndex() != 8 - ) - { - errln("ERROR: parse failed - expected 5.06E-27, 8 - returned %d, %i", - result.getDouble(), parsePos.getIndex()); - } -} +void NumberFormatTest::TestExponentParse() { + + UErrorCode status = U_ZERO_ERROR; + Formattable result; + ParsePosition parsePos(0); + + // set the exponent symbol + status = U_ZERO_ERROR; + DecimalFormatSymbols symbols(Locale::getDefault(), status); + if(U_FAILURE(status)) { + dataerrln((UnicodeString)"ERROR: Could not create DecimalFormatSymbols (Default)"); + return; + } + + // create format instance + status = U_ZERO_ERROR; + DecimalFormat fmt(u"#####", symbols, status); + if(U_FAILURE(status)) { + errln((UnicodeString)"ERROR: Could not create DecimalFormat (pattern, symbols*)"); + } + + // parse the text + fmt.parse("5.06e-27", result, parsePos); + if(result.getType() != Formattable::kDouble && + result.getDouble() != 5.06E-27 && + parsePos.getIndex() != 8 + ) + { + errln("ERROR: parse failed - expected 5.06E-27, 8 - returned %d, %i", + result.getDouble(), parsePos.getIndex()); + } +} void NumberFormatTest::TestExplicitParents() { @@ -7113,13 +6932,13 @@ NumberFormatTest::Test9087(void) { U_STRING_DECL(pattern,"#",1); U_STRING_INIT(pattern,"#",1); - + U_STRING_DECL(infstr,"INF",3); U_STRING_INIT(infstr,"INF",3); U_STRING_DECL(nanstr,"NAN",3); U_STRING_INIT(nanstr,"NAN",3); - + UChar outputbuf[50] = {0}; UErrorCode status = U_ZERO_ERROR; UNumberFormat* fmt = unum_open(UNUM_PATTERN_DECIMAL,pattern,1,NULL,NULL,&status); @@ -7141,7 +6960,7 @@ NumberFormatTest::Test9087(void) UFieldPosition position = { 0, 0, 0}; unum_formatDouble(fmt,inf,outputbuf,50,&position,&status); - + if ( u_strcmp(infstr, outputbuf)) { errln((UnicodeString)"FAIL: unexpected result for infinity - expected " + infstr + " got " + outputbuf); } @@ -7149,25 +6968,11 @@ NumberFormatTest::Test9087(void) unum_close(fmt); } -#include "dcfmtimp.h" - void NumberFormatTest::TestFormatFastpaths() { -#if UCONFIG_FORMAT_FASTPATHS_49 - logln("Sizeof DecimalFormat = %d, Sizeof DecimalFormatInternal=%d, UNUM_DECIMALFORMAT_INTERNAL_SIZE=%d\n", - sizeof(DecimalFormat), sizeof(DecimalFormatInternal), UNUM_DECIMALFORMAT_INTERNAL_SIZE); - if(UNUM_DECIMALFORMAT_INTERNAL_SIZE < sizeof(DecimalFormatInternal)) { - errln("Error: sizeof(DecimalFormatInternal)=%d but UNUM_DECIMALFORMAT_INTERNAL_SIZE is only %d. Increase the #define?\n", sizeof(DecimalFormatInternal), UNUM_DECIMALFORMAT_INTERNAL_SIZE); - } else if(UNUM_DECIMALFORMAT_INTERNAL_SIZE > (sizeof(DecimalFormatInternal)+16)) { - infoln("Note: sizeof(DecimalFormatInternal)=%d but UNUM_DECIMALFORMAT_INTERNAL_SIZE is %d. Decrease the #define? sizeof(DecimalFormat)=%d\n", sizeof(DecimalFormatInternal), UNUM_DECIMALFORMAT_INTERNAL_SIZE, sizeof(DecimalFormat)); - } -#else - infoln("NOTE: UCONFIG_FORMAT_FASTPATHS not set, test skipped."); -#endif - // get some additional case { UErrorCode status=U_ZERO_ERROR; - DecimalFormat df(UnicodeString("0000",""),status); + DecimalFormat df(UnicodeString(u"0000"),status); if (U_FAILURE(status)) { dataerrln("Error creating DecimalFormat - %s", u_errorName(status)); } else { @@ -7177,15 +6982,16 @@ void NumberFormatTest::TestFormatFastpaths() { FieldPosition pos; df.format(long_number, result, pos); if(U_FAILURE(status)||expect!=result) { - errcheckln(status, "FAIL: expected '"+expect+"' got '"+result+"' status "+UnicodeString(u_errorName(status),"")); - } else { + dataerrln("%s:%d FAIL: expected '%s' got '%s' status %s", + __FILE__, __LINE__, CStr(expect)(), CStr(result)(), u_errorName(status)); + } else { logln("OK: got expected '"+result+"' status "+UnicodeString(u_errorName(status),"")); } } } { UErrorCode status=U_ZERO_ERROR; - DecimalFormat df(UnicodeString("0000000000000000000",""),status); + DecimalFormat df(UnicodeString(u"0000000000000000000"),status); if (U_FAILURE(status)) { dataerrln("Error creating DecimalFormat - %s", u_errorName(status)); } else { @@ -7200,7 +7006,8 @@ void NumberFormatTest::TestFormatFastpaths() { FieldPosition pos; df.format(long_number, result, pos); if(U_FAILURE(status)||expect!=result) { - errcheckln(status, "FAIL: expected '"+expect+"' got '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on -9223372036854775808"); + dataerrln("%s:%d FAIL: expected '%s' got '%s' status %s on -9223372036854775808", + __FILE__, __LINE__, CStr(expect)(), CStr(result)(), u_errorName(status)); } else { logln("OK: got expected '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on -9223372036854775808"); } @@ -7208,7 +7015,7 @@ void NumberFormatTest::TestFormatFastpaths() { } { UErrorCode status=U_ZERO_ERROR; - DecimalFormat df(UnicodeString("0000000000000000000",""),status); + DecimalFormat df(UnicodeString(u"0000000000000000000"),status); if (U_FAILURE(status)) { dataerrln("Error creating DecimalFormat - %s", u_errorName(status)); } else { @@ -7223,7 +7030,8 @@ void NumberFormatTest::TestFormatFastpaths() { FieldPosition pos; df.format(long_number, result, pos); if(U_FAILURE(status)||expect!=result) { - errcheckln(status, "FAIL: expected '"+expect+"' got '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on U_INT64_MAX"); + dataerrln("%s:%d FAIL: expected '%s' got '%s' status %s on U_INT64_MAX", + __FILE__, __LINE__, CStr(expect)(), CStr(result)(), u_errorName(status)); } else { logln("OK: got expected '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on U_INT64_MAX"); } @@ -7246,7 +7054,8 @@ void NumberFormatTest::TestFormatFastpaths() { FieldPosition pos; df.format(long_number, result, pos); if(U_FAILURE(status)||expect!=result) { - errcheckln(status, "FAIL: expected '"+expect+"' got '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on 0"); + dataerrln("%s:%d FAIL: expected '%s' got '%s' status %s on 0", + __FILE__, __LINE__, CStr(expect)(), CStr(result)(), u_errorName(status)); } else { logln("OK: got expected '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on 0"); } @@ -7264,7 +7073,8 @@ void NumberFormatTest::TestFormatFastpaths() { FieldPosition pos; df.format(long_number, result, pos); if(U_FAILURE(status)||expect!=result) { - errcheckln(status, "FAIL: expected '"+expect+"' got '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on -9223372036854775807"); + dataerrln("%s:%d FAIL: expected '%s' got '%s' status %s on -9223372036854775807", + __FILE__, __LINE__, CStr(expect)(), CStr(result)(), u_errorName(status)); } else { logln("OK: got expected '"+result+"' status "+UnicodeString(u_errorName(status),"")+" on -9223372036854775807"); } @@ -7273,7 +7083,7 @@ void NumberFormatTest::TestFormatFastpaths() { } -void NumberFormatTest::TestFormattableSize(void) { +void NumberFormatTest::TestFormattableSize(void) { // test ICU 61 behavior if(sizeof(FmtStackData) > UNUM_INTERNAL_STACKARRAY_SIZE) { errln("Error: sizeof(FmtStackData)=%d, UNUM_INTERNAL_STACKARRAY_SIZE=%d\n", sizeof(FmtStackData), UNUM_INTERNAL_STACKARRAY_SIZE); @@ -7281,8 +7091,8 @@ void NumberFormatTest::TestFormattableSize(void) { logln("Warning: sizeof(FmtStackData)=%d, UNUM_INTERNAL_STACKARRAY_SIZE=%d\n", sizeof(FmtStackData), UNUM_INTERNAL_STACKARRAY_SIZE); } else { - logln("sizeof(FmtStackData)=%d, UNUM_INTERNAL_STACKARRAY_SIZE=%d\n", - sizeof(FmtStackData), UNUM_INTERNAL_STACKARRAY_SIZE); + logln("sizeof(Formattable)=%d, 112=%d\n", + sizeof(Formattable), 112); } } @@ -7425,9 +7235,9 @@ UBool NumberFormatTest::testFormattableAsUFormattable(const char *file, int line UErrorCode int64ConversionU = U_ZERO_ERROR; int64_t r = ufmt_getInt64(u, &int64ConversionU); - if( (l==r) + if( (l==r) && ( uType != UFMT_INT64 ) // int64 better not overflow - && (U_INVALID_FORMAT_ERROR==int64ConversionU) + && (U_INVALID_FORMAT_ERROR==int64ConversionU) && (U_INVALID_FORMAT_ERROR==int64ConversionF) ) { logln("%s:%d: OK: 64 bit overflow", file, line); } else { @@ -7552,7 +7362,7 @@ void NumberFormatTest::TestSignificantDigits(void) { numberFormat->setMinimumSignificantDigits(3); numberFormat->setMaximumSignificantDigits(5); numberFormat->setGroupingUsed(false); - + UnicodeString result; UnicodeString expectedResult; for (unsigned int i = 0; i < UPRV_LENGTHOF(input); ++i) { @@ -7574,7 +7384,7 @@ void NumberFormatTest::TestShowZero() { numberFormat->setSignificantDigitsUsed(TRUE); numberFormat->setMaximumSignificantDigits(3); - + UnicodeString result; numberFormat->format(0.0, result); if (result != "0") { @@ -7591,7 +7401,7 @@ void NumberFormatTest::TestBug9936() { dataerrln("File %s, Line %d: status = %s.\n", __FILE__, __LINE__, u_errorName(status)); return; } - + if (numberFormat->areSignificantDigitsUsed() == TRUE) { errln("File %s, Line %d: areSignificantDigitsUsed() was TRUE, expected FALSE.\n", __FILE__, __LINE__); } @@ -7615,7 +7425,7 @@ void NumberFormatTest::TestBug9936() { if (numberFormat->areSignificantDigitsUsed() == FALSE) { errln("File %s, Line %d: areSignificantDigitsUsed() was FALSE, expected TRUE.\n", __FILE__, __LINE__); } - + } void NumberFormatTest::TestParseNegativeWithFaLocale() { @@ -7706,7 +7516,7 @@ void NumberFormatTest::TestParseSignsAndMarks() { { "en@numbers=arabext", FALSE, CharsToUnicodeString("\\u200E-\\u200E\\u06F6\\u06F7"), -67 }, { "en@numbers=arabext", TRUE, CharsToUnicodeString("\\u200E-\\u200E\\u06F6\\u06F7"), -67 }, { "en@numbers=arabext", TRUE, CharsToUnicodeString("\\u200E-\\u200E \\u06F6\\u06F7"), -67 }, - + { "he", FALSE, CharsToUnicodeString("12"), 12 }, { "he", TRUE, CharsToUnicodeString("12"), 12 }, { "he", FALSE, CharsToUnicodeString("-23"), -23 }, @@ -7826,19 +7636,14 @@ void NumberFormatTest::Test10468ApplyPattern() { return; } - if (fmt.getPadCharacterString() != UnicodeString("a")) { - errln("Padding character should be 'a'."); - return; - } + assertEquals("Padding character should be 'a'.", u"a", fmt.getPadCharacterString()); // Padding char of fmt ought to be '*' since that is the default and no // explicit padding char is specified in the new pattern. fmt.applyPattern("AA#,##0.00ZZ", status); - // Oops this still prints 'a' even though we changed the pattern. - if (fmt.getPadCharacterString() != UnicodeString(" ")) { - errln("applyPattern did not clear padding character."); - } + // Oops this still prints 'a' even though we changed the pattern. + assertEquals("applyPattern did not clear padding character.", u" ", fmt.getPadCharacterString()); } void NumberFormatTest::TestRoundingScientific10542() { @@ -7848,7 +7653,7 @@ void NumberFormatTest::TestRoundingScientific10542() { errcheckln(status, "DecimalFormat constructor failed - %s", u_errorName(status)); return; } - + DecimalFormat::ERoundingMode roundingModes[] = { DecimalFormat::kRoundCeiling, DecimalFormat::kRoundDown, @@ -7865,7 +7670,7 @@ void NumberFormatTest::TestRoundingScientific10542() { "Round half even", "Round half up", "Round up"}; - + { double values[] = {-0.003006, -0.003005, -0.003004, 0.003014, 0.003015, 0.003016}; // The order of these expected values correspond to the order of roundingModes and the order of values. @@ -8014,12 +7819,12 @@ void NumberFormatTest::TestRoundingScientific10542() { void NumberFormatTest::TestZeroScientific10547() { UErrorCode status = U_ZERO_ERROR; DecimalFormat fmt("0.00E0", status); - if (!assertSuccess("Formt creation", status)) { + if (!assertSuccess("Format creation", status)) { return; } UnicodeString out; fmt.format(-0.0, out); - assertEquals("format", "-0.00E0", out); + assertEquals("format", "-0.00E0", out, true); } void NumberFormatTest::verifyRounding( @@ -8038,13 +7843,8 @@ void NumberFormatTest::verifyRounding( UnicodeString actual; format.format(values[j], actual); if (currentExpected != actual) { - char buffer[256]; - sprintf( - buffer, - "For %s value %f, expected ", - descriptions[i], - values[j]); - errln(UnicodeString(buffer) + currentExpected + ", got " + actual); + dataerrln("For %s value %f, expected '%s', got '%s'", + descriptions[i], values[j], CStr(currentExpected)(), CStr(actual)()); } } } @@ -8055,44 +7855,42 @@ void NumberFormatTest::TestAccountingCurrency() { UNumberFormatStyle style = UNUM_CURRENCY_ACCOUNTING; expect(NumberFormat::createInstance("en_US", style, status), - (Formattable)1234.5, "$1,234.50", TRUE, status); + (Formattable)(double)1234.5, "$1,234.50", TRUE, status); expect(NumberFormat::createInstance("en_US", style, status), - (Formattable)-1234.5, "($1,234.50)", TRUE, status); + (Formattable)(double)-1234.5, "($1,234.50)", TRUE, status); expect(NumberFormat::createInstance("en_US", style, status), - (Formattable)0, "$0.00", TRUE, status); + (Formattable)(double)0, "$0.00", TRUE, status); expect(NumberFormat::createInstance("en_US", style, status), - (Formattable)-0.2, "($0.20)", TRUE, status); + (Formattable)(double)-0.2, "($0.20)", TRUE, status); expect(NumberFormat::createInstance("ja_JP", style, status), (Formattable)10000, UnicodeString("\\u00A510,000").unescape(), TRUE, status); expect(NumberFormat::createInstance("ja_JP", style, status), (Formattable)-1000.5, UnicodeString("(\\u00A51,000)").unescape(), FALSE, status); expect(NumberFormat::createInstance("de_DE", style, status), - (Formattable)-23456.7, UnicodeString("-23.456,70\\u00A0\\u20AC").unescape(), TRUE, status); + (Formattable)(double)-23456.7, UnicodeString("-23.456,70\\u00A0\\u20AC").unescape(), TRUE, status); } // for #5186 void NumberFormatTest::TestEquality() { UErrorCode status = U_ZERO_ERROR; - DecimalFormatSymbols* symbols = new DecimalFormatSymbols(Locale("root"), status); + DecimalFormatSymbols symbols(Locale("root"), status); if (U_FAILURE(status)) { dataerrln("Fail: can't create DecimalFormatSymbols for root"); return; } UnicodeString pattern("#,##0.###"); - DecimalFormat* fmtBase = new DecimalFormat(pattern, symbols, status); + DecimalFormat fmtBase(pattern, symbols, status); if (U_FAILURE(status)) { dataerrln("Fail: can't create DecimalFormat using root symbols"); return; } - DecimalFormat* fmtClone = (DecimalFormat*)fmtBase->clone(); - fmtClone->setFormatWidth(fmtBase->getFormatWidth() + 32); - if (*fmtClone == *fmtBase) { + DecimalFormat* fmtClone = (DecimalFormat*)fmtBase.clone(); + fmtClone->setFormatWidth(fmtBase.getFormatWidth() + 32); + if (*fmtClone == fmtBase) { errln("Error: DecimalFormat == does not distinguish objects that differ only in FormatWidth"); } delete fmtClone; - - delete fmtBase; } void NumberFormatTest::TestCurrencyUsage() { @@ -8118,11 +7916,11 @@ void NumberFormatTest::TestCurrencyUsage() { UnicodeString original; fmt->format(agent,original); - assertEquals("Test Currency Usage 1", UnicodeString("PKR124"), original); + assertEquals("Test Currency Usage 1", u"PKR124", original); // use ICU 61 behavior // test the getter here UCurrencyUsage curUsage = fmt->getCurrencyUsage(); - assertEquals("Test usage getter - standard", curUsage, UCURR_USAGE_STANDARD); + assertEquals("Test usage getter - standard", (int32_t)curUsage, (int32_t)UCURR_USAGE_STANDARD); fmt->setCurrencyUsage(UCURR_USAGE_CASH, &status); }else{ @@ -8134,11 +7932,11 @@ void NumberFormatTest::TestCurrencyUsage() { // must be usage = cash UCurrencyUsage curUsage = fmt->getCurrencyUsage(); - assertEquals("Test usage getter - cash", curUsage, UCURR_USAGE_CASH); + assertEquals("Test usage getter - cash", (int32_t)curUsage, (int32_t)UCURR_USAGE_CASH); UnicodeString cash_currency; fmt->format(agent,cash_currency); - assertEquals("Test Currency Usage 2", UnicodeString("PKR124"), cash_currency); + assertEquals("Test Currency Usage 2", u"PKR124", cash_currency); // use ICU 61 behavior delete fmt; } @@ -8155,10 +7953,10 @@ void NumberFormatTest::TestCurrencyUsage() { UnicodeString original_rounding; fmt->format(agent, original_rounding); - assertEquals("Test Currency Usage 3", UnicodeString("CA$123.57"), original_rounding); + assertEquals("Test Currency Usage 3", u"CA$123.57", original_rounding); fmt->setCurrencyUsage(UCURR_USAGE_CASH, &status); }else{ - fmt = (DecimalFormat *) NumberFormat::createInstance(enUS_CAD, UNUM_CASH_CURRENCY, status); + fmt = (DecimalFormat *) NumberFormat::createInstance(enUS_CAD, UNUM_CASH_CURRENCY, status); if (assertSuccess("en_US@currency=CAD/CASH", status, TRUE) == FALSE) { continue; } @@ -8166,7 +7964,7 @@ void NumberFormatTest::TestCurrencyUsage() { UnicodeString cash_rounding_currency; fmt->format(agent, cash_rounding_currency); - assertEquals("Test Currency Usage 4", UnicodeString("CA$123.55"), cash_rounding_currency); + assertEquals("Test Currency Usage 4", u"CA$123.55", cash_rounding_currency); delete fmt; } @@ -8191,83 +7989,18 @@ void NumberFormatTest::TestCurrencyUsage() { UnicodeString cur_original; fmt->setCurrencyUsage(UCURR_USAGE_STANDARD, &status); fmt->format(agent, cur_original); - assertEquals("Test Currency Usage 5", UnicodeString("CA$123.57"), cur_original); + assertEquals("Test Currency Usage 5", u"CA$123.57", cur_original); fmt->setCurrency(CUR_PKR, status); assertSuccess("Set currency to PKR", status); UnicodeString PKR_changed; fmt->format(agent, PKR_changed); - assertEquals("Test Currency Usage 6", UnicodeString("PKR124"), PKR_changed); + assertEquals("Test Currency Usage 6", u"PKR124", PKR_changed); // use ICU 61 behavior delete fmt; } } -void NumberFormatTest::TestNumberFormatTestTuple() { - NumberFormatTestTuple tuple; - UErrorCode status = U_ZERO_ERROR; - - tuple.setField( - NumberFormatTestTuple::getFieldByName("locale"), - "en", - status); - tuple.setField( - NumberFormatTestTuple::getFieldByName("pattern"), - "#,##0.00", - status); - tuple.setField( - NumberFormatTestTuple::getFieldByName("minIntegerDigits"), - "-10", - status); - if (!assertSuccess("", status)) { - return; - } - - // only what we set should be set. - assertEquals("", "en", tuple.locale.getName()); - assertEquals("", "#,##0.00", tuple.pattern); - assertEquals("", -10, tuple.minIntegerDigits); - assertTrue("", tuple.localeFlag); - assertTrue("", tuple.patternFlag); - assertTrue("", tuple.minIntegerDigitsFlag); - assertFalse("", tuple.formatFlag); - - UnicodeString appendTo; - assertEquals( - "", - "{locale: en, pattern: #,##0.00, minIntegerDigits: -10}", - tuple.toString(appendTo)); - - tuple.clear(); - appendTo.remove(); - assertEquals( - "", - "{}", - tuple.toString(appendTo)); - tuple.setField( - NumberFormatTestTuple::getFieldByName("aBadFieldName"), - "someValue", - status); - if (status != U_ILLEGAL_ARGUMENT_ERROR) { - errln("Expected U_ILLEGAL_ARGUMENT_ERROR"); - } - status = U_ZERO_ERROR; - tuple.setField( - NumberFormatTestTuple::getFieldByName("minIntegerDigits"), - "someBadValue", - status); - if (status != U_ILLEGAL_ARGUMENT_ERROR) { - errln("Expected U_ILLEGAL_ARGUMENT_ERROR"); - } -} - -void -NumberFormatTest::TestDataDriven() { - NumberFormatTestDataDriven dd; - dd.setCaller(this); - dd.run("numberformattestspecification.txt", FALSE); -} - // Check the constant MAX_INT64_IN_DOUBLE. // The value should convert to a double with no loss of precision. @@ -8285,7 +8018,7 @@ void NumberFormatTest::TestDoubleLimit11439() { sprintf(buf, "%lld", (long long)num); double fNum = 0.0; sscanf(buf, "%lf", &fNum); - int64_t rtNum = fNum; + int64_t rtNum = static_cast(fNum); if (num != rtNum) { errln("%s:%d MAX_INT64_IN_DOUBLE test, %lld did not round trip. Got %lld", __FILE__, __LINE__, (long long)num, (long long)rtNum); return; @@ -8295,7 +8028,7 @@ void NumberFormatTest::TestDoubleLimit11439() { sprintf(buf, "%lld", (long long)num); double fNum = 0.0; sscanf(buf, "%lf", &fNum); - int64_t rtNum = fNum; + int64_t rtNum = static_cast(fNum); if (num != rtNum) { errln("%s:%d MAX_INT64_IN_DOUBLE test, %lld did not round trip. Got %lld", __FILE__, __LINE__, (long long)num, (long long)rtNum); return; @@ -8303,21 +8036,6 @@ void NumberFormatTest::TestDoubleLimit11439() { } } -void NumberFormatTest::TestFastPathConsistent11524() { - UErrorCode status = U_ZERO_ERROR; - NumberFormat *fmt = NumberFormat::createInstance("en", status); - if (U_FAILURE(status) || fmt == NULL) { - dataerrln("Failed call to NumberFormat::createInstance() - %s", u_errorName(status)); - return; - } - fmt->setMaximumIntegerDigits(INT32_MIN); - UnicodeString appendTo; - assertEquals("", "0", fmt->format(123, appendTo)); - appendTo.remove(); - assertEquals("", "0", fmt->format(12345, appendTo)); - delete fmt; -} - void NumberFormatTest::TestGetAffixes() { UErrorCode status = U_ZERO_ERROR; DecimalFormatSymbols sym("en_US", status); @@ -8400,7 +8118,7 @@ void NumberFormatTest::TestBenchmark() { DecimalFormat fmt("0.0000000", new DecimalFormatSymbols(sym), status); // DecimalFormat fmt("0.00000E0", new DecimalFormatSymbols(sym), status); // DecimalFormat fmt("0", new DecimalFormatSymbols(sym), status); - FieldPosition fpos(0); + FieldPosition fpos(FieldPosition::DONT_CARE); clock_t start = clock(); for (int32_t i = 0; i < 1000000; ++i) { UnicodeString append; @@ -8415,7 +8133,7 @@ void NumberFormatTest::TestBenchmark() { UErrorCode status = U_ZERO_ERROR; MessageFormat fmt("{0, plural, one {I have # friend.} other {I have # friends.}}", status); - FieldPosition fpos(0); + FieldPosition fpos(FieldPosition::DONT_CARE); Formattable one(1.0); Formattable three(3.0); clock_t start = clock(); @@ -8432,7 +8150,7 @@ void NumberFormatTest::TestBenchmark() { Locale en("en"); Measure measureC(23, MeasureUnit::createCelsius(status), status); MeasureFormat fmt(en, UMEASFMT_WIDTH_WIDE, status); - FieldPosition fpos(0); + FieldPosition fpos(FieldPosition::DONT_CARE); clock_t start = clock(); for (int32_t i = 0; i < 1000000; ++i) { UnicodeString appendTo; @@ -8487,13 +8205,13 @@ void NumberFormatTest::TestCtorApplyPatternDifference() { assertEquals( "ctor favors precision of currency", "$5.00", - fmt.format(5, result)); + fmt.format((double)5, result)); result.remove(); fmt.applyPattern(pattern.unescape(), status); assertEquals( "applyPattern favors precision of pattern", "$5", - fmt.format(5, result)); + fmt.format((double)5, result)); } void NumberFormatTest::Test11868() { @@ -8556,11 +8274,51 @@ void NumberFormatTest::Test11868() { } void NumberFormatTest::Test10727_RoundingZero() { - DigitList d; - d.set(-0.0); - assertFalse("", d.isPositive()); - d.round(3); - assertFalse("", d.isPositive()); + IcuTestErrorCode status(*this, "Test10727_RoundingZero"); + DecimalQuantity dq; + dq.setToDouble(-0.0); + assertTrue("", dq.isNegative()); + dq.roundToMagnitude(0, UNUM_ROUND_HALFEVEN, status); + assertTrue("", dq.isNegative()); +} + +void NumberFormatTest::Test11739_ParseLongCurrency() { + IcuTestErrorCode status(*this, "Test11739_ParseLongCurrency"); + LocalPointer nf(NumberFormat::createCurrencyInstance("sr_BA", status)); + if (status.errDataIfFailureAndReset()) { return; } + ((DecimalFormat*) nf.getAlias())->applyPattern(u"#,##0.0 ¤¤¤", status); + ParsePosition ppos(0); + LocalPointer result(nf->parseCurrency(u"1.500 амерички долар", ppos)); + assertEquals("Should parse to 1500 USD", -1, ppos.getErrorIndex()); + assertEquals("Should parse to 1500 USD", 1500LL, result->getNumber().getInt64(status)); + assertEquals("Should parse to 1500 USD", u"USD", result->getISOCurrency()); +} + +void NumberFormatTest::Test13035_MultiCodePointPaddingInPattern() { + IcuTestErrorCode status(*this, "Test13035_MultiCodePointPaddingInPattern"); + DecimalFormat df(u"a*'நி'###0b", status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + UnicodeString result; + df.format(12, result.remove()); + // TODO(13034): Re-enable this test when support is added in ICU4C. + //assertEquals("Multi-codepoint padding should not be split", u"aநிநி12b", result); + df = DecimalFormat(u"a*\U0001F601###0b", status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + result = df.format(12, result.remove()); + assertEquals("Single-codepoint padding should not be split", u"a\U0001F601\U0001F60112b", result, true); + df = DecimalFormat(u"a*''###0b", status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + result = df.format(12, result.remove()); + assertEquals("Quote should be escapable in padding syntax", "a''12b", result, true); +} + +void NumberFormatTest::Test13737_ParseScientificStrict() { + IcuTestErrorCode status(*this, "Test13737_ParseScientificStrict"); + LocalPointer df(NumberFormat::createScientificInstance("en", status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) {return;} + df->setLenient(FALSE); + // Parse Test + expect(*df, u"1.2", 1.2); } void NumberFormatTest::Test11376_getAndSetPositivePrefix() { @@ -8575,7 +8333,7 @@ void NumberFormatTest::Test11376_getAndSetPositivePrefix() { DecimalFormat *dfmt = (DecimalFormat *) fmt.getAlias(); dfmt->setCurrency(USD); UnicodeString result; - + // This line should be a no-op. I am setting the positive prefix // to be the same thing it was before. dfmt->setPositivePrefix(dfmt->getPositivePrefix(result)); @@ -8596,7 +8354,7 @@ void NumberFormatTest::Test11376_getAndSetPositivePrefix() { UnicodeString result; UnicodeString tripleIntlCurrency(" \\u00a4\\u00a4\\u00a4"); tripleIntlCurrency = tripleIntlCurrency.unescape(); - assertEquals("", tripleIntlCurrency, dfmt->getPositiveSuffix(result)); + assertEquals("", tripleIntlCurrency, dfmt->getPositiveSuffix(result)); // use ICU 61 behavior dfmt->setCurrency(USD); // getPositiveSuffix() always returns the suffix for the @@ -8699,7 +8457,7 @@ void NumberFormatTest::Test11649_toPatternWithMultiCurrency() { static UChar USD[] = {0x55, 0x53, 0x44, 0x0}; fmt.setCurrency(USD); UnicodeString appendTo; - + assertEquals("", "US dollars 12.34", fmt.format(12.34, appendTo)); UnicodeString topattern; @@ -8709,11 +8467,66 @@ void NumberFormatTest::Test11649_toPatternWithMultiCurrency() { return; } fmt2.setCurrency(USD); - + appendTo.remove(); assertEquals("", "US dollars 12.34", fmt2.format(12.34, appendTo)); } +void NumberFormatTest::Test13327_numberingSystemBufferOverflow() { + UErrorCode status = U_ZERO_ERROR; + for (int runId = 0; runId < 2; runId++) { + // Construct a locale string with a very long "numbers" value. + // The first time, make the value length exactly equal to ULOC_KEYWORDS_CAPACITY. + // The second time, make it exceed ULOC_KEYWORDS_CAPACITY. + int extraLength = (runId == 0) ? 0 : 5; + + CharString localeId("en@numbers=", status); + for (int i = 0; i < ULOC_KEYWORDS_CAPACITY + extraLength; i++) { + localeId.append('x', status); + } + assertSuccess("Constructing locale string", status); + Locale locale(localeId.data()); + + LocalPointer ns(NumberingSystem::createInstance(locale, status)); + assertFalse("Should not be null", ns.getAlias() == nullptr); + assertSuccess("Should create with no error", status); + } +} + +void NumberFormatTest::Test13391_chakmaParsing() { + UErrorCode status = U_ZERO_ERROR; + LocalPointer df(dynamic_cast( + NumberFormat::createInstance(Locale("ccp"), status))); + if (df == nullptr) { + dataerrln("%s %d Chakma df is null", __FILE__, __LINE__); + return; + } + const UChar* expected = u"\U00011137\U00011138,\U00011139\U0001113A\U0001113B"; + UnicodeString actual; + df->format(12345, actual, status); + assertSuccess("Should not fail when formatting in ccp", status); + assertEquals("Should produce expected output in ccp", expected, actual); + + Formattable result; + df->parse(expected, result, status); + assertSuccess("Should not fail when parsing in ccp", status); + assertEquals("Should parse to 12345 in ccp", 12345, result); + + const UChar* expectedScientific = u"\U00011137.\U00011139E\U00011138"; + UnicodeString actualScientific; + df.adoptInstead(static_cast( + NumberFormat::createScientificInstance(Locale("ccp"), status))); + df->format(130, actualScientific, status); + assertSuccess("Should not fail when formatting scientific in ccp", status); + assertEquals("Should produce expected scientific output in ccp", + expectedScientific, actualScientific); + + Formattable resultScientific; + df->parse(expectedScientific, resultScientific, status); + assertSuccess("Should not fail when parsing scientific in ccp", status); + assertEquals("Should parse scientific to 130 in ccp", 130, resultScientific); +} + void NumberFormatTest::verifyFieldPositionIterator( NumberFormatTest_Attributes *expected, FieldPositionIterator &iter) { @@ -8734,25 +8547,618 @@ void NumberFormatTest::verifyFieldPositionIterator( } } -void NumberFormatTest::checkExceptionIssue11735() { - UErrorCode status; +void NumberFormatTest::Test11735_ExceptionIssue() { + IcuTestErrorCode status(*this, "Test11735_ExceptionIssue"); Locale enLocale("en"); DecimalFormatSymbols symbols(enLocale, status); + if (status.isSuccess()) { + DecimalFormat fmt("0", symbols, status); + assertSuccess("Fail: Construct DecimalFormat formatter", status, true, __FILE__, __LINE__); + ParsePosition ppos(0); + fmt.parseCurrency("53.45", ppos); // NPE thrown here in ICU4J. + assertEquals("Issue11735 ppos", 0, ppos.getIndex()); + } +} +void NumberFormatTest::Test11035_FormatCurrencyAmount() { + UErrorCode status = U_ZERO_ERROR; + double amount = 12345.67; + const char16_t* expected = u"12,345$67 ​"; + + // Test two ways to set a currency via API + + Locale loc1 = Locale("pt_PT"); + LocalPointer fmt1(NumberFormat::createCurrencyInstance("loc1", status), + status); if (U_FAILURE(status)) { - errln((UnicodeString) - "Fail: Construct DecimalFormatSymbols"); + dataerrln("%s %d NumberFormat instance fmt1 is null", __FILE__, __LINE__); + return; + } + fmt1->setCurrency(u"PTE", status); + assertSuccess("Setting currency on fmt1", status); + UnicodeString actualSetCurrency; + fmt1->format(amount, actualSetCurrency); + + Locale loc2 = Locale("pt_PT@currency=PTE"); + LocalPointer fmt2(NumberFormat::createCurrencyInstance(loc2, status)); + assertSuccess("Creating fmt2", status); + UnicodeString actualLocaleString; + fmt2->format(amount, actualLocaleString); + + // TODO: The following test will fail until DecimalFormat wraps NumberFormatter. + if (!logKnownIssue("13574")) { + assertEquals("Custom Currency Pattern, Set Currency", expected, actualSetCurrency); } +} - DecimalFormat fmt("0", symbols, status); +void NumberFormatTest::Test11318_DoubleConversion() { + IcuTestErrorCode status(*this, "Test11318_DoubleConversion"); + LocalPointer nf(NumberFormat::createInstance("en", status), status); if (U_FAILURE(status)) { - errln((UnicodeString) - "Fail: Construct DecimalFormat formatter"); + dataerrln("%s %d Error in NumberFormat instance creation", __FILE__, __LINE__); + return; + } + nf->setMaximumFractionDigits(40); + nf->setMaximumIntegerDigits(40); + DecimalFormat* df = dynamic_cast(nf.getAlias()); + if (df != NULL) { + UErrorCode status = U_ZERO_ERROR; + df->setAttribute(UNUM_FORMAT_WITH_FULL_PRECISION, TRUE, status); // Apple } + UnicodeString appendTo; + nf->format(999999999999999.9, appendTo); + assertEquals("Should render all digits", u"999,999,999,999,999.9", appendTo); +} - ParsePosition ppos(0); - fmt.parseCurrency("53.45", ppos); // NPE thrown here in ICU4J. - assertEquals("Issue11735 ppos", 0, ppos.getIndex()); +void NumberFormatTest::TestParsePercentRegression() { + IcuTestErrorCode status(*this, "TestParsePercentRegression"); + LocalPointer df1((DecimalFormat*) NumberFormat::createInstance("en", status), status); + LocalPointer df2((DecimalFormat*) NumberFormat::createPercentInstance("en", status), status); + if (status.isFailure()) {return; } + df1->setLenient(TRUE); + df2->setLenient(TRUE); + + { + ParsePosition ppos; + Formattable result; + df1->parse("50%", result, ppos); + assertEquals("df1 should accept a number but not the percent sign", 2, ppos.getIndex()); + assertEquals("df1 should return the number as 50", 50.0, result.getDouble(status)); + } + { + ParsePosition ppos; + Formattable result; + df2->parse("50%", result, ppos); + assertEquals("df2 should accept the percent sign", 3, ppos.getIndex()); + assertEquals("df2 should return the number as 0.5", 0.5, result.getDouble(status)); + } + { + ParsePosition ppos; + Formattable result; + df2->parse("50", result, ppos); + assertEquals("df2 should return the number as 0.5 even though the percent sign is missing", + 0.5, + result.getDouble(status)); + } +} + +void NumberFormatTest::TestMultiplierWithScale() { + // to be added +} + +void NumberFormatTest::TestFastFormatInt32() { + IcuTestErrorCode status(*this, "TestFastFormatInt32"); + + // The two simplest formatters, old API and new API. + // Old API should use the fastpath for ints. + LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en"); + LocalPointer df(NumberFormat::createInstance("en", status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) {return;} + + double nums[] = { + 0.0, + -0.0, + NAN, + INFINITY, + 0.1, + 1.0, + 1.1, + 2.0, + 3.0, + 9.0, + 10.0, + 99.0, + 100.0, + 999.0, + 1000.0, + 9999.0, + 10000.0, + 99999.0, + 100000.0, + 999999.0, + 1000000.0, + static_cast(INT32_MAX) - 1, + static_cast(INT32_MAX), + static_cast(INT32_MAX) + 1, + static_cast(INT32_MIN) - 1, + static_cast(INT32_MIN), + static_cast(INT32_MIN) + 1}; + + for (auto num : nums) { + UnicodeString expected = lnf.formatDouble(num, status).toString(); + UnicodeString actual; + df->format(num, actual); + assertEquals(UnicodeString("d = ") + num, expected, actual); + } +} + +void NumberFormatTest::Test11646_Equality() { + UErrorCode status = U_ZERO_ERROR; + DecimalFormatSymbols symbols(Locale::getEnglish(), status); + UnicodeString pattern(u"\u00a4\u00a4\u00a4 0.00 %\u00a4\u00a4"); + DecimalFormat fmt(pattern, symbols, status); + if (!assertSuccess("", status)) return; + + // Test equality with affixes. set affix methods can't capture special + // characters which is why equality should fail. + { + DecimalFormat fmtCopy(fmt); + assertTrue("", fmt == fmtCopy); + UnicodeString positivePrefix; + fmtCopy.setPositivePrefix(fmtCopy.getPositivePrefix(positivePrefix)); + assertFalse("", fmt == fmtCopy); + } + { + DecimalFormat fmtCopy = DecimalFormat(fmt); + assertTrue("", fmt == fmtCopy); + UnicodeString positivePrefix; + fmtCopy.setPositiveSuffix(fmtCopy.getPositiveSuffix(positivePrefix)); + assertFalse("", fmt == fmtCopy); + } + { + DecimalFormat fmtCopy(fmt); + assertTrue("", fmt == fmtCopy); + UnicodeString negativePrefix; + fmtCopy.setNegativePrefix(fmtCopy.getNegativePrefix(negativePrefix)); + assertFalse("", fmt == fmtCopy); + } + { + DecimalFormat fmtCopy(fmt); + assertTrue("", fmt == fmtCopy); + UnicodeString negativePrefix; + fmtCopy.setNegativeSuffix(fmtCopy.getNegativeSuffix(negativePrefix)); + assertFalse("", fmt == fmtCopy); + } +} + +void NumberFormatTest::TestParseNaN() { + IcuTestErrorCode status(*this, "TestParseNaN"); + + DecimalFormat df("0", { "en", status }, status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + Formattable parseResult; + df.parse(u"NaN", parseResult, status); + assertEquals("NaN should parse successfully", NAN, parseResult.getDouble()); + assertFalse("Result NaN should be positive", std::signbit(parseResult.getDouble())); + UnicodeString formatResult; + df.format(parseResult.getDouble(), formatResult); + assertEquals("NaN should round-trip", u"NaN", formatResult); +} + +void NumberFormatTest::Test11897_LocalizedPatternSeparator() { + IcuTestErrorCode status(*this, "Test11897_LocalizedPatternSeparator"); + + // In a locale with a different symbol, like arabic, + // kPatternSeparatorSymbol should still be ';' + { + DecimalFormatSymbols dfs("ar", status); + assertEquals("pattern separator symbol should be ;", + u";", + dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)); + } + + // However, the custom symbol should be used in localized notation + // when set manually via API + { + DecimalFormatSymbols dfs("en", status); + dfs.setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, u"!", FALSE); + DecimalFormat df(u"0", dfs, status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + df.applyPattern("a0;b0", status); // should not throw + UnicodeString result; + assertEquals("should apply the normal pattern", + df.getNegativePrefix(result.remove()), + "b"); + df.applyLocalizedPattern(u"c0!d0", status); // should not throw + assertEquals("should apply the localized pattern", + df.getNegativePrefix(result.remove()), + "d"); + } +} + +void NumberFormatTest::Test13055_PercentageRounding() { + IcuTestErrorCode status(*this, "PercentageRounding"); + UnicodeString actual; + LocalPointerpFormat(NumberFormat::createPercentInstance("en_US", status)); + if (U_FAILURE(status)) { + dataerrln("Failure creating DecimalFormat %s", u_errorName(status)); + return; + } + pFormat->setMaximumFractionDigits(0); + pFormat->setRoundingMode(DecimalFormat::kRoundHalfEven); + pFormat->format(2.155, actual); + assertEquals("Should round percent toward even number", "216%", actual); +} + +void NumberFormatTest::Test11839() { + IcuTestErrorCode errorCode(*this, "Test11839"); + // Ticket #11839: DecimalFormat does not respect custom plus sign + LocalPointer dfs(new DecimalFormatSymbols(Locale::getEnglish(), errorCode), errorCode); + if (!assertSuccess("", errorCode, true, __FILE__, __LINE__)) { return; } + dfs->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, u"a∸"); + dfs->setSymbol(DecimalFormatSymbols::kPlusSignSymbol, u"b∔"); // ∔ U+2214 DOT PLUS + DecimalFormat df(u"0.00+;0.00-", dfs.orphan(), errorCode); + UnicodeString result; + df.format(-1.234, result, errorCode); + assertEquals("Locale-specific minus sign should be used", u"1.23a∸", result); + df.format(1.234, result.remove(), errorCode); + assertEquals("Locale-specific plus sign should be used", u"1.23b∔", result); + // Test round-trip with parse + expect2(df, -456, u"456.00a∸"); + expect2(df, 456, u"456.00b∔"); +} + +void NumberFormatTest::Test10354() { + IcuTestErrorCode errorCode(*this, "Test10354"); + // Ticket #10354: invalid FieldPositionIterator when formatting with empty NaN + DecimalFormatSymbols dfs(errorCode); + UnicodeString empty; + dfs.setSymbol(DecimalFormatSymbols::kNaNSymbol, empty); + DecimalFormat df(errorCode); + df.setDecimalFormatSymbols(dfs); + UnicodeString result; + FieldPositionIterator positions; + df.format(NAN, result, &positions, errorCode); + errorCode.errIfFailureAndReset("DecimalFormat.format(NAN, FieldPositionIterator) failed"); + FieldPosition fp; + while (positions.next(fp)) { + // Should not loop forever + } +} + +void NumberFormatTest::Test11645_ApplyPatternEquality() { + IcuTestErrorCode status(*this, "Test11645_ApplyPatternEquality"); + const char16_t* pattern = u"#,##0.0#"; + LocalPointer fmt((DecimalFormat*) NumberFormat::createInstance(status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + fmt->applyPattern(pattern, status); + LocalPointer fmtCopy; + + static const int32_t newMultiplier = 37; + fmtCopy.adoptInstead(new DecimalFormat(*fmt)); + assertFalse("Value before setter", fmtCopy->getMultiplier() == newMultiplier); + fmtCopy->setMultiplier(newMultiplier); + assertEquals("Value after setter", fmtCopy->getMultiplier(), newMultiplier); + fmtCopy->applyPattern(pattern, status); + //assertEquals("Value after applyPattern", fmtCopy->getMultiplier(), newMultiplier); + //assertFalse("multiplier", *fmt == *fmtCopy); + + static const NumberFormat::ERoundingMode newRoundingMode = NumberFormat::ERoundingMode::kRoundCeiling; + fmtCopy.adoptInstead(new DecimalFormat(*fmt)); + assertFalse("Value before setter", fmtCopy->getRoundingMode() == newRoundingMode); + fmtCopy->setRoundingMode(newRoundingMode); + assertEquals("Value after setter", fmtCopy->getRoundingMode(), newRoundingMode); + fmtCopy->applyPattern(pattern, status); + assertEquals("Value after applyPattern", fmtCopy->getRoundingMode(), newRoundingMode); + assertFalse("roundingMode", *fmt == *fmtCopy); + + static const char16_t *const newCurrency = u"EAT"; + fmtCopy.adoptInstead(new DecimalFormat(*fmt)); + assertFalse("Value before setter", fmtCopy->getCurrency() == newCurrency); + fmtCopy->setCurrency(newCurrency); + assertEquals("Value after setter", fmtCopy->getCurrency(), newCurrency); + fmtCopy->applyPattern(pattern, status); + assertEquals("Value after applyPattern", fmtCopy->getCurrency(), newCurrency); + assertFalse("currency", *fmt == *fmtCopy); + + static const UCurrencyUsage newCurrencyUsage = UCurrencyUsage::UCURR_USAGE_CASH; + fmtCopy.adoptInstead(new DecimalFormat(*fmt)); + assertFalse("Value before setter", fmtCopy->getCurrencyUsage() == newCurrencyUsage); + fmtCopy->setCurrencyUsage(newCurrencyUsage, status); + assertEquals("Value after setter", fmtCopy->getCurrencyUsage(), newCurrencyUsage); + fmtCopy->applyPattern(pattern, status); + assertEquals("Value after applyPattern", fmtCopy->getCurrencyUsage(), newCurrencyUsage); + assertFalse("currencyUsage", *fmt == *fmtCopy); +} + +void NumberFormatTest::Test12567() { + IcuTestErrorCode errorCode(*this, "Test12567"); + // Ticket #12567: DecimalFormat.equals() may not be symmetric + LocalPointer df1((DecimalFormat *) + NumberFormat::createInstance(Locale::getUS(), UNUM_CURRENCY, errorCode)); + LocalPointer df2((DecimalFormat *) + NumberFormat::createInstance(Locale::getUS(), UNUM_DECIMAL, errorCode)); + if (!assertSuccess("", errorCode, true, __FILE__, __LINE__)) { return; } + // NOTE: CurrencyPluralInfo equality not tested in C++ because its operator== is not defined. + df1->applyPattern(u"0.00", errorCode); + df2->applyPattern(u"0.00", errorCode); + //assertTrue("df1 == df2", *df1 == *df2); + //assertTrue("df2 == df1", *df2 == *df1); + df2->setPositivePrefix(u"abc"); + assertTrue("df1 != df2", *df1 != *df2); + assertTrue("df2 != df1", *df2 != *df1); +} + +void NumberFormatTest::Test11626_CustomizeCurrencyPluralInfo() { + IcuTestErrorCode errorCode(*this, "Test11626_CustomizeCurrencyPluralInfo"); + // Ticket #11626: No unit test demonstrating how to use CurrencyPluralInfo to + // change formatting spelled out currencies + // Use locale sr because it has interesting plural rules. + Locale locale("sr"); + LocalPointer symbols(new DecimalFormatSymbols(locale, errorCode), errorCode); + CurrencyPluralInfo info(locale, errorCode); + if (!assertSuccess("", errorCode, true, __FILE__, __LINE__)) { return; } + info.setCurrencyPluralPattern(u"one", u"0 qwerty", errorCode); + info.setCurrencyPluralPattern(u"few", u"0 dvorak", errorCode); + DecimalFormat df(u"#", symbols.orphan(), UNUM_CURRENCY_PLURAL, errorCode); + df.setCurrencyPluralInfo(info); + df.setCurrency(u"USD"); + df.setMaximumFractionDigits(0); + + UnicodeString result; + //assertEquals("Plural one", u"1 qwerty", df.format(1, result, errorCode)); + //assertEquals("Plural few", u"3 dvorak", df.format(3, result.remove(), errorCode)); + assertEquals("Plural other", u"99 америчких долара", df.format(99, result.remove(), errorCode)); + + info.setPluralRules(u"few: n is 1; one: n in 2..4", errorCode); + df.setCurrencyPluralInfo(info); + //assertEquals("Plural one", u"1 dvorak", df.format(1, result.remove(), errorCode)); + //assertEquals("Plural few", u"3 qwerty", df.format(3, result.remove(), errorCode)); + assertEquals("Plural other", u"99 америчких долара", df.format(99, result.remove(), errorCode)); +} + +void NumberFormatTest::Test13056_GroupingSize() { + UErrorCode status = U_ZERO_ERROR; + DecimalFormat df(u"#,##0", status); + if (!assertSuccess("", status)) return; + assertEquals("Primary grouping should return 3", 3, df.getGroupingSize()); + assertEquals("Secondary grouping should return 0", 0, df.getSecondaryGroupingSize()); + df.setSecondaryGroupingSize(3); + assertEquals("Primary grouping should still return 3", 3, df.getGroupingSize()); + assertEquals("Secondary grouping should round-trip", 3, df.getSecondaryGroupingSize()); + df.setGroupingSize(4); + assertEquals("Primary grouping should return 4", 4, df.getGroupingSize()); + assertEquals("Secondary should remember explicit setting and return 3", 3, df.getSecondaryGroupingSize()); +} + + +void NumberFormatTest::Test11025_CurrencyPadding() { + UErrorCode status = U_ZERO_ERROR; + UnicodeString pattern(u"¤¤ **####0.00"); + DecimalFormatSymbols sym(Locale::getFrance(), status); + if (!assertSuccess("", status)) return; + DecimalFormat fmt(pattern, sym, status); + if (!assertSuccess("", status)) return; + UnicodeString result; + fmt.format(433.0, result); + assertEquals("Number should be padded to 11 characters", "EUR *433,00", result); +} + +void NumberFormatTest::Test11648_ExpDecFormatMalPattern() { + UErrorCode status = U_ZERO_ERROR; + + DecimalFormat fmt("0.00", {"en", status}, status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + fmt.setScientificNotation(TRUE); + UnicodeString pattern; + + assertEquals("A valid scientific notation pattern should be produced", + "0.00E0", + fmt.toPattern(pattern)); + + DecimalFormat fmt2(pattern, status); + assertSuccess("", status); +} + +void NumberFormatTest::Test11649_DecFmtCurrencies() { + IcuTestErrorCode status(*this, "Test11649_DecFmtCurrencies"); + UnicodeString pattern("\\u00a4\\u00a4\\u00a4 0.00"); + pattern = pattern.unescape(); + DecimalFormat fmt(pattern, status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + static const UChar USD[] = u"USD"; + fmt.setCurrency(USD); + UnicodeString appendTo; + + assertEquals("", "US dollars 12.34", fmt.format(12.34, appendTo)); + UnicodeString topattern; + + // assertEquals("", pattern, fmt.toPattern(topattern)); + DecimalFormat fmt2(topattern, status); + fmt2.setCurrency(USD); + + appendTo.remove(); + assertEquals("", "US dollars 12.34", fmt2.format(12.34, appendTo)); +} + +void NumberFormatTest::Test13148_ParseGroupingSeparators() { + IcuTestErrorCode status(*this, "Test13148"); + LocalPointer fmt( + (DecimalFormat*)NumberFormat::createInstance("en-ZA", status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + + DecimalFormatSymbols symbols = *fmt->getDecimalFormatSymbols(); + + symbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, u'.'); + symbols.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, u','); + fmt->setDecimalFormatSymbols(symbols); + Formattable number; + fmt->parse(u"300,000", number, status); + assertEquals("Should parse as 300000", 300000LL, number.getInt64(status)); +} + +void NumberFormatTest::Test12753_PatternDecimalPoint() { + UErrorCode status = U_ZERO_ERROR; + DecimalFormatSymbols symbols(Locale::getUS(), status); + symbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, u"*", false); + DecimalFormat df(u"0.00", symbols, status); + if (!assertSuccess("", status)) return; + df.setDecimalPatternMatchRequired(true); + Formattable result; + df.parse(u"123",result, status); + assertEquals("Parsing integer succeeded even though setDecimalPatternMatchRequired was set", + U_INVALID_FORMAT_ERROR, status); + } + + void NumberFormatTest::Test11647_PatternCurrencySymbols() { + UErrorCode status = U_ZERO_ERROR; + DecimalFormat df(status); + df.applyPattern(u"¤¤¤¤#", status); + if (!assertSuccess("", status)) return; + UnicodeString actual; + df.format(123, actual); + //assertEquals("Should replace 4 currency signs with U+FFFD", u"\uFFFD123", actual); +} + +void NumberFormatTest::Test11913_BigDecimal() { + UErrorCode status = U_ZERO_ERROR; + LocalPointer df(NumberFormat::createInstance(Locale::getEnglish(), status), status); + if (!assertSuccess("", status)) return; + UnicodeString result; + df->format(StringPiece("1.23456789E400"), result, nullptr, status); + assertSuccess("", status); + assertEquals("Should format more than 309 digits", u"12,345,678", UnicodeString(result, 0, 10)); + assertEquals("Should format more than 309 digits", 534, result.length()); +} + +void NumberFormatTest::Test11020_RoundingInScientificNotation() { + UErrorCode status = U_ZERO_ERROR; + DecimalFormatSymbols sym(Locale::getFrance(), status); + DecimalFormat fmt(u"0.05E0", sym, status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + assertSuccess("", status); + UnicodeString result; + fmt.format(12301.2, result); + assertEquals("Rounding increment should be applied after magnitude scaling", u"1,25E4", result); +} + +void NumberFormatTest::Test11640_TripleCurrencySymbol() { + IcuTestErrorCode status(*this, "Test11640_TripleCurrencySymbol"); + UnicodeString actual; + DecimalFormat dFormat(u"¤¤¤ 0", status); + if (U_FAILURE(status)) { + dataerrln("Failure creating DecimalFormat %s", u_errorName(status)); + return; + } + dFormat.setCurrency(u"USD"); + UnicodeString result; + dFormat.getPositivePrefix(result); + assertEquals("Triple-currency should give long name on getPositivePrefix", + "US dollars ", result); +} + + +void NumberFormatTest::Test13763_FieldPositionIteratorOffset() { + IcuTestErrorCode status(*this, "Test13763_FieldPositionIteratorOffset"); + FieldPositionIterator fpi; + UnicodeString result(u"foo\U0001F4FBbar"); // 8 code units + LocalPointer nf(NumberFormat::createInstance("en", status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + nf->format(5142.3, result, &fpi, status); + + int32_t expected[] = { + UNUM_GROUPING_SEPARATOR_FIELD, 9, 10, + UNUM_INTEGER_FIELD, 8, 13, + UNUM_DECIMAL_SEPARATOR_FIELD, 13, 14, + UNUM_FRACTION_FIELD, 14, 15, + }; + int32_t tupleCount = UPRV_LENGTHOF(expected)/3; + expectPositions(fpi, expected, tupleCount, result); +} + +void NumberFormatTest::Test13777_ParseLongNameNonCurrencyMode() { + IcuTestErrorCode status(*this, "Test13777_ParseLongNameNonCurrencyMode"); + + LocalPointer df( + NumberFormat::createInstance("en-us", UNumberFormatStyle::UNUM_CURRENCY_PLURAL, status), status); + if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; } + expect2(*df, 1.5, u"1.50 US dollars"); +} + +void NumberFormatTest::Test13804_EmptyStringsWhenParsing() { + IcuTestErrorCode status(*this, "Test13804_EmptyStringsWhenParsing"); + + DecimalFormatSymbols dfs("en", status); + if (status.errIfFailureAndReset()) { + return; + } + dfs.setSymbol(DecimalFormatSymbols::kCurrencySymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kOneDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kTwoDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kThreeDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kFourDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kFiveDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kSixDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kSevenDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kEightDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kNineDigitSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kExponentMultiplicationSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kExponentialSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kInfinitySymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kMinusSignSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kNaNSymbol, u"", FALSE); + dfs.setPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, FALSE, u""); + dfs.setPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, TRUE, u""); + dfs.setSymbol(DecimalFormatSymbols::kPercentSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kPerMillSymbol, u"", FALSE); + dfs.setSymbol(DecimalFormatSymbols::kPlusSignSymbol, u"", FALSE); + + DecimalFormat df("0", dfs, status); + if (status.errIfFailureAndReset()) { + return; + } + df.setGroupingUsed(TRUE); + df.setScientificNotation(TRUE); + df.setLenient(TRUE); // enable all matchers + { + UnicodeString result; + df.format(0, result); // should not crash or hit infinite loop + } + const char16_t* samples[] = { + u"", + u"123", + u"$123", + u"-", + u"+", + u"44%", + u"1E+2.3" + }; + for (auto& sample : samples) { + logln(UnicodeString(u"Attempting parse on: ") + sample); + status.setScope(sample); + // We don't care about the results, only that we don't crash and don't loop. + Formattable result; + ParsePosition ppos(0); + df.parse(sample, result, ppos); + ppos = ParsePosition(0); + LocalPointer curramt(df.parseCurrency(sample, ppos)); + status.errIfFailureAndReset(); + } + + // Test with a nonempty exponent separator symbol to cover more code + dfs.setSymbol(DecimalFormatSymbols::kExponentialSymbol, u"E", FALSE); + df.setDecimalFormatSymbols(dfs); + { + Formattable result; + ParsePosition ppos(0); + df.parse(u"1E+2.3", result, ppos); + } } #endif /* #if !UCONFIG_NO_FORMATTING */