// © 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 #include "unicode/unumberformatter.h" #include "unicode/unumberrangeformatter.h" #include "unicode/umisc.h" #include "unicode/unum.h" #include "unicode/ustring.h" #include "cformtst.h" #include "cintltst.h" #include "cmemory.h" static void TestExampleCode(void); static void TestFormattedValue(void); static void TestSkeletonParseError(void); static void TestGetDecimalNumbers(void); void addUNumberRangeFormatterTest(TestNode** root); #define TESTCASE(x) addTest(root, &x, "tsformat/unumberrangeformatter/" #x) void addUNumberRangeFormatterTest(TestNode** root) { TESTCASE(TestExampleCode); TESTCASE(TestFormattedValue); TESTCASE(TestSkeletonParseError); TESTCASE(TestGetDecimalNumbers); } #define CAPACITY 30 static void TestExampleCode() { // This is the example code given in unumberrangeformatter.h. // Setup: UErrorCode ec = U_ZERO_ERROR; UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback( u"currency/USD precision-integer", -1, UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, "en-US", NULL, &ec); UFormattedNumberRange* uresult = unumrf_openResult(&ec); assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE); // Format a double range: unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec); assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE); // Get the result string: int32_t len; const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), &len, &ec); assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE); assertUEquals("Should produce expected string result", u"$3 – $5", str); int32_t resultLength = str != NULL ? u_strlen(str) : 0; assertIntEquals("Length should be as expected", resultLength, len); // Cleanup: unumrf_close(uformatter); unumrf_closeResult(uresult); } static void TestFormattedValue() { UErrorCode ec = U_ZERO_ERROR; UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback( u"K", -1, UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, "en-US", NULL, &ec); assertSuccessCheck("Should create without error", &ec, TRUE); UFormattedNumberRange* uresult = unumrf_openResult(&ec); assertSuccess("Should create result without error", &ec); // Test the decimal number code path, too unumrf_formatDecimalRange(uformatter, "5.5e4", -1, "1.5e5", -1, uresult, &ec); if (assertSuccessCheck("Should format without error", &ec, TRUE)) { const UFormattedValue* fv = unumrf_resultAsValue(uresult, &ec); assertSuccess("Should convert without error", &ec); static const UFieldPosition expectedFieldPositions[] = { // field, begin index, end index {UNUM_INTEGER_FIELD, 0, 2}, {UNUM_COMPACT_FIELD, 2, 3}, {UNUM_INTEGER_FIELD, 6, 9}, {UNUM_COMPACT_FIELD, 9, 10}}; checkFormattedValue( "FormattedNumber as FormattedValue", fv, u"55K – 150K", UFIELD_CATEGORY_NUMBER, expectedFieldPositions, UPRV_LENGTHOF(expectedFieldPositions)); } assertIntEquals("Identity result should match", UNUM_IDENTITY_RESULT_NOT_EQUAL, unumrf_resultGetIdentityResult(uresult, &ec)); // cleanup: unumrf_closeResult(uresult); unumrf_close(uformatter); } static void TestSkeletonParseError() { UErrorCode ec = U_ZERO_ERROR; UNumberRangeFormatter* uformatter; UParseError perror; // The UParseError can be null. The following should not segfault. uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback( u".00 measure-unit/typo", -1, UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, "en", NULL, &ec); unumrf_close(uformatter); // Now test the behavior. ec = U_ZERO_ERROR; uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback( u".00 measure-unit/typo", -1, UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, "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: unumrf_close(uformatter); } static void TestGetDecimalNumbers() { UErrorCode ec = U_ZERO_ERROR; UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback( u"currency/USD", -1, UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, "en-US", NULL, &ec); assertSuccessCheck("Should create without error", &ec, TRUE); UFormattedNumberRange* uresult = unumrf_openResult(&ec); assertSuccess("Should create result without error", &ec); unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec); const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), NULL, &ec); assertSuccessCheck("Formatting should succeed", &ec, TRUE); assertUEquals("Should produce expected string result", u"$3.00 \u2013 $5.00", str); char buffer[CAPACITY]; int32_t len = unumrf_resultGetFirstDecimalNumber(uresult, buffer, CAPACITY, &ec); assertIntEquals("First len should be as expected", strlen(buffer), len); assertEquals("First decimal should be as expected", "3", buffer); len = unumrf_resultGetSecondDecimalNumber(uresult, buffer, CAPACITY, &ec); assertIntEquals("Second len should be as expected", strlen(buffer), len); assertEquals("Second decimal should be as expected", "5", buffer); // cleanup: unumrf_closeResult(uresult); unumrf_close(uformatter); } #endif /* #if !UCONFIG_NO_FORMATTING */