1 // © 2018 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 // Allow implicit conversion from char16_t* to UnicodeString for this file:
9 // Helpful in toString methods and elsewhere.
10 #define UNISTR_FROM_STRING_EXPLICIT
11
12 #include <stdio.h>
13 #include "unicode/unumberformatter.h"
14 #include "unicode/umisc.h"
15 #include "unicode/unum.h"
16 #include "unicode/ustring.h"
17 #include "cformtst.h"
18 #include "cintltst.h"
19 #include "cmemory.h"
20
21 static void TestSkeletonFormatToString(void);
22
23 static void TestSkeletonFormatToFields(void);
24
25 static void TestExampleCode(void);
26
27 static void TestFormattedValue(void);
28
29 static void TestSkeletonParseError(void);
30
31 static void TestToDecimalNumber(void);
32
33 static void TestPerUnitInArabic(void);
34
35 static void Test21674_State(void);
36
37 void addUNumberFormatterTest(TestNode** root);
38
39 #define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
40
addUNumberFormatterTest(TestNode ** root)41 void addUNumberFormatterTest(TestNode** root) {
42 TESTCASE(TestSkeletonFormatToString);
43 TESTCASE(TestSkeletonFormatToFields);
44 TESTCASE(TestExampleCode);
45 TESTCASE(TestFormattedValue);
46 TESTCASE(TestSkeletonParseError);
47 TESTCASE(TestToDecimalNumber);
48 TESTCASE(TestPerUnitInArabic);
49 TESTCASE(Test21674_State);
50 }
51
52
53 #define CAPACITY 30
54
TestSkeletonFormatToString()55 static void TestSkeletonFormatToString() {
56 UErrorCode ec = U_ZERO_ERROR;
57 UChar buffer[CAPACITY];
58 UFormattedNumber* result = NULL;
59
60 // setup:
61 UNumberFormatter* f = unumf_openForSkeletonAndLocale(
62 u"precision-integer currency/USD sign-accounting", -1, "en", &ec);
63 assertSuccessCheck("Should create without error", &ec, TRUE);
64 result = unumf_openResult(&ec);
65 assertSuccess("Should create result without error", &ec);
66
67 // int64 test:
68 unumf_formatInt(f, -444444, result, &ec);
69 // Missing data will give a U_MISSING_RESOURCE_ERROR here.
70 if (assertSuccessCheck("Should format integer without error", &ec, TRUE)) {
71 unumf_resultToString(result, buffer, CAPACITY, &ec);
72 assertSuccess("Should print string to buffer without error", &ec);
73 assertUEquals("Should produce expected string result", u"($444,444)", buffer);
74
75 // double test:
76 unumf_formatDouble(f, -5142.3, result, &ec);
77 assertSuccess("Should format double without error", &ec);
78 unumf_resultToString(result, buffer, CAPACITY, &ec);
79 assertSuccess("Should print string to buffer without error", &ec);
80 assertUEquals("Should produce expected string result", u"($5,142)", buffer);
81
82 // decnumber test:
83 unumf_formatDecimal(f, "9.876E2", -1, result, &ec);
84 assertSuccess("Should format decimal without error", &ec);
85 unumf_resultToString(result, buffer, CAPACITY, &ec);
86 assertSuccess("Should print string to buffer without error", &ec);
87 assertUEquals("Should produce expected string result", u"$988", buffer);
88 }
89
90 // cleanup:
91 unumf_closeResult(result);
92 unumf_close(f);
93 }
94
95
TestSkeletonFormatToFields()96 static void TestSkeletonFormatToFields() {
97 UErrorCode ec = U_ZERO_ERROR;
98 UFieldPositionIterator* ufpositer = NULL;
99
100 // setup:
101 UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(
102 u".00 measure-unit/length-meter sign-always", -1, "en", &ec);
103 assertSuccessCheck("Should create without error", &ec, TRUE);
104 UFormattedNumber* uresult = unumf_openResult(&ec);
105 assertSuccess("Should create result without error", &ec);
106 unumf_formatInt(uformatter, 9876543210L, uresult, &ec); // "+9,876,543,210.00 m"
107 if (assertSuccessCheck("unumf_formatInt() failed", &ec, TRUE)) {
108
109 // field position test:
110 UFieldPosition ufpos = {UNUM_DECIMAL_SEPARATOR_FIELD, 0, 0};
111 unumf_resultNextFieldPosition(uresult, &ufpos, &ec);
112 assertIntEquals("Field position should be correct", 14, ufpos.beginIndex);
113 assertIntEquals("Field position should be correct", 15, ufpos.endIndex);
114
115 // field position iterator test:
116 ufpositer = ufieldpositer_open(&ec);
117 if (assertSuccessCheck("Should create iterator without error", &ec, TRUE)) {
118
119 unumf_resultGetAllFieldPositions(uresult, ufpositer, &ec);
120 static const UFieldPosition expectedFields[] = {
121 // Field, begin index, end index
122 {UNUM_SIGN_FIELD, 0, 1},
123 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
124 {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
125 {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
126 {UNUM_INTEGER_FIELD, 1, 14},
127 {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
128 {UNUM_FRACTION_FIELD, 15, 17},
129 {UNUM_MEASURE_UNIT_FIELD, 18, 19}
130 };
131 UFieldPosition actual;
132 for (int32_t i = 0; i < (int32_t)(sizeof(expectedFields) / sizeof(*expectedFields)); i++) {
133 // Iterate using the UFieldPosition to hold state...
134 UFieldPosition expected = expectedFields[i];
135 actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex);
136 assertTrue("Should not return a negative index yet", actual.field >= 0);
137 if (expected.field != actual.field) {
138 log_err(
139 "FAIL: iteration %d; expected field %d; got %d\n", i, expected.field, actual.field);
140 }
141 if (expected.beginIndex != actual.beginIndex) {
142 log_err(
143 "FAIL: iteration %d; expected beginIndex %d; got %d\n",
144 i,
145 expected.beginIndex,
146 actual.beginIndex);
147 }
148 if (expected.endIndex != actual.endIndex) {
149 log_err(
150 "FAIL: iteration %d; expected endIndex %d; got %d\n",
151 i,
152 expected.endIndex,
153 actual.endIndex);
154 }
155 }
156 actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex);
157 assertTrue("No more fields; should return a negative index", actual.field < 0);
158
159 // next field iteration:
160 actual.field = UNUM_GROUPING_SEPARATOR_FIELD;
161 actual.beginIndex = 0;
162 actual.endIndex = 0;
163 int32_t i = 1;
164 while (unumf_resultNextFieldPosition(uresult, &actual, &ec)) {
165 UFieldPosition expected = expectedFields[i++];
166 assertIntEquals("Grouping separator begin index", expected.beginIndex, actual.beginIndex);
167 assertIntEquals("Grouping separator end index", expected.endIndex, actual.endIndex);
168 }
169 assertIntEquals("Should have seen all grouping separators", 4, i);
170 }
171 }
172
173 // cleanup:
174 unumf_closeResult(uresult);
175 unumf_close(uformatter);
176 ufieldpositer_close(ufpositer);
177 }
178
179
TestExampleCode()180 static void TestExampleCode() {
181 // This is the example code given in unumberformatter.h.
182
183 // Setup:
184 UErrorCode ec = U_ZERO_ERROR;
185 UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(u"precision-integer", -1, "en", &ec);
186 UFormattedNumber* uresult = unumf_openResult(&ec);
187 UChar* buffer = NULL;
188 assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
189
190 // Format a double:
191 unumf_formatDouble(uformatter, 5142.3, uresult, &ec);
192 if (assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE)) {
193
194 // Export the string to a malloc'd buffer:
195 int32_t len = unumf_resultToString(uresult, NULL, 0, &ec);
196 assertTrue("No buffer yet", ec == U_BUFFER_OVERFLOW_ERROR);
197 ec = U_ZERO_ERROR;
198 buffer = (UChar*) uprv_malloc((len+1)*sizeof(UChar));
199 unumf_resultToString(uresult, buffer, len+1, &ec);
200 assertSuccess("There should not be a failure in the example code", &ec);
201 assertUEquals("Should produce expected string result", u"5,142", buffer);
202 }
203
204 // Cleanup:
205 unumf_close(uformatter);
206 unumf_closeResult(uresult);
207 uprv_free(buffer);
208 }
209
210
TestFormattedValue()211 static void TestFormattedValue() {
212 UErrorCode ec = U_ZERO_ERROR;
213 UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(
214 u".00 compact-short", -1, "en", &ec);
215 assertSuccessCheck("Should create without error", &ec, TRUE);
216 UFormattedNumber* uresult = unumf_openResult(&ec);
217 assertSuccess("Should create result without error", &ec);
218
219 unumf_formatInt(uformatter, 55000, uresult, &ec); // "55.00 K"
220 if (assertSuccessCheck("Should format without error", &ec, TRUE)) {
221 const UFormattedValue* fv = unumf_resultAsValue(uresult, &ec);
222 assertSuccess("Should convert without error", &ec);
223 static const UFieldPosition expectedFieldPositions[] = {
224 // field, begin index, end index
225 {UNUM_INTEGER_FIELD, 0, 2},
226 {UNUM_DECIMAL_SEPARATOR_FIELD, 2, 3},
227 {UNUM_FRACTION_FIELD, 3, 5},
228 {UNUM_COMPACT_FIELD, 5, 6}};
229 checkFormattedValue(
230 "FormattedNumber as FormattedValue",
231 fv,
232 u"55.00K",
233 UFIELD_CATEGORY_NUMBER,
234 expectedFieldPositions,
235 UPRV_LENGTHOF(expectedFieldPositions));
236 }
237
238 // cleanup:
239 unumf_closeResult(uresult);
240 unumf_close(uformatter);
241 }
242
243
TestSkeletonParseError()244 static void TestSkeletonParseError() {
245 UErrorCode ec = U_ZERO_ERROR;
246 UNumberFormatter* uformatter;
247 UParseError perror;
248
249 // The UParseError can be null. The following should not segfault.
250 uformatter = unumf_openForSkeletonAndLocaleWithError(
251 u".00 measure-unit/typo", -1, "en", NULL, &ec);
252 unumf_close(uformatter);
253
254 // Now test the behavior.
255 ec = U_ZERO_ERROR;
256 uformatter = unumf_openForSkeletonAndLocaleWithError(
257 u".00 measure-unit/typo", -1, "en", &perror, &ec);
258
259 assertIntEquals("Should have set error code", U_NUMBER_SKELETON_SYNTAX_ERROR, ec);
260 assertIntEquals("Should have correct skeleton error offset", 17, perror.offset);
261 assertUEquals("Should have correct pre context", u"0 measure-unit/", perror.preContext);
262 assertUEquals("Should have correct post context", u"typo", perror.postContext);
263
264 // cleanup:
265 unumf_close(uformatter);
266 }
267
268
TestToDecimalNumber()269 static void TestToDecimalNumber() {
270 UErrorCode ec = U_ZERO_ERROR;
271 UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(
272 u"currency/USD",
273 -1,
274 "en-US",
275 &ec);
276 assertSuccessCheck("Should create without error", &ec, TRUE);
277 UFormattedNumber* uresult = unumf_openResult(&ec);
278 assertSuccess("Should create result without error", &ec);
279
280 unumf_formatDouble(uformatter, 3.0, uresult, &ec);
281 const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), NULL, &ec);
282 assertSuccessCheck("Formatting should succeed", &ec, TRUE);
283 assertUEquals("Should produce expected string result", u"$3.00", str);
284
285 char buffer[CAPACITY];
286
287 int32_t len = unumf_resultToDecimalNumber(uresult, buffer, CAPACITY, &ec);
288 assertIntEquals("Length should be as expected", strlen(buffer), len);
289 assertEquals("Decimal should be as expected", "3", buffer);
290
291 // cleanup:
292 unumf_closeResult(uresult);
293 unumf_close(uformatter);
294 }
295
296
TestPerUnitInArabic()297 static void TestPerUnitInArabic() {
298 const char* simpleMeasureUnits[] = {
299 "area-acre",
300 "digital-bit",
301 "digital-byte",
302 "temperature-celsius",
303 "length-centimeter",
304 "duration-day",
305 "angle-degree",
306 "temperature-fahrenheit",
307 "volume-fluid-ounce",
308 "length-foot",
309 "volume-gallon",
310 "digital-gigabit",
311 "digital-gigabyte",
312 "mass-gram",
313 "area-hectare",
314 "duration-hour",
315 "length-inch",
316 "digital-kilobit",
317 "digital-kilobyte",
318 "mass-kilogram",
319 "length-kilometer",
320 "volume-liter",
321 "digital-megabit",
322 "digital-megabyte",
323 "length-meter",
324 "length-mile",
325 "length-mile-scandinavian",
326 "volume-milliliter",
327 "length-millimeter",
328 "duration-millisecond",
329 "duration-minute",
330 "duration-month",
331 "mass-ounce",
332 "concentr-percent",
333 "digital-petabyte",
334 "mass-pound",
335 "duration-second",
336 "mass-stone",
337 "digital-terabit",
338 "digital-terabyte",
339 "duration-week",
340 "length-yard",
341 "duration-year"
342 };
343 #define BUFFER_LEN 256
344 char buffer[BUFFER_LEN];
345 UChar ubuffer[BUFFER_LEN];
346 const char* locale = "ar";
347 UErrorCode status = U_ZERO_ERROR;
348 UFormattedNumber* formatted = unumf_openResult(&status);
349 if (U_FAILURE(status)) {
350 log_err("FAIL: unumf_openResult failed");
351 return;
352 }
353 for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) {
354 for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) {
355 status = U_ZERO_ERROR;
356 sprintf(buffer, "measure-unit/%s per-measure-unit/%s",
357 simpleMeasureUnits[i], simpleMeasureUnits[j]);
358 int32_t outputlen = 0;
359 u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, (int32_t)strlen(buffer), &status);
360 if (U_FAILURE(status)) {
361 log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer,
362 u_errorName(status));
363 }
364 UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
365 ubuffer, outputlen, locale, &status);
366 if (U_FAILURE(status)) {
367 log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n",
368 locale, buffer, u_errorName(status));
369 } else {
370 unumf_formatDouble(nf, 1, formatted, &status);
371 if (U_FAILURE(status)) {
372 log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n",
373 locale, buffer, u_errorName(status));
374 }
375 }
376 unumf_close(nf);
377 }
378 }
379 unumf_closeResult(formatted);
380 }
381
382
Test21674_State()383 static void Test21674_State() {
384 UErrorCode status = U_ZERO_ERROR;
385 UNumberFormatter* nf = NULL;
386 UFormattedNumber* result = NULL;
387
388 nf = unumf_openForSkeletonAndLocale(u"precision-increment/0.05/w", -1, "en", &status);
389 if (!assertSuccess("unumf_openForSkeletonAndLocale", &status)) { goto cleanup; }
390
391 result = unumf_openResult(&status);
392 if (!assertSuccess("unumf_openResult", &status)) { goto cleanup; }
393
394 typedef struct TestCase {
395 double num;
396 const UChar* expected;
397 } TestCase;
398 TestCase cases[] = {
399 { 1.975, u"2" },
400 { 1.97, u"1.95" },
401 { 1.975, u"2" },
402 };
403 for (int i=0; i<3; i++) {
404 unumf_formatDouble(nf, cases[i].num, result, &status);
405 if (!assertSuccess("unumf_formatDouble", &status)) { goto cleanup; }
406
407 const UFormattedValue* formattedValue = unumf_resultAsValue(result, &status);
408 if (!assertSuccess("unumf_resultAsValue", &status)) { goto cleanup; }
409
410 int32_t length;
411 const UChar* str = ufmtval_getString(formattedValue, &length, &status);
412 if (!assertSuccess("ufmtval_getString", &status)) { goto cleanup; }
413
414 char message[] = {i + '0', '\0'};
415 assertUEquals(message, cases[i].expected, str);
416 }
417
418 cleanup:
419 unumf_close(nf);
420 unumf_closeResult(result);
421 }
422
423
424 #endif /* #if !UCONFIG_NO_FORMATTING */
425