• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 using namespace icu::number::impl;
17 
18 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)19 void 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 
validTokens()35 void 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);
113         NumberFormatter::forSkeleton(skeletonString, status);
114         assertSuccess(CStr(skeletonString)(), status, true);
115         status.errIfFailureAndReset();
116     }
117 }
118 
invalidTokens()119 void NumberSkeletonTest::invalidTokens() {
120     static const char16_t* cases[] = {
121             u".00x",
122             u".00##0",
123             u".##+",
124             u".00##+",
125             u".0#+",
126             u"@@x",
127             u"@@##0",
128             u"@#+",
129             u".00/@",
130             u".00/@@",
131             u".00/@@x",
132             u".00/@@#",
133             u".00/@@#+",
134             u".00/floor/@@+", // wrong order
135             u"precision-increment/français", // non-invariant characters for C++
136             u"scientific/ee",
137             u"precision-increment/xxx",
138             u"precision-increment/NaN",
139             u"precision-increment/0.1.2",
140             u"scale/xxx",
141             u"scale/NaN",
142             u"scale/0.1.2",
143             u"scale/français", // non-invariant characters for C++
144             u"currency/dummy",
145             u"currency/ççç", // three characters but not ASCII
146             u"measure-unit/foo",
147             u"integer-width/xxx",
148             u"integer-width/0+",
149             u"integer-width/+0#",
150             u"scientific/foo"};
151 
152     expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
153 }
154 
unknownTokens()155 void NumberSkeletonTest::unknownTokens() {
156     static const char16_t* cases[] = {
157             u"maesure-unit",
158             u"measure-unit/foo-bar",
159             u"numbering-system/dummy",
160             u"français",
161             u"measure-unit/français-français", // non-invariant characters for C++
162             u"numbering-system/français", // non-invariant characters for C++
163             u"currency-USD"};
164 
165     expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
166 }
167 
unexpectedTokens()168 void NumberSkeletonTest::unexpectedTokens() {
169     static const char16_t* cases[] = {
170             u"group-thousands/foo",
171             u"precision-integer//@## group-off",
172             u"precision-integer//@##  group-off",
173             u"precision-integer/ group-off",
174             u"precision-integer// group-off"};
175 
176     expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
177 }
178 
duplicateValues()179 void NumberSkeletonTest::duplicateValues() {
180     static const char16_t* cases[] = {
181             u"precision-integer precision-integer",
182             u"precision-integer .00+",
183             u"precision-integer precision-unlimited",
184             u"precision-integer @@@",
185             u"scientific engineering",
186             u"engineering compact-long",
187             u"sign-auto sign-always"};
188 
189     expectedErrorSkeleton(cases, UPRV_LENGTHOF(cases));
190 }
191 
stemsRequiringOption()192 void NumberSkeletonTest::stemsRequiringOption() {
193     static const char16_t* stems[] = {
194             u"precision-increment",
195             u"measure-unit",
196             u"per-unit",
197             u"currency",
198             u"integer-width",
199             u"numbering-system",
200             u"scale"};
201     static const char16_t* suffixes[] = {u"", u"/@##", u" scientific", u"/@## scientific"};
202 
203     for (auto& stem : stems) {
204         for (auto& suffix : suffixes) {
205             UnicodeString skeletonString = UnicodeString(stem) + suffix;
206             UErrorCode status = U_ZERO_ERROR;
207             NumberFormatter::forSkeleton(skeletonString, status);
208             assertEquals(skeletonString, U_NUMBER_SKELETON_SYNTAX_ERROR, status);
209         }
210     }
211 }
212 
defaultTokens()213 void NumberSkeletonTest::defaultTokens() {
214     IcuTestErrorCode status(*this, "defaultTokens");
215 
216     static const char16_t* cases[] = {
217             u"notation-simple",
218             u"base-unit",
219             u"group-auto",
220             u"integer-width/+0",
221             u"sign-auto",
222             u"unit-width-short",
223             u"decimal-auto"};
224 
225     for (auto& cas : cases) {
226         UnicodeString skeletonString(cas);
227         status.setScope(skeletonString);
228         UnicodeString normalized = NumberFormatter::forSkeleton(
229                 skeletonString, status).toSkeleton(status);
230         // Skeleton should become empty when normalized
231         assertEquals(skeletonString, u"", normalized);
232         status.errIfFailureAndReset();
233     }
234 }
235 
flexibleSeparators()236 void NumberSkeletonTest::flexibleSeparators() {
237     IcuTestErrorCode status(*this, "flexibleSeparators");
238 
239     static struct TestCase {
240         const char16_t* skeleton;
241         const char16_t* expected;
242     } cases[] = {{u"precision-integer group-off", u"5142"},
243                  {u"precision-integer  group-off", u"5142"},
244                  {u"precision-integer/@## group-off", u"5140"},
245                  {u"precision-integer/@##  group-off", u"5140"}};
246 
247     for (auto& cas : cases) {
248         UnicodeString skeletonString(cas.skeleton);
249         UnicodeString expected(cas.expected);
250         status.setScope(skeletonString);
251         UnicodeString actual = NumberFormatter::forSkeleton(skeletonString, status).locale("en")
252                                .formatDouble(5142.3, status)
253                                .toString();
254         if (!status.errDataIfFailureAndReset()) {
255             assertEquals(skeletonString, expected, actual);
256         }
257         status.errIfFailureAndReset();
258     }
259 }
260 
261 // In C++, there is no distinguishing between "invalid", "unknown", and "unexpected" tokens.
expectedErrorSkeleton(const char16_t ** cases,int32_t casesLen)262 void NumberSkeletonTest::expectedErrorSkeleton(const char16_t** cases, int32_t casesLen) {
263     for (int32_t i = 0; i < casesLen; i++) {
264         UnicodeString skeletonString(cases[i]);
265         UErrorCode status = U_ZERO_ERROR;
266         NumberFormatter::forSkeleton(skeletonString, status);
267         assertEquals(skeletonString, U_NUMBER_SKELETON_SYNTAX_ERROR, status);
268     }
269 }
270 
271 
272 #endif /* #if !UCONFIG_NO_FORMATTING */
273