1 // © 2017 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
12 #include "unicode/unum.h"
13 #include "unicode/numberformatter.h"
14 #include "number_asformat.h"
15 #include "number_types.h"
16 #include "number_utils.h"
17 #include "numbertest.h"
18 #include "unicode/utypes.h"
20 // Horrible workaround for the lack of a status code in the constructor...
21 // (Also affects numbertest_range.cpp)
22 UErrorCode globalNumberFormatterApiTestStatus
= U_ZERO_ERROR
;
24 NumberFormatterApiTest::NumberFormatterApiTest()
25 : NumberFormatterApiTest(globalNumberFormatterApiTestStatus
) {
28 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode
& status
)
29 : USD(u
"USD", status
),
36 FRENCH_SYMBOLS(Locale::getFrench(), status
),
37 SWISS_SYMBOLS(Locale("de-CH"), status
),
38 MYANMAR_SYMBOLS(Locale("my"), status
) {
40 // Check for error on the first MeasureUnit in case there is no data
41 LocalPointer
<MeasureUnit
> unit(MeasureUnit::createMeter(status
));
42 if (U_FAILURE(status
)) {
43 dataerrln("%s %d status = %s", __FILE__
, __LINE__
, u_errorName(status
));
48 DAY
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createDay(status
));
49 SQUARE_METER
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSquareMeter(status
));
50 FAHRENHEIT
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFahrenheit(status
));
51 SECOND
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSecond(status
));
52 POUND
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createPound(status
));
53 SQUARE_MILE
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSquareMile(status
));
54 JOULE
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createJoule(status
));
55 FURLONG
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFurlong(status
));
56 KELVIN
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createKelvin(status
));
58 MATHSANB
= *LocalPointer
<NumberingSystem
>(NumberingSystem::createInstanceByName("mathsanb", status
));
59 LATN
= *LocalPointer
<NumberingSystem
>(NumberingSystem::createInstanceByName("latn", status
));
62 void NumberFormatterApiTest::runIndexedTest(int32_t index
, UBool exec
, const char*& name
, char*) {
64 logln("TestSuite NumberFormatterApiTest: ");
67 TESTCASE_AUTO(notationSimple
);
68 TESTCASE_AUTO(notationScientific
);
69 TESTCASE_AUTO(notationCompact
);
70 TESTCASE_AUTO(unitMeasure
);
71 TESTCASE_AUTO(unitCompoundMeasure
);
72 TESTCASE_AUTO(unitCurrency
);
73 TESTCASE_AUTO(unitPercent
);
75 // Slow test: run in exhaustive mode only
76 TESTCASE_AUTO(percentParity
);
78 TESTCASE_AUTO(roundingFraction
);
79 TESTCASE_AUTO(roundingFigures
);
80 TESTCASE_AUTO(roundingFractionFigures
);
81 TESTCASE_AUTO(roundingOther
);
82 TESTCASE_AUTO(grouping
);
83 TESTCASE_AUTO(padding
);
84 TESTCASE_AUTO(integerWidth
);
85 TESTCASE_AUTO(symbols
);
86 // TODO: Add this method if currency symbols override support is added.
87 //TESTCASE_AUTO(symbolsOverride);
89 TESTCASE_AUTO(decimal
);
91 TESTCASE_AUTO(locale
);
92 TESTCASE_AUTO(formatTypes
);
93 TESTCASE_AUTO(fieldPositionLogic
);
94 TESTCASE_AUTO(fieldPositionCoverage
);
95 TESTCASE_AUTO(toFormat
);
96 TESTCASE_AUTO(errors
);
98 // Slow test: run in exhaustive mode only
99 // (somewhat slow to check all permutations of settings)
100 TESTCASE_AUTO(validRanges
);
102 TESTCASE_AUTO(copyMove
);
103 TESTCASE_AUTO(localPointerCAPI
);
104 TESTCASE_AUTO(toObject
);
108 void NumberFormatterApiTest::notationSimple() {
109 assertFormatDescending(
112 NumberFormatter::with(),
113 Locale::getEnglish(),
124 assertFormatDescendingBig(
127 NumberFormatter::with().notation(Notation::simple()),
128 Locale::getEnglish(),
140 u
"Basic with Negative Sign",
142 NumberFormatter::with(),
143 Locale::getEnglish(),
149 void NumberFormatterApiTest::notationScientific() {
150 assertFormatDescending(
153 NumberFormatter::with().notation(Notation::scientific()),
154 Locale::getEnglish(),
165 assertFormatDescending(
168 NumberFormatter::with().notation(Notation::engineering()),
169 Locale::getEnglish(),
180 assertFormatDescending(
181 u
"Scientific sign always shown",
182 u
"scientific/sign-always",
183 NumberFormatter::with().notation(
184 Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS
)),
185 Locale::getEnglish(),
196 assertFormatDescending(
197 u
"Scientific min exponent digits",
199 NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
200 Locale::getEnglish(),
212 u
"Scientific Negative",
214 NumberFormatter::with().notation(Notation::scientific()),
215 Locale::getEnglish(),
220 void NumberFormatterApiTest::notationCompact() {
221 assertFormatDescending(
224 NumberFormatter::with().notation(Notation::compactShort()),
225 Locale::getEnglish(),
236 assertFormatDescending(
239 NumberFormatter::with().notation(Notation::compactLong()),
240 Locale::getEnglish(),
251 assertFormatDescending(
252 u
"Compact Short Currency",
253 u
"compact-short currency/USD",
254 NumberFormatter::with().notation(Notation::compactShort()).unit(USD
),
255 Locale::getEnglish(),
266 assertFormatDescending(
267 u
"Compact Short with ISO Currency",
268 u
"compact-short currency/USD unit-width-iso-code",
269 NumberFormatter::with().notation(Notation::compactShort())
271 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
272 Locale::getEnglish(),
283 assertFormatDescending(
284 u
"Compact Short with Long Name Currency",
285 u
"compact-short currency/USD unit-width-full-name",
286 NumberFormatter::with().notation(Notation::compactShort())
288 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
289 Locale::getEnglish(),
297 u
"0.0088 US dollars",
300 // Note: Most locales don't have compact long currency, so this currently falls back to short.
301 // This test case should be fixed when proper compact long currency patterns are added.
302 assertFormatDescending(
303 u
"Compact Long Currency",
304 u
"compact-long currency/USD",
305 NumberFormatter::with().notation(Notation::compactLong()).unit(USD
),
306 Locale::getEnglish(),
307 u
"$88K", // should be something like "$88 thousand"
317 // Note: Most locales don't have compact long currency, so this currently falls back to short.
318 // This test case should be fixed when proper compact long currency patterns are added.
319 assertFormatDescending(
320 u
"Compact Long with ISO Currency",
321 u
"compact-long currency/USD unit-width-iso-code",
322 NumberFormatter::with().notation(Notation::compactLong())
324 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
325 Locale::getEnglish(),
326 u
"USD 88K", // should be something like "USD 88 thousand"
336 // TODO: This behavior could be improved and should be revisited.
337 assertFormatDescending(
338 u
"Compact Long with Long Name Currency",
339 u
"compact-long currency/USD unit-width-full-name",
340 NumberFormatter::with().notation(Notation::compactLong())
342 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
343 Locale::getEnglish(),
344 u
"88 thousand US dollars",
345 u
"8.8 thousand US dollars",
351 u
"0.0088 US dollars",
355 u
"Compact Plural One",
357 NumberFormatter::with().notation(Notation::compactLong()),
358 Locale::createFromName("es"),
363 u
"Compact Plural Other",
365 NumberFormatter::with().notation(Notation::compactLong()),
366 Locale::createFromName("es"),
371 u
"Compact with Negative Sign",
373 NumberFormatter::with().notation(Notation::compactShort()),
374 Locale::getEnglish(),
381 NumberFormatter::with().notation(Notation::compactShort()),
382 Locale::getEnglish(),
389 NumberFormatter::with().notation(Notation::compactShort()),
390 Locale::getEnglish(),
397 NumberFormatter::with().notation(Notation::compactShort()),
398 Locale::getEnglish(),
405 NumberFormatter::with().notation(Notation::compactShort()),
406 Locale::getEnglish(),
413 NumberFormatter::with().notation(Notation::compactShort()),
414 Locale::getEnglish(),
419 u
"Compact in zh-Hant-HK",
421 NumberFormatter::with().notation(Notation::compactShort()),
422 Locale("zh-Hant-HK"),
427 u
"Compact in zh-Hant",
429 NumberFormatter::with().notation(Notation::compactShort()),
434 // NOTE: There is no API for compact custom data in C++
435 // and thus no "Compact Somali No Figure" test
438 void NumberFormatterApiTest::unitMeasure() {
439 assertFormatDescending(
440 u
"Meters Short and unit() method",
441 u
"measure-unit/length-meter",
442 NumberFormatter::with().unit(MeasureUnit::getMeter()),
443 Locale::getEnglish(),
454 assertFormatDescending(
455 u
"Meters Long and adoptUnit() method",
456 u
"measure-unit/length-meter unit-width-full-name",
457 NumberFormatter::with().adoptUnit(new MeasureUnit(METER
))
458 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
459 Locale::getEnglish(),
470 assertFormatDescending(
471 u
"Compact Meters Long",
472 u
"compact-long measure-unit/length-meter unit-width-full-name",
473 NumberFormatter::with().notation(Notation::compactLong())
475 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
476 Locale::getEnglish(),
477 u
"88 thousand meters",
478 u
"8.8 thousand meters",
487 // TODO: Implement Measure in C++
488 // assertFormatSingleMeasure(
489 // u"Meters with Measure Input",
490 // NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
491 // Locale::getEnglish(),
492 // new Measure(5.43, new MeasureUnit(METER)),
495 // TODO: Implement Measure in C++
496 // assertFormatSingleMeasure(
497 // u"Measure format method takes precedence over fluent chain",
498 // NumberFormatter::with().unit(METER),
499 // Locale::getEnglish(),
500 // new Measure(5.43, USD),
504 u
"Meters with Negative Sign",
505 u
"measure-unit/length-meter",
506 NumberFormatter::with().unit(METER
),
507 Locale::getEnglish(),
511 // The locale string "सान" appears only in brx.txt:
513 u
"Interesting Data Fallback 1",
514 u
"measure-unit/duration-day unit-width-full-name",
515 NumberFormatter::with().unit(DAY
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
516 Locale::createFromName("brx"),
520 // Requires following the alias from unitsNarrow to unitsShort:
522 u
"Interesting Data Fallback 2",
523 u
"measure-unit/duration-day unit-width-narrow",
524 NumberFormatter::with().unit(DAY
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
525 Locale::createFromName("brx"),
529 // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
530 // requiring fallback to the root.
532 u
"Interesting Data Fallback 3",
533 u
"measure-unit/area-square-meter unit-width-narrow",
534 NumberFormatter::with().unit(SQUARE_METER
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
535 Locale::createFromName("en-GB"),
539 // Try accessing a narrow unit directly from root.
541 u
"Interesting Data Fallback 4",
542 u
"measure-unit/area-square-meter unit-width-narrow",
543 NumberFormatter::with().unit(SQUARE_METER
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
544 Locale::createFromName("root"),
548 // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
549 // NOTE: This example is in the documentation.
551 u
"Difference between Narrow and Short (Narrow Version)",
552 u
"measure-unit/temperature-fahrenheit unit-width-narrow",
553 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
559 u
"Difference between Narrow and Short (Short Version)",
560 u
"measure-unit/temperature-fahrenheit unit-width-short",
561 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
567 u
"MeasureUnit form without {0} in CLDR pattern",
568 u
"measure-unit/temperature-kelvin unit-width-full-name",
569 NumberFormatter::with().unit(KELVIN
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
575 u
"MeasureUnit form without {0} in CLDR pattern and wide base form",
576 u
"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
577 NumberFormatter::with().precision(Precision::fixedFraction(20))
579 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
585 u
"Person unit not in short form",
586 u
"measure-unit/duration-year-person unit-width-full-name",
587 NumberFormatter::with().unit(MeasureUnit::getYearPerson())
588 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
594 void NumberFormatterApiTest::unitCompoundMeasure() {
595 assertFormatDescending(
596 u
"Meters Per Second Short (unit that simplifies) and perUnit method",
597 u
"measure-unit/length-meter per-measure-unit/duration-second",
598 NumberFormatter::with().unit(METER
).perUnit(SECOND
),
599 Locale::getEnglish(),
610 assertFormatDescending(
611 u
"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
612 u
"measure-unit/mass-pound per-measure-unit/area-square-mile",
613 NumberFormatter::with().unit(POUND
).adoptPerUnit(new MeasureUnit(SQUARE_MILE
)),
614 Locale::getEnglish(),
625 assertFormatDescending(
626 u
"Joules Per Furlong Short (unit with no simplifications or special patterns)",
627 u
"measure-unit/energy-joule per-measure-unit/length-furlong",
628 NumberFormatter::with().unit(JOULE
).perUnit(FURLONG
),
629 Locale::getEnglish(),
641 void NumberFormatterApiTest::unitCurrency() {
642 assertFormatDescending(
645 NumberFormatter::with().unit(GBP
),
646 Locale::getEnglish(),
657 assertFormatDescending(
659 u
"currency/GBP unit-width-iso-code",
660 NumberFormatter::with().unit(GBP
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
661 Locale::getEnglish(),
672 assertFormatDescending(
673 u
"Currency Long Name",
674 u
"currency/GBP unit-width-full-name",
675 NumberFormatter::with().unit(GBP
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
676 Locale::getEnglish(),
677 u
"87,650.00 British pounds",
678 u
"8,765.00 British pounds",
679 u
"876.50 British pounds",
680 u
"87.65 British pounds",
681 u
"8.76 British pounds",
682 u
"0.88 British pounds",
683 u
"0.09 British pounds",
684 u
"0.01 British pounds",
685 u
"0.00 British pounds");
687 assertFormatDescending(
689 u
"currency/GBP unit-width-hidden",
690 NumberFormatter::with().unit(GBP
).unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
691 Locale::getEnglish(),
702 // TODO: Implement Measure in C++
703 // assertFormatSingleMeasure(
704 // u"Currency with CurrencyAmount Input",
705 // NumberFormatter::with(),
706 // Locale::getEnglish(),
707 // new CurrencyAmount(5.43, GBP),
710 // TODO: Enable this test when DecimalFormat wrapper is done.
711 // assertFormatSingle(
712 // u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
713 // PatternStringParser.parseToProperties("0 ¤¤¤"),
714 // DecimalFormatSymbols.getInstance(Locale::getEnglish()),
715 // null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
718 u
"Currency with Negative Sign",
720 NumberFormatter::with().unit(GBP
),
721 Locale::getEnglish(),
725 // The full currency symbol is not shown in NARROW format.
726 // NOTE: This example is in the documentation.
728 u
"Currency Difference between Narrow and Short (Narrow Version)",
729 u
"currency/USD unit-width-narrow",
730 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
736 u
"Currency Difference between Narrow and Short (Short Version)",
737 u
"currency/USD unit-width-short",
738 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
744 u
"Currency-dependent format (Control)",
745 u
"currency/USD unit-width-short",
746 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
752 u
"Currency-dependent format (Test)",
753 u
"currency/ESP unit-width-short",
754 NumberFormatter::with().unit(ESP
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
760 u
"Currency-dependent symbols (Control)",
761 u
"currency/USD unit-width-short",
762 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
767 // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
768 // width space), and they set the decimal separator to the $ symbol.
770 u
"Currency-dependent symbols (Test Short)",
771 u
"currency/PTE unit-width-short",
772 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
775 u
"444,444$55 \u200B");
778 u
"Currency-dependent symbols (Test Narrow)",
779 u
"currency/PTE unit-width-narrow",
780 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
783 u
"444,444$55 \u200B");
786 u
"Currency-dependent symbols (Test ISO Code)",
787 u
"currency/PTE unit-width-iso-code",
788 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
),
794 u
"Plural form depending on visible digits (ICU-20499)",
795 u
"currency/RON unit-width-full-name",
796 NumberFormatter::with().unit(RON
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
799 u
"24,00 lei românești");
802 void NumberFormatterApiTest::unitPercent() {
803 assertFormatDescending(
806 NumberFormatter::with().unit(NoUnit::percent()),
807 Locale::getEnglish(),
818 assertFormatDescending(
821 NumberFormatter::with().unit(NoUnit::permille()),
822 Locale::getEnglish(),
836 NumberFormatter::with().unit(NoUnit::base()),
837 Locale::getEnglish(),
842 u
"Percent with Negative Sign",
844 NumberFormatter::with().unit(NoUnit::percent()),
845 Locale::getEnglish(),
850 void NumberFormatterApiTest::percentParity() {
851 IcuTestErrorCode
status(*this, "percentParity");
852 UnlocalizedNumberFormatter uNoUnitPercent
= NumberFormatter::with().unit(NoUnit::percent());
853 UnlocalizedNumberFormatter uNoUnitPermille
= NumberFormatter::with().unit(NoUnit::permille());
854 UnlocalizedNumberFormatter uMeasurePercent
= NumberFormatter::with().unit(MeasureUnit::getPercent());
855 UnlocalizedNumberFormatter uMeasurePermille
= NumberFormatter::with().unit(MeasureUnit::getPermille());
858 auto locales
= Locale::getAvailableLocales(localeCount
);
859 for (int32_t i
=0; i
<localeCount
; i
++) {
860 auto& locale
= locales
[i
];
861 UnicodeString sNoUnitPercent
= uNoUnitPercent
.locale(locale
)
862 .formatDouble(50, status
).toString(status
);
863 UnicodeString sNoUnitPermille
= uNoUnitPermille
.locale(locale
)
864 .formatDouble(50, status
).toString(status
);
865 UnicodeString sMeasurePercent
= uMeasurePercent
.locale(locale
)
866 .formatDouble(50, status
).toString(status
);
867 UnicodeString sMeasurePermille
= uMeasurePermille
.locale(locale
)
868 .formatDouble(50, status
).toString(status
);
870 assertEquals(u
"Percent, locale " + UnicodeString(locale
.getName()),
871 sNoUnitPercent
, sMeasurePercent
);
872 assertEquals(u
"Permille, locale " + UnicodeString(locale
.getName()),
873 sNoUnitPermille
, sMeasurePermille
);
877 void NumberFormatterApiTest::roundingFraction() {
878 assertFormatDescending(
880 u
"precision-integer",
881 NumberFormatter::with().precision(Precision::integer()),
882 Locale::getEnglish(),
893 assertFormatDescending(
896 NumberFormatter::with().precision(Precision::fixedFraction(3)),
897 Locale::getEnglish(),
908 assertFormatDescending(
911 NumberFormatter::with().precision(Precision::minFraction(1)),
912 Locale::getEnglish(),
923 assertFormatDescending(
926 NumberFormatter::with().precision(Precision::maxFraction(1)),
927 Locale::getEnglish(),
938 assertFormatDescending(
941 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
942 Locale::getEnglish(),
954 void NumberFormatterApiTest::roundingFigures() {
956 u
"Fixed Significant",
958 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
959 Locale::getEnglish(),
964 u
"Fixed Significant Rounding",
966 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
967 Locale::getEnglish(),
972 u
"Fixed Significant Zero",
974 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
975 Locale::getEnglish(),
982 NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
983 Locale::getEnglish(),
990 NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
991 Locale::getEnglish(),
996 u
"Min/Max Significant",
998 NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
999 Locale::getEnglish(),
1004 u
"Fixed Significant on zero with lots of integer width",
1005 u
"@ integer-width/+000",
1006 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
1007 .integerWidth(IntegerWidth::zeroFillTo(3)),
1008 Locale::getEnglish(),
1013 u
"Fixed Significant on zero with zero integer width",
1014 u
"@ integer-width/+",
1015 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
1016 .integerWidth(IntegerWidth::zeroFillTo(0)),
1017 Locale::getEnglish(),
1022 void NumberFormatterApiTest::roundingFractionFigures() {
1023 assertFormatDescending(
1024 u
"Basic Significant", // for comparison
1026 NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
1027 Locale::getEnglish(),
1038 assertFormatDescending(
1039 u
"FracSig minMaxFrac minSig",
1041 NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
1042 Locale::getEnglish(),
1048 u
"0.876", // minSig beats maxFrac
1049 u
"0.0876", // minSig beats maxFrac
1050 u
"0.00876", // minSig beats maxFrac
1053 assertFormatDescending(
1054 u
"FracSig minMaxFrac maxSig A",
1056 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
1057 Locale::getEnglish(),
1058 u
"88,000.0", // maxSig beats maxFrac
1059 u
"8,800.0", // maxSig beats maxFrac
1060 u
"880.0", // maxSig beats maxFrac
1061 u
"88.0", // maxSig beats maxFrac
1062 u
"8.8", // maxSig beats maxFrac
1063 u
"0.88", // maxSig beats maxFrac
1068 assertFormatDescending(
1069 u
"FracSig minMaxFrac maxSig B",
1071 NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
1072 Locale::getEnglish(),
1073 u
"88,000.00", // maxSig beats maxFrac
1074 u
"8,800.00", // maxSig beats maxFrac
1075 u
"880.00", // maxSig beats maxFrac
1076 u
"88.00", // maxSig beats maxFrac
1077 u
"8.80", // maxSig beats maxFrac
1084 u
"FracSig with trailing zeros A",
1086 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1087 Locale::getEnglish(),
1092 u
"FracSig with trailing zeros B",
1094 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1095 Locale::getEnglish(),
1100 void NumberFormatterApiTest::roundingOther() {
1101 assertFormatDescending(
1103 u
"precision-unlimited",
1104 NumberFormatter::with().precision(Precision::unlimited()),
1105 Locale::getEnglish(),
1116 assertFormatDescending(
1118 u
"precision-increment/0.5",
1119 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
1120 Locale::getEnglish(),
1131 assertFormatDescending(
1132 u
"Increment with Min Fraction",
1133 u
"precision-increment/0.50",
1134 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
1135 Locale::getEnglish(),
1146 assertFormatDescending(
1147 u
"Strange Increment",
1148 u
"precision-increment/3.140",
1149 NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
1150 Locale::getEnglish(),
1161 assertFormatDescending(
1162 u
"Increment Resolving to Power of 10",
1163 u
"precision-increment/0.010",
1164 NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
1165 Locale::getEnglish(),
1176 assertFormatDescending(
1177 u
"Currency Standard",
1178 u
"currency/CZK precision-currency-standard",
1179 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD
))
1181 Locale::getEnglish(),
1192 assertFormatDescending(
1194 u
"currency/CZK precision-currency-cash",
1195 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
))
1197 Locale::getEnglish(),
1208 assertFormatDescending(
1209 u
"Currency Cash with Nickel Rounding",
1210 u
"currency/CAD precision-currency-cash",
1211 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
))
1213 Locale::getEnglish(),
1224 assertFormatDescending(
1225 u
"Currency not in top-level fluent chain",
1226 u
"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
1227 NumberFormatter::with().precision(
1228 Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
).withCurrency(CZK
)),
1229 Locale::getEnglish(),
1240 // NOTE: Other tests cover the behavior of the other rounding modes.
1241 assertFormatDescending(
1242 u
"Rounding Mode CEILING",
1243 u
"precision-integer rounding-mode-ceiling",
1244 NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING
),
1245 Locale::getEnglish(),
1257 void NumberFormatterApiTest::grouping() {
1258 assertFormatDescendingBig(
1259 u
"Western Grouping",
1261 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1262 Locale::getEnglish(),
1273 assertFormatDescendingBig(
1276 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1288 assertFormatDescendingBig(
1289 u
"Western Grouping, Min 2",
1291 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1292 Locale::getEnglish(),
1303 assertFormatDescendingBig(
1304 u
"Indic Grouping, Min 2",
1306 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1318 assertFormatDescendingBig(
1321 NumberFormatter::with().grouping(UNUM_GROUPING_OFF
),
1333 assertFormatDescendingBig(
1334 u
"Indic locale with THOUSANDS grouping",
1336 NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS
),
1348 // NOTE: Hungarian was interesting because it had minimumGroupingDigits=4 in locale data.
1349 // Open source has fixed Hungarian to have minimumGroupingDigits=1 and changed this test to
1350 // use Polish with minimumGroupingDigits=2. However for now we keep the test using Hungarian
1351 // to verify the change.
1352 assertFormatDescendingBig(
1353 u
"Hungarian Grouping",
1355 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1367 assertFormatDescendingBig(
1368 u
"Hungarian Grouping, Min 2",
1370 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1382 assertFormatDescendingBig(
1383 u
"Hungarian Grouping, Always",
1384 u
"group-on-aligned",
1385 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
),
1397 // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
1398 // If this test breaks due to data changes, find another locale that has no default grouping.
1399 assertFormatDescendingBig(
1400 u
"Bulgarian Currency Grouping",
1401 u
"currency/USD group-auto",
1402 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
).unit(USD
),
1404 u
"87650000,00 щ.д.",
1414 assertFormatDescendingBig(
1415 u
"Bulgarian Currency Grouping, Always",
1416 u
"currency/USD group-on-aligned",
1417 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
).unit(USD
),
1419 u
"87 650 000,00 щ.д.",
1420 u
"8 765 000,00 щ.д.",
1430 macros
.grouper
= Grouper(4, 1, 3, UNUM_GROUPING_COUNT
);
1431 assertFormatDescendingBig(
1432 u
"Custom Grouping via Internal API",
1434 NumberFormatter::with().macros(macros
),
1435 Locale::getEnglish(),
1447 void NumberFormatterApiTest::padding() {
1448 assertFormatDescending(
1451 NumberFormatter::with().padding(Padder::none()),
1452 Locale::getEnglish(),
1463 assertFormatDescending(
1466 NumberFormatter::with().padding(
1468 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1469 Locale::getEnglish(),
1480 assertFormatDescending(
1481 u
"Padding with code points",
1483 NumberFormatter::with().padding(
1485 0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1486 Locale::getEnglish(),
1497 assertFormatDescending(
1498 u
"Padding with wide digits",
1500 NumberFormatter::with().padding(
1502 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1503 .adoptSymbols(new NumberingSystem(MATHSANB
)),
1504 Locale::getEnglish(),
1515 assertFormatDescending(
1516 u
"Padding with currency spacing",
1518 NumberFormatter::with().padding(
1520 '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1522 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1523 Locale::getEnglish(),
1535 u
"Pad Before Prefix",
1537 NumberFormatter::with().padding(
1539 '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX
)),
1540 Locale::getEnglish(),
1545 u
"Pad After Prefix",
1547 NumberFormatter::with().padding(
1549 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1550 Locale::getEnglish(),
1555 u
"Pad Before Suffix",
1557 NumberFormatter::with().padding(
1559 '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX
)).unit(NoUnit::percent()),
1560 Locale::getEnglish(),
1565 u
"Pad After Suffix",
1567 NumberFormatter::with().padding(
1569 '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX
)).unit(NoUnit::percent()),
1570 Locale::getEnglish(),
1575 u
"Currency Spacing with Zero Digit Padding Broken",
1577 NumberFormatter::with().padding(
1579 '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1581 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1582 Locale::getEnglish(),
1584 u
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
1587 void NumberFormatterApiTest::integerWidth() {
1588 assertFormatDescending(
1589 u
"Integer Width Default",
1590 u
"integer-width/+0",
1591 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
1592 Locale::getEnglish(),
1603 assertFormatDescending(
1604 u
"Integer Width Zero Fill 0",
1606 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
1607 Locale::getEnglish(),
1616 u
""); // TODO: Avoid the empty string here?
1618 assertFormatDescending(
1619 u
"Integer Width Zero Fill 3",
1620 u
"integer-width/+000",
1621 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
1622 Locale::getEnglish(),
1633 assertFormatDescending(
1634 u
"Integer Width Max 3",
1635 u
"integer-width/##0",
1636 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
1637 Locale::getEnglish(),
1648 assertFormatDescending(
1649 u
"Integer Width Fixed 2",
1650 u
"integer-width/00",
1651 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1652 Locale::getEnglish(),
1664 u
"Integer Width Remove All A",
1665 u
"integer-width/00",
1666 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1672 u
"Integer Width Remove All B",
1673 u
"integer-width/00",
1674 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1680 u
"Integer Width Remove All B, Bytes Mode",
1681 u
"integer-width/00",
1682 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1684 // Note: this double produces all 17 significant digits
1685 10000000000000002000.0,
1689 void NumberFormatterApiTest::symbols() {
1690 assertFormatDescending(
1691 u
"French Symbols with Japanese Data 1",
1693 NumberFormatter::with().symbols(FRENCH_SYMBOLS
),
1706 u
"French Symbols with Japanese Data 2",
1708 NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS
),
1713 assertFormatDescending(
1714 u
"Latin Numbering System with Arabic Data",
1715 u
"currency/USD latin",
1716 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1728 assertFormatDescending(
1729 u
"Math Numbering System with French Data",
1730 u
"numbering-system/mathsanb",
1731 NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB
)),
1732 Locale::getFrench(),
1744 u
"Swiss Symbols (used in documentation)",
1746 NumberFormatter::with().symbols(SWISS_SYMBOLS
),
1747 Locale::getEnglish(),
1752 u
"Myanmar Symbols (used in documentation)",
1754 NumberFormatter::with().symbols(MYANMAR_SYMBOLS
),
1755 Locale::getEnglish(),
1757 u
"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
1759 // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
1762 u
"Currency symbol should precede number in ar with NS latn",
1763 u
"currency/USD latin",
1764 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1770 u
"Currency symbol should precede number in ar@numbers=latn",
1772 NumberFormatter::with().unit(USD
),
1773 Locale("ar@numbers=latn"),
1778 u
"Currency symbol should follow number in ar-EG with NS arab",
1780 NumberFormatter::with().unit(USD
),
1786 u
"Currency symbol should follow number in ar@numbers=arab",
1788 NumberFormatter::with().unit(USD
),
1789 Locale("ar@numbers=arab"),
1794 u
"NumberingSystem in API should win over @numbers keyword",
1795 u
"currency/USD latin",
1796 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1797 Locale("ar@numbers=arab"),
1801 UErrorCode status
= U_ZERO_ERROR
;
1803 "NumberingSystem in API should win over @numbers keyword in reverse order",
1805 NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN
))
1807 .formatDouble(12345.67, status
)
1810 DecimalFormatSymbols symbols
= SWISS_SYMBOLS
;
1811 UnlocalizedNumberFormatter f
= NumberFormatter::with().symbols(symbols
);
1812 symbols
.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol
, u
"!", status
);
1814 u
"Symbols object should be copied", nullptr, f
, Locale::getEnglish(), 12345.67, u
"12’345.67");
1817 u
"The last symbols setter wins",
1819 NumberFormatter::with().symbols(symbols
).adoptSymbols(new NumberingSystem(LATN
)),
1820 Locale::getEnglish(),
1825 u
"The last symbols setter wins",
1827 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).symbols(symbols
),
1828 Locale::getEnglish(),
1833 // TODO: Enable if/when currency symbol override is added.
1834 //void NumberFormatterTest::symbolsOverride() {
1835 // DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
1836 // dfs.setCurrencySymbol("@");
1837 // dfs.setInternationalCurrencySymbol("foo");
1838 // assertFormatSingle(
1839 // u"Custom Short Currency Symbol",
1840 // NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
1841 // Locale::getEnglish(),
1846 void NumberFormatterApiTest::sign() {
1848 u
"Sign Auto Positive",
1850 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1851 Locale::getEnglish(),
1856 u
"Sign Auto Negative",
1858 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1859 Locale::getEnglish(),
1866 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1867 Locale::getEnglish(),
1872 u
"Sign Always Positive",
1874 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1875 Locale::getEnglish(),
1880 u
"Sign Always Negative",
1882 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1883 Locale::getEnglish(),
1888 u
"Sign Always Zero",
1890 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1891 Locale::getEnglish(),
1896 u
"Sign Never Positive",
1898 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1899 Locale::getEnglish(),
1904 u
"Sign Never Negative",
1906 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1907 Locale::getEnglish(),
1914 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1915 Locale::getEnglish(),
1920 u
"Sign Accounting Positive",
1921 u
"currency/USD sign-accounting",
1922 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1923 Locale::getEnglish(),
1928 u
"Sign Accounting Negative",
1929 u
"currency/USD sign-accounting",
1930 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1931 Locale::getEnglish(),
1936 u
"Sign Accounting Zero",
1937 u
"currency/USD sign-accounting",
1938 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1939 Locale::getEnglish(),
1944 u
"Sign Accounting-Always Positive",
1945 u
"currency/USD sign-accounting-always",
1946 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1947 Locale::getEnglish(),
1952 u
"Sign Accounting-Always Negative",
1953 u
"currency/USD sign-accounting-always",
1954 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1955 Locale::getEnglish(),
1960 u
"Sign Accounting-Always Zero",
1961 u
"currency/USD sign-accounting-always",
1962 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1963 Locale::getEnglish(),
1968 u
"Sign Except-Zero Positive",
1969 u
"sign-except-zero",
1970 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1971 Locale::getEnglish(),
1976 u
"Sign Except-Zero Negative",
1977 u
"sign-except-zero",
1978 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1979 Locale::getEnglish(),
1984 u
"Sign Except-Zero Zero",
1985 u
"sign-except-zero",
1986 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1987 Locale::getEnglish(),
1992 u
"Sign Accounting-Except-Zero Positive",
1993 u
"currency/USD sign-accounting-except-zero",
1994 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
1995 Locale::getEnglish(),
2000 u
"Sign Accounting-Except-Zero Negative",
2001 u
"currency/USD sign-accounting-except-zero",
2002 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2003 Locale::getEnglish(),
2008 u
"Sign Accounting-Except-Zero Zero",
2009 u
"currency/USD sign-accounting-except-zero",
2010 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2011 Locale::getEnglish(),
2016 u
"Sign Accounting Negative Hidden",
2017 u
"currency/USD unit-width-hidden sign-accounting",
2018 NumberFormatter::with()
2019 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2021 .unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
2022 Locale::getEnglish(),
2027 u
"Sign Accounting Negative Narrow",
2028 u
"currency/USD unit-width-narrow sign-accounting",
2029 NumberFormatter::with()
2030 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2032 .unitWidth(UNUM_UNIT_WIDTH_NARROW
),
2033 Locale::getCanada(),
2038 u
"Sign Accounting Negative Short",
2039 u
"currency/USD sign-accounting",
2040 NumberFormatter::with()
2041 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2043 .unitWidth(UNUM_UNIT_WIDTH_SHORT
),
2044 Locale::getCanada(),
2046 u
"(US$444,444.00)");
2049 u
"Sign Accounting Negative Iso Code",
2050 u
"currency/USD unit-width-iso-code sign-accounting",
2051 NumberFormatter::with()
2052 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2054 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
),
2055 Locale::getCanada(),
2057 u
"(USD 444,444.00)");
2059 // Note: CLDR does not provide an accounting pattern for long name currency.
2060 // We fall back to normal currency format. This may change in the future.
2062 u
"Sign Accounting Negative Full Name",
2063 u
"currency/USD unit-width-full-name sign-accounting",
2064 NumberFormatter::with()
2065 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2067 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2068 Locale::getCanada(),
2070 u
"-444,444.00 US dollars");
2073 void NumberFormatterApiTest::decimal() {
2074 assertFormatDescending(
2077 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO
),
2078 Locale::getEnglish(),
2089 assertFormatDescending(
2090 u
"Decimal Always Shown",
2092 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS
),
2093 Locale::getEnglish(),
2105 void NumberFormatterApiTest::scale() {
2106 assertFormatDescending(
2109 NumberFormatter::with().scale(Scale::none()),
2110 Locale::getEnglish(),
2121 assertFormatDescending(
2122 u
"Multiplier Power of Ten",
2124 NumberFormatter::with().scale(Scale::powerOfTen(6)),
2125 Locale::getEnglish(),
2136 assertFormatDescending(
2137 u
"Multiplier Arbitrary Double",
2139 NumberFormatter::with().scale(Scale::byDouble(5.2)),
2140 Locale::getEnglish(),
2151 assertFormatDescending(
2152 u
"Multiplier Arbitrary BigDecimal",
2154 NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
2155 Locale::getEnglish(),
2166 assertFormatDescending(
2167 u
"Multiplier Arbitrary Double And Power Of Ten",
2169 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
2170 Locale::getEnglish(),
2181 assertFormatDescending(
2184 NumberFormatter::with().scale(Scale::byDouble(0)),
2185 Locale::getEnglish(),
2197 u
"Multiplier Skeleton Scientific Notation and Percent",
2198 u
"percent scale/1E2",
2199 NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
2200 Locale::getEnglish(),
2205 u
"Negative Multiplier",
2207 NumberFormatter::with().scale(Scale::byDouble(-5.2)),
2208 Locale::getEnglish(),
2213 u
"Negative One Multiplier",
2215 NumberFormatter::with().scale(Scale::byDouble(-1)),
2216 Locale::getEnglish(),
2221 u
"Two-Type Multiplier with Overlap",
2223 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
2224 Locale::getEnglish(),
2229 void NumberFormatterApiTest::locale() {
2230 // Coverage for the locale setters.
2231 UErrorCode status
= U_ZERO_ERROR
;
2232 UnicodeString actual
= NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status
)
2234 assertEquals("Locale withLocale()", u
"1\u202f234", actual
);
2237 void NumberFormatterApiTest::formatTypes() {
2238 UErrorCode status
= U_ZERO_ERROR
;
2239 LocalizedNumberFormatter formatter
= NumberFormatter::withLocale(Locale::getEnglish());
2242 assertEquals("Format double", "514.23", formatter
.formatDouble(514.23, status
).toString(status
));
2245 assertEquals("Format int64", "51,423", formatter
.formatDouble(51423L, status
).toString(status
));
2248 UnicodeString actual
= formatter
.formatDecimal("98765432123456789E1", status
).toString(status
);
2249 assertEquals("Format decNumber", u
"987,654,321,234,567,890", actual
);
2251 // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
2252 // The number needs to have exactly 40 digits, which is the size of the default buffer.
2253 // (issue discovered by the address sanitizer in C++)
2254 static const char* str
= "0.009876543210987654321098765432109876543211";
2255 actual
= formatter
.precision(Precision::unlimited()).formatDecimal(str
, status
).toString(status
);
2256 assertEquals("Format decNumber to 40 digits", str
, actual
);
2259 void NumberFormatterApiTest::fieldPositionLogic() {
2260 IcuTestErrorCode
status(*this, "fieldPositionLogic");
2262 const char16_t* message
= u
"Field position logic test";
2264 FormattedNumber fmtd
= assertFormatSingle(
2267 NumberFormatter::with(),
2268 Locale::getEnglish(),
2270 u
"-9,876,543,210.12");
2272 static const UFieldPosition expectedFieldPositions
[] = {
2273 // field, begin index, end index
2274 {UNUM_SIGN_FIELD
, 0, 1},
2275 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2276 {UNUM_GROUPING_SEPARATOR_FIELD
, 6, 7},
2277 {UNUM_GROUPING_SEPARATOR_FIELD
, 10, 11},
2278 {UNUM_INTEGER_FIELD
, 1, 14},
2279 {UNUM_DECIMAL_SEPARATOR_FIELD
, 14, 15},
2280 {UNUM_FRACTION_FIELD
, 15, 17}};
2282 assertNumberFieldPositions(
2285 expectedFieldPositions
,
2286 UPRV_LENGTHOF(expectedFieldPositions
));
2288 // Test the iteration functionality of nextFieldPosition
2289 FieldPosition actual
= {UNUM_GROUPING_SEPARATOR_FIELD
};
2291 while (fmtd
.nextFieldPosition(actual
, status
)) {
2292 UFieldPosition expected
= expectedFieldPositions
[i
++];
2294 UnicodeString(u
"Next for grouping, field, case #") + Int64ToUnicodeString(i
),
2298 UnicodeString(u
"Next for grouping, begin index, case #") + Int64ToUnicodeString(i
),
2299 expected
.beginIndex
,
2300 actual
.getBeginIndex());
2302 UnicodeString(u
"Next for grouping, end index, case #") + Int64ToUnicodeString(i
),
2304 actual
.getEndIndex());
2306 assertEquals(u
"Should have seen all grouping separators", 4, i
);
2308 // Make sure strings without fraction do not contain fraction field
2309 actual
= {UNUM_FRACTION_FIELD
};
2310 fmtd
= NumberFormatter::withLocale("en").formatInt(5, status
);
2311 assertFalse(u
"No fraction part in an integer", fmtd
.nextFieldPosition(actual
, status
));
2314 void NumberFormatterApiTest::fieldPositionCoverage() {
2315 IcuTestErrorCode
status(*this, "fieldPositionCoverage");
2318 const char16_t* message
= u
"Measure unit field position basic";
2319 FormattedNumber result
= assertFormatSingle(
2321 u
"measure-unit/temperature-fahrenheit",
2322 NumberFormatter::with().unit(FAHRENHEIT
),
2323 Locale::getEnglish(),
2326 static const UFieldPosition expectedFieldPositions
[] = {
2327 // field, begin index, end index
2328 {UNUM_INTEGER_FIELD
, 0, 2},
2329 {UNUM_MEASURE_UNIT_FIELD
, 2, 4}};
2330 assertNumberFieldPositions(
2333 expectedFieldPositions
,
2334 UPRV_LENGTHOF(expectedFieldPositions
));
2338 const char16_t* message
= u
"Measure unit field position with compound unit";
2339 FormattedNumber result
= assertFormatSingle(
2341 u
"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
2342 NumberFormatter::with().unit(FAHRENHEIT
).perUnit(DAY
),
2343 Locale::getEnglish(),
2346 static const UFieldPosition expectedFieldPositions
[] = {
2347 // field, begin index, end index
2348 {UNUM_INTEGER_FIELD
, 0, 2},
2349 // coverage for old enum:
2350 {DecimalFormat::kMeasureUnitField
, 2, 6}};
2351 assertNumberFieldPositions(
2354 expectedFieldPositions
,
2355 UPRV_LENGTHOF(expectedFieldPositions
));
2359 const char16_t* message
= u
"Measure unit field position with spaces";
2360 FormattedNumber result
= assertFormatSingle(
2362 u
"measure-unit/length-meter unit-width-full-name",
2363 NumberFormatter::with().unit(METER
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2364 Locale::getEnglish(),
2367 static const UFieldPosition expectedFieldPositions
[] = {
2368 // field, begin index, end index
2369 {UNUM_INTEGER_FIELD
, 0, 2},
2370 // note: field starts after the space
2371 {UNUM_MEASURE_UNIT_FIELD
, 3, 9}};
2372 assertNumberFieldPositions(
2375 expectedFieldPositions
,
2376 UPRV_LENGTHOF(expectedFieldPositions
));
2380 const char16_t* message
= u
"Measure unit field position with prefix and suffix";
2381 FormattedNumber result
= assertFormatSingle(
2383 u
"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
2384 NumberFormatter::with().unit(METER
).perUnit(SECOND
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2385 "ky", // locale with the interesting data
2387 u
"секундасына 68 метр");
2388 static const UFieldPosition expectedFieldPositions
[] = {
2389 // field, begin index, end index
2390 {UNUM_MEASURE_UNIT_FIELD
, 0, 11},
2391 {UNUM_INTEGER_FIELD
, 12, 14},
2392 {UNUM_MEASURE_UNIT_FIELD
, 15, 19}};
2393 assertNumberFieldPositions(
2396 expectedFieldPositions
,
2397 UPRV_LENGTHOF(expectedFieldPositions
));
2401 const char16_t* message
= u
"Measure unit field position with inner spaces";
2402 FormattedNumber result
= assertFormatSingle(
2404 u
"measure-unit/temperature-fahrenheit unit-width-full-name",
2405 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2406 "vi", // locale with the interesting data
2409 static const UFieldPosition expectedFieldPositions
[] = {
2410 // field, begin index, end index
2411 {UNUM_INTEGER_FIELD
, 0, 2},
2412 // Should trim leading/trailing spaces, but not inner spaces:
2413 {UNUM_MEASURE_UNIT_FIELD
, 3, 7}};
2414 assertNumberFieldPositions(
2417 expectedFieldPositions
,
2418 UPRV_LENGTHOF(expectedFieldPositions
));
2422 // Data: other{"{0} K"} == "\u200E{0} K"
2423 // If that data changes, try to find another example of a non-empty unit prefix/suffix
2424 // that is also all ignorables (whitespace and bidi control marks).
2425 const char16_t* message
= u
"Measure unit field position with fully ignorable prefix";
2426 FormattedNumber result
= assertFormatSingle(
2428 u
"measure-unit/temperature-kelvin",
2429 NumberFormatter::with().unit(KELVIN
),
2430 "fa", // locale with the interesting data
2433 static const UFieldPosition expectedFieldPositions
[] = {
2434 // field, begin index, end index
2435 {UNUM_INTEGER_FIELD
, 1, 3},
2436 {UNUM_MEASURE_UNIT_FIELD
, 4, 5}};
2437 assertNumberFieldPositions(
2440 expectedFieldPositions
,
2441 UPRV_LENGTHOF(expectedFieldPositions
));
2445 const char16_t* message
= u
"Compact field basic";
2446 FormattedNumber result
= assertFormatSingle(
2449 NumberFormatter::with().notation(Notation::compactShort()),
2453 static const UFieldPosition expectedFieldPositions
[] = {
2454 // field, begin index, end index
2455 {UNUM_INTEGER_FIELD
, 0, 2},
2456 {UNUM_COMPACT_FIELD
, 2, 3}};
2457 assertNumberFieldPositions(
2460 expectedFieldPositions
,
2461 UPRV_LENGTHOF(expectedFieldPositions
));
2465 const char16_t* message
= u
"Compact field with spaces";
2466 FormattedNumber result
= assertFormatSingle(
2469 NumberFormatter::with().notation(Notation::compactLong()),
2473 static const UFieldPosition expectedFieldPositions
[] = {
2474 // field, begin index, end index
2475 {UNUM_INTEGER_FIELD
, 0, 2},
2476 {UNUM_COMPACT_FIELD
, 3, 11}};
2477 assertNumberFieldPositions(
2480 expectedFieldPositions
,
2481 UPRV_LENGTHOF(expectedFieldPositions
));
2485 const char16_t* message
= u
"Compact field with inner space";
2486 FormattedNumber result
= assertFormatSingle(
2489 NumberFormatter::with().notation(Notation::compactLong()),
2490 "fil", // locale with interesting data
2493 static const UFieldPosition expectedFieldPositions
[] = {
2494 // field, begin index, end index
2495 {UNUM_INTEGER_FIELD
, 0, 1},
2496 {UNUM_COMPACT_FIELD
, 2, 9}};
2497 assertNumberFieldPositions(
2500 expectedFieldPositions
,
2501 UPRV_LENGTHOF(expectedFieldPositions
));
2505 const char16_t* message
= u
"Compact field with bidi mark";
2506 FormattedNumber result
= assertFormatSingle(
2509 NumberFormatter::with().notation(Notation::compactLong()),
2510 "he", // locale with interesting data
2513 static const UFieldPosition expectedFieldPositions
[] = {
2514 // field, begin index, end index
2515 {UNUM_INTEGER_FIELD
, 1, 2},
2516 {UNUM_COMPACT_FIELD
, 3, 6}};
2517 assertNumberFieldPositions(
2520 expectedFieldPositions
,
2521 UPRV_LENGTHOF(expectedFieldPositions
));
2525 const char16_t* message
= u
"Compact with currency fields";
2526 FormattedNumber result
= assertFormatSingle(
2528 u
"compact-short currency/USD",
2529 NumberFormatter::with().notation(Notation::compactShort()).unit(USD
),
2530 "sr_Latn", // locale with interesting data
2533 static const UFieldPosition expectedFieldPositions
[] = {
2534 // field, begin index, end index
2535 {UNUM_INTEGER_FIELD
, 0, 2},
2536 {UNUM_COMPACT_FIELD
, 3, 8},
2537 {UNUM_CURRENCY_FIELD
, 9, 12}};
2538 assertNumberFieldPositions(
2541 expectedFieldPositions
,
2542 UPRV_LENGTHOF(expectedFieldPositions
));
2546 const char16_t* message
= u
"Currency long name fields";
2547 FormattedNumber result
= assertFormatSingle(
2549 u
"currency/USD unit-width-full-name",
2550 NumberFormatter::with().unit(USD
)
2551 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
2554 u
"12,345.00 US dollars");
2555 static const UFieldPosition expectedFieldPositions
[] = {
2556 // field, begin index, end index
2557 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2558 {UNUM_INTEGER_FIELD
, 0, 6},
2559 {UNUM_DECIMAL_SEPARATOR_FIELD
, 6, 7},
2560 {UNUM_FRACTION_FIELD
, 7, 9},
2561 {UNUM_CURRENCY_FIELD
, 10, 20}};
2562 assertNumberFieldPositions(
2565 expectedFieldPositions
,
2566 UPRV_LENGTHOF(expectedFieldPositions
));
2570 const char16_t* message
= u
"Compact with measure unit fields";
2571 FormattedNumber result
= assertFormatSingle(
2573 u
"compact-long measure-unit/length-meter unit-width-full-name",
2574 NumberFormatter::with().notation(Notation::compactLong())
2576 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2579 u
"65 thousand meters");
2580 static const UFieldPosition expectedFieldPositions
[] = {
2581 // field, begin index, end index
2582 {UNUM_INTEGER_FIELD
, 0, 2},
2583 {UNUM_COMPACT_FIELD
, 3, 11},
2584 {UNUM_MEASURE_UNIT_FIELD
, 12, 18}};
2585 assertNumberFieldPositions(
2588 expectedFieldPositions
,
2589 UPRV_LENGTHOF(expectedFieldPositions
));
2593 void NumberFormatterApiTest::toFormat() {
2594 IcuTestErrorCode
status(*this, "icuFormat");
2595 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("fr")
2596 .precision(Precision::fixedFraction(3));
2597 LocalPointer
<Format
> format(lnf
.toFormat(status
), status
);
2598 FieldPosition
fpos(UNUM_DECIMAL_SEPARATOR_FIELD
);
2600 format
->format(514.23, sb
, fpos
, status
);
2601 assertEquals("Should correctly format number", u
"514,230", sb
);
2602 assertEquals("Should find decimal separator", 3, fpos
.getBeginIndex());
2603 assertEquals("Should find end of decimal separator", 4, fpos
.getEndIndex());
2605 "ICU Format should round-trip",
2606 lnf
.toSkeleton(status
),
2607 dynamic_cast<LocalizedNumberFormatterAsFormat
*>(format
.getAlias())->getNumberFormatter()
2608 .toSkeleton(status
));
2610 FieldPositionIterator fpi1
;
2611 lnf
.formatDouble(514.23, status
).getAllFieldPositions(fpi1
, status
);
2612 FieldPositionIterator fpi2
;
2613 format
->format(514.23, sb
.remove(), &fpi2
, status
);
2614 assertTrue("Should produce same field position iterator", fpi1
== fpi2
);
2617 void NumberFormatterApiTest::errors() {
2618 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale(Locale::getEnglish()).precision(
2619 Precision::fixedFraction(
2623 UErrorCode status
= U_ZERO_ERROR
;
2624 FormattedNumber fn
= lnf
.formatInt(1, status
);
2626 "Should fail in formatInt method with error code for rounding",
2627 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2631 status
= U_ZERO_ERROR
;
2632 fn
= lnf
.formatDouble(1.0, status
);
2634 "Should fail in formatDouble method with error code for rounding",
2635 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2638 // formatDecimal (decimal error)
2639 status
= U_ZERO_ERROR
;
2640 fn
= NumberFormatter::withLocale("en").formatDecimal("1x2", status
);
2642 "Should fail in formatDecimal method with error code for decimal number syntax",
2643 U_DECIMAL_NUMBER_SYNTAX_ERROR
,
2646 // formatDecimal (setting error)
2647 status
= U_ZERO_ERROR
;
2648 fn
= lnf
.formatDecimal("1.0", status
);
2650 "Should fail in formatDecimal method with error code for rounding",
2651 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2655 status
= U_ZERO_ERROR
;
2656 UnicodeString output
= lnf
.toSkeleton(status
);
2658 "Should fail on toSkeleton terminal method with correct error code",
2659 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2662 "Terminal toSkeleton on error object should be bogus",
2666 status
= U_ZERO_ERROR
;
2668 fn
.nextFieldPosition(fp
, status
);
2670 "Should fail on FieldPosition terminal method with correct error code",
2671 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2674 // FieldPositionIterator
2675 status
= U_ZERO_ERROR
;
2676 FieldPositionIterator fpi
;
2677 fn
.getAllFieldPositions(fpi
, status
);
2679 "Should fail on FieldPositoinIterator terminal method with correct error code",
2680 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2684 status
= U_ZERO_ERROR
;
2685 UnicodeStringAppendable
appendable(output
.remove());
2686 fn
.appendTo(appendable
, status
);
2688 "Should fail on Appendable terminal method with correct error code",
2689 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2693 status
= U_ZERO_ERROR
;
2694 output
= fn
.toString(status
);
2696 "Should fail on UnicodeString terminal method with correct error code",
2697 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2700 "Terminal UnicodeString on error object should be bogus",
2704 status
= U_ZERO_ERROR
;
2705 lnf
.copyErrorTo(status
);
2707 "Should fail since rounder is not legal with correct error code",
2708 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2712 void NumberFormatterApiTest::validRanges() {
2714 #define EXPECTED_MAX_INT_FRAC_SIG 999
2716 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) { \
2717 UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
2719 : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
2721 UnicodeString(u"Incorrect status for " #method " on input ") \
2722 + Int64ToUnicodeString(argument), \
2727 #define VALID_RANGE_ONEARG(setting, method, lowerBound) { \
2728 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2729 UErrorCode status = U_ZERO_ERROR; \
2730 NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
2731 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2735 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) { \
2736 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2737 UErrorCode status = U_ZERO_ERROR; \
2738 /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
2739 NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
2740 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2741 status = U_ZERO_ERROR; \
2742 /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
2743 NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
2744 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2745 /* Check that first argument must be less than or equal to second argument */ \
2746 NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
2747 assertEquals("Incorrect status for " #method " on max < min input", \
2748 U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
2753 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction
, 0);
2754 VALID_RANGE_ONEARG(precision
, Precision::minFraction
, 0);
2755 VALID_RANGE_ONEARG(precision
, Precision::maxFraction
, 0);
2756 VALID_RANGE_TWOARGS(precision
, Precision::minMaxFraction
, 0);
2757 VALID_RANGE_ONEARG(precision
, Precision::fixedSignificantDigits
, 1);
2758 VALID_RANGE_ONEARG(precision
, Precision::minSignificantDigits
, 1);
2759 VALID_RANGE_ONEARG(precision
, Precision::maxSignificantDigits
, 1);
2760 VALID_RANGE_TWOARGS(precision
, Precision::minMaxSignificantDigits
, 1);
2761 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction(1).withMinDigits
, 1);
2762 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction(1).withMaxDigits
, 1);
2763 VALID_RANGE_ONEARG(notation
, Notation::scientific().withMinExponentDigits
, 1);
2764 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo
, 0);
2765 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo(0).truncateAt
, -1);
2768 void NumberFormatterApiTest::copyMove() {
2769 IcuTestErrorCode
status(*this, "copyMove");
2771 // Default constructors
2772 LocalizedNumberFormatter l1
;
2773 assertEquals("Initial behavior", u
"10", l1
.formatInt(10, status
).toString(status
), true);
2774 if (status
.errDataIfFailureAndReset()) { return; }
2775 assertEquals("Initial call count", 1, l1
.getCallCount());
2776 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2779 l1
= NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
2780 assertEquals("Initial behavior", u
"10%", l1
.formatInt(10, status
).toString(status
));
2781 assertEquals("Initial call count", 1, l1
.getCallCount());
2782 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2783 l1
.formatInt(123, status
);
2784 assertEquals("Still not compiled", 2, l1
.getCallCount());
2785 assertTrue("Still not compiled", l1
.getCompiled() == nullptr);
2786 l1
.formatInt(123, status
);
2787 assertEquals("Compiled", u
"10%", l1
.formatInt(10, status
).toString(status
));
2788 assertEquals("Compiled", INT32_MIN
, l1
.getCallCount());
2789 assertTrue("Compiled", l1
.getCompiled() != nullptr);
2792 LocalizedNumberFormatter l2
= l1
;
2793 assertEquals("[constructor] Copy behavior", u
"10%", l2
.formatInt(10, status
).toString(status
));
2794 assertEquals("[constructor] Copy should not have compiled state", 1, l2
.getCallCount());
2795 assertTrue("[constructor] Copy should not have compiled state", l2
.getCompiled() == nullptr);
2798 LocalizedNumberFormatter l3
= std::move(l1
);
2799 assertEquals("[constructor] Move behavior", u
"10%", l3
.formatInt(10, status
).toString(status
));
2800 assertEquals("[constructor] Move *should* have compiled state", INT32_MIN
, l3
.getCallCount());
2801 assertTrue("[constructor] Move *should* have compiled state", l3
.getCompiled() != nullptr);
2802 assertEquals("[constructor] Source should be reset after move", 0, l1
.getCallCount());
2803 assertTrue("[constructor] Source should be reset after move", l1
.getCompiled() == nullptr);
2805 // Reset l1 and l2 to check for macro-props copying for behavior testing
2806 // Make the test more interesting: also warm them up with a compiled formatter.
2807 l1
= NumberFormatter::withLocale("en");
2808 l1
.formatInt(1, status
);
2809 l1
.formatInt(1, status
);
2810 l1
.formatInt(1, status
);
2811 l2
= NumberFormatter::withLocale("en");
2812 l2
.formatInt(1, status
);
2813 l2
.formatInt(1, status
);
2814 l2
.formatInt(1, status
);
2818 assertEquals("[assignment] Copy behavior", u
"10%", l1
.formatInt(10, status
).toString(status
));
2819 assertEquals("[assignment] Copy should not have compiled state", 1, l1
.getCallCount());
2820 assertTrue("[assignment] Copy should not have compiled state", l1
.getCompiled() == nullptr);
2824 assertEquals("[assignment] Move behavior", u
"10%", l2
.formatInt(10, status
).toString(status
));
2825 assertEquals("[assignment] Move *should* have compiled state", INT32_MIN
, l2
.getCallCount());
2826 assertTrue("[assignment] Move *should* have compiled state", l2
.getCompiled() != nullptr);
2827 assertEquals("[assignment] Source should be reset after move", 0, l3
.getCallCount());
2828 assertTrue("[assignment] Source should be reset after move", l3
.getCompiled() == nullptr);
2830 // Coverage tests for UnlocalizedNumberFormatter
2831 UnlocalizedNumberFormatter u1
;
2832 assertEquals("Default behavior", u
"10", u1
.locale("en").formatInt(10, status
).toString(status
));
2833 u1
= u1
.unit(NoUnit::percent());
2834 assertEquals("Copy assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString(status
));
2835 UnlocalizedNumberFormatter u2
= u1
;
2836 assertEquals("Copy constructor", u
"10%", u2
.locale("en").formatInt(10, status
).toString(status
));
2837 UnlocalizedNumberFormatter u3
= std::move(u1
);
2838 assertEquals("Move constructor", u
"10%", u3
.locale("en").formatInt(10, status
).toString(status
));
2839 u1
= NumberFormatter::with();
2841 assertEquals("Move assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString(status
));
2843 // FormattedNumber move operators
2844 FormattedNumber result
= l1
.formatInt(10, status
);
2845 assertEquals("FormattedNumber move constructor", u
"10%", result
.toString(status
));
2846 result
= l1
.formatInt(20, status
);
2847 assertEquals("FormattedNumber move assignment", u
"20%", result
.toString(status
));
2850 void NumberFormatterApiTest::localPointerCAPI() {
2851 // NOTE: This is also the sample code in unumberformatter.h
2852 UErrorCode ec
= U_ZERO_ERROR
;
2855 LocalUNumberFormatterPointer
uformatter(unumf_openForSkeletonAndLocale(u
"percent", -1, "en", &ec
));
2856 LocalUFormattedNumberPointer
uresult(unumf_openResult(&ec
));
2857 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2859 // Format a decimal number:
2860 unumf_formatDecimal(uformatter
.getAlias(), "9.87E-3", -1, uresult
.getAlias(), &ec
);
2861 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2863 // Get the location of the percent sign:
2864 UFieldPosition ufpos
= {UNUM_PERCENT_FIELD
, 0, 0};
2865 unumf_resultNextFieldPosition(uresult
.getAlias(), &ufpos
, &ec
);
2866 assertEquals("Percent sign location within '0.00987%'", 7, ufpos
.beginIndex
);
2867 assertEquals("Percent sign location within '0.00987%'", 8, ufpos
.endIndex
);
2869 // No need to do any cleanup since we are using LocalPointer.
2872 void NumberFormatterApiTest::toObject() {
2873 IcuTestErrorCode
status(*this, "toObject");
2875 // const lvalue version
2877 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("en")
2878 .precision(Precision::fixedFraction(2));
2879 LocalPointer
<LocalizedNumberFormatter
> lnf2(lnf
.clone());
2880 assertFalse("should create successfully, const lvalue", lnf2
.isNull());
2881 assertEquals("object API test, const lvalue", u
"1,000.00",
2882 lnf2
->formatDouble(1000, status
).toString(status
));
2885 // rvalue reference version
2887 LocalPointer
<LocalizedNumberFormatter
> lnf(
2888 NumberFormatter::withLocale("en")
2889 .precision(Precision::fixedFraction(2))
2891 assertFalse("should create successfully, rvalue reference", lnf
.isNull());
2892 assertEquals("object API test, rvalue reference", u
"1,000.00",
2893 lnf
->formatDouble(1000, status
).toString(status
));
2896 // to std::unique_ptr via constructor
2898 std::unique_ptr
<LocalizedNumberFormatter
> lnf(
2899 NumberFormatter::withLocale("en")
2900 .precision(Precision::fixedFraction(2))
2902 assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf
));
2903 assertEquals("object API test, unique_ptr", u
"1,000.00",
2904 lnf
->formatDouble(1000, status
).toString(status
));
2907 // to std::unique_ptr via assignment
2909 std::unique_ptr
<LocalizedNumberFormatter
> lnf
=
2910 NumberFormatter::withLocale("en")
2911 .precision(Precision::fixedFraction(2))
2913 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf
));
2914 assertEquals("object API test, unique_ptr B", u
"1,000.00",
2915 lnf
->formatDouble(1000, status
).toString(status
));
2918 // to LocalPointer via assignment
2920 LocalPointer
<UnlocalizedNumberFormatter
> f
=
2921 NumberFormatter::with().clone();
2924 // make sure no memory leaks
2926 NumberFormatter::with().clone();
2931 void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage
, const char16_t* uskeleton
,
2932 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2935 va_start(args
, locale
);
2936 UnicodeString
message(TRUE
, umessage
, -1);
2937 static double inputs
[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
2938 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2939 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2940 IcuTestErrorCode
status(*this, "assertFormatDescending");
2941 status
.setScope(message
);
2942 UnicodeString expecteds
[10];
2943 for (int16_t i
= 0; i
< 9; i
++) {
2944 char16_t caseNumber
= u
'0' + i
;
2945 double d
= inputs
[i
];
2946 UnicodeString expected
= va_arg(args
, const char16_t*);
2947 expecteds
[i
] = expected
;
2948 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString(status
);
2949 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
2950 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
2951 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString(status
);
2952 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
2953 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
2955 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2956 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2957 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2958 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
2959 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
2960 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
2961 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
2962 for (int32_t i
= 0; i
< 9; i
++) {
2963 double d
= inputs
[i
];
2964 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString(status
);
2965 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
2968 assertUndefinedSkeleton(f
);
2972 void NumberFormatterApiTest::assertFormatDescendingBig(const char16_t* umessage
, const char16_t* uskeleton
,
2973 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2976 va_start(args
, locale
);
2977 UnicodeString
message(TRUE
, umessage
, -1);
2978 static double inputs
[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
2979 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2980 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2981 IcuTestErrorCode
status(*this, "assertFormatDescendingBig");
2982 status
.setScope(message
);
2983 UnicodeString expecteds
[10];
2984 for (int16_t i
= 0; i
< 9; i
++) {
2985 char16_t caseNumber
= u
'0' + i
;
2986 double d
= inputs
[i
];
2987 UnicodeString expected
= va_arg(args
, const char16_t*);
2988 expecteds
[i
] = expected
;
2989 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString(status
);
2990 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
2991 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
2992 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString(status
);
2993 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
2994 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
2996 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2997 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2998 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2999 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
3000 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
3001 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3002 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3003 for (int32_t i
= 0; i
< 9; i
++) {
3004 double d
= inputs
[i
];
3005 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString(status
);
3006 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
3009 assertUndefinedSkeleton(f
);
3014 NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage
, const char16_t* uskeleton
,
3015 const UnlocalizedNumberFormatter
& f
, Locale locale
,
3016 double input
, const UnicodeString
& expected
) {
3017 UnicodeString
message(TRUE
, umessage
, -1);
3018 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
3019 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
3020 IcuTestErrorCode
status(*this, "assertFormatSingle");
3021 status
.setScope(message
);
3022 FormattedNumber result1
= l1
.formatDouble(input
, status
);
3023 UnicodeString actual1
= result1
.toString(status
);
3024 assertSuccess(message
+ u
": Unsafe Path", status
);
3025 assertEquals(message
+ u
": Unsafe Path", expected
, actual1
);
3026 UnicodeString actual2
= l2
.formatDouble(input
, status
).toString(status
);
3027 assertSuccess(message
+ u
": Safe Path", status
);
3028 assertEquals(message
+ u
": Safe Path", expected
, actual2
);
3029 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
3030 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
3031 // Only compare normalized skeletons: the tests need not provide the normalized forms.
3032 // Use the normalized form to construct the testing formatter to ensure no loss of info.
3033 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
3034 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3035 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3036 UnicodeString actual3
= l3
.formatDouble(input
, status
).toString(status
);
3037 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + input
, expected
, actual3
);
3039 assertUndefinedSkeleton(f
);
3044 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter
& f
) {
3045 UErrorCode status
= U_ZERO_ERROR
;
3046 UnicodeString skeleton
= f
.toSkeleton(status
);
3048 u
"Expect toSkeleton to fail, but passed, producing: " + skeleton
,
3049 U_UNSUPPORTED_ERROR
,
3053 void NumberFormatterApiTest::assertNumberFieldPositions(
3054 const char16_t* message
, const FormattedNumber
& formattedNumber
,
3055 const UFieldPosition
* expectedFieldPositions
, int32_t length
) {
3056 IcuTestErrorCode
status(*this, "assertNumberFieldPositions");
3058 // Check FormattedValue functions
3059 checkFormattedValue(
3061 static_cast<const FormattedValue
&>(formattedNumber
),
3062 formattedNumber
.toString(status
),
3063 UFIELD_CATEGORY_NUMBER
,
3064 expectedFieldPositions
,
3067 // Check FormattedNumber-specific functions
3068 UnicodeString baseMessage
= UnicodeString(message
) + u
": " + formattedNumber
.toString(status
) + u
": ";
3069 FieldPositionIterator fpi
;
3070 formattedNumber
.getAllFieldPositions(fpi
, status
);
3072 FieldPosition actual
;
3073 while (fpi
.next(actual
)) {
3074 UFieldPosition expected
= expectedFieldPositions
[i
++];
3076 baseMessage
+ UnicodeString(u
"Field, case #") + Int64ToUnicodeString(i
),
3080 baseMessage
+ UnicodeString(u
"Iterator, begin, case #") + Int64ToUnicodeString(i
),
3081 expected
.beginIndex
,
3082 actual
.getBeginIndex());
3084 baseMessage
+ UnicodeString(u
"Iterator, end, case #") + Int64ToUnicodeString(i
),
3086 actual
.getEndIndex());
3088 // Check for the first location of the field
3089 FieldPosition
actual2(expected
.field
);
3090 // Fast-forward the field to skip previous occurrences of the field:
3091 actual2
.setBeginIndex(expected
.beginIndex
);
3092 actual2
.setEndIndex(expected
.beginIndex
);
3093 UBool found
= formattedNumber
.nextFieldPosition(actual2
, status
);
3095 baseMessage
+ UnicodeString(u
"Next, found first, case #") + Int64ToUnicodeString(i
),
3099 baseMessage
+ UnicodeString(u
"Next, begin, case #") + Int64ToUnicodeString(i
),
3100 expected
.beginIndex
,
3101 actual2
.getBeginIndex());
3103 baseMessage
+ UnicodeString(u
"Next, end, case #") + Int64ToUnicodeString(i
),
3105 actual2
.getEndIndex());
3107 // The next position should be empty unless the field occurs again
3108 UBool occursAgain
= false;
3109 for (int32_t j
=i
; j
<length
; j
++) {
3110 if (expectedFieldPositions
[j
].field
== expected
.field
) {
3116 found
= formattedNumber
.nextFieldPosition(actual2
, status
);
3118 baseMessage
+ UnicodeString(u
"Next, found second, case #") + Int64ToUnicodeString(i
),
3123 assertEquals(baseMessage
+ u
"Should have seen every field position", length
, i
);
3127 #endif /* #if !UCONFIG_NO_FORMATTING */