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
18void 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(testNumberingSystem);
27    TESTCASE_AUTO_END;
28}
29
30/**
31 * Test the API of DecimalFormatSymbols; primarily a simple get/set set.
32 */
33void IntlTestDecimalFormatSymbols::testSymbols(/* char *par */)
34{
35    UErrorCode status = U_ZERO_ERROR;
36
37    DecimalFormatSymbols fr(Locale::getFrench(), status);
38    if(U_FAILURE(status)) {
39        errcheckln(status, "ERROR: Couldn't create French DecimalFormatSymbols - %s", u_errorName(status));
40        return;
41    }
42
43    status = U_ZERO_ERROR;
44    DecimalFormatSymbols en(Locale::getEnglish(), status);
45    if(U_FAILURE(status)) {
46        errcheckln(status, "ERROR: Couldn't create English DecimalFormatSymbols - %s", u_errorName(status));
47        return;
48    }
49
50    if(en == fr || ! (en != fr) ) {
51        errln("ERROR: English DecimalFormatSymbols equal to French");
52    }
53
54    // just do some VERY basic tests to make sure that get/set work
55
56    UnicodeString zero = en.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol);
57    fr.setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, zero);
58    if(fr.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol) != en.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol)) {
59        errln("ERROR: get/set ZeroDigit failed");
60    }
61
62    UnicodeString group = en.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
63    fr.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, group);
64    if(fr.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol) != en.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)) {
65        errln("ERROR: get/set GroupingSeparator failed");
66    }
67
68    UnicodeString decimal = en.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
69    fr.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, decimal);
70    if(fr.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol) != en.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)) {
71        errln("ERROR: get/set DecimalSeparator failed");
72    }
73
74    UnicodeString perMill = en.getSymbol(DecimalFormatSymbols::kPerMillSymbol);
75    fr.setSymbol(DecimalFormatSymbols::kPerMillSymbol, perMill);
76    if(fr.getSymbol(DecimalFormatSymbols::kPerMillSymbol) != en.getSymbol(DecimalFormatSymbols::kPerMillSymbol)) {
77        errln("ERROR: get/set PerMill failed");
78    }
79
80    UnicodeString percent = en.getSymbol(DecimalFormatSymbols::kPercentSymbol);
81    fr.setSymbol(DecimalFormatSymbols::kPercentSymbol, percent);
82    if(fr.getSymbol(DecimalFormatSymbols::kPercentSymbol) != en.getSymbol(DecimalFormatSymbols::kPercentSymbol)) {
83        errln("ERROR: get/set Percent failed");
84    }
85
86    UnicodeString digit(en.getSymbol(DecimalFormatSymbols::kDigitSymbol));
87    fr.setSymbol(DecimalFormatSymbols::kDigitSymbol, digit);
88    if(fr.getSymbol(DecimalFormatSymbols::kDigitSymbol) != en.getSymbol(DecimalFormatSymbols::kDigitSymbol)) {
89        errln("ERROR: get/set Percent failed");
90    }
91
92    UnicodeString patternSeparator = en.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol);
93    fr.setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, patternSeparator);
94    if(fr.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol) != en.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)) {
95        errln("ERROR: get/set PatternSeparator failed");
96    }
97
98    UnicodeString infinity(en.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
99    fr.setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity);
100    UnicodeString infinity2(fr.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
101    if(infinity != infinity2) {
102        errln("ERROR: get/set Infinity failed");
103    }
104
105    UnicodeString nan(en.getSymbol(DecimalFormatSymbols::kNaNSymbol));
106    fr.setSymbol(DecimalFormatSymbols::kNaNSymbol, nan);
107    UnicodeString nan2(fr.getSymbol(DecimalFormatSymbols::kNaNSymbol));
108    if(nan != nan2) {
109        errln("ERROR: get/set NaN failed");
110    }
111
112    UnicodeString minusSign = en.getSymbol(DecimalFormatSymbols::kMinusSignSymbol);
113    fr.setSymbol(DecimalFormatSymbols::kMinusSignSymbol, minusSign);
114    if(fr.getSymbol(DecimalFormatSymbols::kMinusSignSymbol) != en.getSymbol(DecimalFormatSymbols::kMinusSignSymbol)) {
115        errln("ERROR: get/set MinusSign failed");
116    }
117
118    UnicodeString exponential(en.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
119    fr.setSymbol(DecimalFormatSymbols::kExponentialSymbol, exponential);
120    if(fr.getSymbol(DecimalFormatSymbols::kExponentialSymbol) != en.getSymbol(DecimalFormatSymbols::kExponentialSymbol)) {
121        errln("ERROR: get/set Exponential failed");
122    }
123
124    // Test get currency spacing before the currency.
125    status = U_ZERO_ERROR;
126    for (int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; i++) {
127        UnicodeString enCurrencyPattern = en.getPatternForCurrencySpacing(
128             (UCurrencySpacing)i, TRUE, status);
129        if(U_FAILURE(status)) {
130            errln("Error: cannot get CurrencyMatch for locale:en");
131            status = U_ZERO_ERROR;
132        }
133        UnicodeString frCurrencyPattern = fr.getPatternForCurrencySpacing(
134             (UCurrencySpacing)i, TRUE, status);
135        if(U_FAILURE(status)) {
136            errln("Error: cannot get CurrencyMatch for locale:fr");
137        }
138        if (enCurrencyPattern != frCurrencyPattern) {
139           errln("ERROR: get CurrencySpacing failed");
140        }
141    }
142    // Test get currencySpacing after the currency.
143    status = U_ZERO_ERROR;
144    for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) {
145        UnicodeString enCurrencyPattern = en.getPatternForCurrencySpacing(
146            (UCurrencySpacing)i, FALSE, status);
147        if(U_FAILURE(status)) {
148            errln("Error: cannot get CurrencyMatch for locale:en");
149            status = U_ZERO_ERROR;
150        }
151        UnicodeString frCurrencyPattern = fr.getPatternForCurrencySpacing(
152             (UCurrencySpacing)i, FALSE, status);
153        if(U_FAILURE(status)) {
154            errln("Error: cannot get CurrencyMatch for locale:fr");
155        }
156        if (enCurrencyPattern != frCurrencyPattern) {
157            errln("ERROR: get CurrencySpacing failed");
158        }
159    }
160    // Test set curerncySpacing APIs
161    status = U_ZERO_ERROR;
162    UnicodeString dash = UnicodeString("-");
163    en.setPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, TRUE, dash);
164    UnicodeString enCurrencyInsert = en.getPatternForCurrencySpacing(
165        UNUM_CURRENCY_INSERT, TRUE, status);
166    if (dash != enCurrencyInsert) {
167        errln("Error: Failed to setCurrencyInsert for locale:en");
168    }
169
170    status = U_ZERO_ERROR;
171    DecimalFormatSymbols foo(status);
172
173    DecimalFormatSymbols bar(foo);
174
175    en = fr;
176
177    if(en != fr || foo != bar) {
178        errln("ERROR: Copy Constructor or Assignment failed");
179    }
180
181    // test get/setSymbol()
182    if((int) UNUM_FORMAT_SYMBOL_COUNT != (int) DecimalFormatSymbols::kFormatSymbolCount) {
183        errln("unum.h and decimfmt.h have inconsistent numbers of format symbols!");
184        return;
185    }
186
187    int i;
188    for(i = 0; i < (int)DecimalFormatSymbols::kFormatSymbolCount; ++i) {
189        foo.setSymbol((DecimalFormatSymbols::ENumberFormatSymbol)i, UnicodeString((UChar32)(0x10330 + i)));
190    }
191    for(i = 0; i < (int)DecimalFormatSymbols::kFormatSymbolCount; ++i) {
192        if(foo.getSymbol((DecimalFormatSymbols::ENumberFormatSymbol)i) != UnicodeString((UChar32)(0x10330 + i))) {
193            errln("get/setSymbol did not roundtrip, got " +
194                  foo.getSymbol((DecimalFormatSymbols::ENumberFormatSymbol)i) +
195                  ", expected " +
196                  UnicodeString((UChar32)(0x10330 + i)));
197        }
198    }
199
200    DecimalFormatSymbols sym(Locale::getUS(), status);
201
202    UnicodeString customDecSeperator("S");
203    Verify(34.5, (UnicodeString)"00.00", sym, (UnicodeString)"34.50");
204    sym.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, customDecSeperator);
205    Verify(34.5, (UnicodeString)"00.00", sym, (UnicodeString)"34S50");
206    sym.setSymbol(DecimalFormatSymbols::kPercentSymbol, (UnicodeString)"P");
207    Verify(34.5, (UnicodeString)"00 %", sym, (UnicodeString)"3450 P");
208    sym.setSymbol(DecimalFormatSymbols::kCurrencySymbol, (UnicodeString)"D");
209    Verify(34.5, CharsToUnicodeString("\\u00a4##.##"), sym, (UnicodeString)"D34.5");
210    sym.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, (UnicodeString)"|");
211    Verify(3456.5, (UnicodeString)"0,000.##", sym, (UnicodeString)"3|456S5");
212
213}
214
215void IntlTestDecimalFormatSymbols::testLastResortData() {
216    IcuTestErrorCode errorCode(*this, "testLastResortData");
217    LocalPointer<DecimalFormatSymbols> lastResort(
218        DecimalFormatSymbols::createWithLastResortData(errorCode));
219    if(errorCode.logIfFailureAndReset("DecimalFormatSymbols::createWithLastResortData() failed")) {
220        return;
221    }
222    DecimalFormatSymbols root(Locale::getRoot(), errorCode);
223    if(errorCode.logDataIfFailureAndReset("DecimalFormatSymbols(root) failed")) {
224        return;
225    }
226    // Note: It is not necessary that the last resort data matches the root locale,
227    // but it seems weird if most symbols did not match.
228    // Also, one purpose for calling operator==() is to find uninitialized memory in a debug build.
229    if(*lastResort == root) {
230        errln("DecimalFormatSymbols last resort data unexpectedly matches root");
231    }
232    // Here we adjust for expected differences.
233    assertEquals("last-resort grouping separator",
234                 "", lastResort->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
235    lastResort->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, ",");
236    assertEquals("last-resort monetary grouping separator",
237                 "", lastResort->getSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol));
238    lastResort->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, ",");
239    assertEquals("last-resort NaN",
240                 UnicodeString((UChar)0xfffd), lastResort->getSymbol(DecimalFormatSymbols::kNaNSymbol));
241    lastResort->setSymbol(DecimalFormatSymbols::kNaNSymbol, "NaN");
242    // Check that now all of the symbols match root.
243    for(int32_t i = 0; i < DecimalFormatSymbols::kFormatSymbolCount; ++i) {
244        DecimalFormatSymbols::ENumberFormatSymbol e = (DecimalFormatSymbols::ENumberFormatSymbol)i;
245        assertEquals("last-resort symbol vs. root", root.getSymbol(e), lastResort->getSymbol(e));
246    }
247    // Also, the CurrencySpacing patterns are empty in the last resort instance,
248    // but not in root.
249    Verify(1234567.25, "#,##0.##", *lastResort, "1,234,567.25");
250}
251
252void IntlTestDecimalFormatSymbols::testNumberingSystem() {
253    IcuTestErrorCode errorCode(*this, "testNumberingSystem");
254    struct testcase {
255        const char* locid;
256        const char* nsname;
257        const char16_t* expected1; // Expected number format string
258        const char16_t* expected2; // Expected pattern separator
259    };
260    static const testcase cases[9] = {
261            {"en", "latn", u"1,234.56", u";"},
262            {"en", "arab", u"١٬٢٣٤٫٥٦", u"؛"},
263            {"en", "mathsanb", u"��,������.����", u";"},
264            {"en", "mymr", u"၁,၂၃၄.၅၆", u";"},
265            {"my", "latn", u"1,234.56", u";"},
266            {"my", "arab", u"١٬٢٣٤٫٥٦", u"؛"},
267            {"my", "mathsanb", u"��,������.����", u";"},
268            {"my", "mymr", u"၁,၂၃၄.၅၆", u"၊"},
269            {"en@numbers=thai", "mymr", u"၁,၂၃၄.၅၆", u";"}, // conflicting numbering system
270    };
271
272    for (int i=0; i<8; i++) {
273        testcase cas = cases[i];
274        Locale loc(cas.locid);
275        LocalPointer<NumberingSystem> ns(NumberingSystem::createInstanceByName(cas.nsname, errorCode));
276        if (errorCode.logDataIfFailureAndReset("NumberingSystem failed")) {
277            return;
278        }
279        UnicodeString expected1(cas.expected1);
280        UnicodeString expected2(cas.expected2);
281        DecimalFormatSymbols dfs(loc, *ns, errorCode);
282        if (errorCode.logDataIfFailureAndReset("DecimalFormatSymbols failed")) {
283            return;
284        }
285        Verify(1234.56, "#,##0.##", dfs, expected1);
286        // The pattern separator is something that differs by numbering system in my@numbers=mymr.
287        UnicodeString actual2 = dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol);
288        if (expected2 != actual2) {
289            errln((UnicodeString)"ERROR: DecimalFormatSymbols returned pattern separator " + actual2
290                + " but we expected " + expected2);
291        }
292    }
293}
294
295void IntlTestDecimalFormatSymbols::Verify(double value, const UnicodeString& pattern,
296                                          const DecimalFormatSymbols &sym, const UnicodeString& expected){
297    UErrorCode status = U_ZERO_ERROR;
298    DecimalFormat df(pattern, sym, status);
299    if(U_FAILURE(status)){
300        errln("ERROR: construction of decimal format failed - %s", u_errorName(status));
301    }
302    UnicodeString buffer;
303    FieldPosition pos(FieldPosition::DONT_CARE);
304    buffer = df.format(value, buffer, pos);
305    if(buffer != expected){
306        errln((UnicodeString)"ERROR: format() returns wrong result\n Expected " +
307            expected + ", Got " + buffer);
308    }
309}
310
311#endif /* #if !UCONFIG_NO_FORMATTING */
312