]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/intltest/numbertest_skeletons.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / test / intltest / numbertest_skeletons.cpp
CommitLineData
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 "unicode/dcfmtsym.h"
9
10#include "cstr.h"
11#include "numbertest.h"
12#include "number_utils.h"
13#include "number_skeletons.h"
14#include "putilimp.h"
15
16using namespace icu::number::impl;
17
18
19void NumberSkeletonTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
20 if (exec) {
21 logln("TestSuite AffixUtilsTest: ");
22 }
23 TESTCASE_AUTO_BEGIN;
24 TESTCASE_AUTO(validTokens);
25 TESTCASE_AUTO(invalidTokens);
26 TESTCASE_AUTO(unknownTokens);
27 TESTCASE_AUTO(unexpectedTokens);
28 TESTCASE_AUTO(duplicateValues);
29 TESTCASE_AUTO(stemsRequiringOption);
30 TESTCASE_AUTO(defaultTokens);
31 TESTCASE_AUTO(flexibleSeparators);
32 TESTCASE_AUTO_END;
33}
34
35void NumberSkeletonTest::validTokens() {
36 IcuTestErrorCode status(*this, "validTokens");
37
38 // This tests only if the tokens are valid, not their behavior.
39 // Most of these are from the design doc.
40 static const char16_t* cases[] = {
41 u"precision-integer",
42 u"precision-unlimited",
43 u"@@@##",
44 u"@@+",
45 u".000##",
46 u".00+",
47 u".",
48 u".+",
49 u".######",
50 u".00/@@+",
51 u".00/@##",
52 u"precision-increment/3.14",
53 u"precision-currency-standard",
54 u"precision-integer rounding-mode-half-up",
55 u".00# rounding-mode-ceiling",
56 u".00/@@+ rounding-mode-floor",
57 u"scientific",
58 u"scientific/+ee",
59 u"scientific/sign-always",
60 u"scientific/+ee/sign-always",
61 u"scientific/sign-always/+ee",
62 u"scientific/sign-except-zero",
63 u"engineering",
64 u"engineering/+eee",
65 u"compact-short",
66 u"compact-long",
67 u"notation-simple",
68 u"percent",
69 u"permille",
70 u"measure-unit/length-meter",
71 u"measure-unit/area-square-meter",
72 u"measure-unit/energy-joule per-measure-unit/length-meter",
73 u"currency/XXX",
74 u"currency/ZZZ",
75 u"currency/usd",
76 u"group-off",
77 u"group-min2",
78 u"group-auto",
79 u"group-on-aligned",
80 u"group-thousands",
81 u"integer-width/00",
82 u"integer-width/#0",
83 u"integer-width/+00",
84 u"sign-always",
85 u"sign-auto",
86 u"sign-never",
87 u"sign-accounting",
88 u"sign-accounting-always",
89 u"sign-except-zero",
90 u"sign-accounting-except-zero",
91 u"unit-width-narrow",
92 u"unit-width-short",
93 u"unit-width-iso-code",
94 u"unit-width-full-name",
95 u"unit-width-hidden",
96 u"decimal-auto",
97 u"decimal-always",
98 u"scale/5.2",
99 u"scale/-5.2",
100 u"scale/100",
101 u"scale/1E2",
102 u"scale/1",
103 u"latin",
104 u"numbering-system/arab",
105 u"numbering-system/latn",
106 u"precision-integer/@##",
107 u"precision-integer rounding-mode-ceiling",
108 u"precision-currency-cash rounding-mode-ceiling"};
109
110 for (auto& cas : cases) {
111 UnicodeString skeletonString(cas);
112 status.setScope(skeletonString);
3d1f044b
A
113 UParseError perror;
114 NumberFormatter::forSkeleton(skeletonString, perror, status);
0f5d89e8 115 assertSuccess(CStr(skeletonString)(), status, true);
3d1f044b 116 assertEquals(skeletonString, -1, perror.offset);
0f5d89e8
A
117 status.errIfFailureAndReset();
118 }
119}
120
121void NumberSkeletonTest::invalidTokens() {
122 static const char16_t* cases[] = {
123 u".00x",
124 u".00##0",
125 u".##+",
126 u".00##+",
127 u".0#+",
128 u"@@x",
129 u"@@##0",
130 u"@#+",
131 u".00/@",
132 u".00/@@",
133 u".00/@@x",
134 u".00/@@#",
135 u".00/@@#+",
136 u".00/floor/@@+", // wrong order
137 u"precision-increment/français", // non-invariant characters for C++
138 u"scientific/ee",
139 u"precision-increment/xxx",
140 u"precision-increment/NaN",
141 u"precision-increment/0.1.2",
142 u"scale/xxx",
143 u"scale/NaN",
144 u"scale/0.1.2",
145 u"scale/français", // non-invariant characters for C++
146 u"currency/dummy",
147 u"currency/ççç", // three characters but not ASCII
148 u"measure-unit/foo",
149 u"integer-width/xxx",
150 u"integer-width/0+",
151 u"integer-width/+0#",
340931cb
A
152 u"integer-width/+#",
153 u"integer-width/+#0",
0f5d89e8
A
154 u"scientific/foo"};
155
156 expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
157}
158
159void NumberSkeletonTest::unknownTokens() {
160 static const char16_t* cases[] = {
161 u"maesure-unit",
162 u"measure-unit/foo-bar",
163 u"numbering-system/dummy",
164 u"français",
165 u"measure-unit/français-français", // non-invariant characters for C++
166 u"numbering-system/français", // non-invariant characters for C++
167 u"currency-USD"};
168
169 expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
170}
171
172void NumberSkeletonTest::unexpectedTokens() {
173 static const char16_t* cases[] = {
174 u"group-thousands/foo",
175 u"precision-integer//@## group-off",
176 u"precision-integer//@## group-off",
177 u"precision-integer/ group-off",
178 u"precision-integer// group-off"};
179
180 expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
181}
182
183void NumberSkeletonTest::duplicateValues() {
184 static const char16_t* cases[] = {
185 u"precision-integer precision-integer",
186 u"precision-integer .00+",
187 u"precision-integer precision-unlimited",
188 u"precision-integer @@@",
189 u"scientific engineering",
190 u"engineering compact-long",
191 u"sign-auto sign-always"};
192
193 expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
194}
195
196void NumberSkeletonTest::stemsRequiringOption() {
197 static const char16_t* stems[] = {
198 u"precision-increment",
199 u"measure-unit",
3d1f044b 200 u"per-measure-unit",
0f5d89e8
A
201 u"currency",
202 u"integer-width",
203 u"numbering-system",
204 u"scale"};
205 static const char16_t* suffixes[] = {u"", u"/@##", u" scientific", u"/@## scientific"};
206
207 for (auto& stem : stems) {
208 for (auto& suffix : suffixes) {
209 UnicodeString skeletonString = UnicodeString(stem) + suffix;
210 UErrorCode status = U_ZERO_ERROR;
3d1f044b
A
211 UParseError perror;
212 NumberFormatter::forSkeleton(skeletonString, perror, status);
0f5d89e8 213 assertEquals(skeletonString, U_NUMBER_SKELETON_SYNTAX_ERROR, status);
3d1f044b
A
214
215 // Check the UParseError for integrity.
216 // If an option is present, the option is wrong; error offset is at the start of the option
217 // If an option is not present, the error offset is at the token separator (end of stem)
218 int32_t expectedOffset = u_strlen(stem) + ((suffix[0] == u'/') ? 1 : 0);
219 assertEquals(skeletonString, expectedOffset, perror.offset);
220 UnicodeString expectedPreContext = skeletonString.tempSubString(0, expectedOffset);
221 if (expectedPreContext.length() >= U_PARSE_CONTEXT_LEN - 1) {
222 expectedPreContext = expectedPreContext.tempSubString(expectedOffset - U_PARSE_CONTEXT_LEN + 1);
223 }
224 assertEquals(skeletonString, expectedPreContext, perror.preContext);
225 UnicodeString expectedPostContext = skeletonString.tempSubString(expectedOffset);
226 // None of the postContext strings in this test exceed U_PARSE_CONTEXT_LEN
227 assertEquals(skeletonString, expectedPostContext, perror.postContext);
0f5d89e8
A
228 }
229 }
230}
231
232void NumberSkeletonTest::defaultTokens() {
233 IcuTestErrorCode status(*this, "defaultTokens");
234
235 static const char16_t* cases[] = {
236 u"notation-simple",
237 u"base-unit",
238 u"group-auto",
239 u"integer-width/+0",
240 u"sign-auto",
241 u"unit-width-short",
242 u"decimal-auto"};
243
244 for (auto& cas : cases) {
245 UnicodeString skeletonString(cas);
246 status.setScope(skeletonString);
247 UnicodeString normalized = NumberFormatter::forSkeleton(
248 skeletonString, status).toSkeleton(status);
249 // Skeleton should become empty when normalized
250 assertEquals(skeletonString, u"", normalized);
251 status.errIfFailureAndReset();
252 }
253}
254
255void NumberSkeletonTest::flexibleSeparators() {
256 IcuTestErrorCode status(*this, "flexibleSeparators");
257
258 static struct TestCase {
259 const char16_t* skeleton;
260 const char16_t* expected;
261 } cases[] = {{u"precision-integer group-off", u"5142"},
262 {u"precision-integer group-off", u"5142"},
263 {u"precision-integer/@## group-off", u"5140"},
264 {u"precision-integer/@## group-off", u"5140"}};
265
266 for (auto& cas : cases) {
267 UnicodeString skeletonString(cas.skeleton);
268 UnicodeString expected(cas.expected);
269 status.setScope(skeletonString);
270 UnicodeString actual = NumberFormatter::forSkeleton(skeletonString, status).locale("en")
271 .formatDouble(5142.3, status)
3d1f044b 272 .toString(status);
0f5d89e8
A
273 if (!status.errDataIfFailureAndReset()) {
274 assertEquals(skeletonString, expected, actual);
275 }
276 status.errIfFailureAndReset();
277 }
278}
279
280// In C++, there is no distinguishing between "invalid", "unknown", and "unexpected" tokens.
281void NumberSkeletonTest::expectedErrorSkeleton(const char16_t** cases, int32_t casesLen) {
282 for (int32_t i = 0; i < casesLen; i++) {
283 UnicodeString skeletonString(cases[i]);
284 UErrorCode status = U_ZERO_ERROR;
285 NumberFormatter::forSkeleton(skeletonString, status);
286 assertEquals(skeletonString, U_NUMBER_SKELETON_SYNTAX_ERROR, status);
287 }
288}
289
290
291#endif /* #if !UCONFIG_NO_FORMATTING */