| // © 2018 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| // Allow implicit conversion from char16_t* to UnicodeString for this file: |
| // Helpful in toString methods and elsewhere. |
| #define UNISTR_FROM_STRING_EXPLICIT |
| |
| #include <stdio.h> |
| #include "unicode/unumberformatter.h" |
| #include "unicode/umisc.h" |
| #include "unicode/unum.h" |
| #include "unicode/ustring.h" |
| #include "cformtst.h" |
| #include "cintltst.h" |
| #include "cmemory.h" |
| |
| static void TestSkeletonFormatToString(void); |
| |
| static void TestSkeletonFormatToFields(void); |
| |
| static void TestExampleCode(void); |
| |
| static void TestFormattedValue(void); |
| |
| static void TestSkeletonParseError(void); |
| |
| static void TestToDecimalNumber(void); |
| |
| static void TestPerUnitInArabic(void); |
| |
| void addUNumberFormatterTest(TestNode** root); |
| |
| #define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x) |
| |
| void addUNumberFormatterTest(TestNode** root) { |
| TESTCASE(TestSkeletonFormatToString); |
| TESTCASE(TestSkeletonFormatToFields); |
| TESTCASE(TestExampleCode); |
| TESTCASE(TestFormattedValue); |
| TESTCASE(TestSkeletonParseError); |
| TESTCASE(TestToDecimalNumber); |
| TESTCASE(TestPerUnitInArabic); |
| } |
| |
| |
| #define CAPACITY 30 |
| |
| static void TestSkeletonFormatToString() { |
| UErrorCode ec = U_ZERO_ERROR; |
| UChar buffer[CAPACITY]; |
| UFormattedNumber* result = NULL; |
| |
| // setup: |
| UNumberFormatter* f = unumf_openForSkeletonAndLocale( |
| u"precision-integer currency/USD sign-accounting", -1, "en", &ec); |
| assertSuccessCheck("Should create without error", &ec, TRUE); |
| result = unumf_openResult(&ec); |
| assertSuccess("Should create result without error", &ec); |
| |
| // int64 test: |
| unumf_formatInt(f, -444444, result, &ec); |
| // Missing data will give a U_MISSING_RESOURCE_ERROR here. |
| if (assertSuccessCheck("Should format integer without error", &ec, TRUE)) { |
| unumf_resultToString(result, buffer, CAPACITY, &ec); |
| assertSuccess("Should print string to buffer without error", &ec); |
| assertUEquals("Should produce expected string result", u"($444,444)", buffer); |
| |
| // double test: |
| unumf_formatDouble(f, -5142.3, result, &ec); |
| assertSuccess("Should format double without error", &ec); |
| unumf_resultToString(result, buffer, CAPACITY, &ec); |
| assertSuccess("Should print string to buffer without error", &ec); |
| assertUEquals("Should produce expected string result", u"($5,142)", buffer); |
| |
| // decnumber test: |
| unumf_formatDecimal(f, "9.876E2", -1, result, &ec); |
| assertSuccess("Should format decimal without error", &ec); |
| unumf_resultToString(result, buffer, CAPACITY, &ec); |
| assertSuccess("Should print string to buffer without error", &ec); |
| assertUEquals("Should produce expected string result", u"$988", buffer); |
| } |
| |
| // cleanup: |
| unumf_closeResult(result); |
| unumf_close(f); |
| } |
| |
| |
| static void TestSkeletonFormatToFields() { |
| UErrorCode ec = U_ZERO_ERROR; |
| UFieldPositionIterator* ufpositer = NULL; |
| |
| // setup: |
| UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale( |
| u".00 measure-unit/length-meter sign-always", -1, "en", &ec); |
| assertSuccessCheck("Should create without error", &ec, TRUE); |
| UFormattedNumber* uresult = unumf_openResult(&ec); |
| assertSuccess("Should create result without error", &ec); |
| unumf_formatInt(uformatter, 9876543210L, uresult, &ec); // "+9,876,543,210.00 m" |
| if (assertSuccessCheck("unumf_formatInt() failed", &ec, TRUE)) { |
| |
| // field position test: |
| UFieldPosition ufpos = {UNUM_DECIMAL_SEPARATOR_FIELD, 0, 0}; |
| unumf_resultNextFieldPosition(uresult, &ufpos, &ec); |
| assertIntEquals("Field position should be correct", 14, ufpos.beginIndex); |
| assertIntEquals("Field position should be correct", 15, ufpos.endIndex); |
| |
| // field position iterator test: |
| ufpositer = ufieldpositer_open(&ec); |
| if (assertSuccessCheck("Should create iterator without error", &ec, TRUE)) { |
| |
| unumf_resultGetAllFieldPositions(uresult, ufpositer, &ec); |
| static const UFieldPosition expectedFields[] = { |
| // Field, begin index, end index |
| {UNUM_SIGN_FIELD, 0, 1}, |
| {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3}, |
| {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7}, |
| {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11}, |
| {UNUM_INTEGER_FIELD, 1, 14}, |
| {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15}, |
| {UNUM_FRACTION_FIELD, 15, 17}, |
| {UNUM_MEASURE_UNIT_FIELD, 18, 19} |
| }; |
| UFieldPosition actual; |
| for (int32_t i = 0; i < (int32_t)(sizeof(expectedFields) / sizeof(*expectedFields)); i++) { |
| // Iterate using the UFieldPosition to hold state... |
| UFieldPosition expected = expectedFields[i]; |
| actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex); |
| assertTrue("Should not return a negative index yet", actual.field >= 0); |
| if (expected.field != actual.field) { |
| log_err( |
| "FAIL: iteration %d; expected field %d; got %d\n", i, expected.field, actual.field); |
| } |
| if (expected.beginIndex != actual.beginIndex) { |
| log_err( |
| "FAIL: iteration %d; expected beginIndex %d; got %d\n", |
| i, |
| expected.beginIndex, |
| actual.beginIndex); |
| } |
| if (expected.endIndex != actual.endIndex) { |
| log_err( |
| "FAIL: iteration %d; expected endIndex %d; got %d\n", |
| i, |
| expected.endIndex, |
| actual.endIndex); |
| } |
| } |
| actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex); |
| assertTrue("No more fields; should return a negative index", actual.field < 0); |
| |
| // next field iteration: |
| actual.field = UNUM_GROUPING_SEPARATOR_FIELD; |
| actual.beginIndex = 0; |
| actual.endIndex = 0; |
| int32_t i = 1; |
| while (unumf_resultNextFieldPosition(uresult, &actual, &ec)) { |
| UFieldPosition expected = expectedFields[i++]; |
| assertIntEquals("Grouping separator begin index", expected.beginIndex, actual.beginIndex); |
| assertIntEquals("Grouping separator end index", expected.endIndex, actual.endIndex); |
| } |
| assertIntEquals("Should have seen all grouping separators", 4, i); |
| } |
| } |
| |
| // cleanup: |
| unumf_closeResult(uresult); |
| unumf_close(uformatter); |
| ufieldpositer_close(ufpositer); |
| } |
| |
| |
| static void TestExampleCode() { |
| // This is the example code given in unumberformatter.h. |
| |
| // Setup: |
| UErrorCode ec = U_ZERO_ERROR; |
| UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(u"precision-integer", -1, "en", &ec); |
| UFormattedNumber* uresult = unumf_openResult(&ec); |
| UChar* buffer = NULL; |
| assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE); |
| |
| // Format a double: |
| unumf_formatDouble(uformatter, 5142.3, uresult, &ec); |
| if (assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE)) { |
| |
| // Export the string to a malloc'd buffer: |
| int32_t len = unumf_resultToString(uresult, NULL, 0, &ec); |
| assertTrue("No buffer yet", ec == U_BUFFER_OVERFLOW_ERROR); |
| ec = U_ZERO_ERROR; |
| buffer = (UChar*) uprv_malloc((len+1)*sizeof(UChar)); |
| unumf_resultToString(uresult, buffer, len+1, &ec); |
| assertSuccess("There should not be a failure in the example code", &ec); |
| assertUEquals("Should produce expected string result", u"5,142", buffer); |
| } |
| |
| // Cleanup: |
| unumf_close(uformatter); |
| unumf_closeResult(uresult); |
| uprv_free(buffer); |
| } |
| |
| |
| static void TestFormattedValue() { |
| UErrorCode ec = U_ZERO_ERROR; |
| UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale( |
| u".00 compact-short", -1, "en", &ec); |
| assertSuccessCheck("Should create without error", &ec, TRUE); |
| UFormattedNumber* uresult = unumf_openResult(&ec); |
| assertSuccess("Should create result without error", &ec); |
| |
| unumf_formatInt(uformatter, 55000, uresult, &ec); // "55.00 K" |
| if (assertSuccessCheck("Should format without error", &ec, TRUE)) { |
| const UFormattedValue* fv = unumf_resultAsValue(uresult, &ec); |
| assertSuccess("Should convert without error", &ec); |
| static const UFieldPosition expectedFieldPositions[] = { |
| // field, begin index, end index |
| {UNUM_INTEGER_FIELD, 0, 2}, |
| {UNUM_DECIMAL_SEPARATOR_FIELD, 2, 3}, |
| {UNUM_FRACTION_FIELD, 3, 5}, |
| {UNUM_COMPACT_FIELD, 5, 6}}; |
| checkFormattedValue( |
| "FormattedNumber as FormattedValue", |
| fv, |
| u"55.00K", |
| UFIELD_CATEGORY_NUMBER, |
| expectedFieldPositions, |
| UPRV_LENGTHOF(expectedFieldPositions)); |
| } |
| |
| // cleanup: |
| unumf_closeResult(uresult); |
| unumf_close(uformatter); |
| } |
| |
| |
| static void TestSkeletonParseError() { |
| UErrorCode ec = U_ZERO_ERROR; |
| UNumberFormatter* uformatter; |
| UParseError perror; |
| |
| // The UParseError can be null. The following should not segfault. |
| uformatter = unumf_openForSkeletonAndLocaleWithError( |
| u".00 measure-unit/typo", -1, "en", NULL, &ec); |
| unumf_close(uformatter); |
| |
| // Now test the behavior. |
| ec = U_ZERO_ERROR; |
| uformatter = unumf_openForSkeletonAndLocaleWithError( |
| u".00 measure-unit/typo", -1, "en", &perror, &ec); |
| |
| assertIntEquals("Should have set error code", U_NUMBER_SKELETON_SYNTAX_ERROR, ec); |
| assertIntEquals("Should have correct skeleton error offset", 17, perror.offset); |
| assertUEquals("Should have correct pre context", u"0 measure-unit/", perror.preContext); |
| assertUEquals("Should have correct post context", u"typo", perror.postContext); |
| |
| // cleanup: |
| unumf_close(uformatter); |
| } |
| |
| |
| static void TestToDecimalNumber() { |
| UErrorCode ec = U_ZERO_ERROR; |
| UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale( |
| u"currency/USD", |
| -1, |
| "en-US", |
| &ec); |
| assertSuccessCheck("Should create without error", &ec, TRUE); |
| UFormattedNumber* uresult = unumf_openResult(&ec); |
| assertSuccess("Should create result without error", &ec); |
| |
| unumf_formatDouble(uformatter, 3.0, uresult, &ec); |
| const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), NULL, &ec); |
| assertSuccessCheck("Formatting should succeed", &ec, TRUE); |
| assertUEquals("Should produce expected string result", u"$3.00", str); |
| |
| char buffer[CAPACITY]; |
| |
| int32_t len = unumf_resultToDecimalNumber(uresult, buffer, CAPACITY, &ec); |
| assertIntEquals("Length should be as expected", strlen(buffer), len); |
| assertEquals("Decimal should be as expected", "3", buffer); |
| |
| // cleanup: |
| unumf_closeResult(uresult); |
| unumf_close(uformatter); |
| } |
| |
| |
| static void TestPerUnitInArabic() { |
| const char* simpleMeasureUnits[] = { |
| "area-acre", |
| "digital-bit", |
| "digital-byte", |
| "temperature-celsius", |
| "length-centimeter", |
| "duration-day", |
| "angle-degree", |
| "temperature-fahrenheit", |
| "volume-fluid-ounce", |
| "length-foot", |
| "volume-gallon", |
| "digital-gigabit", |
| "digital-gigabyte", |
| "mass-gram", |
| "area-hectare", |
| "duration-hour", |
| "length-inch", |
| "digital-kilobit", |
| "digital-kilobyte", |
| "mass-kilogram", |
| "length-kilometer", |
| "volume-liter", |
| "digital-megabit", |
| "digital-megabyte", |
| "length-meter", |
| "length-mile", |
| "length-mile-scandinavian", |
| "volume-milliliter", |
| "length-millimeter", |
| "duration-millisecond", |
| "duration-minute", |
| "duration-month", |
| "mass-ounce", |
| "concentr-percent", |
| "digital-petabyte", |
| "mass-pound", |
| "duration-second", |
| "mass-stone", |
| "digital-terabit", |
| "digital-terabyte", |
| "duration-week", |
| "length-yard", |
| "duration-year" |
| }; |
| #define BUFFER_LEN 256 |
| char buffer[BUFFER_LEN]; |
| UChar ubuffer[BUFFER_LEN]; |
| const char* locale = "ar"; |
| UErrorCode status = U_ZERO_ERROR; |
| UFormattedNumber* formatted = unumf_openResult(&status); |
| if (U_FAILURE(status)) { |
| log_err("FAIL: unumf_openResult failed"); |
| return; |
| } |
| for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) { |
| for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) { |
| status = U_ZERO_ERROR; |
| sprintf(buffer, "measure-unit/%s per-measure-unit/%s", |
| simpleMeasureUnits[i], simpleMeasureUnits[j]); |
| int32_t outputlen = 0; |
| u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, (int32_t)strlen(buffer), &status); |
| if (U_FAILURE(status)) { |
| log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer, |
| u_errorName(status)); |
| } |
| UNumberFormatter* nf = unumf_openForSkeletonAndLocale( |
| ubuffer, outputlen, locale, &status); |
| if (U_FAILURE(status)) { |
| log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n", |
| locale, buffer, u_errorName(status)); |
| } else { |
| unumf_formatDouble(nf, 1, formatted, &status); |
| if (U_FAILURE(status)) { |
| log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n", |
| locale, buffer, u_errorName(status)); |
| } |
| } |
| unumf_close(nf); |
| } |
| } |
| unumf_closeResult(formatted); |
| } |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |