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 is interesting because it has minimumGroupingDigits=4 in locale data
1349 // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
1350 assertFormatDescendingBig(
1351 u
"Hungarian Grouping",
1353 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1365 assertFormatDescendingBig(
1366 u
"Hungarian Grouping, Min 2",
1368 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1380 assertFormatDescendingBig(
1381 u
"Hungarian Grouping, Always",
1382 u
"group-on-aligned",
1383 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
),
1395 // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
1396 // If this test breaks due to data changes, find another locale that has no default grouping.
1397 assertFormatDescendingBig(
1398 u
"Bulgarian Currency Grouping",
1399 u
"currency/USD group-auto",
1400 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
).unit(USD
),
1402 u
"87650000,00 щ.д.",
1412 assertFormatDescendingBig(
1413 u
"Bulgarian Currency Grouping, Always",
1414 u
"currency/USD group-on-aligned",
1415 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
).unit(USD
),
1417 u
"87 650 000,00 щ.д.",
1418 u
"8 765 000,00 щ.д.",
1428 macros
.grouper
= Grouper(4, 1, 3, UNUM_GROUPING_COUNT
);
1429 assertFormatDescendingBig(
1430 u
"Custom Grouping via Internal API",
1432 NumberFormatter::with().macros(macros
),
1433 Locale::getEnglish(),
1445 void NumberFormatterApiTest::padding() {
1446 assertFormatDescending(
1449 NumberFormatter::with().padding(Padder::none()),
1450 Locale::getEnglish(),
1461 assertFormatDescending(
1464 NumberFormatter::with().padding(
1466 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1467 Locale::getEnglish(),
1478 assertFormatDescending(
1479 u
"Padding with code points",
1481 NumberFormatter::with().padding(
1483 0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1484 Locale::getEnglish(),
1495 assertFormatDescending(
1496 u
"Padding with wide digits",
1498 NumberFormatter::with().padding(
1500 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1501 .adoptSymbols(new NumberingSystem(MATHSANB
)),
1502 Locale::getEnglish(),
1513 assertFormatDescending(
1514 u
"Padding with currency spacing",
1516 NumberFormatter::with().padding(
1518 '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1520 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1521 Locale::getEnglish(),
1533 u
"Pad Before Prefix",
1535 NumberFormatter::with().padding(
1537 '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX
)),
1538 Locale::getEnglish(),
1543 u
"Pad After Prefix",
1545 NumberFormatter::with().padding(
1547 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1548 Locale::getEnglish(),
1553 u
"Pad Before Suffix",
1555 NumberFormatter::with().padding(
1557 '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX
)).unit(NoUnit::percent()),
1558 Locale::getEnglish(),
1563 u
"Pad After Suffix",
1565 NumberFormatter::with().padding(
1567 '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX
)).unit(NoUnit::percent()),
1568 Locale::getEnglish(),
1573 u
"Currency Spacing with Zero Digit Padding Broken",
1575 NumberFormatter::with().padding(
1577 '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1579 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1580 Locale::getEnglish(),
1582 u
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
1585 void NumberFormatterApiTest::integerWidth() {
1586 assertFormatDescending(
1587 u
"Integer Width Default",
1588 u
"integer-width/+0",
1589 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
1590 Locale::getEnglish(),
1601 assertFormatDescending(
1602 u
"Integer Width Zero Fill 0",
1604 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
1605 Locale::getEnglish(),
1614 u
""); // TODO: Avoid the empty string here?
1616 assertFormatDescending(
1617 u
"Integer Width Zero Fill 3",
1618 u
"integer-width/+000",
1619 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
1620 Locale::getEnglish(),
1631 assertFormatDescending(
1632 u
"Integer Width Max 3",
1633 u
"integer-width/##0",
1634 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
1635 Locale::getEnglish(),
1646 assertFormatDescending(
1647 u
"Integer Width Fixed 2",
1648 u
"integer-width/00",
1649 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1650 Locale::getEnglish(),
1662 u
"Integer Width Remove All A",
1663 u
"integer-width/00",
1664 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1670 u
"Integer Width Remove All B",
1671 u
"integer-width/00",
1672 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1678 u
"Integer Width Remove All B, Bytes Mode",
1679 u
"integer-width/00",
1680 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1682 // Note: this double produces all 17 significant digits
1683 10000000000000002000.0,
1687 void NumberFormatterApiTest::symbols() {
1688 assertFormatDescending(
1689 u
"French Symbols with Japanese Data 1",
1691 NumberFormatter::with().symbols(FRENCH_SYMBOLS
),
1704 u
"French Symbols with Japanese Data 2",
1706 NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS
),
1711 assertFormatDescending(
1712 u
"Latin Numbering System with Arabic Data",
1713 u
"currency/USD latin",
1714 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1726 assertFormatDescending(
1727 u
"Math Numbering System with French Data",
1728 u
"numbering-system/mathsanb",
1729 NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB
)),
1730 Locale::getFrench(),
1742 u
"Swiss Symbols (used in documentation)",
1744 NumberFormatter::with().symbols(SWISS_SYMBOLS
),
1745 Locale::getEnglish(),
1750 u
"Myanmar Symbols (used in documentation)",
1752 NumberFormatter::with().symbols(MYANMAR_SYMBOLS
),
1753 Locale::getEnglish(),
1755 u
"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
1757 // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
1760 u
"Currency symbol should precede number in ar with NS latn",
1761 u
"currency/USD latin",
1762 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1768 u
"Currency symbol should precede number in ar@numbers=latn",
1770 NumberFormatter::with().unit(USD
),
1771 Locale("ar@numbers=latn"),
1776 u
"Currency symbol should follow number in ar-EG with NS arab",
1778 NumberFormatter::with().unit(USD
),
1784 u
"Currency symbol should follow number in ar@numbers=arab",
1786 NumberFormatter::with().unit(USD
),
1787 Locale("ar@numbers=arab"),
1792 u
"NumberingSystem in API should win over @numbers keyword",
1793 u
"currency/USD latin",
1794 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1795 Locale("ar@numbers=arab"),
1799 UErrorCode status
= U_ZERO_ERROR
;
1801 "NumberingSystem in API should win over @numbers keyword in reverse order",
1803 NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN
))
1805 .formatDouble(12345.67, status
)
1808 DecimalFormatSymbols symbols
= SWISS_SYMBOLS
;
1809 UnlocalizedNumberFormatter f
= NumberFormatter::with().symbols(symbols
);
1810 symbols
.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol
, u
"!", status
);
1812 u
"Symbols object should be copied", nullptr, f
, Locale::getEnglish(), 12345.67, u
"12’345.67");
1815 u
"The last symbols setter wins",
1817 NumberFormatter::with().symbols(symbols
).adoptSymbols(new NumberingSystem(LATN
)),
1818 Locale::getEnglish(),
1823 u
"The last symbols setter wins",
1825 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).symbols(symbols
),
1826 Locale::getEnglish(),
1831 // TODO: Enable if/when currency symbol override is added.
1832 //void NumberFormatterTest::symbolsOverride() {
1833 // DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
1834 // dfs.setCurrencySymbol("@");
1835 // dfs.setInternationalCurrencySymbol("foo");
1836 // assertFormatSingle(
1837 // u"Custom Short Currency Symbol",
1838 // NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
1839 // Locale::getEnglish(),
1844 void NumberFormatterApiTest::sign() {
1846 u
"Sign Auto Positive",
1848 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1849 Locale::getEnglish(),
1854 u
"Sign Auto Negative",
1856 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1857 Locale::getEnglish(),
1864 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1865 Locale::getEnglish(),
1870 u
"Sign Always Positive",
1872 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1873 Locale::getEnglish(),
1878 u
"Sign Always Negative",
1880 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1881 Locale::getEnglish(),
1886 u
"Sign Always Zero",
1888 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1889 Locale::getEnglish(),
1894 u
"Sign Never Positive",
1896 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1897 Locale::getEnglish(),
1902 u
"Sign Never Negative",
1904 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1905 Locale::getEnglish(),
1912 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1913 Locale::getEnglish(),
1918 u
"Sign Accounting Positive",
1919 u
"currency/USD sign-accounting",
1920 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1921 Locale::getEnglish(),
1926 u
"Sign Accounting Negative",
1927 u
"currency/USD sign-accounting",
1928 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1929 Locale::getEnglish(),
1934 u
"Sign Accounting Zero",
1935 u
"currency/USD sign-accounting",
1936 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1937 Locale::getEnglish(),
1942 u
"Sign Accounting-Always Positive",
1943 u
"currency/USD sign-accounting-always",
1944 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1945 Locale::getEnglish(),
1950 u
"Sign Accounting-Always Negative",
1951 u
"currency/USD sign-accounting-always",
1952 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1953 Locale::getEnglish(),
1958 u
"Sign Accounting-Always Zero",
1959 u
"currency/USD sign-accounting-always",
1960 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1961 Locale::getEnglish(),
1966 u
"Sign Except-Zero Positive",
1967 u
"sign-except-zero",
1968 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1969 Locale::getEnglish(),
1974 u
"Sign Except-Zero Negative",
1975 u
"sign-except-zero",
1976 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1977 Locale::getEnglish(),
1982 u
"Sign Except-Zero Zero",
1983 u
"sign-except-zero",
1984 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1985 Locale::getEnglish(),
1990 u
"Sign Accounting-Except-Zero Positive",
1991 u
"currency/USD sign-accounting-except-zero",
1992 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
1993 Locale::getEnglish(),
1998 u
"Sign Accounting-Except-Zero Negative",
1999 u
"currency/USD sign-accounting-except-zero",
2000 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2001 Locale::getEnglish(),
2006 u
"Sign Accounting-Except-Zero Zero",
2007 u
"currency/USD sign-accounting-except-zero",
2008 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2009 Locale::getEnglish(),
2014 u
"Sign Accounting Negative Hidden",
2015 u
"currency/USD unit-width-hidden sign-accounting",
2016 NumberFormatter::with()
2017 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2019 .unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
2020 Locale::getEnglish(),
2025 u
"Sign Accounting Negative Narrow",
2026 u
"currency/USD unit-width-narrow sign-accounting",
2027 NumberFormatter::with()
2028 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2030 .unitWidth(UNUM_UNIT_WIDTH_NARROW
),
2031 Locale::getCanada(),
2036 u
"Sign Accounting Negative Short",
2037 u
"currency/USD sign-accounting",
2038 NumberFormatter::with()
2039 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2041 .unitWidth(UNUM_UNIT_WIDTH_SHORT
),
2042 Locale::getCanada(),
2044 u
"(US$444,444.00)");
2047 u
"Sign Accounting Negative Iso Code",
2048 u
"currency/USD unit-width-iso-code sign-accounting",
2049 NumberFormatter::with()
2050 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2052 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
),
2053 Locale::getCanada(),
2055 u
"(USD 444,444.00)");
2057 // Note: CLDR does not provide an accounting pattern for long name currency.
2058 // We fall back to normal currency format. This may change in the future.
2060 u
"Sign Accounting Negative Full Name",
2061 u
"currency/USD unit-width-full-name sign-accounting",
2062 NumberFormatter::with()
2063 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2065 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2066 Locale::getCanada(),
2068 u
"-444,444.00 US dollars");
2071 void NumberFormatterApiTest::decimal() {
2072 assertFormatDescending(
2075 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO
),
2076 Locale::getEnglish(),
2087 assertFormatDescending(
2088 u
"Decimal Always Shown",
2090 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS
),
2091 Locale::getEnglish(),
2103 void NumberFormatterApiTest::scale() {
2104 assertFormatDescending(
2107 NumberFormatter::with().scale(Scale::none()),
2108 Locale::getEnglish(),
2119 assertFormatDescending(
2120 u
"Multiplier Power of Ten",
2122 NumberFormatter::with().scale(Scale::powerOfTen(6)),
2123 Locale::getEnglish(),
2134 assertFormatDescending(
2135 u
"Multiplier Arbitrary Double",
2137 NumberFormatter::with().scale(Scale::byDouble(5.2)),
2138 Locale::getEnglish(),
2149 assertFormatDescending(
2150 u
"Multiplier Arbitrary BigDecimal",
2152 NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
2153 Locale::getEnglish(),
2164 assertFormatDescending(
2165 u
"Multiplier Arbitrary Double And Power Of Ten",
2167 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
2168 Locale::getEnglish(),
2179 assertFormatDescending(
2182 NumberFormatter::with().scale(Scale::byDouble(0)),
2183 Locale::getEnglish(),
2195 u
"Multiplier Skeleton Scientific Notation and Percent",
2196 u
"percent scale/1E2",
2197 NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
2198 Locale::getEnglish(),
2203 u
"Negative Multiplier",
2205 NumberFormatter::with().scale(Scale::byDouble(-5.2)),
2206 Locale::getEnglish(),
2211 u
"Negative One Multiplier",
2213 NumberFormatter::with().scale(Scale::byDouble(-1)),
2214 Locale::getEnglish(),
2219 u
"Two-Type Multiplier with Overlap",
2221 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
2222 Locale::getEnglish(),
2227 void NumberFormatterApiTest::locale() {
2228 // Coverage for the locale setters.
2229 UErrorCode status
= U_ZERO_ERROR
;
2230 UnicodeString actual
= NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status
)
2232 assertEquals("Locale withLocale()", u
"1\u202f234", actual
);
2235 void NumberFormatterApiTest::formatTypes() {
2236 UErrorCode status
= U_ZERO_ERROR
;
2237 LocalizedNumberFormatter formatter
= NumberFormatter::withLocale(Locale::getEnglish());
2240 assertEquals("Format double", "514.23", formatter
.formatDouble(514.23, status
).toString(status
));
2243 assertEquals("Format int64", "51,423", formatter
.formatDouble(51423L, status
).toString(status
));
2246 UnicodeString actual
= formatter
.formatDecimal("98765432123456789E1", status
).toString(status
);
2247 assertEquals("Format decNumber", u
"987,654,321,234,567,890", actual
);
2249 // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
2250 // The number needs to have exactly 40 digits, which is the size of the default buffer.
2251 // (issue discovered by the address sanitizer in C++)
2252 static const char* str
= "0.009876543210987654321098765432109876543211";
2253 actual
= formatter
.precision(Precision::unlimited()).formatDecimal(str
, status
).toString(status
);
2254 assertEquals("Format decNumber to 40 digits", str
, actual
);
2257 void NumberFormatterApiTest::fieldPositionLogic() {
2258 IcuTestErrorCode
status(*this, "fieldPositionLogic");
2260 const char16_t* message
= u
"Field position logic test";
2262 FormattedNumber fmtd
= assertFormatSingle(
2265 NumberFormatter::with(),
2266 Locale::getEnglish(),
2268 u
"-9,876,543,210.12");
2270 static const UFieldPosition expectedFieldPositions
[] = {
2271 // field, begin index, end index
2272 {UNUM_SIGN_FIELD
, 0, 1},
2273 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2274 {UNUM_GROUPING_SEPARATOR_FIELD
, 6, 7},
2275 {UNUM_GROUPING_SEPARATOR_FIELD
, 10, 11},
2276 {UNUM_INTEGER_FIELD
, 1, 14},
2277 {UNUM_DECIMAL_SEPARATOR_FIELD
, 14, 15},
2278 {UNUM_FRACTION_FIELD
, 15, 17}};
2280 assertNumberFieldPositions(
2283 expectedFieldPositions
,
2284 UPRV_LENGTHOF(expectedFieldPositions
));
2286 // Test the iteration functionality of nextFieldPosition
2287 FieldPosition actual
= {UNUM_GROUPING_SEPARATOR_FIELD
};
2289 while (fmtd
.nextFieldPosition(actual
, status
)) {
2290 UFieldPosition expected
= expectedFieldPositions
[i
++];
2292 UnicodeString(u
"Next for grouping, field, case #") + Int64ToUnicodeString(i
),
2296 UnicodeString(u
"Next for grouping, begin index, case #") + Int64ToUnicodeString(i
),
2297 expected
.beginIndex
,
2298 actual
.getBeginIndex());
2300 UnicodeString(u
"Next for grouping, end index, case #") + Int64ToUnicodeString(i
),
2302 actual
.getEndIndex());
2304 assertEquals(u
"Should have seen all grouping separators", 4, i
);
2306 // Make sure strings without fraction do not contain fraction field
2307 actual
= {UNUM_FRACTION_FIELD
};
2308 fmtd
= NumberFormatter::withLocale("en").formatInt(5, status
);
2309 assertFalse(u
"No fraction part in an integer", fmtd
.nextFieldPosition(actual
, status
));
2312 void NumberFormatterApiTest::fieldPositionCoverage() {
2313 IcuTestErrorCode
status(*this, "fieldPositionCoverage");
2316 const char16_t* message
= u
"Measure unit field position basic";
2317 FormattedNumber result
= assertFormatSingle(
2319 u
"measure-unit/temperature-fahrenheit",
2320 NumberFormatter::with().unit(FAHRENHEIT
),
2321 Locale::getEnglish(),
2324 static const UFieldPosition expectedFieldPositions
[] = {
2325 // field, begin index, end index
2326 {UNUM_INTEGER_FIELD
, 0, 2},
2327 {UNUM_MEASURE_UNIT_FIELD
, 2, 4}};
2328 assertNumberFieldPositions(
2331 expectedFieldPositions
,
2332 UPRV_LENGTHOF(expectedFieldPositions
));
2336 const char16_t* message
= u
"Measure unit field position with compound unit";
2337 FormattedNumber result
= assertFormatSingle(
2339 u
"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
2340 NumberFormatter::with().unit(FAHRENHEIT
).perUnit(DAY
),
2341 Locale::getEnglish(),
2344 static const UFieldPosition expectedFieldPositions
[] = {
2345 // field, begin index, end index
2346 {UNUM_INTEGER_FIELD
, 0, 2},
2347 // coverage for old enum:
2348 {DecimalFormat::kMeasureUnitField
, 2, 6}};
2349 assertNumberFieldPositions(
2352 expectedFieldPositions
,
2353 UPRV_LENGTHOF(expectedFieldPositions
));
2357 const char16_t* message
= u
"Measure unit field position with spaces";
2358 FormattedNumber result
= assertFormatSingle(
2360 u
"measure-unit/length-meter unit-width-full-name",
2361 NumberFormatter::with().unit(METER
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2362 Locale::getEnglish(),
2365 static const UFieldPosition expectedFieldPositions
[] = {
2366 // field, begin index, end index
2367 {UNUM_INTEGER_FIELD
, 0, 2},
2368 // note: field starts after the space
2369 {UNUM_MEASURE_UNIT_FIELD
, 3, 9}};
2370 assertNumberFieldPositions(
2373 expectedFieldPositions
,
2374 UPRV_LENGTHOF(expectedFieldPositions
));
2378 const char16_t* message
= u
"Measure unit field position with prefix and suffix";
2379 FormattedNumber result
= assertFormatSingle(
2381 u
"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
2382 NumberFormatter::with().unit(METER
).perUnit(SECOND
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2383 "ky", // locale with the interesting data
2385 u
"секундасына 68 метр");
2386 static const UFieldPosition expectedFieldPositions
[] = {
2387 // field, begin index, end index
2388 {UNUM_MEASURE_UNIT_FIELD
, 0, 11},
2389 {UNUM_INTEGER_FIELD
, 12, 14},
2390 {UNUM_MEASURE_UNIT_FIELD
, 15, 19}};
2391 assertNumberFieldPositions(
2394 expectedFieldPositions
,
2395 UPRV_LENGTHOF(expectedFieldPositions
));
2399 const char16_t* message
= u
"Measure unit field position with inner spaces";
2400 FormattedNumber result
= assertFormatSingle(
2402 u
"measure-unit/temperature-fahrenheit unit-width-full-name",
2403 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2404 "vi", // locale with the interesting data
2407 static const UFieldPosition expectedFieldPositions
[] = {
2408 // field, begin index, end index
2409 {UNUM_INTEGER_FIELD
, 0, 2},
2410 // Should trim leading/trailing spaces, but not inner spaces:
2411 {UNUM_MEASURE_UNIT_FIELD
, 3, 7}};
2412 assertNumberFieldPositions(
2415 expectedFieldPositions
,
2416 UPRV_LENGTHOF(expectedFieldPositions
));
2420 // Data: other{"{0} K"} == "\u200E{0} K"
2421 // If that data changes, try to find another example of a non-empty unit prefix/suffix
2422 // that is also all ignorables (whitespace and bidi control marks).
2423 const char16_t* message
= u
"Measure unit field position with fully ignorable prefix";
2424 FormattedNumber result
= assertFormatSingle(
2426 u
"measure-unit/temperature-kelvin",
2427 NumberFormatter::with().unit(KELVIN
),
2428 "fa", // locale with the interesting data
2431 static const UFieldPosition expectedFieldPositions
[] = {
2432 // field, begin index, end index
2433 {UNUM_INTEGER_FIELD
, 1, 3},
2434 {UNUM_MEASURE_UNIT_FIELD
, 4, 5}};
2435 assertNumberFieldPositions(
2438 expectedFieldPositions
,
2439 UPRV_LENGTHOF(expectedFieldPositions
));
2443 const char16_t* message
= u
"Compact field basic";
2444 FormattedNumber result
= assertFormatSingle(
2447 NumberFormatter::with().notation(Notation::compactShort()),
2451 static const UFieldPosition expectedFieldPositions
[] = {
2452 // field, begin index, end index
2453 {UNUM_INTEGER_FIELD
, 0, 2},
2454 {UNUM_COMPACT_FIELD
, 2, 3}};
2455 assertNumberFieldPositions(
2458 expectedFieldPositions
,
2459 UPRV_LENGTHOF(expectedFieldPositions
));
2463 const char16_t* message
= u
"Compact field with spaces";
2464 FormattedNumber result
= assertFormatSingle(
2467 NumberFormatter::with().notation(Notation::compactLong()),
2471 static const UFieldPosition expectedFieldPositions
[] = {
2472 // field, begin index, end index
2473 {UNUM_INTEGER_FIELD
, 0, 2},
2474 {UNUM_COMPACT_FIELD
, 3, 11}};
2475 assertNumberFieldPositions(
2478 expectedFieldPositions
,
2479 UPRV_LENGTHOF(expectedFieldPositions
));
2483 const char16_t* message
= u
"Compact field with inner space";
2484 FormattedNumber result
= assertFormatSingle(
2487 NumberFormatter::with().notation(Notation::compactLong()),
2488 "fil", // locale with interesting data
2491 static const UFieldPosition expectedFieldPositions
[] = {
2492 // field, begin index, end index
2493 {UNUM_INTEGER_FIELD
, 0, 1},
2494 {UNUM_COMPACT_FIELD
, 2, 9}};
2495 assertNumberFieldPositions(
2498 expectedFieldPositions
,
2499 UPRV_LENGTHOF(expectedFieldPositions
));
2503 const char16_t* message
= u
"Compact field with bidi mark";
2504 FormattedNumber result
= assertFormatSingle(
2507 NumberFormatter::with().notation(Notation::compactLong()),
2508 "he", // locale with interesting data
2511 static const UFieldPosition expectedFieldPositions
[] = {
2512 // field, begin index, end index
2513 {UNUM_INTEGER_FIELD
, 1, 2},
2514 {UNUM_COMPACT_FIELD
, 3, 6}};
2515 assertNumberFieldPositions(
2518 expectedFieldPositions
,
2519 UPRV_LENGTHOF(expectedFieldPositions
));
2523 const char16_t* message
= u
"Compact with currency fields";
2524 FormattedNumber result
= assertFormatSingle(
2526 u
"compact-short currency/USD",
2527 NumberFormatter::with().notation(Notation::compactShort()).unit(USD
),
2528 "sr_Latn", // locale with interesting data
2531 static const UFieldPosition expectedFieldPositions
[] = {
2532 // field, begin index, end index
2533 {UNUM_INTEGER_FIELD
, 0, 2},
2534 {UNUM_COMPACT_FIELD
, 3, 8},
2535 {UNUM_CURRENCY_FIELD
, 9, 12}};
2536 assertNumberFieldPositions(
2539 expectedFieldPositions
,
2540 UPRV_LENGTHOF(expectedFieldPositions
));
2544 const char16_t* message
= u
"Currency long name fields";
2545 FormattedNumber result
= assertFormatSingle(
2547 u
"currency/USD unit-width-full-name",
2548 NumberFormatter::with().unit(USD
)
2549 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
2552 u
"12,345.00 US dollars");
2553 static const UFieldPosition expectedFieldPositions
[] = {
2554 // field, begin index, end index
2555 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2556 {UNUM_INTEGER_FIELD
, 0, 6},
2557 {UNUM_DECIMAL_SEPARATOR_FIELD
, 6, 7},
2558 {UNUM_FRACTION_FIELD
, 7, 9},
2559 {UNUM_CURRENCY_FIELD
, 10, 20}};
2560 assertNumberFieldPositions(
2563 expectedFieldPositions
,
2564 UPRV_LENGTHOF(expectedFieldPositions
));
2568 const char16_t* message
= u
"Compact with measure unit fields";
2569 FormattedNumber result
= assertFormatSingle(
2571 u
"compact-long measure-unit/length-meter unit-width-full-name",
2572 NumberFormatter::with().notation(Notation::compactLong())
2574 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2577 u
"65 thousand meters");
2578 static const UFieldPosition expectedFieldPositions
[] = {
2579 // field, begin index, end index
2580 {UNUM_INTEGER_FIELD
, 0, 2},
2581 {UNUM_COMPACT_FIELD
, 3, 11},
2582 {UNUM_MEASURE_UNIT_FIELD
, 12, 18}};
2583 assertNumberFieldPositions(
2586 expectedFieldPositions
,
2587 UPRV_LENGTHOF(expectedFieldPositions
));
2591 void NumberFormatterApiTest::toFormat() {
2592 IcuTestErrorCode
status(*this, "icuFormat");
2593 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("fr")
2594 .precision(Precision::fixedFraction(3));
2595 LocalPointer
<Format
> format(lnf
.toFormat(status
), status
);
2596 FieldPosition
fpos(UNUM_DECIMAL_SEPARATOR_FIELD
);
2598 format
->format(514.23, sb
, fpos
, status
);
2599 assertEquals("Should correctly format number", u
"514,230", sb
);
2600 assertEquals("Should find decimal separator", 3, fpos
.getBeginIndex());
2601 assertEquals("Should find end of decimal separator", 4, fpos
.getEndIndex());
2603 "ICU Format should round-trip",
2604 lnf
.toSkeleton(status
),
2605 dynamic_cast<LocalizedNumberFormatterAsFormat
*>(format
.getAlias())->getNumberFormatter()
2606 .toSkeleton(status
));
2608 FieldPositionIterator fpi1
;
2609 lnf
.formatDouble(514.23, status
).getAllFieldPositions(fpi1
, status
);
2610 FieldPositionIterator fpi2
;
2611 format
->format(514.23, sb
.remove(), &fpi2
, status
);
2612 assertTrue("Should produce same field position iterator", fpi1
== fpi2
);
2615 void NumberFormatterApiTest::errors() {
2616 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale(Locale::getEnglish()).precision(
2617 Precision::fixedFraction(
2621 UErrorCode status
= U_ZERO_ERROR
;
2622 FormattedNumber fn
= lnf
.formatInt(1, status
);
2624 "Should fail in formatInt method with error code for rounding",
2625 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2629 status
= U_ZERO_ERROR
;
2630 fn
= lnf
.formatDouble(1.0, status
);
2632 "Should fail in formatDouble method with error code for rounding",
2633 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2636 // formatDecimal (decimal error)
2637 status
= U_ZERO_ERROR
;
2638 fn
= NumberFormatter::withLocale("en").formatDecimal("1x2", status
);
2640 "Should fail in formatDecimal method with error code for decimal number syntax",
2641 U_DECIMAL_NUMBER_SYNTAX_ERROR
,
2644 // formatDecimal (setting error)
2645 status
= U_ZERO_ERROR
;
2646 fn
= lnf
.formatDecimal("1.0", status
);
2648 "Should fail in formatDecimal method with error code for rounding",
2649 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2653 status
= U_ZERO_ERROR
;
2654 UnicodeString output
= lnf
.toSkeleton(status
);
2656 "Should fail on toSkeleton terminal method with correct error code",
2657 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2660 "Terminal toSkeleton on error object should be bogus",
2664 status
= U_ZERO_ERROR
;
2666 fn
.nextFieldPosition(fp
, status
);
2668 "Should fail on FieldPosition terminal method with correct error code",
2669 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2672 // FieldPositionIterator
2673 status
= U_ZERO_ERROR
;
2674 FieldPositionIterator fpi
;
2675 fn
.getAllFieldPositions(fpi
, status
);
2677 "Should fail on FieldPositoinIterator terminal method with correct error code",
2678 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2682 status
= U_ZERO_ERROR
;
2683 UnicodeStringAppendable
appendable(output
.remove());
2684 fn
.appendTo(appendable
, status
);
2686 "Should fail on Appendable terminal method with correct error code",
2687 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2691 status
= U_ZERO_ERROR
;
2692 output
= fn
.toString(status
);
2694 "Should fail on UnicodeString terminal method with correct error code",
2695 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2698 "Terminal UnicodeString on error object should be bogus",
2702 status
= U_ZERO_ERROR
;
2703 lnf
.copyErrorTo(status
);
2705 "Should fail since rounder is not legal with correct error code",
2706 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2710 void NumberFormatterApiTest::validRanges() {
2712 #define EXPECTED_MAX_INT_FRAC_SIG 999
2714 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) { \
2715 UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
2717 : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
2719 UnicodeString(u"Incorrect status for " #method " on input ") \
2720 + Int64ToUnicodeString(argument), \
2725 #define VALID_RANGE_ONEARG(setting, method, lowerBound) { \
2726 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2727 UErrorCode status = U_ZERO_ERROR; \
2728 NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
2729 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2733 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) { \
2734 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2735 UErrorCode status = U_ZERO_ERROR; \
2736 /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
2737 NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
2738 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2739 status = U_ZERO_ERROR; \
2740 /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
2741 NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
2742 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2743 /* Check that first argument must be less than or equal to second argument */ \
2744 NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
2745 assertEquals("Incorrect status for " #method " on max < min input", \
2746 U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
2751 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction
, 0);
2752 VALID_RANGE_ONEARG(precision
, Precision::minFraction
, 0);
2753 VALID_RANGE_ONEARG(precision
, Precision::maxFraction
, 0);
2754 VALID_RANGE_TWOARGS(precision
, Precision::minMaxFraction
, 0);
2755 VALID_RANGE_ONEARG(precision
, Precision::fixedSignificantDigits
, 1);
2756 VALID_RANGE_ONEARG(precision
, Precision::minSignificantDigits
, 1);
2757 VALID_RANGE_ONEARG(precision
, Precision::maxSignificantDigits
, 1);
2758 VALID_RANGE_TWOARGS(precision
, Precision::minMaxSignificantDigits
, 1);
2759 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction(1).withMinDigits
, 1);
2760 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction(1).withMaxDigits
, 1);
2761 VALID_RANGE_ONEARG(notation
, Notation::scientific().withMinExponentDigits
, 1);
2762 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo
, 0);
2763 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo(0).truncateAt
, -1);
2766 void NumberFormatterApiTest::copyMove() {
2767 IcuTestErrorCode
status(*this, "copyMove");
2769 // Default constructors
2770 LocalizedNumberFormatter l1
;
2771 assertEquals("Initial behavior", u
"10", l1
.formatInt(10, status
).toString(status
), true);
2772 if (status
.errDataIfFailureAndReset()) { return; }
2773 assertEquals("Initial call count", 1, l1
.getCallCount());
2774 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2777 l1
= NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
2778 assertEquals("Initial behavior", u
"10%", l1
.formatInt(10, status
).toString(status
));
2779 assertEquals("Initial call count", 1, l1
.getCallCount());
2780 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2781 l1
.formatInt(123, status
);
2782 assertEquals("Still not compiled", 2, l1
.getCallCount());
2783 assertTrue("Still not compiled", l1
.getCompiled() == nullptr);
2784 l1
.formatInt(123, status
);
2785 assertEquals("Compiled", u
"10%", l1
.formatInt(10, status
).toString(status
));
2786 assertEquals("Compiled", INT32_MIN
, l1
.getCallCount());
2787 assertTrue("Compiled", l1
.getCompiled() != nullptr);
2790 LocalizedNumberFormatter l2
= l1
;
2791 assertEquals("[constructor] Copy behavior", u
"10%", l2
.formatInt(10, status
).toString(status
));
2792 assertEquals("[constructor] Copy should not have compiled state", 1, l2
.getCallCount());
2793 assertTrue("[constructor] Copy should not have compiled state", l2
.getCompiled() == nullptr);
2796 LocalizedNumberFormatter l3
= std::move(l1
);
2797 assertEquals("[constructor] Move behavior", u
"10%", l3
.formatInt(10, status
).toString(status
));
2798 assertEquals("[constructor] Move *should* have compiled state", INT32_MIN
, l3
.getCallCount());
2799 assertTrue("[constructor] Move *should* have compiled state", l3
.getCompiled() != nullptr);
2800 assertEquals("[constructor] Source should be reset after move", 0, l1
.getCallCount());
2801 assertTrue("[constructor] Source should be reset after move", l1
.getCompiled() == nullptr);
2803 // Reset l1 and l2 to check for macro-props copying for behavior testing
2804 // Make the test more interesting: also warm them up with a compiled formatter.
2805 l1
= NumberFormatter::withLocale("en");
2806 l1
.formatInt(1, status
);
2807 l1
.formatInt(1, status
);
2808 l1
.formatInt(1, status
);
2809 l2
= NumberFormatter::withLocale("en");
2810 l2
.formatInt(1, status
);
2811 l2
.formatInt(1, status
);
2812 l2
.formatInt(1, status
);
2816 assertEquals("[assignment] Copy behavior", u
"10%", l1
.formatInt(10, status
).toString(status
));
2817 assertEquals("[assignment] Copy should not have compiled state", 1, l1
.getCallCount());
2818 assertTrue("[assignment] Copy should not have compiled state", l1
.getCompiled() == nullptr);
2822 assertEquals("[assignment] Move behavior", u
"10%", l2
.formatInt(10, status
).toString(status
));
2823 assertEquals("[assignment] Move *should* have compiled state", INT32_MIN
, l2
.getCallCount());
2824 assertTrue("[assignment] Move *should* have compiled state", l2
.getCompiled() != nullptr);
2825 assertEquals("[assignment] Source should be reset after move", 0, l3
.getCallCount());
2826 assertTrue("[assignment] Source should be reset after move", l3
.getCompiled() == nullptr);
2828 // Coverage tests for UnlocalizedNumberFormatter
2829 UnlocalizedNumberFormatter u1
;
2830 assertEquals("Default behavior", u
"10", u1
.locale("en").formatInt(10, status
).toString(status
));
2831 u1
= u1
.unit(NoUnit::percent());
2832 assertEquals("Copy assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString(status
));
2833 UnlocalizedNumberFormatter u2
= u1
;
2834 assertEquals("Copy constructor", u
"10%", u2
.locale("en").formatInt(10, status
).toString(status
));
2835 UnlocalizedNumberFormatter u3
= std::move(u1
);
2836 assertEquals("Move constructor", u
"10%", u3
.locale("en").formatInt(10, status
).toString(status
));
2837 u1
= NumberFormatter::with();
2839 assertEquals("Move assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString(status
));
2841 // FormattedNumber move operators
2842 FormattedNumber result
= l1
.formatInt(10, status
);
2843 assertEquals("FormattedNumber move constructor", u
"10%", result
.toString(status
));
2844 result
= l1
.formatInt(20, status
);
2845 assertEquals("FormattedNumber move assignment", u
"20%", result
.toString(status
));
2848 void NumberFormatterApiTest::localPointerCAPI() {
2849 // NOTE: This is also the sample code in unumberformatter.h
2850 UErrorCode ec
= U_ZERO_ERROR
;
2853 LocalUNumberFormatterPointer
uformatter(unumf_openForSkeletonAndLocale(u
"percent", -1, "en", &ec
));
2854 LocalUFormattedNumberPointer
uresult(unumf_openResult(&ec
));
2855 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2857 // Format a decimal number:
2858 unumf_formatDecimal(uformatter
.getAlias(), "9.87E-3", -1, uresult
.getAlias(), &ec
);
2859 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2861 // Get the location of the percent sign:
2862 UFieldPosition ufpos
= {UNUM_PERCENT_FIELD
, 0, 0};
2863 unumf_resultNextFieldPosition(uresult
.getAlias(), &ufpos
, &ec
);
2864 assertEquals("Percent sign location within '0.00987%'", 7, ufpos
.beginIndex
);
2865 assertEquals("Percent sign location within '0.00987%'", 8, ufpos
.endIndex
);
2867 // No need to do any cleanup since we are using LocalPointer.
2870 void NumberFormatterApiTest::toObject() {
2871 IcuTestErrorCode
status(*this, "toObject");
2873 // const lvalue version
2875 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("en")
2876 .precision(Precision::fixedFraction(2));
2877 LocalPointer
<LocalizedNumberFormatter
> lnf2(lnf
.clone());
2878 assertFalse("should create successfully, const lvalue", lnf2
.isNull());
2879 assertEquals("object API test, const lvalue", u
"1,000.00",
2880 lnf2
->formatDouble(1000, status
).toString(status
));
2883 // rvalue reference version
2885 LocalPointer
<LocalizedNumberFormatter
> lnf(
2886 NumberFormatter::withLocale("en")
2887 .precision(Precision::fixedFraction(2))
2889 assertFalse("should create successfully, rvalue reference", lnf
.isNull());
2890 assertEquals("object API test, rvalue reference", u
"1,000.00",
2891 lnf
->formatDouble(1000, status
).toString(status
));
2894 // to std::unique_ptr via constructor
2896 std::unique_ptr
<LocalizedNumberFormatter
> lnf(
2897 NumberFormatter::withLocale("en")
2898 .precision(Precision::fixedFraction(2))
2900 assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf
));
2901 assertEquals("object API test, unique_ptr", u
"1,000.00",
2902 lnf
->formatDouble(1000, status
).toString(status
));
2905 // to std::unique_ptr via assignment
2907 std::unique_ptr
<LocalizedNumberFormatter
> lnf
=
2908 NumberFormatter::withLocale("en")
2909 .precision(Precision::fixedFraction(2))
2911 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf
));
2912 assertEquals("object API test, unique_ptr B", u
"1,000.00",
2913 lnf
->formatDouble(1000, status
).toString(status
));
2916 // to LocalPointer via assignment
2918 LocalPointer
<UnlocalizedNumberFormatter
> f
=
2919 NumberFormatter::with().clone();
2922 // make sure no memory leaks
2924 NumberFormatter::with().clone();
2929 void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage
, const char16_t* uskeleton
,
2930 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2933 va_start(args
, locale
);
2934 UnicodeString
message(TRUE
, umessage
, -1);
2935 static double inputs
[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
2936 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2937 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2938 IcuTestErrorCode
status(*this, "assertFormatDescending");
2939 status
.setScope(message
);
2940 UnicodeString expecteds
[10];
2941 for (int16_t i
= 0; i
< 9; i
++) {
2942 char16_t caseNumber
= u
'0' + i
;
2943 double d
= inputs
[i
];
2944 UnicodeString expected
= va_arg(args
, const char16_t*);
2945 expecteds
[i
] = expected
;
2946 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString(status
);
2947 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
2948 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
2949 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString(status
);
2950 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
2951 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
2953 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2954 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2955 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2956 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
2957 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
2958 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
2959 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
2960 for (int32_t i
= 0; i
< 9; i
++) {
2961 double d
= inputs
[i
];
2962 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString(status
);
2963 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
2966 assertUndefinedSkeleton(f
);
2970 void NumberFormatterApiTest::assertFormatDescendingBig(const char16_t* umessage
, const char16_t* uskeleton
,
2971 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2974 va_start(args
, locale
);
2975 UnicodeString
message(TRUE
, umessage
, -1);
2976 static double inputs
[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
2977 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2978 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2979 IcuTestErrorCode
status(*this, "assertFormatDescendingBig");
2980 status
.setScope(message
);
2981 UnicodeString expecteds
[10];
2982 for (int16_t i
= 0; i
< 9; i
++) {
2983 char16_t caseNumber
= u
'0' + i
;
2984 double d
= inputs
[i
];
2985 UnicodeString expected
= va_arg(args
, const char16_t*);
2986 expecteds
[i
] = expected
;
2987 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString(status
);
2988 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
2989 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
2990 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString(status
);
2991 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
2992 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
2994 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2995 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2996 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2997 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
2998 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
2999 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3000 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3001 for (int32_t i
= 0; i
< 9; i
++) {
3002 double d
= inputs
[i
];
3003 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString(status
);
3004 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
3007 assertUndefinedSkeleton(f
);
3012 NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage
, const char16_t* uskeleton
,
3013 const UnlocalizedNumberFormatter
& f
, Locale locale
,
3014 double input
, const UnicodeString
& expected
) {
3015 UnicodeString
message(TRUE
, umessage
, -1);
3016 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
3017 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
3018 IcuTestErrorCode
status(*this, "assertFormatSingle");
3019 status
.setScope(message
);
3020 FormattedNumber result1
= l1
.formatDouble(input
, status
);
3021 UnicodeString actual1
= result1
.toString(status
);
3022 assertSuccess(message
+ u
": Unsafe Path", status
);
3023 assertEquals(message
+ u
": Unsafe Path", expected
, actual1
);
3024 UnicodeString actual2
= l2
.formatDouble(input
, status
).toString(status
);
3025 assertSuccess(message
+ u
": Safe Path", status
);
3026 assertEquals(message
+ u
": Safe Path", expected
, actual2
);
3027 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
3028 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
3029 // Only compare normalized skeletons: the tests need not provide the normalized forms.
3030 // Use the normalized form to construct the testing formatter to ensure no loss of info.
3031 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
3032 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3033 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3034 UnicodeString actual3
= l3
.formatDouble(input
, status
).toString(status
);
3035 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + input
, expected
, actual3
);
3037 assertUndefinedSkeleton(f
);
3042 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter
& f
) {
3043 UErrorCode status
= U_ZERO_ERROR
;
3044 UnicodeString skeleton
= f
.toSkeleton(status
);
3046 u
"Expect toSkeleton to fail, but passed, producing: " + skeleton
,
3047 U_UNSUPPORTED_ERROR
,
3051 void NumberFormatterApiTest::assertNumberFieldPositions(
3052 const char16_t* message
, const FormattedNumber
& formattedNumber
,
3053 const UFieldPosition
* expectedFieldPositions
, int32_t length
) {
3054 IcuTestErrorCode
status(*this, "assertNumberFieldPositions");
3056 // Check FormattedValue functions
3057 checkFormattedValue(
3059 static_cast<const FormattedValue
&>(formattedNumber
),
3060 formattedNumber
.toString(status
),
3061 UFIELD_CATEGORY_NUMBER
,
3062 expectedFieldPositions
,
3065 // Check FormattedNumber-specific functions
3066 UnicodeString baseMessage
= UnicodeString(message
) + u
": " + formattedNumber
.toString(status
) + u
": ";
3067 FieldPositionIterator fpi
;
3068 formattedNumber
.getAllFieldPositions(fpi
, status
);
3070 FieldPosition actual
;
3071 while (fpi
.next(actual
)) {
3072 UFieldPosition expected
= expectedFieldPositions
[i
++];
3074 baseMessage
+ UnicodeString(u
"Field, case #") + Int64ToUnicodeString(i
),
3078 baseMessage
+ UnicodeString(u
"Iterator, begin, case #") + Int64ToUnicodeString(i
),
3079 expected
.beginIndex
,
3080 actual
.getBeginIndex());
3082 baseMessage
+ UnicodeString(u
"Iterator, end, case #") + Int64ToUnicodeString(i
),
3084 actual
.getEndIndex());
3086 // Check for the first location of the field
3087 FieldPosition
actual2(expected
.field
);
3088 // Fast-forward the field to skip previous occurrences of the field:
3089 actual2
.setBeginIndex(expected
.beginIndex
);
3090 actual2
.setEndIndex(expected
.beginIndex
);
3091 UBool found
= formattedNumber
.nextFieldPosition(actual2
, status
);
3093 baseMessage
+ UnicodeString(u
"Next, found first, case #") + Int64ToUnicodeString(i
),
3097 baseMessage
+ UnicodeString(u
"Next, begin, case #") + Int64ToUnicodeString(i
),
3098 expected
.beginIndex
,
3099 actual2
.getBeginIndex());
3101 baseMessage
+ UnicodeString(u
"Next, end, case #") + Int64ToUnicodeString(i
),
3103 actual2
.getEndIndex());
3105 // The next position should be empty unless the field occurs again
3106 UBool occursAgain
= false;
3107 for (int32_t j
=i
; j
<length
; j
++) {
3108 if (expectedFieldPositions
[j
].field
== expected
.field
) {
3114 found
= formattedNumber
.nextFieldPosition(actual2
, status
);
3116 baseMessage
+ UnicodeString(u
"Next, found second, case #") + Int64ToUnicodeString(i
),
3121 assertEquals(baseMessage
+ u
"Should have seen every field position", length
, i
);
3125 #endif /* #if !UCONFIG_NO_FORMATTING */