]>
Commit | Line | Data |
---|---|---|
0f5d89e8 A |
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 "putilimp.h" | |
9 | #include "unicode/dcfmtsym.h" | |
10 | #include "numbertest.h" | |
11 | #include "number_utils.h" | |
12 | ||
13 | using namespace icu::number::impl; | |
14 | ||
15 | class DefaultSymbolProvider : public SymbolProvider { | |
16 | DecimalFormatSymbols fSymbols; | |
17 | ||
18 | public: | |
19 | DefaultSymbolProvider(UErrorCode &status) : fSymbols(Locale("ar_SA"), status) {} | |
20 | ||
21 | UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE { | |
22 | switch (type) { | |
23 | case TYPE_MINUS_SIGN: | |
24 | return u"−"; | |
25 | case TYPE_PLUS_SIGN: | |
26 | return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); | |
27 | case TYPE_PERCENT: | |
28 | return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); | |
29 | case TYPE_PERMILLE: | |
30 | return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); | |
31 | case TYPE_CURRENCY_SINGLE: | |
32 | return u"$"; | |
33 | case TYPE_CURRENCY_DOUBLE: | |
34 | return u"XXX"; | |
35 | case TYPE_CURRENCY_TRIPLE: | |
36 | return u"long name"; | |
37 | case TYPE_CURRENCY_QUAD: | |
38 | return u"\uFFFD"; | |
39 | case TYPE_CURRENCY_QUINT: | |
40 | // TODO: Add support for narrow currency symbols here. | |
41 | return u"\uFFFD"; | |
42 | case TYPE_CURRENCY_OVERFLOW: | |
43 | return u"\uFFFD"; | |
44 | default: | |
3d1f044b | 45 | UPRV_UNREACHABLE; |
0f5d89e8 A |
46 | } |
47 | } | |
48 | }; | |
49 | ||
50 | void AffixUtilsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) { | |
51 | if (exec) { | |
52 | logln("TestSuite AffixUtilsTest: "); | |
53 | } | |
54 | TESTCASE_AUTO_BEGIN; | |
55 | TESTCASE_AUTO(testEscape); | |
56 | TESTCASE_AUTO(testUnescape); | |
57 | TESTCASE_AUTO(testContainsReplaceType); | |
58 | TESTCASE_AUTO(testInvalid); | |
59 | TESTCASE_AUTO(testUnescapeWithSymbolProvider); | |
60 | TESTCASE_AUTO_END; | |
61 | } | |
62 | ||
63 | void AffixUtilsTest::testEscape() { | |
64 | static const char16_t *cases[][2] = {{u"", u""}, | |
65 | {u"abc", u"abc"}, | |
66 | {u"-", u"'-'"}, | |
67 | {u"-!", u"'-'!"}, | |
68 | {u"−", u"−"}, | |
69 | {u"---", u"'---'"}, | |
70 | {u"-%-", u"'-%-'"}, | |
71 | {u"'", u"''"}, | |
72 | {u"-'", u"'-'''"}, | |
73 | {u"-'-", u"'-''-'"}, | |
74 | {u"a-'-", u"a'-''-'"}}; | |
75 | ||
76 | for (auto &cas : cases) { | |
77 | UnicodeString input(cas[0]); | |
78 | UnicodeString expected(cas[1]); | |
79 | UnicodeString result = AffixUtils::escape(input); | |
80 | assertEquals(input, expected, result); | |
81 | } | |
82 | } | |
83 | ||
84 | void AffixUtilsTest::testUnescape() { | |
85 | static struct TestCase { | |
86 | const char16_t *input; | |
87 | bool currency; | |
88 | int32_t expectedLength; | |
89 | const char16_t *output; | |
90 | } cases[] = {{u"", false, 0, u""}, | |
91 | {u"abc", false, 3, u"abc"}, | |
92 | {u"-", false, 1, u"−"}, | |
93 | {u"-!", false, 2, u"−!"}, | |
94 | {u"+", false, 1, u"\u061C+"}, | |
95 | {u"+!", false, 2, u"\u061C+!"}, | |
96 | {u"‰", false, 1, u"؉"}, | |
97 | {u"‰!", false, 2, u"؉!"}, | |
98 | {u"-x", false, 2, u"−x"}, | |
99 | {u"'-'x", false, 2, u"-x"}, | |
100 | {u"'--''-'-x", false, 6, u"--'-−x"}, | |
101 | {u"''", false, 1, u"'"}, | |
102 | {u"''''", false, 2, u"''"}, | |
103 | {u"''''''", false, 3, u"'''"}, | |
104 | {u"''x''", false, 3, u"'x'"}, | |
105 | {u"¤", true, 1, u"$"}, | |
106 | {u"¤¤", true, 2, u"XXX"}, | |
107 | {u"¤¤¤", true, 3, u"long name"}, | |
108 | {u"¤¤¤¤", true, 4, u"\uFFFD"}, | |
109 | {u"¤¤¤¤¤", true, 5, u"\uFFFD"}, | |
110 | {u"¤¤¤¤¤¤", true, 6, u"\uFFFD"}, | |
111 | {u"¤¤¤a¤¤¤¤", true, 8, u"long namea\uFFFD"}, | |
112 | {u"a¤¤¤¤b¤¤¤¤¤c", true, 12, u"a\uFFFDb\uFFFDc"}, | |
113 | {u"¤!", true, 2, u"$!"}, | |
114 | {u"¤¤!", true, 3, u"XXX!"}, | |
115 | {u"¤¤¤!", true, 4, u"long name!"}, | |
116 | {u"-¤¤", true, 3, u"−XXX"}, | |
117 | {u"¤¤-", true, 3, u"XXX−"}, | |
118 | {u"'¤'", false, 1, u"¤"}, | |
119 | {u"%", false, 1, u"٪\u061C"}, | |
120 | {u"'%'", false, 1, u"%"}, | |
121 | {u"¤'-'%", true, 3, u"$-٪\u061C"}, | |
122 | {u"#0#@#*#;#", false, 9, u"#0#@#*#;#"}}; | |
123 | ||
124 | UErrorCode status = U_ZERO_ERROR; | |
125 | DefaultSymbolProvider defaultProvider(status); | |
126 | assertSuccess("Constructing DefaultSymbolProvider", status); | |
127 | ||
128 | for (TestCase cas : cases) { | |
129 | UnicodeString input(cas.input); | |
130 | UnicodeString output(cas.output); | |
131 | ||
132 | assertEquals(input, cas.currency, AffixUtils::hasCurrencySymbols(input, status)); | |
133 | assertSuccess("Spot 1", status); | |
134 | assertEquals(input, cas.expectedLength, AffixUtils::estimateLength(input, status)); | |
135 | assertSuccess("Spot 2", status); | |
136 | ||
137 | UnicodeString actual = unescapeWithDefaults(defaultProvider, input, status); | |
138 | assertSuccess("Spot 3", status); | |
139 | assertEquals(input, output, actual); | |
140 | ||
141 | int32_t ulength = AffixUtils::unescapedCodePointCount(input, defaultProvider, status); | |
142 | assertSuccess("Spot 4", status); | |
143 | assertEquals(input, output.countChar32(), ulength); | |
144 | } | |
145 | } | |
146 | ||
147 | void AffixUtilsTest::testContainsReplaceType() { | |
148 | static struct TestCase { | |
149 | const char16_t *input; | |
150 | bool hasMinusSign; | |
151 | const char16_t *output; | |
152 | } cases[] = {{u"", false, u""}, | |
153 | {u"-", true, u"+"}, | |
154 | {u"-a", true, u"+a"}, | |
155 | {u"a-", true, u"a+"}, | |
156 | {u"a-b", true, u"a+b"}, | |
157 | {u"--", true, u"++"}, | |
158 | {u"x", false, u"x"}}; | |
159 | ||
160 | UErrorCode status = U_ZERO_ERROR; | |
161 | for (TestCase cas : cases) { | |
162 | UnicodeString input(cas.input); | |
163 | bool hasMinusSign = cas.hasMinusSign; | |
164 | UnicodeString output(cas.output); | |
165 | ||
166 | assertEquals( | |
167 | input, hasMinusSign, AffixUtils::containsType(input, TYPE_MINUS_SIGN, status)); | |
168 | assertSuccess("Spot 1", status); | |
169 | assertEquals( | |
170 | input, output, AffixUtils::replaceType(input, TYPE_MINUS_SIGN, u'+', status)); | |
171 | assertSuccess("Spot 2", status); | |
172 | } | |
173 | } | |
174 | ||
175 | void AffixUtilsTest::testInvalid() { | |
176 | static const char16_t *invalidExamples[] = { | |
177 | u"'", u"x'", u"'x", u"'x''", u"''x'"}; | |
178 | ||
179 | UErrorCode status = U_ZERO_ERROR; | |
180 | DefaultSymbolProvider defaultProvider(status); | |
181 | assertSuccess("Constructing DefaultSymbolProvider", status); | |
182 | ||
183 | for (const char16_t *strPtr : invalidExamples) { | |
184 | UnicodeString str(strPtr); | |
185 | ||
186 | status = U_ZERO_ERROR; | |
187 | AffixUtils::hasCurrencySymbols(str, status); | |
188 | assertEquals("Should set error code spot 1", status, U_ILLEGAL_ARGUMENT_ERROR); | |
189 | ||
190 | status = U_ZERO_ERROR; | |
191 | AffixUtils::estimateLength(str, status); | |
192 | assertEquals("Should set error code spot 2", status, U_ILLEGAL_ARGUMENT_ERROR); | |
193 | ||
194 | status = U_ZERO_ERROR; | |
195 | unescapeWithDefaults(defaultProvider, str, status); | |
196 | assertEquals("Should set error code spot 3", status, U_ILLEGAL_ARGUMENT_ERROR); | |
197 | } | |
198 | } | |
199 | ||
200 | class NumericSymbolProvider : public SymbolProvider { | |
201 | public: | |
202 | virtual UnicodeString getSymbol(AffixPatternType type) const { | |
203 | return Int64ToUnicodeString(type < 0 ? -type : type); | |
204 | } | |
205 | }; | |
206 | ||
207 | void AffixUtilsTest::testUnescapeWithSymbolProvider() { | |
208 | static const char16_t* cases[][2] = { | |
209 | {u"", u""}, | |
210 | {u"-", u"1"}, | |
211 | {u"'-'", u"-"}, | |
212 | {u"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", u"1 2 3 4 5 6 7 8 9"}, | |
213 | {u"'¤¤¤¤¤¤'", u"¤¤¤¤¤¤"}, | |
214 | {u"¤¤¤¤¤¤", u"\uFFFD"} | |
215 | }; | |
216 | ||
217 | NumericSymbolProvider provider; | |
218 | ||
219 | UErrorCode status = U_ZERO_ERROR; | |
220 | NumberStringBuilder sb; | |
221 | for (auto& cas : cases) { | |
222 | UnicodeString input(cas[0]); | |
223 | UnicodeString expected(cas[1]); | |
224 | sb.clear(); | |
3d1f044b | 225 | AffixUtils::unescape(input, sb, 0, provider, UNUM_FIELD_COUNT, status); |
0f5d89e8 A |
226 | assertSuccess("Spot 1", status); |
227 | assertEquals(input, expected, sb.toUnicodeString()); | |
228 | assertEquals(input, expected, sb.toTempUnicodeString()); | |
229 | } | |
230 | ||
231 | // Test insertion position | |
232 | sb.clear(); | |
233 | sb.append(u"abcdefg", UNUM_FIELD_COUNT, status); | |
234 | assertSuccess("Spot 2", status); | |
3d1f044b | 235 | AffixUtils::unescape(u"-+%", sb, 4, provider, UNUM_FIELD_COUNT, status); |
0f5d89e8 A |
236 | assertSuccess("Spot 3", status); |
237 | assertEquals(u"Symbol provider into middle", u"abcd123efg", sb.toUnicodeString()); | |
238 | } | |
239 | ||
240 | UnicodeString AffixUtilsTest::unescapeWithDefaults(const SymbolProvider &defaultProvider, | |
241 | UnicodeString input, UErrorCode &status) { | |
242 | NumberStringBuilder nsb; | |
3d1f044b | 243 | int32_t length = AffixUtils::unescape(input, nsb, 0, defaultProvider, UNUM_FIELD_COUNT, status); |
0f5d89e8 A |
244 | assertEquals("Return value of unescape", nsb.length(), length); |
245 | return nsb.toUnicodeString(); | |
246 | } | |
247 | ||
248 | #endif /* #if !UCONFIG_NO_FORMATTING */ |