1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3
4 #include "unicode/utypes.h"
5
6 #if !UCONFIG_NO_FORMATTING
7
8 #include "numbertest.h"
9 #include "number_patternstring.h"
10
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)11 void PatternStringTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
12 if (exec) {
13 logln("TestSuite PatternStringTest: ");
14 }
15 TESTCASE_AUTO_BEGIN;
16 TESTCASE_AUTO(testLocalized);
17 TESTCASE_AUTO(testToPatternSimple);
18 TESTCASE_AUTO(testExceptionOnInvalid);
19 TESTCASE_AUTO(testBug13117);
20 TESTCASE_AUTO(testCurrencyDecimal);
21 TESTCASE_AUTO_END;
22 }
23
testLocalized()24 void PatternStringTest::testLocalized() {
25 IcuTestErrorCode status(*this, "testLocalized");
26 DecimalFormatSymbols symbols(Locale::getEnglish(), status);
27 if (status.isFailure()) { return; }
28 symbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, u"a", status);
29 symbols.setSymbol(DecimalFormatSymbols::kPercentSymbol, u"b", status);
30 symbols.setSymbol(DecimalFormatSymbols::kMinusSignSymbol, u".", status);
31 symbols.setSymbol(DecimalFormatSymbols::kPlusSignSymbol, u"'", status);
32
33 UnicodeString standard = u"+-abcb''a''#,##0.0%'a%'";
34 UnicodeString localized = u"’.'ab'c'b''a'''#,##0a0b'a%'";
35 UnicodeString toStandard = u"+-'ab'c'b''a'''#,##0.0%'a%'";
36
37 assertEquals(
38 "standard to localized",
39 localized,
40 PatternStringUtils::convertLocalized(standard, symbols, true, status));
41 assertEquals(
42 "localized to standard",
43 toStandard,
44 PatternStringUtils::convertLocalized(localized, symbols, false, status));
45 }
46
testToPatternSimple()47 void PatternStringTest::testToPatternSimple() {
48 const char16_t* cases[][2] = {{u"#", u"0"},
49 {u"0", u"0"},
50 {u"#0", u"0"},
51 {u"###", u"0"},
52 {u"0.##", u"0.##"},
53 {u"0.00", u"0.00"},
54 {u"0.00#", u"0.00#"},
55 {u"0.05", u"0.05"},
56 {u"#E0", u"#E0"},
57 {u"0E0", u"0E0"},
58 {u"#00E00", u"#00E00"},
59 {u"#,##0", u"#,##0"},
60 {u"0¤", u"0¤"},
61 {u"0¤a", u"0¤a"},
62 {u"0¤00", u"0¤00"},
63 {u"#;#", u"0;0"},
64 // ignore a negative prefix pattern of '-' since that is the default:
65 {u"#;-#", u"0"},
66 {u"pp#,000;(#)", u"pp#,000;(#,000)"},
67 {u"**##0", u"**##0"},
68 {u"*'x'##0", u"*x##0"},
69 {u"a''b0", u"a''b0"},
70 {u"*''##0", u"*''##0"},
71 {u"*##0", u"*''##0"},
72 {u"*'நி'##0", u"*'நி'##0"},};
73
74 UErrorCode status = U_ZERO_ERROR;
75 for (const char16_t** cas : cases) {
76 UnicodeString input(cas[0]);
77 UnicodeString output(cas[1]);
78
79 DecimalFormatProperties properties = PatternParser::parseToProperties(
80 input, IGNORE_ROUNDING_NEVER, status);
81 assertSuccess(input, status);
82 UnicodeString actual = PatternStringUtils::propertiesToPatternString(properties, status);
83 assertEquals(input, output, actual);
84 status = U_ZERO_ERROR;
85 }
86 }
87
testExceptionOnInvalid()88 void PatternStringTest::testExceptionOnInvalid() {
89 static const char16_t* invalidPatterns[] = {
90 u"#.#.#",
91 u"0#",
92 u"0#.",
93 u".#0",
94 u"0#.#0",
95 u"@0",
96 u"0@",
97 u"0,",
98 u"0,,",
99 u"0,,0",
100 u"0,,0,",
101 u"#,##0E0"};
102
103 for (auto pattern : invalidPatterns) {
104 UErrorCode status = U_ZERO_ERROR;
105 ParsedPatternInfo patternInfo;
106 PatternParser::parseToPatternInfo(pattern, patternInfo, status);
107 assertTrue(pattern, U_FAILURE(status));
108 }
109 }
110
testBug13117()111 void PatternStringTest::testBug13117() {
112 UErrorCode status = U_ZERO_ERROR;
113 DecimalFormatProperties expected = PatternParser::parseToProperties(
114 u"0", IGNORE_ROUNDING_NEVER, status);
115 DecimalFormatProperties actual = PatternParser::parseToProperties(
116 u"0;", IGNORE_ROUNDING_NEVER, status);
117 assertSuccess("Spot 1", status);
118 assertTrue("Should not consume negative subpattern", expected == actual);
119 }
120
testCurrencyDecimal()121 void PatternStringTest::testCurrencyDecimal() {
122 IcuTestErrorCode status(*this, "testCurrencyDecimal");
123
124 // Manually create a NumberFormatter from a specific pattern
125 ParsedPatternInfo patternInfo;
126 PatternParser::parseToPatternInfo(u"a0¤00b", patternInfo, status);
127 MacroProps macros;
128 macros.unit = CurrencyUnit(u"EUR", status);
129 macros.affixProvider = &patternInfo;
130 LocalizedNumberFormatter nf = NumberFormatter::with().macros(macros).locale("und");
131
132 // Test that the output is as expected
133 FormattedNumber fn = nf.formatDouble(3.14, status);
134 assertEquals("Should substitute currency symbol", u"a3€14b", fn.toTempString(status));
135
136 // Test field positions
137 static const UFieldPosition expectedFieldPositions[] = {
138 {UNUM_INTEGER_FIELD, 1, 2},
139 {UNUM_CURRENCY_FIELD, 2, 3},
140 {UNUM_FRACTION_FIELD, 3, 5}};
141 checkFormattedValue(
142 u"Currency as decimal basic field positions",
143 fn,
144 u"a3€14b",
145 UFIELD_CATEGORY_NUMBER,
146 expectedFieldPositions,
147 UPRV_LENGTHOF(expectedFieldPositions)
148 );
149 }
150
151 #endif /* #if !UCONFIG_NO_FORMATTING */
152