xref: /aosp_15_r20/external/icu/icu4c/source/test/cintltst/unumberformattertst.c (revision 0e209d3975ff4a8c132096b14b0e9364a753506e)
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 <stdbool.h>
13 #include <stdio.h>
14 #include "unicode/unumberformatter.h"
15 #include "unicode/usimplenumberformatter.h"
16 #include "unicode/umisc.h"
17 #include "unicode/unum.h"
18 #include "unicode/ustring.h"
19 #include "cformtst.h"
20 #include "cintltst.h"
21 #include "cmemory.h"
22 
23 static void TestSkeletonFormatToString(void);
24 
25 static void TestSkeletonFormatToFields(void);
26 
27 static void TestExampleCode(void);
28 
29 static void TestSimpleNumberFormatterExample(void);
30 
31 static void TestSimpleNumberFormatterFull(void);
32 
33 static void TestFormattedValue(void);
34 
35 static void TestSkeletonParseError(void);
36 
37 static void TestToDecimalNumber(void);
38 
39 static void TestPerUnitInArabic(void);
40 
41 static void Test21674_State(void);
42 
43 static void TestNegativeDegrees(void);
44 
45 void addUNumberFormatterTest(TestNode** root);
46 
47 #define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
48 
addUNumberFormatterTest(TestNode ** root)49 void addUNumberFormatterTest(TestNode** root) {
50     TESTCASE(TestSkeletonFormatToString);
51     TESTCASE(TestSkeletonFormatToFields);
52     TESTCASE(TestExampleCode);
53     TESTCASE(TestSimpleNumberFormatterExample);
54     TESTCASE(TestSimpleNumberFormatterFull);
55     TESTCASE(TestFormattedValue);
56     TESTCASE(TestSkeletonParseError);
57     TESTCASE(TestToDecimalNumber);
58     TESTCASE(TestPerUnitInArabic);
59     TESTCASE(Test21674_State);
60     TESTCASE(TestNegativeDegrees);
61 }
62 
63 
64 #define CAPACITY 30
65 
TestSkeletonFormatToString(void)66 static void TestSkeletonFormatToString(void) {
67     UErrorCode ec = U_ZERO_ERROR;
68     UChar buffer[CAPACITY];
69     UFormattedNumber* result = NULL;
70 
71     // setup:
72     UNumberFormatter* f = unumf_openForSkeletonAndLocale(
73                               u"precision-integer currency/USD sign-accounting", -1, "en", &ec);
74     assertSuccessCheck("Should create without error", &ec, true);
75     result = unumf_openResult(&ec);
76     assertSuccess("Should create result without error", &ec);
77 
78     // int64 test:
79     unumf_formatInt(f, -444444, result, &ec);
80     // Missing data will give a U_MISSING_RESOURCE_ERROR here.
81     if (assertSuccessCheck("Should format integer without error", &ec, true)) {
82         unumf_resultToString(result, buffer, CAPACITY, &ec);
83         assertSuccess("Should print string to buffer without error", &ec);
84         assertUEquals("Should produce expected string result", u"($444,444)", buffer);
85 
86         // double test:
87         unumf_formatDouble(f, -5142.3, result, &ec);
88         assertSuccess("Should format double without error", &ec);
89         unumf_resultToString(result, buffer, CAPACITY, &ec);
90         assertSuccess("Should print string to buffer without error", &ec);
91         assertUEquals("Should produce expected string result", u"($5,142)", buffer);
92 
93         // decnumber test:
94         unumf_formatDecimal(f, "9.876E2", -1, result, &ec);
95         assertSuccess("Should format decimal without error", &ec);
96         unumf_resultToString(result, buffer, CAPACITY, &ec);
97         assertSuccess("Should print string to buffer without error", &ec);
98         assertUEquals("Should produce expected string result", u"$988", buffer);
99     }
100 
101     // cleanup:
102     unumf_closeResult(result);
103     unumf_close(f);
104 }
105 
106 
TestSkeletonFormatToFields(void)107 static void TestSkeletonFormatToFields(void) {
108     UErrorCode ec = U_ZERO_ERROR;
109     UFieldPositionIterator* ufpositer = NULL;
110 
111     // setup:
112     UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(
113             u".00 measure-unit/length-meter sign-always", -1, "en", &ec);
114     assertSuccessCheck("Should create without error", &ec, true);
115     UFormattedNumber* uresult = unumf_openResult(&ec);
116     assertSuccess("Should create result without error", &ec);
117     unumf_formatInt(uformatter, 9876543210L, uresult, &ec); // "+9,876,543,210.00 m"
118     if (assertSuccessCheck("unumf_formatInt() failed", &ec, true)) {
119 
120         // field position test:
121         UFieldPosition ufpos = {UNUM_DECIMAL_SEPARATOR_FIELD, 0, 0};
122         unumf_resultNextFieldPosition(uresult, &ufpos, &ec);
123         assertIntEquals("Field position should be correct", 14, ufpos.beginIndex);
124         assertIntEquals("Field position should be correct", 15, ufpos.endIndex);
125 
126         // field position iterator test:
127         ufpositer = ufieldpositer_open(&ec);
128         if (assertSuccessCheck("Should create iterator without error", &ec, true)) {
129 
130             unumf_resultGetAllFieldPositions(uresult, ufpositer, &ec);
131             static const UFieldPosition expectedFields[] = {
132                 // Field, begin index, end index
133                 {UNUM_SIGN_FIELD, 0, 1},
134                 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
135                 {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
136                 {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
137                 {UNUM_INTEGER_FIELD, 1, 14},
138                 {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
139                 {UNUM_FRACTION_FIELD, 15, 17},
140                 {UNUM_MEASURE_UNIT_FIELD, 18, 19}
141             };
142             UFieldPosition actual;
143             for (int32_t i = 0; i < (int32_t)(sizeof(expectedFields) / sizeof(*expectedFields)); i++) {
144                 // Iterate using the UFieldPosition to hold state...
145                 UFieldPosition expected = expectedFields[i];
146                 actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex);
147                 assertTrue("Should not return a negative index yet", actual.field >= 0);
148                 if (expected.field != actual.field) {
149                     log_err(
150                         "FAIL: iteration %d; expected field %d; got %d\n", i, expected.field, actual.field);
151                 }
152                 if (expected.beginIndex != actual.beginIndex) {
153                     log_err(
154                         "FAIL: iteration %d; expected beginIndex %d; got %d\n",
155                         i,
156                         expected.beginIndex,
157                         actual.beginIndex);
158                 }
159                 if (expected.endIndex != actual.endIndex) {
160                     log_err(
161                         "FAIL: iteration %d; expected endIndex %d; got %d\n",
162                         i,
163                         expected.endIndex,
164                         actual.endIndex);
165                 }
166             }
167             actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex);
168             assertTrue("No more fields; should return a negative index", actual.field < 0);
169 
170             // next field iteration:
171             actual.field = UNUM_GROUPING_SEPARATOR_FIELD;
172             actual.beginIndex = 0;
173             actual.endIndex = 0;
174             int32_t i = 1;
175             while (unumf_resultNextFieldPosition(uresult, &actual, &ec)) {
176                 UFieldPosition expected = expectedFields[i++];
177                 assertIntEquals("Grouping separator begin index", expected.beginIndex, actual.beginIndex);
178                 assertIntEquals("Grouping separator end index", expected.endIndex, actual.endIndex);
179             }
180             assertIntEquals("Should have seen all grouping separators", 4, i);
181         }
182     }
183 
184     // cleanup:
185     unumf_closeResult(uresult);
186     unumf_close(uformatter);
187     ufieldpositer_close(ufpositer);
188 }
189 
190 
TestExampleCode(void)191 static void TestExampleCode(void) {
192     // This is the example code given in unumberformatter.h.
193 
194     // Setup:
195     UErrorCode ec = U_ZERO_ERROR;
196     UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(u"precision-integer", -1, "en", &ec);
197     UFormattedNumber* uresult = unumf_openResult(&ec);
198     UChar* buffer = NULL;
199     assertSuccessCheck("There should not be a failure in the example code", &ec, true);
200 
201     // Format a double:
202     unumf_formatDouble(uformatter, 5142.3, uresult, &ec);
203     if (assertSuccessCheck("There should not be a failure in the example code", &ec, true)) {
204 
205         // Export the string to a malloc'd buffer:
206         int32_t len = unumf_resultToString(uresult, NULL, 0, &ec);
207         assertTrue("No buffer yet", ec == U_BUFFER_OVERFLOW_ERROR);
208         ec = U_ZERO_ERROR;
209         buffer = (UChar*) uprv_malloc((len+1)*sizeof(UChar));
210         unumf_resultToString(uresult, buffer, len+1, &ec);
211         assertSuccess("There should not be a failure in the example code", &ec);
212         assertUEquals("Should produce expected string result", u"5,142", buffer);
213     }
214 
215     // Cleanup:
216     unumf_close(uformatter);
217     unumf_closeResult(uresult);
218     uprv_free(buffer);
219 }
220 
221 
TestSimpleNumberFormatterExample(void)222 static void TestSimpleNumberFormatterExample(void) {
223     // This is the example in usimplenumberformatter.h
224     UErrorCode ec = U_ZERO_ERROR;
225     USimpleNumberFormatter* uformatter = usnumf_openForLocale("bn", &ec);
226     USimpleNumber* unumber = usnum_openForInt64(1000007, &ec);
227     UFormattedNumber* uresult = unumf_openResult(&ec);
228     usnumf_format(uformatter, unumber, uresult, &ec);
229     int32_t len;
230     const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
231     if (assertSuccess("Formatting end-to-end 1", &ec)) {
232         assertUEquals("Should produce a result in Bangla digits", u"১০,০০,০০৭", str);
233     }
234 
235     // Cleanup:
236     unumf_closeResult(uresult);
237     usnum_close(unumber);
238     usnumf_close(uformatter);
239 }
240 
241 
TestSimpleNumberFormatterFull(void)242 static void TestSimpleNumberFormatterFull(void) {
243     UErrorCode ec = U_ZERO_ERROR;
244     USimpleNumberFormatter* uformatter = usnumf_openForLocaleAndGroupingStrategy("de-CH", UNUM_GROUPING_ON_ALIGNED, &ec);
245     UFormattedNumber* uresult = unumf_openResult(&ec);
246 
247     usnumf_formatInt64(uformatter, 4321, uresult, &ec);
248     int32_t len;
249     const UChar* str = str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
250     if (assertSuccess("Formatting end-to-end 2", &ec)) {
251         assertUEquals("Should produce a result with Swiss symbols", u"4’321", str);
252     }
253 
254     USimpleNumber* unumber = usnum_openForInt64(1000007, &ec);
255     usnum_setToInt64(unumber, 98765, &ec);
256     usnum_multiplyByPowerOfTen(unumber, -2, &ec);
257     usnum_roundTo(unumber, -1, UNUM_ROUND_HALFDOWN, &ec);
258     usnum_setMaximumIntegerDigits(unumber, 1, &ec);
259     usnum_setMinimumIntegerDigits(unumber, 4, &ec);
260     usnum_setMinimumFractionDigits(unumber, 3, &ec);
261     usnum_setSign(unumber, UNUM_SIMPLE_NUMBER_PLUS_SIGN, &ec);
262 
263     usnumf_format(uformatter, unumber, uresult, &ec);
264     str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
265     if (assertSuccess("Formatting end-to-end 3", &ec)) {
266         assertUEquals("Should produce a result with mutated number", u"+0’007.600", str);
267     }
268 
269     // Cleanup:
270     unumf_closeResult(uresult);
271     usnum_close(unumber);
272     usnumf_close(uformatter);
273 }
274 
275 
TestFormattedValue(void)276 static void TestFormattedValue(void) {
277     UErrorCode ec = U_ZERO_ERROR;
278     UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(
279             u".00 compact-short", -1, "en", &ec);
280     assertSuccessCheck("Should create without error", &ec, true);
281     UFormattedNumber* uresult = unumf_openResult(&ec);
282     assertSuccess("Should create result without error", &ec);
283 
284     unumf_formatInt(uformatter, 55000, uresult, &ec); // "55.00 K"
285     if (assertSuccessCheck("Should format without error", &ec, true)) {
286         const UFormattedValue* fv = unumf_resultAsValue(uresult, &ec);
287         assertSuccess("Should convert without error", &ec);
288         static const UFieldPosition expectedFieldPositions[] = {
289             // field, begin index, end index
290             {UNUM_INTEGER_FIELD, 0, 2},
291             {UNUM_DECIMAL_SEPARATOR_FIELD, 2, 3},
292             {UNUM_FRACTION_FIELD, 3, 5},
293             {UNUM_COMPACT_FIELD, 5, 6}};
294         checkFormattedValue(
295             "FormattedNumber as FormattedValue",
296             fv,
297             u"55.00K",
298             UFIELD_CATEGORY_NUMBER,
299             expectedFieldPositions,
300             UPRV_LENGTHOF(expectedFieldPositions));
301     }
302 
303     // cleanup:
304     unumf_closeResult(uresult);
305     unumf_close(uformatter);
306 }
307 
308 
TestSkeletonParseError(void)309 static void TestSkeletonParseError(void) {
310     UErrorCode ec = U_ZERO_ERROR;
311     UNumberFormatter* uformatter;
312     UParseError perror;
313 
314     // The UParseError can be null. The following should not segfault.
315     uformatter = unumf_openForSkeletonAndLocaleWithError(
316             u".00 measure-unit/typo", -1, "en", NULL, &ec);
317     unumf_close(uformatter);
318 
319     // Now test the behavior.
320     ec = U_ZERO_ERROR;
321     uformatter = unumf_openForSkeletonAndLocaleWithError(
322             u".00 measure-unit/typo", -1, "en", &perror, &ec);
323 
324     assertIntEquals("Should have set error code", U_NUMBER_SKELETON_SYNTAX_ERROR, ec);
325     assertIntEquals("Should have correct skeleton error offset", 17, perror.offset);
326     assertUEquals("Should have correct pre context", u"0 measure-unit/", perror.preContext);
327     assertUEquals("Should have correct post context", u"typo", perror.postContext);
328 
329     // cleanup:
330     unumf_close(uformatter);
331 }
332 
333 
TestToDecimalNumber(void)334 static void TestToDecimalNumber(void) {
335     UErrorCode ec = U_ZERO_ERROR;
336     UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(
337         u"currency/USD",
338         -1,
339         "en-US",
340         &ec);
341     assertSuccessCheck("Should create without error", &ec, true);
342     UFormattedNumber* uresult = unumf_openResult(&ec);
343     assertSuccess("Should create result without error", &ec);
344 
345     unumf_formatDouble(uformatter, 3.0, uresult, &ec);
346     const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), NULL, &ec);
347     assertSuccessCheck("Formatting should succeed", &ec, true);
348     assertUEquals("Should produce expected string result", u"$3.00", str);
349 
350     char buffer[CAPACITY];
351 
352     int32_t len = unumf_resultToDecimalNumber(uresult, buffer, CAPACITY, &ec);
353     assertIntEquals("Length should be as expected", strlen(buffer), len);
354     assertEquals("Decimal should be as expected", "3", buffer);
355 
356     // cleanup:
357     unumf_closeResult(uresult);
358     unumf_close(uformatter);
359 }
360 
361 
TestPerUnitInArabic(void)362 static void TestPerUnitInArabic(void) {
363     const char* simpleMeasureUnits[] = {
364         "area-acre",
365         "digital-bit",
366         "digital-byte",
367         "temperature-celsius",
368         "length-centimeter",
369         "duration-day",
370         "angle-degree",
371         "temperature-fahrenheit",
372         "volume-fluid-ounce",
373         "length-foot",
374         "volume-gallon",
375         "digital-gigabit",
376         "digital-gigabyte",
377         "mass-gram",
378         "area-hectare",
379         "duration-hour",
380         "length-inch",
381         "digital-kilobit",
382         "digital-kilobyte",
383         "mass-kilogram",
384         "length-kilometer",
385         "volume-liter",
386         "digital-megabit",
387         "digital-megabyte",
388         "length-meter",
389         "length-mile",
390         "length-mile-scandinavian",
391         "volume-milliliter",
392         "length-millimeter",
393         "duration-millisecond",
394         "duration-minute",
395         "duration-month",
396         "mass-ounce",
397         "concentr-percent",
398         "digital-petabyte",
399         "mass-pound",
400         "duration-second",
401         "mass-stone",
402         "digital-terabit",
403         "digital-terabyte",
404         "duration-week",
405         "length-yard",
406         "duration-year"
407     };
408 #define BUFFER_LEN 256
409     char buffer[BUFFER_LEN];
410     UChar ubuffer[BUFFER_LEN];
411     const char* locale = "ar";
412     UErrorCode status = U_ZERO_ERROR;
413     UFormattedNumber* formatted = unumf_openResult(&status);
414     if (U_FAILURE(status)) {
415         log_err("FAIL: unumf_openResult failed");
416         return;
417     }
418     for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) {
419         for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) {
420             status = U_ZERO_ERROR;
421             snprintf(buffer, sizeof(buffer), "measure-unit/%s per-measure-unit/%s",
422                     simpleMeasureUnits[i], simpleMeasureUnits[j]);
423             int32_t outputlen = 0;
424             u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, (int32_t)strlen(buffer), &status);
425             if (U_FAILURE(status)) {
426                 log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer,
427                         u_errorName(status));
428             }
429             UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
430                 ubuffer, outputlen, locale, &status);
431             if (U_FAILURE(status)) {
432                 log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n",
433                         locale, buffer, u_errorName(status));
434             } else {
435                 unumf_formatDouble(nf, 1, formatted, &status);
436                 if (U_FAILURE(status)) {
437                     log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n",
438                             locale, buffer, u_errorName(status));
439                 }
440             }
441             unumf_close(nf);
442         }
443     }
444     unumf_closeResult(formatted);
445 }
446 
447 
Test21674_State(void)448 static void Test21674_State(void) {
449     UErrorCode status = U_ZERO_ERROR;
450     UNumberFormatter* nf = NULL;
451     UFormattedNumber* result = NULL;
452 
453     nf = unumf_openForSkeletonAndLocale(u"precision-increment/0.05/w", -1, "en", &status);
454     if (!assertSuccess("unumf_openForSkeletonAndLocale", &status)) { goto cleanup; }
455 
456     result = unumf_openResult(&status);
457     if (!assertSuccess("unumf_openResult", &status)) { goto cleanup; }
458 
459     typedef struct TestCase {
460         double num;
461         const UChar* expected;
462     } TestCase;
463     TestCase cases[] = {
464         { 1.975, u"2" },
465         { 1.97, u"1.95" },
466         { 1.975, u"2" },
467     };
468     for (int i=0; i<3; i++) {
469         unumf_formatDouble(nf, cases[i].num, result, &status);
470         if (!assertSuccess("unumf_formatDouble", &status)) { goto cleanup; }
471 
472         const UFormattedValue* formattedValue = unumf_resultAsValue(result, &status);
473         if (!assertSuccess("unumf_resultAsValue", &status)) { goto cleanup; }
474 
475         int32_t length;
476         const UChar* str = ufmtval_getString(formattedValue, &length, &status);
477         if (!assertSuccess("ufmtval_getString", &status)) { goto cleanup; }
478 
479         char message[] = {i + '0', '\0'};
480         assertUEquals(message, cases[i].expected, str);
481     }
482 
483 cleanup:
484     unumf_close(nf);
485     unumf_closeResult(result);
486 }
487 
488 // Test for ICU-22105
TestNegativeDegrees(void)489 static void TestNegativeDegrees(void) {
490     typedef struct {
491         const UChar* skeleton;
492         double value;
493         const UChar* expectedResult;
494     } TestCase;
495 
496     TestCase testCases[] = {
497         { u"measure-unit/temperature-celsius unit-width-short",               0,  u"0°C" },
498         { u"measure-unit/temperature-celsius unit-width-short usage/default", 0,  u"32°F" },
499         { u"measure-unit/temperature-celsius unit-width-short usage/weather", 0,  u"32°F" },
500 
501         { u"measure-unit/temperature-celsius unit-width-short",               -1, u"-1°C" },
502         { u"measure-unit/temperature-celsius unit-width-short usage/default", -1, u"30°F" },
503         { u"measure-unit/temperature-celsius unit-width-short usage/weather", -1, u"30°F" }
504     };
505 
506     for (int32_t i = 0; i < UPRV_LENGTHOF(testCases); i++) {
507         UErrorCode err = U_ZERO_ERROR;
508         UNumberFormatter* nf = unumf_openForSkeletonAndLocale(testCases[i].skeleton, -1, "en_US", &err);
509         UFormattedNumber* fn = unumf_openResult(&err);
510 
511         if (assertSuccess("Failed to create formatter or result", &err)) {
512             UChar result[200];
513             unumf_formatDouble(nf, testCases[i].value, fn, &err);
514             unumf_resultToString(fn, result, 200, &err);
515 
516             if (assertSuccess("Formatting number failed", &err)) {
517                 assertUEquals("Got wrong result", testCases[i].expectedResult, result);
518             }
519         }
520 
521         unumf_closeResult(fn);
522         unumf_close(nf);
523     }
524 }
525 
526 
527 #endif /* #if !UCONFIG_NO_FORMATTING */
528