]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/intltest/itrbnf.cpp
ICU-59117.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / itrbnf.cpp
index ee416d452115bf1d7601fecfd7b315a83183db55..9b6a85dd97340547423a1d225b7c9b7eeacfde3f 100644 (file)
@@ -1,7 +1,9 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
  *******************************************************************************
- * Copyright (C) 1996-2015, International Business Machines Corporation and    *
- * others. All Rights Reserved.                                                *
+ * Copyright (C) 1996-2016, International Business Machines Corporation and
+ * others. All Rights Reserved.
  *******************************************************************************
  */
 
@@ -19,6 +21,8 @@
 #include "unicode/ustring.h"
 #include "unicode/decimfmt.h"
 #include "unicode/udata.h"
+#include "cmemory.h"
+#include "putilimp.h"
 #include "testutil.h"
 
 #include <string.h>
@@ -67,6 +71,10 @@ void IntlTestRBNF::runIndexedTest(int32_t index, UBool exec, const char* &name,
         TESTCASE(19, TestSetDecimalFormatSymbols);
         TESTCASE(20, TestPluralRules);
         TESTCASE(21, TestMultiplePluralRules);
+        TESTCASE(22, TestInfinityNaN);
+        TESTCASE(23, TestVariableDecimalPoint);
+        TESTCASE(24, TestLargeNumbers);
+        TESTCASE(25, TestCompactDecimalFormatStyle);
 #else
         TESTCASE(0, TestRBNFDisabled);
 #endif
@@ -393,11 +401,17 @@ void IntlTestRBNF::TestMultiplePluralRules() {
     UnicodeString rules("%spellout-cardinal-feminine-genitive:"
                 "0: zero;"
                 "1: ono;"
+                "2: two;"
                 "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$[ >>];"
                 "%spellout-cardinal-feminine:"
+                "x.x: [<< $(cardinal,one{singleton}other{plurality})$ ]>%%fractions>;"
                 "0: zero;"
                 "1: one;"
-                "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$[ >>];");
+                "2: two;"
+                "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$[ >>];"
+                "%%fractions:"
+                "10: <%spellout-cardinal-feminine< $(cardinal,one{oneth}other{tenth})$;"
+                "100: <%spellout-cardinal-feminine< $(cardinal,one{1hundredth}other{hundredth})$;");
     UErrorCode status = U_ZERO_ERROR;
     UParseError pError;
     RuleBasedNumberFormat formatter(rules, Locale("ru"), pError, status);
@@ -427,6 +441,21 @@ void IntlTestRBNF::TestMultiplePluralRules() {
         errln("RuleBasedNumberFormat(spellout-cardinal-feminine) did not return the correct value. Got: %d", result.getLong());
         errln(resultStr);
     }
+    static const char* const testData[][2] = {
+        { "0", "zero" },
+        { "1", "one" },
+        { "2", "two" },
+        { "0.1", "one oneth" },
+        { "0.2", "two tenth" },
+        { "1.1", "one singleton one oneth" },
+        { "1.2", "one singleton two tenth" },
+        { "2.1", "two plurality one oneth" },
+        { "2.2", "two plurality two tenth" },
+        { "0.01", "one 1hundredth" },
+        { "0.02", "two hundredth" },
+        { NULL, NULL }
+    };
+    doTest(&formatter, testData, TRUE);
 }
 
 void IntlTestRBNF::TestFractionalRuleSet()
@@ -514,7 +543,7 @@ void IntlTestRBNF::TestFractionalRuleSet()
             { "1.2856", "1 2/7" },
             { NULL, NULL }
         };
-       doTest(&formatter, testData, FALSE); // exact values aren't parsable from fractions
+        doTest(&formatter, testData, FALSE); // exact values aren't parsable from fractions
     }
 }
 
@@ -977,7 +1006,7 @@ void IntlTestRBNF::TestLLong()
             &NEG_TWO_TO_32X5, &TWO_TO_32, &NEG_FIVE
         };
         const int TUPLE_WIDTH = 3;
