1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
8 #include "unicode/dcfmtsym.h"
11 #include "numbertest.h"
12 #include "number_utils.h"
13 #include "number_skeletons.h"
16 using namespace icu::number::impl
;
19 void NumberSkeletonTest::runIndexedTest(int32_t index
, UBool exec
, const char*& name
, char*) {
21 logln("TestSuite AffixUtilsTest: ");
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
);
35 void NumberSkeletonTest::validTokens() {
36 IcuTestErrorCode
status(*this, "validTokens");
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
[] = {
42 u
"precision-unlimited",
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",
59 u
"scientific/sign-always",
60 u
"scientific/+ee/sign-always",
61 u
"scientific/sign-always/+ee",
62 u
"scientific/sign-except-zero",
70 u
"measure-unit/length-meter",
71 u
"measure-unit/area-square-meter",
72 u
"measure-unit/energy-joule per-measure-unit/length-meter",
88 u
"sign-accounting-always",
90 u
"sign-accounting-except-zero",
93 u
"unit-width-iso-code",
94 u
"unit-width-full-name",
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"};
110 for (auto& cas
: cases
) {
111 UnicodeString
skeletonString(cas
);
112 status
.setScope(skeletonString
);
114 NumberFormatter::forSkeleton(skeletonString
, perror
, status
);
115 assertSuccess(CStr(skeletonString
)(), status
, true);
116 assertEquals(skeletonString
, -1, perror
.offset
);
117 status
.errIfFailureAndReset();
121 void NumberSkeletonTest::invalidTokens() {
122 static const char16_t* cases
[] = {
136 u
".00/floor/@@+", // wrong order
137 u
"precision-increment/français", // non-invariant characters for C++
139 u
"precision-increment/xxx",
140 u
"precision-increment/NaN",
141 u
"precision-increment/0.1.2",
145 u
"scale/français", // non-invariant characters for C++
147 u
"currency/ççç", // three characters but not ASCII
149 u
"integer-width/xxx",
151 u
"integer-width/+0#",
153 u
"integer-width/+#0",
156 expectedErrorSkeleton(cases
, UPRV_LENGTHOF(cases
));
159 void NumberSkeletonTest::unknownTokens() {
160 static const char16_t* cases
[] = {
162 u
"measure-unit/foo-bar",
163 u
"numbering-system/dummy",
165 u
"measure-unit/français-français", // non-invariant characters for C++
166 u
"numbering-system/français", // non-invariant characters for C++
169 expectedErrorSkeleton(cases
, UPRV_LENGTHOF(cases
));
172 void 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"};
180 expectedErrorSkeleton(cases
, UPRV_LENGTHOF(cases
));
183 void 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"};
193 expectedErrorSkeleton(cases
, UPRV_LENGTHOF(cases
));
196 void NumberSkeletonTest::stemsRequiringOption() {
197 static const char16_t* stems
[] = {
198 u
"precision-increment",
205 static const char16_t* suffixes
[] = {u
"", u
"/@##", u
" scientific", u
"/@## scientific"};
207 for (auto& stem
: stems
) {
208 for (auto& suffix
: suffixes
) {
209 UnicodeString skeletonString
= UnicodeString(stem
) + suffix
;
210 UErrorCode status
= U_ZERO_ERROR
;
212 NumberFormatter::forSkeleton(skeletonString
, perror
, status
);
213 assertEquals(skeletonString
, U_NUMBER_SKELETON_SYNTAX_ERROR
, status
);
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);
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
);
232 void NumberSkeletonTest::defaultTokens() {
233 IcuTestErrorCode
status(*this, "defaultTokens");
235 static const char16_t* cases
[] = {
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();
255 void NumberSkeletonTest::flexibleSeparators() {
256 IcuTestErrorCode
status(*this, "flexibleSeparators");
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"}};
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
)
273 if (!status
.errDataIfFailureAndReset()) {
274 assertEquals(skeletonString
, expected
, actual
);
276 status
.errIfFailureAndReset();
280 // In C++, there is no distinguishing between "invalid", "unknown", and "unexpected" tokens.
281 void 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
);
291 #endif /* #if !UCONFIG_NO_FORMATTING */