OLD | NEW |
1 /* | 1 /* |
2 ******************************************************************************* | 2 ******************************************************************************* |
3 * Copyright (C) 1996-2014, International Business Machines Corporation and * | 3 * Copyright (C) 1996-2015, International Business Machines Corporation and * |
4 * others. All Rights Reserved. * | 4 * others. All Rights Reserved. * |
5 ******************************************************************************* | 5 ******************************************************************************* |
6 */ | 6 */ |
7 | 7 |
8 #include "unicode/utypes.h" | 8 #include "unicode/utypes.h" |
9 | 9 |
10 #if !UCONFIG_NO_FORMATTING | 10 #if !UCONFIG_NO_FORMATTING |
11 | 11 |
12 #include "itrbnf.h" | 12 #include "itrbnf.h" |
13 | 13 |
14 #include "unicode/umachine.h" | 14 #include "unicode/umachine.h" |
15 | 15 |
16 #include "unicode/tblcoll.h" | 16 #include "unicode/tblcoll.h" |
17 #include "unicode/coleitr.h" | 17 #include "unicode/coleitr.h" |
18 #include "unicode/ures.h" | 18 #include "unicode/ures.h" |
19 #include "unicode/ustring.h" | 19 #include "unicode/ustring.h" |
20 #include "unicode/decimfmt.h" | 20 #include "unicode/decimfmt.h" |
21 #include "unicode/udata.h" | 21 #include "unicode/udata.h" |
| 22 #include "putilimp.h" |
22 #include "testutil.h" | 23 #include "testutil.h" |
23 | 24 |
24 #include <string.h> | 25 #include <string.h> |
25 | 26 |
26 // import com.ibm.text.RuleBasedNumberFormat; | 27 // import com.ibm.text.RuleBasedNumberFormat; |
27 // import com.ibm.test.TestFmwk; | 28 // import com.ibm.test.TestFmwk; |
28 | 29 |
29 // import java.util.Locale; | 30 // import java.util.Locale; |
30 // import java.text.NumberFormat; | 31 // import java.text.NumberFormat; |
31 | 32 |
(...skipping 27 matching lines...) Expand all Loading... |
59 TESTCASE(11, TestSwedishSpellout); | 60 TESTCASE(11, TestSwedishSpellout); |
60 TESTCASE(12, TestBelgianFrenchSpellout); | 61 TESTCASE(12, TestBelgianFrenchSpellout); |
61 TESTCASE(13, TestSmallValues); | 62 TESTCASE(13, TestSmallValues); |
62 TESTCASE(14, TestLocalizations); | 63 TESTCASE(14, TestLocalizations); |
63 TESTCASE(15, TestAllLocales); | 64 TESTCASE(15, TestAllLocales); |
64 TESTCASE(16, TestHebrewFraction); | 65 TESTCASE(16, TestHebrewFraction); |
65 TESTCASE(17, TestPortugueseSpellout); | 66 TESTCASE(17, TestPortugueseSpellout); |
66 TESTCASE(18, TestMultiplierSubstitution); | 67 TESTCASE(18, TestMultiplierSubstitution); |
67 TESTCASE(19, TestSetDecimalFormatSymbols); | 68 TESTCASE(19, TestSetDecimalFormatSymbols); |
68 TESTCASE(20, TestPluralRules); | 69 TESTCASE(20, TestPluralRules); |
| 70 TESTCASE(21, TestMultiplePluralRules); |
| 71 TESTCASE(22, TestInfinityNaN); |
| 72 TESTCASE(23, TestVariableDecimalPoint); |
69 #else | 73 #else |
70 TESTCASE(0, TestRBNFDisabled); | 74 TESTCASE(0, TestRBNFDisabled); |
71 #endif | 75 #endif |
72 default: | 76 default: |
73 name = ""; | 77 name = ""; |
74 break; | 78 break; |
75 } | 79 } |
76 } | 80 } |
77 | 81 |
78 #if U_HAVE_RBNF | 82 #if U_HAVE_RBNF |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
334 errln("Formatted 4 int64_t, expected " + expected + " got " + result); | 338 errln("Formatted 4 int64_t, expected " + expected + " got " + result); |
335 } else { | 339 } else { |
336 logln("Formatted 4 int64_t, expected " + expected + " got " + result); | 340 logln("Formatted 4 int64_t, expected " + expected + " got " + result); |
337 } | 341 } |
338 | 342 |
339 // clean up | 343 // clean up |
340 logln("Cleaning up"); | 344 logln("Cleaning up"); |
341 delete formatter; | 345 delete formatter; |
342 } | 346 } |
343 | 347 |
| 348 /** |
| 349 * Perform a simple spot check on the parsing going into an infinite loop for al
ternate rules. |
| 350 */ |
| 351 void IntlTestRBNF::TestMultiplePluralRules() { |
| 352 // This is trying to model the feminine form, but don't worry about the deta
ils too much. |
| 353 // We're trying to test the plural rules where there are different prefixes. |
| 354 UnicodeString rules("%spellout-cardinal-feminine-genitive:" |
| 355 "0: zero;" |
| 356 "1: ono;" |
| 357 "2: two;" |
| 358 "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$
[ >>];" |
| 359 "%spellout-cardinal-feminine:" |
| 360 "x.x: [<< $(cardinal,one{singleton}other{plurality})$ ]>%%fracti
ons>;" |
| 361 "0: zero;" |
| 362 "1: one;" |
| 363 "2: two;" |
| 364 "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$
[ >>];" |
| 365 "%%fractions:" |
| 366 "10: <%spellout-cardinal-feminine< $(cardinal,one{oneth}other{te
nth})$;" |
| 367 "100: <%spellout-cardinal-feminine< $(cardinal,one{1hundredth}ot
her{hundredth})$;"); |
| 368 UErrorCode status = U_ZERO_ERROR; |
| 369 UParseError pError; |
| 370 RuleBasedNumberFormat formatter(rules, Locale("ru"), pError, status); |
| 371 Formattable result; |
| 372 UnicodeString resultStr; |
| 373 FieldPosition pos; |
| 374 |
| 375 if (U_FAILURE(status)) { |
| 376 dataerrln("Unable to create formatter - %s", u_errorName(status)); |
| 377 return; |
| 378 } |
| 379 |
| 380 formatter.parse(formatter.format(1000.0, resultStr, pos, status), result, st
atus); |
| 381 if (1000 != result.getLong() || resultStr != UNICODE_STRING_SIMPLE("one thou
sand")) { |
| 382 errln("RuleBasedNumberFormat did not return the correct value. Got: %d",
result.getLong()); |
| 383 errln(resultStr); |
| 384 } |
| 385 resultStr.remove(); |
| 386 formatter.parse(formatter.format(1000.0, UnicodeString("%spellout-cardinal-f
eminine-genitive"), resultStr, pos, status), result, status); |
| 387 if (1000 != result.getLong() || resultStr != UNICODE_STRING_SIMPLE("ono thou
sand")) { |
| 388 errln("RuleBasedNumberFormat(cardinal-feminine-genitive) did not return
the correct value. Got: %d", result.getLong()); |
| 389 errln(resultStr); |
| 390 } |
| 391 resultStr.remove(); |
| 392 formatter.parse(formatter.format(1000.0, UnicodeString("%spellout-cardinal-f
eminine"), resultStr, pos, status), result, status); |
| 393 if (1000 != result.getLong() || resultStr != UNICODE_STRING_SIMPLE("one thou
sand")) { |
| 394 errln("RuleBasedNumberFormat(spellout-cardinal-feminine) did not return
the correct value. Got: %d", result.getLong()); |
| 395 errln(resultStr); |
| 396 } |
| 397 static const char* const testData[][2] = { |
| 398 { "0", "zero" }, |
| 399 { "1", "one" }, |
| 400 { "2", "two" }, |
| 401 { "0.1", "one oneth" }, |
| 402 { "0.2", "two tenth" }, |
| 403 { "1.1", "one singleton one oneth" }, |
| 404 { "1.2", "one singleton two tenth" }, |
| 405 { "2.1", "two plurality one oneth" }, |
| 406 { "2.2", "two plurality two tenth" }, |
| 407 { "0.01", "one 1hundredth" }, |
| 408 { "0.02", "two hundredth" }, |
| 409 { NULL, NULL } |
| 410 }; |
| 411 doTest(&formatter, testData, TRUE); |
| 412 } |
| 413 |
344 void IntlTestRBNF::TestFractionalRuleSet() | 414 void IntlTestRBNF::TestFractionalRuleSet() |
345 { | 415 { |
346 UnicodeString fracRules( | 416 UnicodeString fracRules( |
347 "%main:\n" | 417 "%main:\n" |
348 // this rule formats the number if it's 1 or more. It formats | 418 // this rule formats the number if it's 1 or more. It formats |
349 // the integral part using a DecimalFormat ("#,##0" puts | 419 // the integral part using a DecimalFormat ("#,##0" puts |
350 // thousands separators in the right places) and the fractional | 420 // thousands separators in the right places) and the fractional |
351 // part using %%frac. If there is no fractional part, it | 421 // part using %%frac. If there is no fractional part, it |
352 // just shows the integral part. | 422 // just shows the integral part. |
353 " x.0: <#,##0<[ >%%frac>];\n" | 423 " x.0: <#,##0<[ >%%frac>];\n" |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
419 { "6.2", "6 1/5" }, | 489 { "6.2", "6 1/5" }, |
420 { "7.25", "7 1/4" }, | 490 { "7.25", "7 1/4" }, |
421 { "8.333", "8 1/3" }, | 491 { "8.333", "8 1/3" }, |
422 { "9.5", "9 1/2" }, | 492 { "9.5", "9 1/2" }, |
423 { ".2222", "2/9" }, | 493 { ".2222", "2/9" }, |
424 { ".4444", "4/9" }, | 494 { ".4444", "4/9" }, |
425 { ".5555", "5/9" }, | 495 { ".5555", "5/9" }, |
426 { "1.2856", "1 2/7" }, | 496 { "1.2856", "1 2/7" }, |
427 { NULL, NULL } | 497 { NULL, NULL } |
428 }; | 498 }; |
429 doTest(&formatter, testData, FALSE); // exact values aren't parsable from
fractions | 499 doTest(&formatter, testData, FALSE); // exact values aren't parsable fro
m fractions |
430 } | 500 } |
431 } | 501 } |
432 | 502 |
433 #if 0 | 503 #if 0 |
434 #define LLAssert(a) \ | 504 #define LLAssert(a) \ |
435 if (!(a)) errln("FAIL: " #a) | 505 if (!(a)) errln("FAIL: " #a) |
436 | 506 |
437 void IntlTestRBNF::TestLLongConstructors() | 507 void IntlTestRBNF::TestLLongConstructors() |
438 { | 508 { |
439 logln("Testing constructors"); | 509 logln("Testing constructors"); |
(...skipping 1613 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2053 | 2123 |
2054 // Make sure there are no divide by 0 errors. | 2124 // Make sure there are no divide by 0 errors. |
2055 UnicodeString result; | 2125 UnicodeString result; |
2056 RuleBasedNumberFormat(ruRules, Locale("ru"), parseError, status).format(2100
0, result); | 2126 RuleBasedNumberFormat(ruRules, Locale("ru"), parseError, status).format(2100
0, result); |
2057 if (result.compare(UNICODE_STRING_SIMPLE("twenty-one thousand")) != 0) { | 2127 if (result.compare(UNICODE_STRING_SIMPLE("twenty-one thousand")) != 0) { |
2058 errln("Got " + result + " for 21000"); | 2128 errln("Got " + result + " for 21000"); |
2059 } | 2129 } |
2060 | 2130 |
2061 } | 2131 } |
2062 | 2132 |
| 2133 void IntlTestRBNF::TestInfinityNaN() { |
| 2134 UErrorCode status = U_ZERO_ERROR; |
| 2135 UParseError parseError; |
| 2136 UnicodeString enRules("%default:" |
| 2137 "-x: minus >>;" |
| 2138 "Inf: infinite;" |
| 2139 "NaN: not a number;" |
| 2140 "0: =#,##0=;"); |
| 2141 RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError,
status); |
| 2142 const char * const enTestData[][2] = { |
| 2143 {"1", "1"}, |
| 2144 {"\\u221E", "infinite"}, |
| 2145 {"-\\u221E", "minus infinite"}, |
| 2146 {"NaN", "not a number"}, |
| 2147 { NULL, NULL } |
| 2148 }; |
| 2149 if (U_FAILURE(status)) { |
| 2150 dataerrln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_
errorName(status))); |
| 2151 return; |
| 2152 } |
| 2153 |
| 2154 doTest(&enFormatter, enTestData, true); |
| 2155 |
| 2156 // Test the default behavior when the rules are undefined. |
| 2157 UnicodeString enRules2("%default:" |
| 2158 "-x: ->>;" |
| 2159 "0: =#,##0=;"); |
| 2160 RuleBasedNumberFormat enFormatter2(enRules2, Locale::getEnglish(), parseErro
r, status); |
| 2161 if (U_FAILURE(status)) { |
| 2162 errln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_erro
rName(status))); |
| 2163 return; |
| 2164 } |
| 2165 const char * const enDefaultTestData[][2] = { |
| 2166 {"1", "1"}, |
| 2167 {"\\u221E", "\\u221E"}, |
| 2168 {"-\\u221E", "-\\u221E"}, |
| 2169 {"NaN", "NaN"}, |
| 2170 { NULL, NULL } |
| 2171 }; |
| 2172 |
| 2173 doTest(&enFormatter2, enDefaultTestData, true); |
| 2174 } |
| 2175 |
| 2176 void IntlTestRBNF::TestVariableDecimalPoint() { |
| 2177 UErrorCode status = U_ZERO_ERROR; |
| 2178 UParseError parseError; |
| 2179 UnicodeString enRules("%spellout-numbering:" |
| 2180 "-x: minus >>;" |
| 2181 "x.x: << point >>;" |
| 2182 "x,x: << comma >>;" |
| 2183 "0.x: xpoint >>;" |
| 2184 "0,x: xcomma >>;" |
| 2185 "0: zero;" |
| 2186 "1: one;" |
| 2187 "2: two;" |
| 2188 "3: three;" |
| 2189 "4: four;" |
| 2190 "5: five;" |
| 2191 "6: six;" |
| 2192 "7: seven;" |
| 2193 "8: eight;" |
| 2194 "9: nine;"); |
| 2195 RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError,
status); |
| 2196 const char * const enTestPointData[][2] = { |
| 2197 {"1.1", "one point one"}, |
| 2198 {"1.23", "one point two three"}, |
| 2199 {"0.4", "xpoint four"}, |
| 2200 { NULL, NULL } |
| 2201 }; |
| 2202 if (U_FAILURE(status)) { |
| 2203 dataerrln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_
errorName(status))); |
| 2204 return; |
| 2205 } |
| 2206 doTest(&enFormatter, enTestPointData, true); |
| 2207 |
| 2208 DecimalFormatSymbols decimalFormatSymbols(Locale::getEnglish(), status); |
| 2209 decimalFormatSymbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol
, UNICODE_STRING_SIMPLE(",")); |
| 2210 enFormatter.setDecimalFormatSymbols(decimalFormatSymbols); |
| 2211 const char * const enTestCommaData[][2] = { |
| 2212 {"1.1", "one comma one"}, |
| 2213 {"1.23", "one comma two three"}, |
| 2214 {"0.4", "xcomma four"}, |
| 2215 { NULL, NULL } |
| 2216 }; |
| 2217 doTest(&enFormatter, enTestCommaData, true); |
| 2218 } |
| 2219 |
2063 void | 2220 void |
2064 IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testDat
a[][2], UBool testParsing) | 2221 IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testDat
a[][2], UBool testParsing) |
2065 { | 2222 { |
2066 // man, error reporting would be easier with printf-style syntax for unicode s
tring and formattable | 2223 // man, error reporting would be easier with printf-style syntax for unicode s
tring and formattable |
2067 | 2224 |
2068 UErrorCode status = U_ZERO_ERROR; | 2225 UErrorCode status = U_ZERO_ERROR; |
2069 DecimalFormatSymbols dfs("en", status); | 2226 DecimalFormatSymbols dfs("en", status); |
2070 // NumberFormat* decFmt = NumberFormat::createInstance(Locale::getUS(), stat
us); | 2227 // NumberFormat* decFmt = NumberFormat::createInstance(Locale::getUS(), stat
us); |
2071 DecimalFormat decFmt("#,###.################", dfs, status); | 2228 DecimalFormat decFmt("#,###.################", dfs, status); |
2072 if (U_FAILURE(status)) { | 2229 if (U_FAILURE(status)) { |
2073 errcheckln(status, "FAIL: could not create NumberFormat - %s", u_errorNa
me(status)); | 2230 errcheckln(status, "FAIL: could not create NumberFormat - %s", u_errorNa
me(status)); |
2074 } else { | 2231 } else { |
2075 for (int i = 0; testData[i][0]; ++i) { | 2232 for (int i = 0; testData[i][0]; ++i) { |
2076 const char* numString = testData[i][0]; | 2233 const char* numString = testData[i][0]; |
2077 const char* expectedWords = testData[i][1]; | 2234 const char* expectedWords = testData[i][1]; |
2078 | 2235 |
2079 log("[%i] %s = ", i, numString); | 2236 log("[%i] %s = ", i, numString); |
2080 Formattable expectedNumber; | 2237 Formattable expectedNumber; |
2081 decFmt.parse(numString, expectedNumber, status); | 2238 UnicodeString escapedNumString = UnicodeString(numString, -1, US_INV
).unescape(); |
| 2239 decFmt.parse(escapedNumString, expectedNumber, status); |
2082 if (U_FAILURE(status)) { | 2240 if (U_FAILURE(status)) { |
2083 errln("FAIL: decFmt could not parse %s", numString); | 2241 errln("FAIL: decFmt could not parse %s", numString); |
2084 break; | 2242 break; |
2085 } else { | 2243 } else { |
2086 UnicodeString actualString; | 2244 UnicodeString actualString; |
2087 FieldPosition pos; | 2245 FieldPosition pos; |
2088 formatter->format(expectedNumber, actualString/* , pos*/, status
); | 2246 formatter->format(expectedNumber, actualString/* , pos*/, status
); |
2089 if (U_FAILURE(status)) { | 2247 if (U_FAILURE(status)) { |
2090 UnicodeString msg = "Fail: formatter could not format "; | 2248 UnicodeString msg = "Fail: formatter could not format "; |
2091 decFmt.format(expectedNumber, msg, status); | 2249 decFmt.format(expectedNumber, msg, status); |
(...skipping 16 matching lines...) Expand all Loading... |
2108 Formattable parsedNumber; | 2266 Formattable parsedNumber; |
2109 formatter->parse(actualString, parsedNumber, status)
; | 2267 formatter->parse(actualString, parsedNumber, status)
; |
2110 if (U_FAILURE(status)) { | 2268 if (U_FAILURE(status)) { |
2111 UnicodeString msg = "FAIL: formatter could not p
arse "; | 2269 UnicodeString msg = "FAIL: formatter could not p
arse "; |
2112 msg.append(actualString); | 2270 msg.append(actualString); |
2113 msg.append(" status code: " ); | 2271 msg.append(" status code: " ); |
2114 msg.append(u_errorName(status)); | 2272 msg.append(u_errorName(status)); |
2115 errln(msg); | 2273 errln(msg); |
2116 break; | 2274 break; |
2117 } else { | 2275 } else { |
2118 if (parsedNumber != expectedNumber) { | 2276 if (parsedNumber != expectedNumber |
| 2277 && (!uprv_isNaN(parsedNumber.getDouble()) ||
!uprv_isNaN(expectedNumber.getDouble()))) |
| 2278 { |
2119 UnicodeString msg = "FAIL: parse failed for
"; | 2279 UnicodeString msg = "FAIL: parse failed for
"; |
2120 msg.append(actualString); | 2280 msg.append(actualString); |
2121 msg.append(", expected "); | 2281 msg.append(", expected "); |
2122 decFmt.format(expectedNumber, msg, status); | 2282 decFmt.format(expectedNumber, msg, status); |
2123 msg.append(", but got "); | 2283 msg.append(", but got "); |
2124 decFmt.format(parsedNumber, msg, status); | 2284 decFmt.format(parsedNumber, msg, status); |
2125 errln(msg); | 2285 errln(msg); |
2126 break; | 2286 break; |
2127 } | 2287 } |
2128 } | 2288 } |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2197 | 2357 |
2198 void | 2358 void |
2199 IntlTestRBNF::TestRBNFDisabled() { | 2359 IntlTestRBNF::TestRBNFDisabled() { |
2200 errln("*** RBNF currently disabled on this platform ***\n"); | 2360 errln("*** RBNF currently disabled on this platform ***\n"); |
2201 } | 2361 } |
2202 | 2362 |
2203 /* U_HAVE_RBNF */ | 2363 /* U_HAVE_RBNF */ |
2204 #endif | 2364 #endif |
2205 | 2365 |
2206 #endif /* #if !UCONFIG_NO_FORMATTING */ | 2366 #endif /* #if !UCONFIG_NO_FORMATTING */ |
OLD | NEW |