-        const int TUPLE_COUNT = (int)(sizeof(tuples)/sizeof(tuples[0]))/TUPLE_WIDTH;
+        const int TUPLE_COUNT = UPRV_LENGTHOF(tuples)/TUPLE_WIDTH;
         for (int i = 0; i < TUPLE_COUNT; ++i) {
             const llong lhs = *tuples[i*TUPLE_WIDTH+0];
             const llong rhs = *tuples[i*TUPLE_WIDTH+1];
@@ -1057,7 +1086,7 @@ void IntlTestRBNF::TestLLong()
             &BIG_FIVEp1, &FIVE, &ONE
         };
         const int TUPLE_WIDTH = 3;
-        const int TUPLE_COUNT = (int)(sizeof(tuples)/sizeof(tuples[0]))/TUPLE_WIDTH;
+        const int TUPLE_COUNT = UPRV_LENGTHOF(tuples)/TUPLE_WIDTH;
         for (int i = 0; i < TUPLE_COUNT; ++i) {
             const llong lhs = *tuples[i*TUPLE_WIDTH+0];
             const llong rhs = *tuples[i*TUPLE_WIDTH+1];
@@ -1183,9 +1212,8 @@ IntlTestRBNF::TestEnglishSpellout()
         doTest(formatter, testData, TRUE);
 
 #if !UCONFIG_NO_COLLATION
-        if( !logKnownIssue("9503") ) {
-          formatter->setLenient(TRUE);
-          static const char* lpTestData[][2] = {
+        formatter->setLenient(TRUE);
+        static const char* lpTestData[][2] = {
             { "fifty-7", "57" },
             { " fifty-7", "57" },
             { "  fifty-7", "57" },
@@ -1193,9 +1221,8 @@ IntlTestRBNF::TestEnglishSpellout()
             { "fifteen hundred and zero", "1,500" },
             { "FOurhundred     thiRTY six", "436" },
             { NULL, NULL}
-          };
-          doLenientParseTest(formatter, lpTestData);
-        }
+        };
+        doLenientParseTest(formatter, lpTestData);
 #endif
     }
     delete formatter;
@@ -1831,7 +1858,7 @@ IntlTestRBNF::TestLocalizations(void)
                 "<<%main>,<'en', \"it's ok\">>", // double quotes work too
                 "  \n <\n  <\n  %main\n  >\n  , \t <\t   en\t  ,  \tfoo \t\t > \n\n >  \n ", // Pattern_White_Space ok
            }; 
-            int32_t goodLocsLen = sizeof(goodLocs)/sizeof(goodLocs[0]);
+            int32_t goodLocsLen = UPRV_LENGTHOF(goodLocs);
 
             static const char* badLocs[] = {
                 " ", // non-zero length
@@ -1858,7 +1885,7 @@ IntlTestRBNF::TestLocalizations(void)
                 "<<%main>> x", // extra non-space text at end
 
             };
-            int32_t badLocsLen = sizeof(badLocs)/sizeof(badLocs[0]);
+            int32_t badLocsLen = UPRV_LENGTHOF(badLocs);
 
             for (i = 0; i < goodLocsLen; ++i) {
                 logln("[%d] '%s'", i, goodLocs[i]);
@@ -1913,7 +1940,7 @@ IntlTestRBNF::TestAllLocales()
                 continue;
             }
 #if !UCONFIG_NO_COLLATION
