• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4  * COPYRIGHT:
5  * Copyright (c) 1997-2013, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  ********************************************************************/
8 
9 #include "unicode/utypes.h"
10 
11 #if !UCONFIG_NO_FORMATTING
12 
13 #include "unicode/dcfmtsym.h"
14 #include "unicode/decimfmt.h"
15 #include "unicode/unum.h"
16 #include "tsdcfmsy.h"
17 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)18 void IntlTestDecimalFormatSymbols::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
19 {
20     if (exec) {
21         logln("TestSuite DecimalFormatSymbols:");
22     }
23     TESTCASE_AUTO_BEGIN;
24     TESTCASE_AUTO(testSymbols);
25     TESTCASE_AUTO(testLastResortData);
26     TESTCASE_AUTO(testDigitSymbols);
27     TESTCASE_AUTO(testNumberingSystem);
28     TESTCASE_AUTO_END;
29 }
30 
31 /**
32  * Test the API of DecimalFormatSymbols; primarily a simple get/set set.
33  */
testSymbols()34 void IntlTestDecimalFormatSymbols::testSymbols(/* char *par */)
35 {
36     UErrorCode status = U_ZERO_ERROR;
37 
38     DecimalFormatSymbols fr(Locale::getFrench(), status);
39     if(U_FAILURE(status)) {
40         errcheckln(status, "ERROR: Couldn't create French DecimalFormatSymbols - %s", u_errorName(status));
41         return;
42     }
43 
44     status = U_ZERO_ERROR;
45     DecimalFormatSymbols en(Locale::getEnglish(), status);
46     if(U_FAILURE(status)) {
47         errcheckln(status, "ERROR: Couldn't create English DecimalFormatSymbols - %s", u_errorName(status));
48         return;
49     }
50 
51     if(en == fr || ! (en != fr) ) {
52         errln("ERROR: English DecimalFormatSymbols equal to French");
53     }
54 
55     // just do some VERY basic tests to make sure that get/set work
56 
57     UnicodeString zero = en.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol);
58     fr.setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, zero);
59     if(fr.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol) != en.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol)) {
60         errln("ERROR: get/set ZeroDigit failed");
61     }
62 
63     UnicodeString group = en.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
64     fr.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, group);
65     if(fr.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol) != en.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)) {
66         errln("ERROR: get/set GroupingSeparator failed");
67     }
68 
69     UnicodeString decimal = en.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
70     fr.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, decimal);
71     if(fr.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol) != en.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)) {
72         errln("ERROR: get/set DecimalSeparator failed");
73     }
74 
75     UnicodeString perMill = en.getSymbol(DecimalFormatSymbols::kPerMillSymbol);
76     fr.setSymbol(DecimalFormatSymbols::kPerMillSymbol, perMill);
77     if(fr.getSymbol(DecimalFormatSymbols::kPerMillSymbol) != en.getSymbol(DecimalFormatSymbols::kPerMillSymbol)) {
78         errln("ERROR: get/set PerMill failed");
79     }
80 
81     UnicodeString percent = en.getSymbol(DecimalFormatSymbols::kPercentSymbol);
82     fr.setSymbol(DecimalFormatSymbols::kPercentSymbol, percent);
83     if(fr.getSymbol(DecimalFormatSymbols::kPercentSymbol) != en.getSymbol(DecimalFormatSymbols::kPercentSymbol)) {
84         errln("ERROR: get/set Percent failed");
85     }
86 
87     UnicodeString digit(en.getSymbol(DecimalFormatSymbols::kDigitSymbol));
88     fr.setSymbol(DecimalFormatSymbols::kDigitSymbol, digit);
89     if(fr.getSymbol(DecimalFormatSymbols::kDigitSymbol) != en.getSymbol(DecimalFormatSymbols::kDigitSymbol)) {
90         errln("ERROR: get/set Percent failed");
91     }
92 
93     UnicodeString patternSeparator = en.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol);
94     fr.setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, patternSeparator);
95     if(fr.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol) != en.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)) {
96         errln("ERROR: get/set PatternSeparator failed");
97     }
98 
99     UnicodeString infinity(en.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
100     fr.setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity);
101     UnicodeString infinity2(fr.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
102     if(infinity != infinity2) {
103         errln("ERROR: get/set Infinity failed");
104     }
105 
106     UnicodeString nan(en.getSymbol(DecimalFormatSymbols::kNaNSymbol));
107     fr.setSymbol(DecimalFormatSymbols::kNaNSymbol, nan);
108     UnicodeString nan2(fr.getSymbol(DecimalFormatSymbols::kNaNSymbol));
109     if(nan != nan2) {
110         errln("ERROR: get/set NaN failed");
111     }
112 
113     UnicodeString minusSign = en.getSymbol(DecimalFormatSymbols::kMinusSignSymbol);
114     fr.setSymbol(DecimalFormatSymbols::kMinusSignSymbol, minusSign);
115     if(fr.getSymbol(DecimalFormatSymbols::kMinusSignSymbol) != en.getSymbol(DecimalFormatSymbols::kMinusSignSymbol)) {
116         errln("ERROR: get/set MinusSign failed");
117     }
118 
119     UnicodeString exponential(en.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
120     fr.setSymbol(DecimalFormatSymbols::kExponentialSymbol, exponential);
121     if(fr.getSymbol(DecimalFormatSymbols::kExponentialSymbol) != en.getSymbol(DecimalFormatSymbols::kExponentialSymbol)) {
122         errln("ERROR: get/set Exponential failed");
123     }
124 
125     // Test get currency spacing before the currency.
126     status = U_ZERO_ERROR;
127     for (int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; i++) {
128         UnicodeString enCurrencyPattern = en.getPatternForCurrencySpacing(
129              (UCurrencySpacing)i, TRUE, status);
130         if(U_FAILURE(status)) {
131             errln("Error: cannot get CurrencyMatch for locale:en");
132             status = U_ZERO_ERROR;
133         }
134         UnicodeString frCurrencyPattern = fr.getPatternForCurrencySpacing(
135              (UCurrencySpacing)i, TRUE, status);
136         if(U_FAILURE(status)) {
137             errln("Error: cannot get CurrencyMatch for locale:fr");
138         }
139         if (enCurrencyPattern != frCurrencyPattern) {
140            errln("ERROR: get CurrencySpacing failed");
141         }
142     }
143     // Test get currencySpacing after the currency.
144     status = U_ZERO_ERROR;
145     for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) {
146         UnicodeString enCurrencyPattern = en.getPatternForCurrencySpacing(
147             (UCurrencySpacing)i, FALSE, status);
148         if(U_FAILURE(status)) {
149             errln("Error: cannot get CurrencyMatch for locale:en");
150             status = U_ZERO_ERROR;
151         }
152         UnicodeString frCurrencyPattern = fr.getPatternForCurrencySpacing(
153              (UCurrencySpacing)i, FALSE, status);
154         if(U_FAILURE(status)) {
155             errln("Error: cannot get CurrencyMatch for locale:fr");
156         }
157         if (enCurrencyPattern != frCurrencyPattern) {
158             errln("ERROR: get CurrencySpacing failed");
159         }
160     }
161     // Test set curerncySpacing APIs
162     status = U_ZERO_ERROR;
163     UnicodeString dash = UnicodeString("-");
164     en.setPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, TRUE, dash);
165     UnicodeString enCurrencyInsert = en.getPatternForCurrencySpacing(
166         UNUM_CURRENCY_INSERT, TRUE, status);
167     if (dash != enCurrencyInsert) {
168         errln("Error: Failed to setCurrencyInsert for locale:en");
169     }
170 
171     status = U_ZERO_ERROR;
172     DecimalFormatSymbols foo(status);
173 
174     DecimalFormatSymbols bar(foo);
175 
176     en = fr;
177 
178     if(en != fr || foo != bar) {
179         errln("ERROR: Copy Constructor or Assignment failed");
180     }
181 
182     // test get/setSymbol()
183     if((int) UNUM_FORMAT_SYMBOL_COUNT != (int) DecimalFormatSymbols::kFormatSymbolCount) {
184         errln("unum.h and decimfmt.h have inconsistent numbers of format symbols!");
185         return;
186     }
187 
188     int i;
189     for(i = 0; i < (int)DecimalFormatSymbols::kFormatSymbolCount; ++i) {
190         foo.setSymbol((DecimalFormatSymbols::ENumberFormatSymbol)i, UnicodeString((UChar32)(0x10330 + i)));
191     }
192     for(i = 0; i < (int)DecimalFormatSymbols::kFormatSymbolCount; ++i) {
193         if(foo.getSymbol((DecimalFormatSymbols::ENumberFormatSymbol)i) != UnicodeString((UChar32)(0x10330 + i))) {
194             errln("get/setSymbol did not roundtrip, got " +
195                   foo.getSymbol((DecimalFormatSymbols::ENumberFormatSymbol)i) +
196                   ", expected " +
197                   UnicodeString((UChar32)(0x10330 + i)));
198         }
199     }
200 
201     DecimalFormatSymbols sym(Locale::getUS(), status);
202 
203     UnicodeString customDecSeperator("S");
204     Verify(34.5, u"00.00", sym, u"34.50");
205     sym.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, customDecSeperator);
206     Verify(34.5, u"00.00", sym, u"34S50");
207     sym.setSymbol(DecimalFormatSymbols::kPercentSymbol, u"P");
208     Verify(34.5, u"00 %", sym, u"3450 P");
209     sym.setSymbol(DecimalFormatSymbols::kCurrencySymbol, u"D");
210     Verify(34.5, u"\u00a4##.##", sym, u"D\u00a034.50");
211     sym.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, u"|");
212     Verify(3456.5, u"0,000.##", sym, u"3|456S5");
213 
214 }
215 
testLastResortData()216 void IntlTestDecimalFormatSymbols::testLastResortData() {
217     IcuTestErrorCode errorCode(*this, "testLastResortData");
218     LocalPointer<DecimalFormatSymbols> lastResort(
219         DecimalFormatSymbols::createWithLastResortData(errorCode));
220     if(errorCode.errIfFailureAndReset("DecimalFormatSymbols::createWithLastResortData() failed")) {
221         return;
222     }
223     DecimalFormatSymbols root(Locale::getRoot(), errorCode);
224     if(errorCode.errDataIfFailureAndReset("DecimalFormatSymbols(root) failed")) {
225         return;
226     }
227     // Note: It is not necessary that the last resort data matches the root locale,
228     // but it seems weird if most symbols did not match.
229     // Also, one purpose for calling operator==() is to find uninitialized memory in a debug build.
230     if(*lastResort == root) {
231         errln("DecimalFormatSymbols last resort data unexpectedly matches root");
232     }
233     // Here we adjust for expected differences.
234     assertEquals("last-resort grouping separator",
235                  "", lastResort->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
236     lastResort->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, ",");
237     assertEquals("last-resort monetary grouping separator",
238                  "", lastResort->getSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol));
239     lastResort->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, ",");
240     assertEquals("last-resort NaN",
241                  UnicodeString((UChar)0xfffd), lastResort->getSymbol(DecimalFormatSymbols::kNaNSymbol));
242     lastResort->setSymbol(DecimalFormatSymbols::kNaNSymbol, "NaN");
243     // Check that now all of the symbols match root.
244     for(int32_t i = 0; i < DecimalFormatSymbols::kFormatSymbolCount; ++i) {
245         DecimalFormatSymbols::ENumberFormatSymbol e = (DecimalFormatSymbols::ENumberFormatSymbol)i;
246         assertEquals("last-resort symbol vs. root", root.getSymbol(e), lastResort->getSymbol(e));
247     }
248     // Also, the CurrencySpacing patterns are empty in the last resort instance,
249     // but not in root.
250     Verify(1234567.25, "#,##0.##", *lastResort, "1,234,567.25");
251 }
252 
testDigitSymbols()253 void IntlTestDecimalFormatSymbols::testDigitSymbols() {
254     // This test does more in ICU4J than in ICU4C right now.
255     // In ICU4C, it is basically just a test for codePointZero and getConstDigitSymbol.
256     UChar defZero = u'0';
257     UChar32 osmanyaZero = U'\U000104A0';
258     static const UChar* osmanyaDigitStrings[] = {
259         u"\U000104A0", u"\U000104A1", u"\U000104A2", u"\U000104A3", u"\U000104A4",
260         u"\U000104A5", u"\U000104A6", u"\U000104A7", u"\U000104A8", u"\U000104A9"
261     };
262 
263     IcuTestErrorCode status(*this, "testDigitSymbols()");
264     DecimalFormatSymbols symbols(Locale("en"), status);
265 
266     if (defZero != symbols.getCodePointZero()) {
267         errln("ERROR: Code point zero be ASCII 0");
268     }
269     for (int32_t i=0; i<=9; i++) {
270         assertEquals(UnicodeString("i. ASCII Digit at index ") + Int64ToUnicodeString(i),
271             UnicodeString(u'0' + i),
272             symbols.getConstDigitSymbol(i));
273     }
274 
275     for (int32_t i=0; i<=9; i++) {
276         DecimalFormatSymbols::ENumberFormatSymbol key =
277             i == 0
278             ? DecimalFormatSymbols::kZeroDigitSymbol
279             : static_cast<DecimalFormatSymbols::ENumberFormatSymbol>
280                 (DecimalFormatSymbols::kOneDigitSymbol + i - 1);
281         symbols.setSymbol(key, UnicodeString(osmanyaDigitStrings[i]), FALSE);
282     }
283     // NOTE: in ICU4J, the calculation of codePointZero is smarter;
284     // in ICU4C, it is more conservative and is only set if propogateDigits is true.
285     if (-1 != symbols.getCodePointZero()) {
286         errln("ERROR: Code point zero be invalid");
287     }
288     for (int32_t i=0; i<=9; i++) {
289         assertEquals(UnicodeString("ii. Osmanya digit at index ") + Int64ToUnicodeString(i),
290             UnicodeString(osmanyaDigitStrings[i]),
291             symbols.getConstDigitSymbol(i));
292     }
293 
294     // Check Osmanya codePointZero
295     symbols.setSymbol(
296         DecimalFormatSymbols::kZeroDigitSymbol,
297         UnicodeString(osmanyaDigitStrings[0]), TRUE);
298     if (osmanyaZero != symbols.getCodePointZero()) {
299         errln("ERROR: Code point zero be Osmanya code point zero");
300     }
301     for (int32_t i=0; i<=9; i++) {
302         assertEquals(UnicodeString("iii. Osmanya digit at index ") + Int64ToUnicodeString(i),
303             UnicodeString(osmanyaDigitStrings[i]),
304             symbols.getConstDigitSymbol(i));
305     }
306 
307     // Check after copy
308     DecimalFormatSymbols copy(symbols);
309     if (osmanyaZero != copy.getCodePointZero()) {
310         errln("ERROR: Code point zero be Osmanya code point zero");
311     }
312     for (int32_t i=0; i<=9; i++) {
313         assertEquals(UnicodeString("iv. After copy at index ") + Int64ToUnicodeString(i),
314             UnicodeString(osmanyaDigitStrings[i]),
315             copy.getConstDigitSymbol(i));
316     }
317 
318     // Check when loaded from resource bundle
319     DecimalFormatSymbols fromData(Locale("en@numbers=osma"), status);
320     if (osmanyaZero != fromData.getCodePointZero()) {
321         errln("ERROR: Code point zero be Osmanya code point zero");
322     }
323     for (int32_t i=0; i<=9; i++) {
324         assertEquals(UnicodeString("v. Resource bundle at index ") + Int64ToUnicodeString(i),
325             UnicodeString(osmanyaDigitStrings[i]),
326             fromData.getConstDigitSymbol(i));
327     }
328 
329     // Setting a digit somewhere in the middle should invalidate codePointZero
330     symbols.setSymbol(DecimalFormatSymbols::kOneDigitSymbol, u"foo", FALSE);
331     if (-1 != symbols.getCodePointZero()) {
332         errln("ERROR: Code point zero be invalid");
333     }
334 
335     // Reset digits to Latin
336     symbols.setSymbol(
337         DecimalFormatSymbols::kZeroDigitSymbol,
338         UnicodeString(defZero));
339     if (defZero != symbols.getCodePointZero()) {
340         errln("ERROR: Code point zero be ASCII 0");
341     }
342     for (int32_t i=0; i<=9; i++) {
343         assertEquals(UnicodeString("vi. ASCII Digit at index ") + Int64ToUnicodeString(i),
344             UnicodeString(u'0' + i),
345             symbols.getConstDigitSymbol(i));
346     }
347 }
348 
testNumberingSystem()349 void IntlTestDecimalFormatSymbols::testNumberingSystem() {
350     IcuTestErrorCode errorCode(*this, "testNumberingSystem");
351     struct testcase {
352         const char* locid;
353         const char* nsname;
354         const char16_t* expected1; // Expected number format string
355         const char16_t* expected2; // Expected pattern separator
356     };
357     static const testcase cases[] = {
358             {"en", "latn", u"1,234.56", u"%"},
359             {"en", "arab", u"١٬٢٣٤٫٥٦", u"٪\u061C"},
360             {"en", "mathsanb", u"��,������.����", u"%"},
361             {"en", "mymr", u"၁,၂၃၄.၅၆", u"%"},
362             {"my", "latn", u"1,234.56", u"%"},
363             {"my", "arab", u"١٬٢٣٤٫٥٦", u"٪\u061C"},
364             {"my", "mathsanb", u"��,������.����", u"%"},
365             {"my", "mymr", u"၁,၂၃၄.၅၆", u"%"},
366             {"ar", "latn", u"1,234.56", u"\u200E%\u200E"},
367             {"ar", "arab", u"١٬٢٣٤٫٥٦", u"٪\u061C"},
368             {"en@numbers=thai", "mymr", u"၁,၂၃၄.၅၆", u"%"}, // conflicting numbering system
369     };
370 
371     for (int i=0; i<8; i++) {
372         testcase cas = cases[i];
373         Locale loc(cas.locid);
374         LocalPointer<NumberingSystem> ns(NumberingSystem::createInstanceByName(cas.nsname, errorCode));
375         if (errorCode.errDataIfFailureAndReset("NumberingSystem failed")) {
376             return;
377         }
378         UnicodeString expected1(cas.expected1);
379         UnicodeString expected2(cas.expected2);
380         DecimalFormatSymbols dfs(loc, *ns, errorCode);
381         if (errorCode.errDataIfFailureAndReset("DecimalFormatSymbols failed")) {
382             return;
383         }
384         Verify(1234.56, "#,##0.##", dfs, expected1);
385         // The percent sign differs by numbering system.
386         UnicodeString actual2 = dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol);
387         assertEquals((UnicodeString) "Percent sign with " + cas.locid + " and " + cas.nsname,
388             expected2,
389             actual2);
390     }
391 }
392 
Verify(double value,const UnicodeString & pattern,const DecimalFormatSymbols & sym,const UnicodeString & expected)393 void IntlTestDecimalFormatSymbols::Verify(double value, const UnicodeString& pattern,
394                                           const DecimalFormatSymbols &sym, const UnicodeString& expected){
395     UErrorCode status = U_ZERO_ERROR;
396     DecimalFormat df(pattern, sym, status);
397     if(U_FAILURE(status)){
398         errln("ERROR: construction of decimal format failed - %s", u_errorName(status));
399     }
400     UnicodeString buffer;
401     FieldPosition pos(FieldPosition::DONT_CARE);
402     buffer = df.format(value, buffer, pos);
403     if(buffer != expected){
404         errln((UnicodeString)"ERROR: format() returns wrong result\n Expected " +
405             expected + ", Got " + buffer);
406     }
407 }
408 
409 #endif /* #if !UCONFIG_NO_FORMATTING */
410