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
11 #include "unicode/unum.h"
12 #include "unicode/numberformatter.h"
13 #include "number_asformat.h"
14 #include "number_types.h"
15 #include "number_utils.h"
16 #include "numbertest.h"
17 #include "unicode/utypes.h"
19 // Horrible workaround for the lack of a status code in the constructor...
20 UErrorCode globalNumberFormatterApiTestStatus
= U_ZERO_ERROR
;
22 NumberFormatterApiTest::NumberFormatterApiTest()
23 : NumberFormatterApiTest(globalNumberFormatterApiTestStatus
) {
26 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode
& status
)
27 : USD(u
"USD", status
),
33 FRENCH_SYMBOLS(Locale::getFrench(), status
),
34 SWISS_SYMBOLS(Locale("de-CH"), status
),
35 MYANMAR_SYMBOLS(Locale("my"), status
) {
37 // Check for error on the first MeasureUnit in case there is no data
38 LocalPointer
<MeasureUnit
> unit(MeasureUnit::createMeter(status
));
39 if (U_FAILURE(status
)) {
40 dataerrln("%s %d status = %s", __FILE__
, __LINE__
, u_errorName(status
));
45 DAY
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createDay(status
));
46 SQUARE_METER
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSquareMeter(status
));
47 FAHRENHEIT
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFahrenheit(status
));
48 SECOND
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSecond(status
));
49 POUND
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createPound(status
));
50 SQUARE_MILE
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSquareMile(status
));
51 JOULE
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createJoule(status
));
52 FURLONG
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFurlong(status
));
53 KELVIN
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createKelvin(status
));
55 MATHSANB
= *LocalPointer
<NumberingSystem
>(NumberingSystem::createInstanceByName("mathsanb", status
));
56 LATN
= *LocalPointer
<NumberingSystem
>(NumberingSystem::createInstanceByName("latn", status
));
59 void NumberFormatterApiTest::runIndexedTest(int32_t index
, UBool exec
, const char*& name
, char*) {
61 logln("TestSuite NumberFormatterApiTest: ");
64 TESTCASE_AUTO(notationSimple
);
65 TESTCASE_AUTO(notationScientific
);
66 TESTCASE_AUTO(notationCompact
);
67 TESTCASE_AUTO(unitMeasure
);
68 TESTCASE_AUTO(unitCompoundMeasure
);
69 TESTCASE_AUTO(unitCurrency
);
70 TESTCASE_AUTO(unitPercent
);
71 TESTCASE_AUTO(roundingFraction
);
72 TESTCASE_AUTO(roundingFigures
);
73 TESTCASE_AUTO(roundingFractionFigures
);
74 TESTCASE_AUTO(roundingOther
);
75 TESTCASE_AUTO(grouping
);
76 TESTCASE_AUTO(padding
);
77 TESTCASE_AUTO(integerWidth
);
78 TESTCASE_AUTO(symbols
);
79 // TODO: Add this method if currency symbols override support is added.
80 //TESTCASE_AUTO(symbolsOverride);
82 TESTCASE_AUTO(decimal
);
84 TESTCASE_AUTO(locale
);
85 TESTCASE_AUTO(formatTypes
);
86 TESTCASE_AUTO(fieldPosition
);
87 TESTCASE_AUTO(toFormat
);
88 TESTCASE_AUTO(errors
);
89 TESTCASE_AUTO(validRanges
);
90 TESTCASE_AUTO(copyMove
);
91 TESTCASE_AUTO(localPointerCAPI
);
95 void NumberFormatterApiTest::notationSimple() {
96 assertFormatDescending(
99 NumberFormatter::with(),
100 Locale::getEnglish(),
111 assertFormatDescendingBig(
114 NumberFormatter::with().notation(Notation::simple()),
115 Locale::getEnglish(),
127 u
"Basic with Negative Sign",
129 NumberFormatter::with(),
130 Locale::getEnglish(),
136 void NumberFormatterApiTest::notationScientific() {
137 assertFormatDescending(
140 NumberFormatter::with().notation(Notation::scientific()),
141 Locale::getEnglish(),
152 assertFormatDescending(
155 NumberFormatter::with().notation(Notation::engineering()),
156 Locale::getEnglish(),
167 assertFormatDescending(
168 u
"Scientific sign always shown",
169 u
"scientific/sign-always",
170 NumberFormatter::with().notation(
171 Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS
)),
172 Locale::getEnglish(),
183 assertFormatDescending(
184 u
"Scientific min exponent digits",
186 NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
187 Locale::getEnglish(),
199 u
"Scientific Negative",
201 NumberFormatter::with().notation(Notation::scientific()),
202 Locale::getEnglish(),
207 void NumberFormatterApiTest::notationCompact() {
208 assertFormatDescending(
211 NumberFormatter::with().notation(Notation::compactShort()),
212 Locale::getEnglish(),
223 assertFormatDescending(
226 NumberFormatter::with().notation(Notation::compactLong()),
227 Locale::getEnglish(),
238 assertFormatDescending(
239 u
"Compact Short Currency",
240 u
"compact-short currency/USD",
241 NumberFormatter::with().notation(Notation::compactShort()).unit(USD
),
242 Locale::getEnglish(),
253 assertFormatDescending(
254 u
"Compact Short with ISO Currency",
255 u
"compact-short currency/USD unit-width-iso-code",
256 NumberFormatter::with().notation(Notation::compactShort())
258 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
259 Locale::getEnglish(),
270 assertFormatDescending(
271 u
"Compact Short with Long Name Currency",
272 u
"compact-short currency/USD unit-width-full-name",
273 NumberFormatter::with().notation(Notation::compactShort())
275 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
276 Locale::getEnglish(),
284 u
"0.0088 US dollars",
287 // Note: Most locales don't have compact long currency, so this currently falls back to short.
288 // This test case should be fixed when proper compact long currency patterns are added.
289 assertFormatDescending(
290 u
"Compact Long Currency",
291 u
"compact-long currency/USD",
292 NumberFormatter::with().notation(Notation::compactLong()).unit(USD
),
293 Locale::getEnglish(),
294 u
"$88K", // should be something like "$88 thousand"
304 // Note: Most locales don't have compact long currency, so this currently falls back to short.
305 // This test case should be fixed when proper compact long currency patterns are added.
306 assertFormatDescending(
307 u
"Compact Long with ISO Currency",
308 u
"compact-long currency/USD unit-width-iso-code",
309 NumberFormatter::with().notation(Notation::compactLong())
311 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
312 Locale::getEnglish(),
313 u
"USD 88K", // should be something like "USD 88 thousand"
323 // TODO: This behavior could be improved and should be revisited.
324 assertFormatDescending(
325 u
"Compact Long with Long Name Currency",
326 u
"compact-long currency/USD unit-width-full-name",
327 NumberFormatter::with().notation(Notation::compactLong())
329 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
330 Locale::getEnglish(),
331 u
"88 thousand US dollars",
332 u
"8.8 thousand US dollars",
338 u
"0.0088 US dollars",
342 u
"Compact Plural One",
344 NumberFormatter::with().notation(Notation::compactLong()),
345 Locale::createFromName("es"),
350 u
"Compact Plural Other",
352 NumberFormatter::with().notation(Notation::compactLong()),
353 Locale::createFromName("es"),
358 u
"Compact with Negative Sign",
360 NumberFormatter::with().notation(Notation::compactShort()),
361 Locale::getEnglish(),
368 NumberFormatter::with().notation(Notation::compactShort()),
369 Locale::getEnglish(),
376 NumberFormatter::with().notation(Notation::compactShort()),
377 Locale::getEnglish(),
384 NumberFormatter::with().notation(Notation::compactShort()),
385 Locale::getEnglish(),
392 NumberFormatter::with().notation(Notation::compactShort()),
393 Locale::getEnglish(),
400 NumberFormatter::with().notation(Notation::compactShort()),
401 Locale::getEnglish(),
405 // NOTE: There is no API for compact custom data in C++
406 // and thus no "Compact Somali No Figure" test
409 void NumberFormatterApiTest::unitMeasure() {
410 assertFormatDescending(
411 u
"Meters Short and unit() method",
412 u
"measure-unit/length-meter",
413 NumberFormatter::with().unit(METER
),
414 Locale::getEnglish(),
425 assertFormatDescending(
426 u
"Meters Long and adoptUnit() method",
427 u
"measure-unit/length-meter unit-width-full-name",
428 NumberFormatter::with().adoptUnit(new MeasureUnit(METER
))
429 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
430 Locale::getEnglish(),
441 assertFormatDescending(
442 u
"Compact Meters Long",
443 u
"compact-long measure-unit/length-meter unit-width-full-name",
444 NumberFormatter::with().notation(Notation::compactLong())
446 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
447 Locale::getEnglish(),
448 u
"88 thousand meters",
449 u
"8.8 thousand meters",
458 // TODO: Implement Measure in C++
459 // assertFormatSingleMeasure(
460 // u"Meters with Measure Input",
461 // NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
462 // Locale::getEnglish(),
463 // new Measure(5.43, new MeasureUnit(METER)),
466 // TODO: Implement Measure in C++
467 // assertFormatSingleMeasure(
468 // u"Measure format method takes precedence over fluent chain",
469 // NumberFormatter::with().unit(METER),
470 // Locale::getEnglish(),
471 // new Measure(5.43, USD),
475 u
"Meters with Negative Sign",
476 u
"measure-unit/length-meter",
477 NumberFormatter::with().unit(METER
),
478 Locale::getEnglish(),
482 // The locale string "सान" appears only in brx.txt:
484 u
"Interesting Data Fallback 1",
485 u
"measure-unit/duration-day unit-width-full-name",
486 NumberFormatter::with().unit(DAY
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
487 Locale::createFromName("brx"),
491 // Requires following the alias from unitsNarrow to unitsShort:
493 u
"Interesting Data Fallback 2",
494 u
"measure-unit/duration-day unit-width-narrow",
495 NumberFormatter::with().unit(DAY
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
496 Locale::createFromName("brx"),
500 // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
501 // requiring fallback to the root.
503 u
"Interesting Data Fallback 3",
504 u
"measure-unit/area-square-meter unit-width-narrow",
505 NumberFormatter::with().unit(SQUARE_METER
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
506 Locale::createFromName("en-GB"),
510 // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
511 // NOTE: This example is in the documentation.
513 u
"Difference between Narrow and Short (Narrow Version)",
514 u
"measure-unit/temperature-fahrenheit unit-width-narrow",
515 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
521 u
"Difference between Narrow and Short (Short Version)",
522 u
"measure-unit/temperature-fahrenheit unit-width-short",
523 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
529 u
"MeasureUnit form without {0} in CLDR pattern",
530 u
"measure-unit/temperature-kelvin unit-width-full-name",
531 NumberFormatter::with().unit(KELVIN
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
537 u
"MeasureUnit form without {0} in CLDR pattern and wide base form",
538 u
"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
539 NumberFormatter::with().precision(Precision::fixedFraction(20))
541 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
547 void NumberFormatterApiTest::unitCompoundMeasure() {
548 assertFormatDescending(
549 u
"Meters Per Second Short (unit that simplifies) and perUnit method",
550 u
"measure-unit/length-meter per-measure-unit/duration-second",
551 NumberFormatter::with().unit(METER
).perUnit(SECOND
),
552 Locale::getEnglish(),
563 assertFormatDescending(
564 u
"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
565 u
"measure-unit/mass-pound per-measure-unit/area-square-mile",
566 NumberFormatter::with().unit(POUND
).adoptPerUnit(new MeasureUnit(SQUARE_MILE
)),
567 Locale::getEnglish(),
578 assertFormatDescending(
579 u
"Joules Per Furlong Short (unit with no simplifications or special patterns)",
580 u
"measure-unit/energy-joule per-measure-unit/length-furlong",
581 NumberFormatter::with().unit(JOULE
).perUnit(FURLONG
),
582 Locale::getEnglish(),
594 void NumberFormatterApiTest::unitCurrency() {
595 assertFormatDescending(
598 NumberFormatter::with().unit(GBP
),
599 Locale::getEnglish(),
610 assertFormatDescending(
612 u
"currency/GBP unit-width-iso-code",
613 NumberFormatter::with().unit(GBP
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
614 Locale::getEnglish(),
625 assertFormatDescending(
626 u
"Currency Long Name",
627 u
"currency/GBP unit-width-full-name",
628 NumberFormatter::with().unit(GBP
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
629 Locale::getEnglish(),
630 u
"87,650.00 British pounds",
631 u
"8,765.00 British pounds",
632 u
"876.50 British pounds",
633 u
"87.65 British pounds",
634 u
"8.76 British pounds",
635 u
"0.88 British pounds",
636 u
"0.09 British pounds",
637 u
"0.01 British pounds",
638 u
"0.00 British pounds");
640 assertFormatDescending(
642 u
"currency/GBP unit-width-hidden",
643 NumberFormatter::with().unit(GBP
).unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
644 Locale::getEnglish(),
655 // TODO: Implement Measure in C++
656 // assertFormatSingleMeasure(
657 // u"Currency with CurrencyAmount Input",
658 // NumberFormatter::with(),
659 // Locale::getEnglish(),
660 // new CurrencyAmount(5.43, GBP),
663 // TODO: Enable this test when DecimalFormat wrapper is done.
664 // assertFormatSingle(
665 // u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
666 // PatternStringParser.parseToProperties("0 ¤¤¤"),
667 // DecimalFormatSymbols.getInstance(Locale::getEnglish()),
668 // null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
671 u
"Currency with Negative Sign",
673 NumberFormatter::with().unit(GBP
),
674 Locale::getEnglish(),
678 // The full currency symbol is not shown in NARROW format.
679 // NOTE: This example is in the documentation.
681 u
"Currency Difference between Narrow and Short (Narrow Version)",
682 u
"currency/USD unit-width-narrow",
683 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
689 u
"Currency Difference between Narrow and Short (Short Version)",
690 u
"currency/USD unit-width-short",
691 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
697 u
"Currency-dependent format (Control)",
698 u
"currency/USD unit-width-short",
699 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
705 u
"Currency-dependent format (Test)",
706 u
"currency/ESP unit-width-short",
707 NumberFormatter::with().unit(ESP
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
713 u
"Currency-dependent symbols (Control)",
714 u
"currency/USD unit-width-short",
715 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
720 // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
721 // width space), and they set the decimal separator to the $ symbol.
723 u
"Currency-dependent symbols (Test Short)",
724 u
"currency/PTE unit-width-short",
725 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
728 u
"444,444$55 \u200B");
731 u
"Currency-dependent symbols (Test Narrow)",
732 u
"currency/PTE unit-width-narrow",
733 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
739 u
"Currency-dependent symbols (Test ISO Code)",
740 u
"currency/PTE unit-width-iso-code",
741 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
),
747 void NumberFormatterApiTest::unitPercent() {
748 assertFormatDescending(
751 NumberFormatter::with().unit(NoUnit::percent()),
752 Locale::getEnglish(),
763 assertFormatDescending(
766 NumberFormatter::with().unit(NoUnit::permille()),
767 Locale::getEnglish(),
781 NumberFormatter::with().unit(NoUnit::base()),
782 Locale::getEnglish(),
787 u
"Percent with Negative Sign",
789 NumberFormatter::with().unit(NoUnit::percent()),
790 Locale::getEnglish(),
795 void NumberFormatterApiTest::roundingFraction() {
796 assertFormatDescending(
798 u
"precision-integer",
799 NumberFormatter::with().precision(Precision::integer()),
800 Locale::getEnglish(),
811 assertFormatDescending(
814 NumberFormatter::with().precision(Precision::fixedFraction(3)),
815 Locale::getEnglish(),
826 assertFormatDescending(
829 NumberFormatter::with().precision(Precision::minFraction(1)),
830 Locale::getEnglish(),
841 assertFormatDescending(
844 NumberFormatter::with().precision(Precision::maxFraction(1)),
845 Locale::getEnglish(),
856 assertFormatDescending(
859 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
860 Locale::getEnglish(),
872 void NumberFormatterApiTest::roundingFigures() {
874 u
"Fixed Significant",
876 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
877 Locale::getEnglish(),
882 u
"Fixed Significant Rounding",
884 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
885 Locale::getEnglish(),
890 u
"Fixed Significant Zero",
892 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
893 Locale::getEnglish(),
900 NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
901 Locale::getEnglish(),
908 NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
909 Locale::getEnglish(),
914 u
"Min/Max Significant",
916 NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
917 Locale::getEnglish(),
922 u
"Fixed Significant on zero with lots of integer width",
923 u
"@ integer-width/+000",
924 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
925 .integerWidth(IntegerWidth::zeroFillTo(3)),
926 Locale::getEnglish(),
931 u
"Fixed Significant on zero with zero integer width",
932 u
"@ integer-width/+",
933 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
934 .integerWidth(IntegerWidth::zeroFillTo(0)),
935 Locale::getEnglish(),
940 void NumberFormatterApiTest::roundingFractionFigures() {
941 assertFormatDescending(
942 u
"Basic Significant", // for comparison
944 NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
945 Locale::getEnglish(),
956 assertFormatDescending(
957 u
"FracSig minMaxFrac minSig",
959 NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
960 Locale::getEnglish(),
966 u
"0.876", // minSig beats maxFrac
967 u
"0.0876", // minSig beats maxFrac
968 u
"0.00876", // minSig beats maxFrac
971 assertFormatDescending(
972 u
"FracSig minMaxFrac maxSig A",
974 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
975 Locale::getEnglish(),
976 u
"88,000.0", // maxSig beats maxFrac
977 u
"8,800.0", // maxSig beats maxFrac
978 u
"880.0", // maxSig beats maxFrac
979 u
"88.0", // maxSig beats maxFrac
980 u
"8.8", // maxSig beats maxFrac
981 u
"0.88", // maxSig beats maxFrac
986 assertFormatDescending(
987 u
"FracSig minMaxFrac maxSig B",
989 NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
990 Locale::getEnglish(),
991 u
"88,000.00", // maxSig beats maxFrac
992 u
"8,800.00", // maxSig beats maxFrac
993 u
"880.00", // maxSig beats maxFrac
994 u
"88.00", // maxSig beats maxFrac
995 u
"8.80", // maxSig beats maxFrac
1002 u
"FracSig with trailing zeros A",
1004 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1005 Locale::getEnglish(),
1010 u
"FracSig with trailing zeros B",
1012 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1013 Locale::getEnglish(),
1018 void NumberFormatterApiTest::roundingOther() {
1019 assertFormatDescending(
1021 u
"precision-unlimited",
1022 NumberFormatter::with().precision(Precision::unlimited()),
1023 Locale::getEnglish(),
1034 assertFormatDescending(
1036 u
"precision-increment/0.5",
1037 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
1038 Locale::getEnglish(),
1049 assertFormatDescending(
1050 u
"Increment with Min Fraction",
1051 u
"precision-increment/0.50",
1052 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
1053 Locale::getEnglish(),
1064 assertFormatDescending(
1065 u
"Currency Standard",
1066 u
"currency/CZK precision-currency-standard",
1067 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD
))
1069 Locale::getEnglish(),
1080 assertFormatDescending(
1082 u
"currency/CZK precision-currency-cash",
1083 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
))
1085 Locale::getEnglish(),
1096 assertFormatDescending(
1097 u
"Currency Cash with Nickel Rounding",
1098 u
"currency/CAD precision-currency-cash",
1099 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
))
1101 Locale::getEnglish(),
1112 assertFormatDescending(
1113 u
"Currency not in top-level fluent chain",
1114 u
"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
1115 NumberFormatter::with().precision(
1116 Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
).withCurrency(CZK
)),
1117 Locale::getEnglish(),
1128 // NOTE: Other tests cover the behavior of the other rounding modes.
1129 assertFormatDescending(
1130 u
"Rounding Mode CEILING",
1131 u
"precision-integer rounding-mode-ceiling",
1132 NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING
),
1133 Locale::getEnglish(),
1145 void NumberFormatterApiTest::grouping() {
1146 assertFormatDescendingBig(
1147 u
"Western Grouping",
1149 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1150 Locale::getEnglish(),
1161 assertFormatDescendingBig(
1164 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1176 assertFormatDescendingBig(
1177 u
"Western Grouping, Min 2",
1179 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1180 Locale::getEnglish(),
1191 assertFormatDescendingBig(
1192 u
"Indic Grouping, Min 2",
1194 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1206 assertFormatDescendingBig(
1209 NumberFormatter::with().grouping(UNUM_GROUPING_OFF
),
1221 assertFormatDescendingBig(
1222 u
"Indic locale with THOUSANDS grouping",
1224 NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS
),
1236 // NOTE: Hungarian is interesting because it has minimumGroupingDigits=4 in locale data
1237 // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
1238 assertFormatDescendingBig(
1239 u
"Hungarian Grouping",
1241 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1253 assertFormatDescendingBig(
1254 u
"Hungarian Grouping, Min 2",
1256 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1268 assertFormatDescendingBig(
1269 u
"Hungarian Grouping, Always",
1270 u
"group-on-aligned",
1271 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
),
1283 // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
1284 // If this test breaks due to data changes, find another locale that has no default grouping.
1285 assertFormatDescendingBig(
1286 u
"Bulgarian Currency Grouping",
1287 u
"currency/USD group-auto",
1288 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
).unit(USD
),
1290 u
"87650000,00 щ.д.",
1300 assertFormatDescendingBig(
1301 u
"Bulgarian Currency Grouping, Always",
1302 u
"currency/USD group-on-aligned",
1303 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
).unit(USD
),
1305 u
"87 650 000,00 щ.д.",
1306 u
"8 765 000,00 щ.д.",
1316 macros
.grouper
= Grouper(4, 1, 3, UNUM_GROUPING_COUNT
);
1317 assertFormatDescendingBig(
1318 u
"Custom Grouping via Internal API",
1320 NumberFormatter::with().macros(macros
),
1321 Locale::getEnglish(),
1333 void NumberFormatterApiTest::padding() {
1334 assertFormatDescending(
1337 NumberFormatter::with().padding(Padder::none()),
1338 Locale::getEnglish(),
1349 assertFormatDescending(
1352 NumberFormatter::with().padding(
1354 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1355 Locale::getEnglish(),
1366 assertFormatDescending(
1367 u
"Padding with code points",
1369 NumberFormatter::with().padding(
1371 0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1372 Locale::getEnglish(),
1383 assertFormatDescending(
1384 u
"Padding with wide digits",
1386 NumberFormatter::with().padding(
1388 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1389 .adoptSymbols(new NumberingSystem(MATHSANB
)),
1390 Locale::getEnglish(),
1401 assertFormatDescending(
1402 u
"Padding with currency spacing",
1404 NumberFormatter::with().padding(
1406 '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1408 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1409 Locale::getEnglish(),
1421 u
"Pad Before Prefix",
1423 NumberFormatter::with().padding(
1425 '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX
)),
1426 Locale::getEnglish(),
1431 u
"Pad After Prefix",
1433 NumberFormatter::with().padding(
1435 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1436 Locale::getEnglish(),
1441 u
"Pad Before Suffix",
1443 NumberFormatter::with().padding(
1445 '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX
)).unit(NoUnit::percent()),
1446 Locale::getEnglish(),
1451 u
"Pad After Suffix",
1453 NumberFormatter::with().padding(
1455 '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX
)).unit(NoUnit::percent()),
1456 Locale::getEnglish(),
1461 u
"Currency Spacing with Zero Digit Padding Broken",
1463 NumberFormatter::with().padding(
1465 '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1467 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1468 Locale::getEnglish(),
1470 u
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
1473 void NumberFormatterApiTest::integerWidth() {
1474 assertFormatDescending(
1475 u
"Integer Width Default",
1476 u
"integer-width/+0",
1477 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
1478 Locale::getEnglish(),
1489 assertFormatDescending(
1490 u
"Integer Width Zero Fill 0",
1492 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
1493 Locale::getEnglish(),
1502 u
""); // TODO: Avoid the empty string here?
1504 assertFormatDescending(
1505 u
"Integer Width Zero Fill 3",
1506 u
"integer-width/+000",
1507 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
1508 Locale::getEnglish(),
1519 assertFormatDescending(
1520 u
"Integer Width Max 3",
1521 u
"integer-width/##0",
1522 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
1523 Locale::getEnglish(),
1534 assertFormatDescending(
1535 u
"Integer Width Fixed 2",
1536 u
"integer-width/00",
1537 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1538 Locale::getEnglish(),
1550 void NumberFormatterApiTest::symbols() {
1551 assertFormatDescending(
1552 u
"French Symbols with Japanese Data 1",
1554 NumberFormatter::with().symbols(FRENCH_SYMBOLS
),
1567 u
"French Symbols with Japanese Data 2",
1569 NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS
),
1574 assertFormatDescending(
1575 u
"Latin Numbering System with Arabic Data",
1576 u
"currency/USD latin",
1577 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1589 assertFormatDescending(
1590 u
"Math Numbering System with French Data",
1591 u
"numbering-system/mathsanb",
1592 NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB
)),
1593 Locale::getFrench(),
1605 u
"Swiss Symbols (used in documentation)",
1607 NumberFormatter::with().symbols(SWISS_SYMBOLS
),
1608 Locale::getEnglish(),
1613 u
"Myanmar Symbols (used in documentation)",
1615 NumberFormatter::with().symbols(MYANMAR_SYMBOLS
),
1616 Locale::getEnglish(),
1618 u
"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
1620 // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
1623 u
"Currency symbol should precede number in ar with NS latn",
1624 u
"currency/USD latin",
1625 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1631 u
"Currency symbol should precede number in ar@numbers=latn",
1633 NumberFormatter::with().unit(USD
),
1634 Locale("ar@numbers=latn"),
1639 u
"Currency symbol should follow number in ar-EG with NS arab",
1641 NumberFormatter::with().unit(USD
),
1647 u
"Currency symbol should follow number in ar@numbers=arab",
1649 NumberFormatter::with().unit(USD
),
1650 Locale("ar@numbers=arab"),
1655 u
"NumberingSystem in API should win over @numbers keyword",
1656 u
"currency/USD latin",
1657 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1658 Locale("ar@numbers=arab"),
1662 UErrorCode status
= U_ZERO_ERROR
;
1664 "NumberingSystem in API should win over @numbers keyword in reverse order",
1666 NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN
))
1668 .formatDouble(12345.67, status
)
1671 DecimalFormatSymbols symbols
= SWISS_SYMBOLS
;
1672 UnlocalizedNumberFormatter f
= NumberFormatter::with().symbols(symbols
);
1673 symbols
.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol
, u
"!", status
);
1675 u
"Symbols object should be copied", nullptr, f
, Locale::getEnglish(), 12345.67, u
"12’345.67");
1678 u
"The last symbols setter wins",
1680 NumberFormatter::with().symbols(symbols
).adoptSymbols(new NumberingSystem(LATN
)),
1681 Locale::getEnglish(),
1686 u
"The last symbols setter wins",
1688 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).symbols(symbols
),
1689 Locale::getEnglish(),
1694 // TODO: Enable if/when currency symbol override is added.
1695 //void NumberFormatterTest::symbolsOverride() {
1696 // DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
1697 // dfs.setCurrencySymbol("@");
1698 // dfs.setInternationalCurrencySymbol("foo");
1699 // assertFormatSingle(
1700 // u"Custom Short Currency Symbol",
1701 // NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
1702 // Locale::getEnglish(),
1707 void NumberFormatterApiTest::sign() {
1709 u
"Sign Auto Positive",
1711 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1712 Locale::getEnglish(),
1717 u
"Sign Auto Negative",
1719 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1720 Locale::getEnglish(),
1727 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1728 Locale::getEnglish(),
1733 u
"Sign Always Positive",
1735 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1736 Locale::getEnglish(),
1741 u
"Sign Always Negative",
1743 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1744 Locale::getEnglish(),
1749 u
"Sign Always Zero",
1751 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1752 Locale::getEnglish(),
1757 u
"Sign Never Positive",
1759 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1760 Locale::getEnglish(),
1765 u
"Sign Never Negative",
1767 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1768 Locale::getEnglish(),
1775 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1776 Locale::getEnglish(),
1781 u
"Sign Accounting Positive",
1782 u
"currency/USD sign-accounting",
1783 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1784 Locale::getEnglish(),
1789 u
"Sign Accounting Negative",
1790 u
"currency/USD sign-accounting",
1791 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1792 Locale::getEnglish(),
1797 u
"Sign Accounting Zero",
1798 u
"currency/USD sign-accounting",
1799 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1800 Locale::getEnglish(),
1805 u
"Sign Accounting-Always Positive",
1806 u
"currency/USD sign-accounting-always",
1807 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1808 Locale::getEnglish(),
1813 u
"Sign Accounting-Always Negative",
1814 u
"currency/USD sign-accounting-always",
1815 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1816 Locale::getEnglish(),
1821 u
"Sign Accounting-Always Zero",
1822 u
"currency/USD sign-accounting-always",
1823 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1824 Locale::getEnglish(),
1829 u
"Sign Except-Zero Positive",
1830 u
"sign-except-zero",
1831 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1832 Locale::getEnglish(),
1837 u
"Sign Except-Zero Negative",
1838 u
"sign-except-zero",
1839 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1840 Locale::getEnglish(),
1845 u
"Sign Except-Zero Zero",
1846 u
"sign-except-zero",
1847 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
1848 Locale::getEnglish(),
1853 u
"Sign Accounting-Except-Zero Positive",
1854 u
"currency/USD sign-accounting-except-zero",
1855 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
1856 Locale::getEnglish(),
1861 u
"Sign Accounting-Except-Zero Negative",
1862 u
"currency/USD sign-accounting-except-zero",
1863 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
1864 Locale::getEnglish(),
1869 u
"Sign Accounting-Except-Zero Zero",
1870 u
"currency/USD sign-accounting-except-zero",
1871 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
1872 Locale::getEnglish(),
1877 u
"Sign Accounting Negative Hidden",
1878 u
"currency/USD unit-width-hidden sign-accounting",
1879 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
1881 .unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
1882 Locale::getEnglish(),
1887 void NumberFormatterApiTest::decimal() {
1888 assertFormatDescending(
1891 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO
),
1892 Locale::getEnglish(),
1903 assertFormatDescending(
1904 u
"Decimal Always Shown",
1906 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS
),
1907 Locale::getEnglish(),
1919 void NumberFormatterApiTest::scale() {
1920 assertFormatDescending(
1923 NumberFormatter::with().scale(Scale::none()),
1924 Locale::getEnglish(),
1935 assertFormatDescending(
1936 u
"Multiplier Power of Ten",
1938 NumberFormatter::with().scale(Scale::powerOfTen(6)),
1939 Locale::getEnglish(),
1950 assertFormatDescending(
1951 u
"Multiplier Arbitrary Double",
1953 NumberFormatter::with().scale(Scale::byDouble(5.2)),
1954 Locale::getEnglish(),
1965 assertFormatDescending(
1966 u
"Multiplier Arbitrary BigDecimal",
1968 NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
1969 Locale::getEnglish(),
1980 assertFormatDescending(
1981 u
"Multiplier Arbitrary Double And Power Of Ten",
1983 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
1984 Locale::getEnglish(),
1995 assertFormatDescending(
1998 NumberFormatter::with().scale(Scale::byDouble(0)),
1999 Locale::getEnglish(),
2011 u
"Multiplier Skeleton Scientific Notation and Percent",
2012 u
"percent scale/1E2",
2013 NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
2014 Locale::getEnglish(),
2019 u
"Negative Multiplier",
2021 NumberFormatter::with().scale(Scale::byDouble(-5.2)),
2022 Locale::getEnglish(),
2027 u
"Negative One Multiplier",
2029 NumberFormatter::with().scale(Scale::byDouble(-1)),
2030 Locale::getEnglish(),
2035 u
"Two-Type Multiplier with Overlap",
2037 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
2038 Locale::getEnglish(),
2043 void NumberFormatterApiTest::locale() {
2044 // Coverage for the locale setters.
2045 UErrorCode status
= U_ZERO_ERROR
;
2046 UnicodeString actual
= NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status
)
2048 assertEquals("Locale withLocale()", u
"1 234", actual
);
2051 void NumberFormatterApiTest::formatTypes() {
2052 UErrorCode status
= U_ZERO_ERROR
;
2053 LocalizedNumberFormatter formatter
= NumberFormatter::withLocale(Locale::getEnglish());
2056 assertEquals("Format double", "514.23", formatter
.formatDouble(514.23, status
).toString());
2059 assertEquals("Format int64", "51,423", formatter
.formatDouble(51423L, status
).toString());
2062 UnicodeString actual
= formatter
.formatDecimal("98765432123456789E1", status
).toString();
2063 assertEquals("Format decNumber", u
"987,654,321,234,567,890", actual
);
2065 // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
2066 // The number needs to have exactly 40 digits, which is the size of the default buffer.
2067 // (issue discovered by the address sanitizer in C++)
2068 static const char* str
= "0.009876543210987654321098765432109876543211";
2069 actual
= formatter
.precision(Precision::unlimited()).formatDecimal(str
, status
).toString();
2070 assertEquals("Format decNumber to 40 digits", str
, actual
);
2073 void NumberFormatterApiTest::fieldPosition() {
2074 IcuTestErrorCode
status(*this, "fieldPosition");
2075 FormattedNumber fmtd
= NumberFormatter::withLocale("en").formatDouble(-9876543210.12, status
);
2076 assertEquals("Should have expected format output", u
"-9,876,543,210.12", fmtd
.toString(status
));
2078 static const UFieldPosition expectedFieldPositions
[] = {
2079 // field, begin index, end index
2080 {UNUM_SIGN_FIELD
, 0, 1},
2081 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2082 {UNUM_GROUPING_SEPARATOR_FIELD
, 6, 7},
2083 {UNUM_GROUPING_SEPARATOR_FIELD
, 10, 11},
2084 {UNUM_INTEGER_FIELD
, 1, 14},
2085 {UNUM_DECIMAL_SEPARATOR_FIELD
, 14, 15},
2086 {UNUM_FRACTION_FIELD
, 15, 17}};
2088 FieldPositionIterator fpi
;
2089 fmtd
.getAllFieldPositions(fpi
, status
);
2091 FieldPosition actual
;
2092 while (fpi
.next(actual
)) {
2093 UFieldPosition expected
= expectedFieldPositions
[i
++];
2095 UnicodeString(u
"Field, case #") + Int64ToUnicodeString(i
),
2099 UnicodeString(u
"Iterator, begin index, case #") + Int64ToUnicodeString(i
),
2100 expected
.beginIndex
,
2101 actual
.getBeginIndex());
2103 UnicodeString(u
"Iterator, end index, case #") + Int64ToUnicodeString(i
),
2105 actual
.getEndIndex());
2107 // Check for the first location of the field
2108 if (expected
.field
!= UNUM_GROUPING_SEPARATOR_FIELD
) {
2109 FieldPosition
actual2(expected
.field
);
2110 UBool found
= fmtd
.nextFieldPosition(actual2
, status
);
2112 UnicodeString(u
"Next, found first time, case #") + Int64ToUnicodeString(i
),
2116 UnicodeString(u
"Next, begin index, case #") + Int64ToUnicodeString(i
),
2117 expected
.beginIndex
,
2118 actual2
.getBeginIndex());
2120 UnicodeString(u
"Next, end index, case #") + Int64ToUnicodeString(i
),
2122 actual2
.getEndIndex());
2123 found
= fmtd
.nextFieldPosition(actual2
, status
);
2125 UnicodeString(u
"Next, found second time, case #") + Int64ToUnicodeString(i
),
2131 "Should have seen every field position",
2132 sizeof(expectedFieldPositions
) / sizeof(*expectedFieldPositions
),
2135 // Test the iteration functionality of nextFieldPosition
2136 actual
= {UNUM_GROUPING_SEPARATOR_FIELD
};
2138 while (fmtd
.nextFieldPosition(actual
, status
)) {
2139 UFieldPosition expected
= expectedFieldPositions
[i
++];
2141 UnicodeString(u
"Next for grouping, field, case #") + Int64ToUnicodeString(i
),
2145 UnicodeString(u
"Next for grouping, begin index, case #") + Int64ToUnicodeString(i
),
2146 expected
.beginIndex
,
2147 actual
.getBeginIndex());
2149 UnicodeString(u
"Next for grouping, end index, case #") + Int64ToUnicodeString(i
),
2151 actual
.getEndIndex());
2153 assertEquals(u
"Should have seen all grouping separators", 4, i
);
2155 // Make sure strings without fraction do not contain fraction field
2156 actual
= {UNUM_FRACTION_FIELD
};
2157 fmtd
= NumberFormatter::withLocale("en").formatInt(5, status
);
2158 assertFalse(u
"No fraction part in an integer", fmtd
.nextFieldPosition(actual
, status
));
2161 void NumberFormatterApiTest::toFormat() {
2162 IcuTestErrorCode
status(*this, "icuFormat");
2163 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("fr")
2164 .precision(Precision::fixedFraction(3));
2165 LocalPointer
<Format
> format(lnf
.toFormat(status
), status
);
2166 FieldPosition
fpos(UNUM_DECIMAL_SEPARATOR_FIELD
);
2168 format
->format(514.23, sb
, fpos
, status
);
2169 assertEquals("Should correctly format number", u
"514,230", sb
);
2170 assertEquals("Should find decimal separator", 3, fpos
.getBeginIndex());
2171 assertEquals("Should find end of decimal separator", 4, fpos
.getEndIndex());
2173 "ICU Format should round-trip",
2174 lnf
.toSkeleton(status
),
2175 dynamic_cast<LocalizedNumberFormatterAsFormat
*>(format
.getAlias())->getNumberFormatter()
2176 .toSkeleton(status
));
2178 FieldPositionIterator fpi1
;
2179 lnf
.formatDouble(514.23, status
).getAllFieldPositions(fpi1
, status
);
2180 FieldPositionIterator fpi2
;
2181 format
->format(514.23, sb
.remove(), &fpi2
, status
);
2182 assertTrue("Should produce same field position iterator", fpi1
== fpi2
);
2185 void NumberFormatterApiTest::errors() {
2186 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale(Locale::getEnglish()).precision(
2187 Precision::fixedFraction(
2191 UErrorCode status
= U_ZERO_ERROR
;
2192 FormattedNumber fn
= lnf
.formatInt(1, status
);
2194 "Should fail in formatInt method with error code for rounding",
2195 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2199 status
= U_ZERO_ERROR
;
2200 fn
= lnf
.formatDouble(1.0, status
);
2202 "Should fail in formatDouble method with error code for rounding",
2203 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2206 // formatDecimal (decimal error)
2207 status
= U_ZERO_ERROR
;
2208 fn
= NumberFormatter::withLocale("en").formatDecimal("1x2", status
);
2210 "Should fail in formatDecimal method with error code for decimal number syntax",
2211 U_DECIMAL_NUMBER_SYNTAX_ERROR
,
2214 // formatDecimal (setting error)
2215 status
= U_ZERO_ERROR
;
2216 fn
= lnf
.formatDecimal("1.0", status
);
2218 "Should fail in formatDecimal method with error code for rounding",
2219 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2223 status
= U_ZERO_ERROR
;
2224 UnicodeString output
= lnf
.toSkeleton(status
);
2226 "Should fail on toSkeleton terminal method with correct error code",
2227 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2230 "Terminal toSkeleton on error object should be bogus",
2234 status
= U_ZERO_ERROR
;
2236 fn
.populateFieldPosition(fp
, status
);
2238 "Should fail on FieldPosition terminal method with correct error code",
2239 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2242 // FieldPositionIterator
2243 status
= U_ZERO_ERROR
;
2244 FieldPositionIterator fpi
;
2245 fn
.populateFieldPositionIterator(fpi
, status
);
2247 "Should fail on FieldPositoinIterator terminal method with correct error code",
2248 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2252 status
= U_ZERO_ERROR
;
2253 UnicodeStringAppendable
appendable(output
.remove());
2254 fn
.appendTo(appendable
, status
);
2256 "Should fail on Appendable terminal method with correct error code",
2257 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2261 status
= U_ZERO_ERROR
;
2262 output
= fn
.toString(status
);
2264 "Should fail on UnicodeString terminal method with correct error code",
2265 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2268 "Terminal UnicodeString on error object should be bogus",
2272 status
= U_ZERO_ERROR
;
2273 lnf
.copyErrorTo(status
);
2275 "Should fail since rounder is not legal with correct error code",
2276 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2280 void NumberFormatterApiTest::validRanges() {
2282 #define EXPECTED_MAX_INT_FRAC_SIG 999
2284 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) { \
2285 UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
2287 : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
2289 UnicodeString(u"Incorrect status for " #method " on input ") \
2290 + Int64ToUnicodeString(argument), \
2295 #define VALID_RANGE_ONEARG(setting, method, lowerBound) { \
2296 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2297 UErrorCode status = U_ZERO_ERROR; \
2298 NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
2299 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2303 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) { \
2304 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2305 UErrorCode status = U_ZERO_ERROR; \
2306 /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
2307 NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
2308 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2309 status = U_ZERO_ERROR; \
2310 /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
2311 NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
2312 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2313 /* Check that first argument must be less than or equal to second argument */ \
2314 NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
2315 assertEquals("Incorrect status for " #method " on max < min input", \
2316 U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
2321 VALID_RANGE_ONEARG(rounding
, Precision::fixedFraction
, 0);
2322 VALID_RANGE_ONEARG(rounding
, Precision::minFraction
, 0);
2323 VALID_RANGE_ONEARG(rounding
, Precision::maxFraction
, 0);
2324 VALID_RANGE_TWOARGS(rounding
, Precision::minMaxFraction
, 0);
2325 VALID_RANGE_ONEARG(rounding
, Precision::fixedSignificantDigits
, 1);
2326 VALID_RANGE_ONEARG(rounding
, Precision::minSignificantDigits
, 1);
2327 VALID_RANGE_ONEARG(rounding
, Precision::maxSignificantDigits
, 1);
2328 VALID_RANGE_TWOARGS(rounding
, Precision::minMaxSignificantDigits
, 1);
2329 VALID_RANGE_ONEARG(rounding
, Precision::fixedFraction(1).withMinDigits
, 1);
2330 VALID_RANGE_ONEARG(rounding
, Precision::fixedFraction(1).withMaxDigits
, 1);
2331 VALID_RANGE_ONEARG(notation
, Notation::scientific().withMinExponentDigits
, 1);
2332 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo
, 0);
2333 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo(0).truncateAt
, -1);
2336 void NumberFormatterApiTest::copyMove() {
2337 IcuTestErrorCode
status(*this, "copyMove");
2339 // Default constructors
2340 LocalizedNumberFormatter l1
;
2341 assertEquals("Initial behavior", u
"10", l1
.formatInt(10, status
).toString(), true);
2342 if (status
.errDataIfFailureAndReset()) { return; }
2343 assertEquals("Initial call count", 1, l1
.getCallCount());
2344 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2347 l1
= NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
2348 assertEquals("Initial behavior", u
"10%", l1
.formatInt(10, status
).toString());
2349 assertEquals("Initial call count", 1, l1
.getCallCount());
2350 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2351 l1
.formatInt(123, status
);
2352 assertEquals("Still not compiled", 2, l1
.getCallCount());
2353 assertTrue("Still not compiled", l1
.getCompiled() == nullptr);
2354 l1
.formatInt(123, status
);
2355 assertEquals("Compiled", u
"10%", l1
.formatInt(10, status
).toString());
2356 assertEquals("Compiled", INT32_MIN
, l1
.getCallCount());
2357 assertTrue("Compiled", l1
.getCompiled() != nullptr);
2360 LocalizedNumberFormatter l2
= l1
;
2361 assertEquals("[constructor] Copy behavior", u
"10%", l2
.formatInt(10, status
).toString());
2362 assertEquals("[constructor] Copy should not have compiled state", 1, l2
.getCallCount());
2363 assertTrue("[constructor] Copy should not have compiled state", l2
.getCompiled() == nullptr);
2366 LocalizedNumberFormatter l3
= std::move(l1
);
2367 assertEquals("[constructor] Move behavior", u
"10%", l3
.formatInt(10, status
).toString());
2368 assertEquals("[constructor] Move *should* have compiled state", INT32_MIN
, l3
.getCallCount());
2369 assertTrue("[constructor] Move *should* have compiled state", l3
.getCompiled() != nullptr);
2370 assertEquals("[constructor] Source should be reset after move", 0, l1
.getCallCount());
2371 assertTrue("[constructor] Source should be reset after move", l1
.getCompiled() == nullptr);
2373 // Reset l1 and l2 to check for macro-props copying for behavior testing
2374 l1
= NumberFormatter::withLocale("en");
2375 l2
= NumberFormatter::withLocale("en");
2379 assertEquals("[assignment] Copy behavior", u
"10%", l1
.formatInt(10, status
).toString());
2380 assertEquals("[assignment] Copy should not have compiled state", 1, l1
.getCallCount());
2381 assertTrue("[assignment] Copy should not have compiled state", l1
.getCompiled() == nullptr);
2385 assertEquals("[assignment] Move behavior", u
"10%", l2
.formatInt(10, status
).toString());
2386 assertEquals("[assignment] Move *should* have compiled state", INT32_MIN
, l2
.getCallCount());
2387 assertTrue("[assignment] Move *should* have compiled state", l2
.getCompiled() != nullptr);
2388 assertEquals("[assignment] Source should be reset after move", 0, l3
.getCallCount());
2389 assertTrue("[assignment] Source should be reset after move", l3
.getCompiled() == nullptr);
2391 // Coverage tests for UnlocalizedNumberFormatter
2392 UnlocalizedNumberFormatter u1
;
2393 assertEquals("Default behavior", u
"10", u1
.locale("en").formatInt(10, status
).toString());
2394 u1
= u1
.unit(NoUnit::percent());
2395 assertEquals("Copy assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString());
2396 UnlocalizedNumberFormatter u2
= u1
;
2397 assertEquals("Copy constructor", u
"10%", u2
.locale("en").formatInt(10, status
).toString());
2398 UnlocalizedNumberFormatter u3
= std::move(u1
);
2399 assertEquals("Move constructor", u
"10%", u3
.locale("en").formatInt(10, status
).toString());
2400 u1
= NumberFormatter::with();
2402 assertEquals("Move assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString());
2404 // FormattedNumber move operators
2405 FormattedNumber result
= l1
.formatInt(10, status
);
2406 assertEquals("FormattedNumber move constructor", u
"10%", result
.toString());
2407 result
= l1
.formatInt(20, status
);
2408 assertEquals("FormattedNumber move assignment", u
"20%", result
.toString());
2411 void NumberFormatterApiTest::localPointerCAPI() {
2412 // NOTE: This is also the sample code in unumberformatter.h
2413 UErrorCode ec
= U_ZERO_ERROR
;
2416 LocalUNumberFormatterPointer
uformatter(unumf_openForSkeletonAndLocale(u
"percent", -1, "en", &ec
));
2417 LocalUFormattedNumberPointer
uresult(unumf_openResult(&ec
));
2418 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2420 // Format a decimal number:
2421 unumf_formatDecimal(uformatter
.getAlias(), "9.87E-3", -1, uresult
.getAlias(), &ec
);
2422 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2424 // Get the location of the percent sign:
2425 UFieldPosition ufpos
= {UNUM_PERCENT_FIELD
, 0, 0};
2426 unumf_resultNextFieldPosition(uresult
.getAlias(), &ufpos
, &ec
);
2427 assertEquals("Percent sign location within '0.00987%'", 7, ufpos
.beginIndex
);
2428 assertEquals("Percent sign location within '0.00987%'", 8, ufpos
.endIndex
);
2430 // No need to do any cleanup since we are using LocalPointer.
2434 void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage
, const char16_t* uskeleton
,
2435 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2438 va_start(args
, locale
);
2439 UnicodeString
message(TRUE
, umessage
, -1);
2440 static double inputs
[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
2441 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2442 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2443 IcuTestErrorCode
status(*this, "assertFormatDescending");
2444 status
.setScope(message
);
2445 UnicodeString expecteds
[10];
2446 for (int16_t i
= 0; i
< 9; i
++) {
2447 char16_t caseNumber
= u
'0' + i
;
2448 double d
= inputs
[i
];
2449 UnicodeString expected
= va_arg(args
, const char16_t*);
2450 expecteds
[i
] = expected
;
2451 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString();
2452 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
2453 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
2454 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString();
2455 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
2456 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
2458 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2459 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2460 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2461 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
2462 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
2463 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
2464 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
2465 for (int32_t i
= 0; i
< 9; i
++) {
2466 double d
= inputs
[i
];
2467 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString();
2468 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
2471 assertUndefinedSkeleton(f
);
2475 void NumberFormatterApiTest::assertFormatDescendingBig(const char16_t* umessage
, const char16_t* uskeleton
,
2476 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2479 va_start(args
, locale
);
2480 UnicodeString
message(TRUE
, umessage
, -1);
2481 static double inputs
[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
2482 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2483 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2484 IcuTestErrorCode
status(*this, "assertFormatDescendingBig");
2485 status
.setScope(message
);
2486 UnicodeString expecteds
[10];
2487 for (int16_t i
= 0; i
< 9; i
++) {
2488 char16_t caseNumber
= u
'0' + i
;
2489 double d
= inputs
[i
];
2490 UnicodeString expected
= va_arg(args
, const char16_t*);
2491 expecteds
[i
] = expected
;
2492 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString();
2493 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
2494 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
2495 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString();
2496 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
2497 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
2499 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2500 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2501 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2502 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
2503 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
2504 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
2505 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
2506 for (int32_t i
= 0; i
< 9; i
++) {
2507 double d
= inputs
[i
];
2508 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString();
2509 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
2512 assertUndefinedSkeleton(f
);
2516 void NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage
, const char16_t* uskeleton
,
2517 const UnlocalizedNumberFormatter
& f
, Locale locale
,
2518 double input
, const UnicodeString
& expected
) {
2519 UnicodeString
message(TRUE
, umessage
, -1);
2520 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
2521 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
2522 IcuTestErrorCode
status(*this, "assertFormatSingle");
2523 status
.setScope(message
);
2524 UnicodeString actual1
= l1
.formatDouble(input
, status
).toString();
2525 assertSuccess(message
+ u
": Unsafe Path", status
);
2526 assertEquals(message
+ u
": Unsafe Path", expected
, actual1
);
2527 UnicodeString actual2
= l2
.formatDouble(input
, status
).toString();
2528 assertSuccess(message
+ u
": Safe Path", status
);
2529 assertEquals(message
+ u
": Safe Path", expected
, actual2
);
2530 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
2531 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
2532 // Only compare normalized skeletons: the tests need not provide the normalized forms.
2533 // Use the normalized form to construct the testing formatter to ensure no loss of info.
2534 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
2535 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
2536 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
2537 UnicodeString actual3
= l3
.formatDouble(input
, status
).toString();
2538 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + input
, expected
, actual3
);
2540 assertUndefinedSkeleton(f
);
2544 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter
& f
) {
2545 UErrorCode status
= U_ZERO_ERROR
;
2546 UnicodeString skeleton
= f
.toSkeleton(status
);
2548 u
"Expect toSkeleton to fail, but passed, producing: " + skeleton
,
2549 U_UNSUPPORTED_ERROR
,
2553 #endif /* #if !UCONFIG_NO_FORMATTING */