-            for (unsigned int numidx = 0; numidx < sizeof(numbers)/sizeof(double); numidx++) {
+            for (unsigned int numidx = 0; numidx < UPRV_LENGTHOF(numbers); numidx++) {
                 double n = numbers[numidx];
                 UnicodeString str;
                 f->format(n, str);
@@ -2141,13 +2168,163 @@ void IntlTestRBNF::TestPluralRules() {
 
     // Make sure there are no divide by 0 errors.
     UnicodeString result;
-    RuleBasedNumberFormat(ruRules, Locale("ru"), parseError, status).format(21000, result);
+    RuleBasedNumberFormat(ruRules, Locale("ru"), parseError, status).format((int32_t)21000, result);
     if (result.compare(UNICODE_STRING_SIMPLE("twenty-one thousand")) != 0) {
         errln("Got " + result + " for 21000");
     }
 
 }
 
+void IntlTestRBNF::TestInfinityNaN() {
+    UErrorCode status = U_ZERO_ERROR;
+    UParseError parseError;
+    UnicodeString enRules("%default:"
+            "-x: minus >>;"
+            "Inf: infinite;"
+            "NaN: not a number;"
+            "0: =#,##0=;");
+    RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError, status);
+    const char * const enTestData[][2] = {
+            {"1", "1"},
+            {"\\u221E", "infinite"},
+            {"-\\u221E", "minus infinite"},
+            {"NaN", "not a number"},
+            { NULL, NULL }
+    };
+    if (U_FAILURE(status)) {
+        dataerrln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
+        return;
+    }
+
+    doTest(&enFormatter, enTestData, true);
+
+    // Test the default behavior when the rules are undefined.
+    UnicodeString enRules2("%default:"
+            "-x: ->>;"
+            "0: =#,##0=;");
+    RuleBasedNumberFormat enFormatter2(enRules2, Locale::getEnglish(), parseError, status);
+    if (U_FAILURE(status)) {
+        errln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
+        return;
+    }
+    const char * const enDefaultTestData[][2] = {
+            {"1", "1"},
+            {"\\u221E", "\\u221E"},
+            {"-\\u221E", "-\\u221E"},
+            {"NaN", "NaN"},
+            { NULL, NULL }
+    };
+
+    doTest(&enFormatter2, enDefaultTestData, true);
+}
+
+void IntlTestRBNF::TestVariableDecimalPoint() {
+    UErrorCode status = U_ZERO_ERROR;
+    UParseError parseError;
+    UnicodeString enRules("%spellout-numbering:"
+            "-x: minus >>;"
+            "x.x: << point >>;"
+            "x,x: << comma >>;"
+            "0.x: xpoint >>;"
+            "0,x: xcomma >>;"
+            "0: zero;"
+            "1: one;"
+            "2: two;"
+            "3: three;"
+            "4: four;"
+            "5: five;"
+            "6: six;"
+            "7: seven;"
+            "8: eight;"
+            "9: nine;");
+    RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError, status);
+    const char * const enTestPointData[][2] = {
+            {"1.1", "one point one"},
+            {"1.23", "one point two three"},
+            {"0.4", "xpoint four"},
+            { NULL, NULL }
+    };
+    if (U_FAILURE(status)) {
+        dataerrln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
+        return;
+    }
+    doTest(&enFormatter, enTestPointData, true);
+
+    DecimalFormatSymbols decimalFormatSymbols(Locale::getEnglish(), status);
+    decimalFormatSymbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UNICODE_STRING_SIMPLE(","));
+    enFormatter.setDecimalFormatSymbols(decimalFormatSymbols);
+    const char * const enTestCommaData[][2] = {
+            {"1.1", "one comma one"},
+            {"1.23", "one comma two three"},
+            {"0.4", "xcomma four"},
+            { NULL, NULL }
+    };
+    doTest(&enFormatter, enTestCommaData, true);
+}
+
+void IntlTestRBNF::TestLargeNumbers() {
+    UErrorCode status = U_ZERO_ERROR;
+    RuleBasedNumberFormat rbnf(URBNF_SPELLOUT, Locale::getEnglish(), status);
+
+    const char * const enTestFullData[][2] = {
+            {"-9007199254740991", "minus nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one"}, // Maximum precision in both a double and a long
+            {"9007199254740991", "nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one"}, // Maximum precision in both a double and a long
+            {"-9007199254740992", "minus nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two"}, // Only precisely contained in a long
+            {"9007199254740992", "nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two"}, // Only precisely contained in a long
+            {"9999999999999998", "nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-eight"},
+            {"9999999999999999", "nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine"},
+            {"999999999999999999", "nine hundred ninety-nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine"},
+            {"1000000000000000000", "1,000,000,000,000,000,000"}, // The rules don't go to 1 quintillion yet
+            {"-9223372036854775809", "-9,223,372,036,854,775,809"}, // We've gone beyond 64-bit precision
+            {"-9223372036854775808", "-9,223,372,036,854,775,808"}, // We've gone beyond +64-bit precision
+            {"-9223372036854775807", "minus 9,223,372,036,854,775,807"}, // Minimum 64-bit precision
+            {"-9223372036854775806", "minus 9,223,372,036,854,775,806"}, // Minimum 64-bit precision + 1
+            {"9223372036854774111", "9,223,372,036,854,774,111"}, // Below 64-bit precision
+            {"9223372036854774999", "9,223,372,036,854,774,999"}, // Below 64-bit precision
+            {"9223372036854775000", "9,223,372,036,854,775,000"}, // Below 64-bit precision
+            {"9223372036854775806", "9,223,372,036,854,775,806"}, // Maximum 64-bit precision - 1
+            {"9223372036854775807", "9,223,372,036,854,775,807"}, // Maximum 64-bit precision
+            {"9223372036854775808", "9,223,372,036,854,775,808"}, // We've gone beyond 64-bit precision. This can only be represented with BigDecimal.
+            { NULL, NULL }
+    };
+    doTest(&rbnf, enTestFullData, false);
+}
+
+void IntlTestRBNF::TestCompactDecimalFormatStyle() {
+    UErrorCode status = U_ZERO_ERROR;
+    UParseError parseError;
+    // This is not a common use case, but we're testing it anyway.
+    UnicodeString numberPattern("=###0.#####=;"
+            "1000: <###0.00< K;"
+            "1000000: <###0.00< M;"
+            "1000000000: <###0.00< B;"
+            "1000000000000: <###0.00< T;"
+            "1000000000000000: <###0.00< Q;");
+    RuleBasedNumberFormat rbnf(numberPattern, UnicodeString(), Locale::getEnglish(), parseError, status);
+
+    const char * const enTestFullData[][2] = {
+            {"1000", "1.00 K"},
+            {"1234", "1.23 K"},
+            {"999994", "999.99 K"},
+            {"999995", "1000.00 K"},
+            {"1000000", "1.00 M"},
+            {"1200000", "1.20 M"},
+            {"1200000000", "1.20 B"},
+            {"1200000000000", "1.20 T"},
+            {"1200000000000000", "1.20 Q"},
+            {"4503599627370495", "4.50 Q"},
+            {"4503599627370496", "4.50 Q"},
+            {"8990000000000000", "8.99 Q"},
+            {"9008000000000000", "9.00 Q"}, // Number doesn't precisely fit into a double
+            {"9456000000000000", "9.00 Q"},  // Number doesn't precisely fit into a double
+            {"10000000000000000", "10.00 Q"},  // Number doesn't precisely fit into a double
+            {"9223372036854775807", "9223.00 Q"}, // Maximum 64-bit precision
+            {"9223372036854775808", "9,223,372,036,854,775,808"}, // We've gone beyond 64-bit precision. This can only be represented with BigDecimal.
+            { NULL, NULL }
+    };
+    doTest(&rbnf, enTestFullData, false);
+}
+
 void 
 IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing) 
 {
@@ -2166,7 +2343,8 @@ IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testDat
 
             log("[%i] %s = ", i, numString);
             Formattable expectedNumber;
-            decFmt.parse(numString, expectedNumber, status);
+            UnicodeString escapedNumString = UnicodeString(numString, -1, US_INV).unescape();
+            decFmt.parse(escapedNumString, expectedNumber, status);
             if (U_FAILURE(status)) {
                 errln("FAIL: decFmt could not parse %s", numString);
                 break;
@@ -2203,7 +2381,9 @@ IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testDat
                                 errln(msg);
                                 break;
                             } else {
-                                if (parsedNumber != expectedNumber) {
+                                if (parsedNumber != expectedNumber
+                                    && (!uprv_isNaN(parsedNumber.getDouble()) || !uprv_isNaN(expectedNumber.getDouble())))
+                                {
                                     UnicodeString msg = "FAIL: parse failed for ";
                                     msg.append(actualString);
                                     msg.append(", expected ");