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 propagateDigits 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