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