1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
8 #include "numbertest.h"
9 #include "unicode/numberrangeformatter.h"
12 #include <numparse_affixes.h>
14 // Horrible workaround for the lack of a status code in the constructor...
15 // (Also affects numbertest_api.cpp)
16 UErrorCode globalNumberRangeFormatterTestStatus
= U_ZERO_ERROR
;
18 NumberRangeFormatterTest::NumberRangeFormatterTest()
19 : NumberRangeFormatterTest(globalNumberRangeFormatterTestStatus
) {
22 NumberRangeFormatterTest::NumberRangeFormatterTest(UErrorCode
& status
)
23 : USD(u
"USD", status
),
27 // Check for error on the first MeasureUnit in case there is no data
28 LocalPointer
<MeasureUnit
> unit(MeasureUnit::createMeter(status
));
29 if (U_FAILURE(status
)) {
30 dataerrln("%s %d status = %s", __FILE__
, __LINE__
, u_errorName(status
));
35 KILOMETER
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createKilometer(status
));
36 FAHRENHEIT
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFahrenheit(status
));
37 KELVIN
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createKelvin(status
));
40 void NumberRangeFormatterTest::runIndexedTest(int32_t index
, UBool exec
, const char*& name
, char*) {
42 logln("TestSuite NumberRangeFormatterTest: ");
45 TESTCASE_AUTO(testSanity
);
46 TESTCASE_AUTO(testBasic
);
47 TESTCASE_AUTO(testCollapse
);
48 TESTCASE_AUTO(testIdentity
);
49 TESTCASE_AUTO(testDifferentFormatters
);
50 TESTCASE_AUTO(testPlurals
);
51 TESTCASE_AUTO(testFieldPositions
);
52 TESTCASE_AUTO(testCopyMove
);
53 TESTCASE_AUTO(toObject
);
57 void NumberRangeFormatterTest::testSanity() {
58 IcuTestErrorCode
status(*this, "testSanity");
59 LocalizedNumberRangeFormatter lnrf1
= NumberRangeFormatter::withLocale("en-us");
60 LocalizedNumberRangeFormatter lnrf2
= NumberRangeFormatter::with().locale("en-us");
61 assertEquals("Formatters should have same behavior 1",
62 lnrf1
.formatFormattableRange(4, 6, status
).toString(status
),
63 lnrf2
.formatFormattableRange(4, 6, status
).toString(status
));
66 void NumberRangeFormatterTest::testBasic() {
69 NumberRangeFormatter::with(),
84 NumberRangeFormatter::with()
85 .numberFormatterBoth(NumberFormatter::with().unit(METER
)),
96 u
"5,000–5,000,000 m");
99 u
"Basic with different units",
100 NumberRangeFormatter::with()
101 .numberFormatterFirst(NumberFormatter::with().unit(METER
))
102 .numberFormatterSecond(NumberFormatter::with().unit(KILOMETER
)),
110 u
"3,000 m – 5,000 km",
111 u
"4,999 m – 5,001 km",
112 u
"5,000 m – 5,000 km",
113 u
"5,000 m – 5,000,000 km");
117 NumberRangeFormatter::with()
118 .numberFormatterBoth(NumberFormatter::with().unit(METER
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
)),
126 u
"3,000–5,000 meters",
127 u
"4,999–5,001 meters",
129 u
"5,000–5,000,000 meters");
132 u
"Non-English locale and unit",
133 NumberRangeFormatter::with()
134 .numberFormatterBoth(NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
)),
136 u
"1–5\u00A0degrés Fahrenheit",
137 u
"≈5\u00A0degrés Fahrenheit",
138 u
"≈5\u00A0degrés Fahrenheit",
139 u
"0–3\u00A0degrés Fahrenheit",
140 u
"≈0\u00A0degré Fahrenheit",
141 u
"3–3\u202F000\u00A0degrés Fahrenheit",
142 u
"3\u202F000–5\u202F000\u00A0degrés Fahrenheit",
143 u
"4\u202F999–5\u202F001\u00A0degrés Fahrenheit",
144 u
"≈5\u202F000\u00A0degrés Fahrenheit",
145 u
"5\u202F000–5\u202F000\u202F000\u00A0degrés Fahrenheit");
148 u
"Locale with custom range separator",
149 NumberRangeFormatter::with(),
163 u
"Locale that already has spaces around range separator",
164 NumberRangeFormatter::with()
165 .collapse(UNUM_RANGE_COLLAPSE_NONE
)
166 .numberFormatterBoth(NumberFormatter::with().unit(KELVIN
)),
174 u
"3.000 K – 5.000 K",
175 u
"4.999 K – 5.001 K",
177 u
"5.000 K – 5.000.000 K");
180 u
"Locale with custom numbering system and no plural ranges data",
181 NumberRangeFormatter::with(),
182 Locale("shn@numbers=beng"),
196 u
"Portuguese currency",
197 NumberRangeFormatter::with()
198 .numberFormatterBoth(NumberFormatter::with().unit(PTE
)),
200 u
"1$00 - 5$00 \u200B",
203 u
"0$00 - 3$00 \u200B",
205 u
"3$00 - 3000$00 \u200B",
206 u
"3000$00 - 5000$00 \u200B",
207 u
"4999$00 - 5001$00 \u200B",
209 u
"5000$00 - 5,000,000$00 \u200B");
212 void NumberRangeFormatterTest::testCollapse() {
214 u
"Default collapse on currency (default rounding)",
215 NumberRangeFormatter::with()
216 .numberFormatterBoth(NumberFormatter::with().unit(USD
)),
223 u
"$3.00 – $3,000.00",
224 u
"$3,000.00 – $5,000.00",
225 u
"$4,999.00 – $5,001.00",
227 u
"$5,000.00 – $5,000,000.00");
230 u
"Default collapse on currency",
231 NumberRangeFormatter::with()
232 .numberFormatterBoth(NumberFormatter::with().unit(USD
).precision(Precision::integer())),
243 u
"$5,000 – $5,000,000");
246 u
"No collapse on currency",
247 NumberRangeFormatter::with()
248 .collapse(UNUM_RANGE_COLLAPSE_NONE
)
249 .numberFormatterBoth(NumberFormatter::with().unit(USD
).precision(Precision::integer())),
260 u
"$5,000 – $5,000,000");
263 u
"Unit collapse on currency",
264 NumberRangeFormatter::with()
265 .collapse(UNUM_RANGE_COLLAPSE_UNIT
)
266 .numberFormatterBoth(NumberFormatter::with().unit(USD
).precision(Precision::integer())),
277 u
"$5,000–5,000,000");
280 u
"All collapse on currency",
281 NumberRangeFormatter::with()
282 .collapse(UNUM_RANGE_COLLAPSE_ALL
)
283 .numberFormatterBoth(NumberFormatter::with().unit(USD
).precision(Precision::integer())),
294 u
"$5,000–5,000,000");
297 u
"Default collapse on currency ISO code",
298 NumberRangeFormatter::with()
299 .numberFormatterBoth(NumberFormatter::with()
301 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
)
302 .precision(Precision::integer())),
305 u
"~GBP 5", // TODO: Fix this at some point
313 u
"GBP 5,000–5,000,000");
316 u
"No collapse on currency ISO code",
317 NumberRangeFormatter::with()
318 .collapse(UNUM_RANGE_COLLAPSE_NONE
)
319 .numberFormatterBoth(NumberFormatter::with()
321 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
)
322 .precision(Precision::integer())),
325 u
"~GBP 5", // TODO: Fix this at some point
329 u
"GBP 3 – GBP 3,000",
330 u
"GBP 3,000 – GBP 5,000",
331 u
"GBP 4,999 – GBP 5,001",
333 u
"GBP 5,000 – GBP 5,000,000");
336 u
"Unit collapse on currency ISO code",
337 NumberRangeFormatter::with()
338 .collapse(UNUM_RANGE_COLLAPSE_UNIT
)
339 .numberFormatterBoth(NumberFormatter::with()
341 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
)
342 .precision(Precision::integer())),
345 u
"~GBP 5", // TODO: Fix this at some point
353 u
"GBP 5,000–5,000,000");
356 u
"All collapse on currency ISO code",
357 NumberRangeFormatter::with()
358 .collapse(UNUM_RANGE_COLLAPSE_ALL
)
359 .numberFormatterBoth(NumberFormatter::with()
361 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
)
362 .precision(Precision::integer())),
365 u
"~GBP 5", // TODO: Fix this at some point
373 u
"GBP 5,000–5,000,000");
375 // Default collapse on measurement unit is in testBasic()
378 u
"No collapse on measurement unit",
379 NumberRangeFormatter::with()
380 .collapse(UNUM_RANGE_COLLAPSE_NONE
)
381 .numberFormatterBoth(NumberFormatter::with().unit(METER
)),
389 u
"3,000 m – 5,000 m",
390 u
"4,999 m – 5,001 m",
392 u
"5,000 m – 5,000,000 m");
395 u
"Unit collapse on measurement unit",
396 NumberRangeFormatter::with()
397 .collapse(UNUM_RANGE_COLLAPSE_UNIT
)
398 .numberFormatterBoth(NumberFormatter::with().unit(METER
)),
409 u
"5,000–5,000,000 m");
412 u
"All collapse on measurement unit",
413 NumberRangeFormatter::with()
414 .collapse(UNUM_RANGE_COLLAPSE_ALL
)
415 .numberFormatterBoth(NumberFormatter::with().unit(METER
)),
426 u
"5,000–5,000,000 m");
429 u
"Default collapse, long-form compact notation",
430 NumberRangeFormatter::with()
431 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactLong())),
442 u
"5 Tausend – 5 Millionen");
445 u
"Unit collapse, long-form compact notation",
446 NumberRangeFormatter::with()
447 .collapse(UNUM_RANGE_COLLAPSE_UNIT
)
448 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactLong())),
456 u
"3 Tausend – 5 Tausend",
459 u
"5 Tausend – 5 Millionen");
462 u
"Default collapse on measurement unit with compact-short notation",
463 NumberRangeFormatter::with()
464 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER
)),
478 u
"No collapse on measurement unit with compact-short notation",
479 NumberRangeFormatter::with()
480 .collapse(UNUM_RANGE_COLLAPSE_NONE
)
481 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER
)),
495 u
"Unit collapse on measurement unit with compact-short notation",
496 NumberRangeFormatter::with()
497 .collapse(UNUM_RANGE_COLLAPSE_UNIT
)
498 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER
)),
512 u
"All collapse on measurement unit with compact-short notation",
513 NumberRangeFormatter::with()
514 .collapse(UNUM_RANGE_COLLAPSE_ALL
)
515 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER
)),
523 u
"3–5K m", // this one is the key use case for ALL
529 u
"No collapse on scientific notation",
530 NumberRangeFormatter::with()
531 .collapse(UNUM_RANGE_COLLAPSE_NONE
)
532 .numberFormatterBoth(NumberFormatter::with().notation(Notation::scientific())),
541 u
"4.999E3 – 5.001E3",
546 u
"All collapse on scientific notation",
547 NumberRangeFormatter::with()
548 .collapse(UNUM_RANGE_COLLAPSE_ALL
)
549 .numberFormatterBoth(NumberFormatter::with().notation(Notation::scientific())),
562 // TODO: Test compact currency?
563 // The code is not smart enough to differentiate the notation from the unit.
566 void NumberRangeFormatterTest::testIdentity() {
568 u
"Identity fallback Range",
569 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_RANGE
),
583 u
"Identity fallback Approximately or Single Value",
584 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE
),
598 u
"Identity fallback Single Value",
599 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE
),
613 u
"Identity fallback Approximately or Single Value with compact notation",
614 NumberRangeFormatter::with()
615 .identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE
)
616 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort())),
630 u
"Approximately in middle of unit string",
631 NumberRangeFormatter::with().numberFormatterBoth(
632 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
)),
643 u
"華氏5,000-5,000,000度");
646 void NumberRangeFormatterTest::testDifferentFormatters() {
648 u
"Different rounding rules",
649 NumberRangeFormatter::with()
650 .numberFormatterFirst(NumberFormatter::with().precision(Precision::integer()))
651 .numberFormatterSecond(NumberFormatter::with().precision(Precision::fixedSignificantDigits(2))),
661 u
"5,000–5,000", // TODO: Should this one be ~5,000?
665 void NumberRangeFormatterTest::testPlurals() {
666 IcuTestErrorCode
status(*this, "testPlurals");
668 // Locale sl has interesting plural forms:
670 // one{"britanski funt"}
671 // two{"britanska funta"}
672 // few{"britanski funti"}
673 // other{"britanskih funtov"}
677 UnlocalizedNumberFormatter unf
= NumberFormatter::with()
679 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
)
680 .precision(Precision::integer());
681 LocalizedNumberFormatter lnf
= unf
.locale(locale
);
683 // For comparison, run the non-range version of the formatter
684 assertEquals(Int64ToUnicodeString(1), u
"1 britanski funt", lnf
.formatDouble(1, status
).toString(status
));
685 assertEquals(Int64ToUnicodeString(2), u
"2 britanska funta", lnf
.formatDouble(2, status
).toString(status
));
686 assertEquals(Int64ToUnicodeString(3), u
"3 britanski funti", lnf
.formatDouble(3, status
).toString(status
));
687 assertEquals(Int64ToUnicodeString(5), u
"5 britanskih funtov", lnf
.formatDouble(5, status
).toString(status
));
688 if (status
.errIfFailureAndReset()) { return; }
690 LocalizedNumberRangeFormatter lnrf
= NumberRangeFormatter::with()
691 .numberFormatterBoth(unf
)
692 .identityFallback(UNUM_IDENTITY_FALLBACK_RANGE
)
698 const char16_t* expected
;
700 {1, 1, u
"1–1 britanski funti"}, // one + one -> few
701 {1, 2, u
"1–2 britanska funta"}, // one + two -> two
702 {1, 3, u
"1–3 britanski funti"}, // one + few -> few
703 {1, 5, u
"1–5 britanskih funtov"}, // one + other -> other
704 {2, 1, u
"2–1 britanski funti"}, // two + one -> few
705 {2, 2, u
"2–2 britanska funta"}, // two + two -> two
706 {2, 3, u
"2–3 britanski funti"}, // two + few -> few
707 {2, 5, u
"2–5 britanskih funtov"}, // two + other -> other
708 {3, 1, u
"3–1 britanski funti"}, // few + one -> few
709 {3, 2, u
"3–2 britanska funta"}, // few + two -> two
710 {3, 3, u
"3–3 britanski funti"}, // few + few -> few
711 {3, 5, u
"3–5 britanskih funtov"}, // few + other -> other
712 {5, 1, u
"5–1 britanski funti"}, // other + one -> few
713 {5, 2, u
"5–2 britanska funta"}, // other + two -> two
714 {5, 3, u
"5–3 britanski funti"}, // other + few -> few
715 {5, 5, u
"5–5 britanskih funtov"}, // other + other -> other
717 for (auto& cas
: cases
) {
718 UnicodeString message
= Int64ToUnicodeString(static_cast<int64_t>(cas
.first
));
720 message
+= Int64ToUnicodeString(static_cast<int64_t>(cas
.second
));
721 status
.setScope(message
);
722 UnicodeString actual
= lnrf
.formatFormattableRange(cas
.first
, cas
.second
, status
).toString(status
);
723 assertEquals(message
, cas
.expected
, actual
);
724 status
.errIfFailureAndReset();
728 void NumberRangeFormatterTest::testFieldPositions() {
730 const char16_t* message
= u
"Field position test 1";
731 const char16_t* expectedString
= u
"3K – 5K m";
732 FormattedNumberRange result
= assertFormattedRangeEquals(
734 NumberRangeFormatter::with()
735 .numberFormatterBoth(NumberFormatter::with()
737 .notation(Notation::compactShort()))
742 static const UFieldPosition expectedFieldPositions
[] = {
743 // field, begin index, end index
744 {UNUM_INTEGER_FIELD
, 0, 1},
745 {UNUM_COMPACT_FIELD
, 1, 2},
746 {UNUM_INTEGER_FIELD
, 5, 6},
747 {UNUM_COMPACT_FIELD
, 6, 7},
748 {UNUM_MEASURE_UNIT_FIELD
, 8, 9}};
753 UFIELD_CATEGORY_NUMBER
,
754 expectedFieldPositions
,
755 UPRV_LENGTHOF(expectedFieldPositions
));
759 const char16_t* message
= u
"Field position test 2";
760 const char16_t* expectedString
= u
"87,654,321–98,765,432";
761 FormattedNumberRange result
= assertFormattedRangeEquals(
763 NumberRangeFormatter::withLocale("en-us"),
767 static const UFieldPosition expectedFieldPositions
[] = {
768 // field, begin index, end index
769 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
770 {UNUM_GROUPING_SEPARATOR_FIELD
, 6, 7},
771 {UNUM_INTEGER_FIELD
, 0, 10},
772 {UNUM_GROUPING_SEPARATOR_FIELD
, 13, 14},
773 {UNUM_GROUPING_SEPARATOR_FIELD
, 17, 18},
774 {UNUM_INTEGER_FIELD
, 11, 21}};
779 UFIELD_CATEGORY_NUMBER
,
780 expectedFieldPositions
,
781 UPRV_LENGTHOF(expectedFieldPositions
));
785 void NumberRangeFormatterTest::testCopyMove() {
786 IcuTestErrorCode
status(*this, "testCopyMove");
788 // Default constructors
789 LocalizedNumberRangeFormatter l1
;
790 assertEquals("Initial behavior", u
"1–5", l1
.formatFormattableRange(1, 5, status
).toString(status
));
791 if (status
.errDataIfFailureAndReset()) { return; }
794 l1
= NumberRangeFormatter::withLocale("fr-FR")
795 .numberFormatterBoth(NumberFormatter::with().unit(USD
));
796 assertEquals("Currency behavior", u
"1,00–5,00 $US", l1
.formatFormattableRange(1, 5, status
).toString(status
));
799 LocalizedNumberRangeFormatter l2
= l1
;
800 assertEquals("Copy constructor", u
"1,00–5,00 $US", l2
.formatFormattableRange(1, 5, status
).toString(status
));
803 LocalizedNumberRangeFormatter l3
= std::move(l1
);
804 assertEquals("Move constructor", u
"1,00–5,00 $US", l3
.formatFormattableRange(1, 5, status
).toString(status
));
806 // Reset objects for assignment tests
807 l1
= NumberRangeFormatter::withLocale("en-us");
808 l2
= NumberRangeFormatter::withLocale("en-us");
809 assertEquals("Rest behavior, l1", u
"1–5", l1
.formatFormattableRange(1, 5, status
).toString(status
));
810 assertEquals("Rest behavior, l2", u
"1–5", l2
.formatFormattableRange(1, 5, status
).toString(status
));
814 assertEquals("Copy constructor", u
"1,00–5,00 $US", l1
.formatFormattableRange(1, 5, status
).toString(status
));
818 assertEquals("Copy constructor", u
"1,00–5,00 $US", l2
.formatFormattableRange(1, 5, status
).toString(status
));
820 // FormattedNumberRange
821 FormattedNumberRange result
= l1
.formatFormattableRange(1, 5, status
);
822 assertEquals("FormattedNumberRange move constructor", u
"1,00–5,00 $US", result
.toString(status
));
823 result
= l1
.formatFormattableRange(3, 6, status
);
824 assertEquals("FormattedNumberRange move assignment", u
"3,00–6,00 $US", result
.toString(status
));
827 void NumberRangeFormatterTest::toObject() {
828 IcuTestErrorCode
status(*this, "toObject");
830 // const lvalue version
832 LocalizedNumberRangeFormatter lnf
= NumberRangeFormatter::withLocale("en");
833 LocalPointer
<LocalizedNumberRangeFormatter
> lnf2(lnf
.clone());
834 assertFalse("should create successfully, const lvalue", lnf2
.isNull());
835 assertEquals("object API test, const lvalue", u
"5–7",
836 lnf2
->formatFormattableRange(5, 7, status
).toString(status
));
839 // rvalue reference version
841 LocalPointer
<LocalizedNumberRangeFormatter
> lnf(
842 NumberRangeFormatter::withLocale("en").clone());
843 assertFalse("should create successfully, rvalue reference", lnf
.isNull());
844 assertEquals("object API test, rvalue reference", u
"5–7",
845 lnf
->formatFormattableRange(5, 7, status
).toString(status
));
848 // to std::unique_ptr via assignment
850 std::unique_ptr
<LocalizedNumberRangeFormatter
> lnf
=
851 NumberRangeFormatter::withLocale("en").clone();
852 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf
));
853 assertEquals("object API test, unique_ptr B", u
"5–7",
854 lnf
->formatFormattableRange(5, 7, status
).toString(status
));
857 // make sure no memory leaks
859 NumberRangeFormatter::with().clone();
863 void NumberRangeFormatterTest::assertFormatRange(
864 const char16_t* message
,
865 const UnlocalizedNumberRangeFormatter
& f
,
867 const char16_t* expected_10_50
,
868 const char16_t* expected_49_51
,
869 const char16_t* expected_50_50
,
870 const char16_t* expected_00_30
,
871 const char16_t* expected_00_00
,
872 const char16_t* expected_30_3K
,
873 const char16_t* expected_30K_50K
,
874 const char16_t* expected_49K_51K
,
875 const char16_t* expected_50K_50K
,
876 const char16_t* expected_50K_50M
) {
877 LocalizedNumberRangeFormatter l
= f
.locale(locale
);
878 assertFormattedRangeEquals(message
, l
, 1, 5, expected_10_50
);
879 assertFormattedRangeEquals(message
, l
, 4.9999999, 5.0000001, expected_49_51
);
880 assertFormattedRangeEquals(message
, l
, 5, 5, expected_50_50
);
881 assertFormattedRangeEquals(message
, l
, 0, 3, expected_00_30
);
882 assertFormattedRangeEquals(message
, l
, 0, 0, expected_00_00
);
883 assertFormattedRangeEquals(message
, l
, 3, 3000, expected_30_3K
);
884 assertFormattedRangeEquals(message
, l
, 3000, 5000, expected_30K_50K
);
885 assertFormattedRangeEquals(message
, l
, 4999, 5001, expected_49K_51K
);
886 assertFormattedRangeEquals(message
, l
, 5000, 5000, expected_50K_50K
);
887 assertFormattedRangeEquals(message
, l
, 5e3
, 5e6
, expected_50K_50M
);
890 FormattedNumberRange
NumberRangeFormatterTest::assertFormattedRangeEquals(
891 const char16_t* message
,
892 const LocalizedNumberRangeFormatter
& l
,
895 const char16_t* expected
) {
896 IcuTestErrorCode
status(*this, "assertFormattedRangeEquals");
897 UnicodeString fullMessage
= UnicodeString(message
) + u
": " + DoubleToUnicodeString(first
) + u
", " + DoubleToUnicodeString(second
);
898 status
.setScope(fullMessage
);
899 FormattedNumberRange fnr
= l
.formatFormattableRange(first
, second
, status
);
900 UnicodeString actual
= fnr
.toString(status
);
901 assertEquals(fullMessage
, expected
, actual
);