• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 #include <cmath>
9 #include <cstdarg>
10 #include <memory>
11 
12 #include "unicode/displayoptions.h"
13 #include "unicode/numberformatter.h"
14 #include "unicode/testlog.h"
15 #include "unicode/unum.h"
16 #include "unicode/utypes.h"
17 
18 #include "charstr.h"
19 #include "number_asformat.h"
20 #include "number_microprops.h"
21 #include "number_types.h"
22 #include "number_utils.h"
23 #include "number_utypes.h"
24 #include "numbertest.h"
25 
26 using number::impl::UFormattedNumberData;
27 
28 // Horrible workaround for the lack of a status code in the constructor...
29 // (Also affects numbertest_range.cpp)
30 UErrorCode globalNumberFormatterApiTestStatus = U_ZERO_ERROR;
31 
NumberFormatterApiTest()32 NumberFormatterApiTest::NumberFormatterApiTest()
33         : NumberFormatterApiTest(globalNumberFormatterApiTestStatus) {
34 }
35 
NumberFormatterApiTest(UErrorCode & status)36 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode& status)
37         : USD(u"USD", status),
38           GBP(u"GBP", status),
39           CZK(u"CZK", status),
40           CAD(u"CAD", status),
41           ESP(u"ESP", status),
42           PTE(u"PTE", status),
43           RON(u"RON", status),
44           TWD(u"TWD", status),
45           TRY(u"TRY", status),
46           CNY(u"CNY", status),
47           FRENCH_SYMBOLS(Locale::getFrench(), status),
48           SWISS_SYMBOLS(Locale("de-CH"), status),
49           MYANMAR_SYMBOLS(Locale("my"), status) {
50 
51     // Check for error on the first MeasureUnit in case there is no data
52     LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
53     if (U_FAILURE(status)) {
54         dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
55         return;
56     }
57     METER = *unit;
58 
59     METER_PER_SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createMeterPerSecond(status));
60     DAY = *LocalPointer<MeasureUnit>(MeasureUnit::createDay(status));
61     SQUARE_METER = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMeter(status));
62     FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
63     SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createSecond(status));
64     POUND = *LocalPointer<MeasureUnit>(MeasureUnit::createPound(status));
65     POUND_FORCE = *LocalPointer<MeasureUnit>(MeasureUnit::createPoundForce(status));
66     SQUARE_MILE = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMile(status));
67     SQUARE_INCH = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareInch(status));
68     JOULE = *LocalPointer<MeasureUnit>(MeasureUnit::createJoule(status));
69     FURLONG = *LocalPointer<MeasureUnit>(MeasureUnit::createFurlong(status));
70     KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
71 
72     MATHSANB = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("mathsanb", status));
73     LATN = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("latn", status));
74 }
75 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)76 void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
77     if (exec) {
78         logln("TestSuite NumberFormatterApiTest: ");
79     }
80     TESTCASE_AUTO_BEGIN;
81         TESTCASE_AUTO(notationSimple);
82         TESTCASE_AUTO(notationScientific);
83         TESTCASE_AUTO(notationCompact);
84         TESTCASE_AUTO(unitMeasure);
85         TESTCASE_AUTO(unitCompoundMeasure);
86         TESTCASE_AUTO(unitArbitraryMeasureUnits);
87         TESTCASE_AUTO(unitSkeletons);
88         TESTCASE_AUTO(unitUsage);
89         TESTCASE_AUTO(unitUsageErrorCodes);
90         TESTCASE_AUTO(unitUsageSkeletons);
91         TESTCASE_AUTO(unitCurrency);
92         TESTCASE_AUTO(unitInflections);
93         TESTCASE_AUTO(unitNounClass);
94         TESTCASE_AUTO(unitNotConvertible);
95         TESTCASE_AUTO(unitPercent);
96         TESTCASE_AUTO(unitLocaleTags);
97         if (!quick) {
98             // Slow test: run in exhaustive mode only
99             TESTCASE_AUTO(percentParity);
100         }
101         TESTCASE_AUTO(roundingFraction);
102         TESTCASE_AUTO(roundingFigures);
103         TESTCASE_AUTO(roundingFractionFigures);
104         TESTCASE_AUTO(roundingOther);
105         TESTCASE_AUTO(roundingIncrementRegressionTest);
106         TESTCASE_AUTO(roundingPriorityCoverageTest);
107         TESTCASE_AUTO(grouping);
108         TESTCASE_AUTO(padding);
109         TESTCASE_AUTO(integerWidth);
110         TESTCASE_AUTO(symbols);
111         // TODO: Add this method if currency symbols override support is added.
112         //TESTCASE_AUTO(symbolsOverride);
113         TESTCASE_AUTO(sign);
114         TESTCASE_AUTO(signNearZero);
115         TESTCASE_AUTO(signCoverage);
116         TESTCASE_AUTO(decimal);
117         TESTCASE_AUTO(scale);
118         TESTCASE_AUTO(locale);
119         TESTCASE_AUTO(skeletonUserGuideExamples);
120         TESTCASE_AUTO(formatTypes);
121         TESTCASE_AUTO(fieldPositionLogic);
122         TESTCASE_AUTO(fieldPositionCoverage);
123         TESTCASE_AUTO(toFormat);
124         TESTCASE_AUTO(errors);
125         if (!quick) {
126             // Slow test: run in exhaustive mode only
127             // (somewhat slow to check all permutations of settings)
128             TESTCASE_AUTO(validRanges);
129         }
130         TESTCASE_AUTO(copyMove);
131         TESTCASE_AUTO(localPointerCAPI);
132         TESTCASE_AUTO(toObject);
133         TESTCASE_AUTO(toDecimalNumber);
134         TESTCASE_AUTO(microPropsInternals);
135         TESTCASE_AUTO(formatUnitsAliases);
136         TESTCASE_AUTO(testIssue22378);
137     TESTCASE_AUTO_END;
138 }
139 
notationSimple()140 void NumberFormatterApiTest::notationSimple() {
141     assertFormatDescending(
142             u"Basic",
143             u"",
144             u"",
145             NumberFormatter::with(),
146             Locale::getEnglish(),
147             u"87,650",
148             u"8,765",
149             u"876.5",
150             u"87.65",
151             u"8.765",
152             u"0.8765",
153             u"0.08765",
154             u"0.008765",
155             u"0");
156 
157     assertFormatDescendingBig(
158             u"Big Simple",
159             u"notation-simple",
160             u"",
161             NumberFormatter::with().notation(Notation::simple()),
162             Locale::getEnglish(),
163             u"87,650,000",
164             u"8,765,000",
165             u"876,500",
166             u"87,650",
167             u"8,765",
168             u"876.5",
169             u"87.65",
170             u"8.765",
171             u"0");
172 
173     assertFormatSingle(
174             u"Basic with Negative Sign",
175             u"",
176             u"",
177             NumberFormatter::with(),
178             Locale::getEnglish(),
179             -9876543.21,
180             u"-9,876,543.21");
181 }
182 
183 
notationScientific()184 void NumberFormatterApiTest::notationScientific() {
185     assertFormatDescending(
186             u"Scientific",
187             u"scientific",
188             u"E0",
189             NumberFormatter::with().notation(Notation::scientific()),
190             Locale::getEnglish(),
191             u"8.765E4",
192             u"8.765E3",
193             u"8.765E2",
194             u"8.765E1",
195             u"8.765E0",
196             u"8.765E-1",
197             u"8.765E-2",
198             u"8.765E-3",
199             u"0E0");
200 
201     assertFormatDescending(
202             u"Engineering",
203             u"engineering",
204             u"EE0",
205             NumberFormatter::with().notation(Notation::engineering()),
206             Locale::getEnglish(),
207             u"87.65E3",
208             u"8.765E3",
209             u"876.5E0",
210             u"87.65E0",
211             u"8.765E0",
212             u"876.5E-3",
213             u"87.65E-3",
214             u"8.765E-3",
215             u"0E0");
216 
217     assertFormatDescending(
218             u"Scientific sign always shown",
219             u"scientific/sign-always",
220             u"E+!0",
221             NumberFormatter::with().notation(
222                     Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)),
223             Locale::getEnglish(),
224             u"8.765E+4",
225             u"8.765E+3",
226             u"8.765E+2",
227             u"8.765E+1",
228             u"8.765E+0",
229             u"8.765E-1",
230             u"8.765E-2",
231             u"8.765E-3",
232             u"0E+0");
233 
234     assertFormatDescending(
235             u"Scientific min exponent digits",
236             u"scientific/*ee",
237             u"E00",
238             NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
239             Locale::getEnglish(),
240             u"8.765E04",
241             u"8.765E03",
242             u"8.765E02",
243             u"8.765E01",
244             u"8.765E00",
245             u"8.765E-01",
246             u"8.765E-02",
247             u"8.765E-03",
248             u"0E00");
249 
250     assertFormatSingle(
251             u"Scientific Negative",
252             u"scientific",
253             u"E0",
254             NumberFormatter::with().notation(Notation::scientific()),
255             Locale::getEnglish(),
256             -1000000,
257             u"-1E6");
258 
259     assertFormatSingle(
260             u"Scientific Infinity",
261             u"scientific",
262             u"E0",
263             NumberFormatter::with().notation(Notation::scientific()),
264             Locale::getEnglish(),
265             -uprv_getInfinity(),
266             u"-∞");
267 
268     assertFormatSingle(
269             u"Scientific NaN",
270             u"scientific",
271             u"E0",
272             NumberFormatter::with().notation(Notation::scientific()),
273             Locale::getEnglish(),
274             uprv_getNaN(),
275             u"NaN");
276 }
277 
notationCompact()278 void NumberFormatterApiTest::notationCompact() {
279     assertFormatDescending(
280             u"Compact Short",
281             u"compact-short",
282             u"K",
283             NumberFormatter::with().notation(Notation::compactShort()),
284             Locale::getEnglish(),
285             u"88K",
286             u"8.8K",
287             u"876",
288             u"88",
289             u"8.8",
290             u"0.88",
291             u"0.088",
292             u"0.0088",
293             u"0");
294 
295     assertFormatDescending(
296             u"Compact Long",
297             u"compact-long",
298             u"KK",
299             NumberFormatter::with().notation(Notation::compactLong()),
300             Locale::getEnglish(),
301             u"88 thousand",
302             u"8.8 thousand",
303             u"876",
304             u"88",
305             u"8.8",
306             u"0.88",
307             u"0.088",
308             u"0.0088",
309             u"0");
310 
311     assertFormatDescending(
312             u"Compact Short Currency",
313             u"compact-short currency/USD",
314             u"K currency/USD",
315             NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
316             Locale::getEnglish(),
317             u"$88K",
318             u"$8.8K",
319             u"$876",
320             u"$88",
321             u"$8.8",
322             u"$0.88",
323             u"$0.088",
324             u"$0.0088",
325             u"$0");
326 
327     assertFormatDescending(
328             u"Compact Short with ISO Currency",
329             u"compact-short currency/USD unit-width-iso-code",
330             u"K currency/USD unit-width-iso-code",
331             NumberFormatter::with().notation(Notation::compactShort())
332                     .unit(USD)
333                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
334             Locale::getEnglish(),
335             u"USD 88K",
336             u"USD 8.8K",
337             u"USD 876",
338             u"USD 88",
339             u"USD 8.8",
340             u"USD 0.88",
341             u"USD 0.088",
342             u"USD 0.0088",
343             u"USD 0");
344 
345     assertFormatDescending(
346             u"Compact Short with Long Name Currency",
347             u"compact-short currency/USD unit-width-full-name",
348             u"K currency/USD unit-width-full-name",
349             NumberFormatter::with().notation(Notation::compactShort())
350                     .unit(USD)
351                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
352             Locale::getEnglish(),
353             u"88K US dollars",
354             u"8.8K US dollars",
355             u"876 US dollars",
356             u"88 US dollars",
357             u"8.8 US dollars",
358             u"0.88 US dollars",
359             u"0.088 US dollars",
360             u"0.0088 US dollars",
361             u"0 US dollars");
362 
363     // Note: Most locales don't have compact long currency, so this currently falls back to short.
364     // This test case should be fixed when proper compact long currency patterns are added.
365     assertFormatDescending(
366             u"Compact Long Currency",
367             u"compact-long currency/USD",
368             u"KK currency/USD",
369             NumberFormatter::with().notation(Notation::compactLong()).unit(USD),
370             Locale::getEnglish(),
371             u"$88K", // should be something like "$88 thousand"
372             u"$8.8K",
373             u"$876",
374             u"$88",
375             u"$8.8",
376             u"$0.88",
377             u"$0.088",
378             u"$0.0088",
379             u"$0");
380 
381     // Note: Most locales don't have compact long currency, so this currently falls back to short.
382     // This test case should be fixed when proper compact long currency patterns are added.
383     assertFormatDescending(
384             u"Compact Long with ISO Currency",
385             u"compact-long currency/USD unit-width-iso-code",
386             u"KK currency/USD unit-width-iso-code",
387             NumberFormatter::with().notation(Notation::compactLong())
388                     .unit(USD)
389                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
390             Locale::getEnglish(),
391             u"USD 88K", // should be something like "USD 88 thousand"
392             u"USD 8.8K",
393             u"USD 876",
394             u"USD 88",
395             u"USD 8.8",
396             u"USD 0.88",
397             u"USD 0.088",
398             u"USD 0.0088",
399             u"USD 0");
400 
401     // TODO: This behavior could be improved and should be revisited.
402     assertFormatDescending(
403             u"Compact Long with Long Name Currency",
404             u"compact-long currency/USD unit-width-full-name",
405             u"KK currency/USD unit-width-full-name",
406             NumberFormatter::with().notation(Notation::compactLong())
407                     .unit(USD)
408                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
409             Locale::getEnglish(),
410             u"88 thousand US dollars",
411             u"8.8 thousand US dollars",
412             u"876 US dollars",
413             u"88 US dollars",
414             u"8.8 US dollars",
415             u"0.88 US dollars",
416             u"0.088 US dollars",
417             u"0.0088 US dollars",
418             u"0 US dollars");
419 
420     assertFormatSingle(
421             u"Compact Plural One",
422             u"compact-long",
423             u"KK",
424             NumberFormatter::with().notation(Notation::compactLong()),
425             Locale::createFromName("es"),
426             1000000,
427             u"1 millón");
428 
429     assertFormatSingle(
430             u"Compact Plural Other",
431             u"compact-long",
432             u"KK",
433             NumberFormatter::with().notation(Notation::compactLong()),
434             Locale::createFromName("es"),
435             2000000,
436             u"2 millones");
437 
438     assertFormatSingle(
439             u"Compact with Negative Sign",
440             u"compact-short",
441             u"K",
442             NumberFormatter::with().notation(Notation::compactShort()),
443             Locale::getEnglish(),
444             -9876543.21,
445             u"-9.9M");
446 
447     assertFormatSingle(
448             u"Compact Rounding",
449             u"compact-short",
450             u"K",
451             NumberFormatter::with().notation(Notation::compactShort()),
452             Locale::getEnglish(),
453             990000,
454             u"990K");
455 
456     assertFormatSingle(
457             u"Compact Rounding",
458             u"compact-short",
459             u"K",
460             NumberFormatter::with().notation(Notation::compactShort()),
461             Locale::getEnglish(),
462             999000,
463             u"999K");
464 
465     assertFormatSingle(
466             u"Compact Rounding",
467             u"compact-short",
468             u"K",
469             NumberFormatter::with().notation(Notation::compactShort()),
470             Locale::getEnglish(),
471             999900,
472             u"1M");
473 
474     assertFormatSingle(
475             u"Compact Rounding",
476             u"compact-short",
477             u"K",
478             NumberFormatter::with().notation(Notation::compactShort()),
479             Locale::getEnglish(),
480             9900000,
481             u"9.9M");
482 
483     assertFormatSingle(
484             u"Compact Rounding",
485             u"compact-short",
486             u"K",
487             NumberFormatter::with().notation(Notation::compactShort()),
488             Locale::getEnglish(),
489             9990000,
490             u"10M");
491 
492     assertFormatSingle(
493             u"Compact in zh-Hant-HK",
494             u"compact-short",
495             u"K",
496             NumberFormatter::with().notation(Notation::compactShort()),
497             Locale("zh-Hant-HK"),
498             1e7,
499             u"10M");
500 
501     assertFormatSingle(
502             u"Compact in zh-Hant",
503             u"compact-short",
504             u"K",
505             NumberFormatter::with().notation(Notation::compactShort()),
506             Locale("zh-Hant"),
507             1e7,
508             u"1000\u842C");
509 
510     assertFormatSingle(
511             u"Compact with plural form =1 (ICU-21258)",
512             u"compact-long",
513             u"KK",
514             NumberFormatter::with().notation(Notation::compactLong()),
515             Locale("fr-FR"),
516             1e3,
517             u"mille");
518 
519     assertFormatSingle(
520             u"Compact Infinity",
521             u"compact-short",
522             u"K",
523             NumberFormatter::with().notation(Notation::compactShort()),
524             Locale::getEnglish(),
525             -uprv_getInfinity(),
526             u"-∞");
527 
528     assertFormatSingle(
529             u"Compact NaN",
530             u"compact-short",
531             u"K",
532             NumberFormatter::with().notation(Notation::compactShort()),
533             Locale::getEnglish(),
534             uprv_getNaN(),
535             u"NaN");
536 
537     // NOTE: There is no API for compact custom data in C++
538     // and thus no "Compact Somali No Figure" test
539 }
540 
unitMeasure()541 void NumberFormatterApiTest::unitMeasure() {
542     IcuTestErrorCode status(*this, "unitMeasure()");
543 
544     assertFormatDescending(
545             u"Meters Short and unit() method",
546             u"measure-unit/length-meter",
547             u"unit/meter",
548             NumberFormatter::with().unit(MeasureUnit::getMeter()),
549             Locale::getEnglish(),
550             u"87,650 m",
551             u"8,765 m",
552             u"876.5 m",
553             u"87.65 m",
554             u"8.765 m",
555             u"0.8765 m",
556             u"0.08765 m",
557             u"0.008765 m",
558             u"0 m");
559 
560     assertFormatDescending(
561             u"Meters Long and adoptUnit() method",
562             u"measure-unit/length-meter unit-width-full-name",
563             u"unit/meter unit-width-full-name",
564             NumberFormatter::with().adoptUnit(new MeasureUnit(METER))
565                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
566             Locale::getEnglish(),
567             u"87,650 meters",
568             u"8,765 meters",
569             u"876.5 meters",
570             u"87.65 meters",
571             u"8.765 meters",
572             u"0.8765 meters",
573             u"0.08765 meters",
574             u"0.008765 meters",
575             u"0 meters");
576 
577     assertFormatDescending(
578             u"Compact Meters Long",
579             u"compact-long measure-unit/length-meter unit-width-full-name",
580             u"KK unit/meter unit-width-full-name",
581             NumberFormatter::with().notation(Notation::compactLong())
582                     .unit(METER)
583                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
584             Locale::getEnglish(),
585             u"88 thousand meters",
586             u"8.8 thousand meters",
587             u"876 meters",
588             u"88 meters",
589             u"8.8 meters",
590             u"0.88 meters",
591             u"0.088 meters",
592             u"0.0088 meters",
593             u"0 meters");
594 
595     assertFormatDescending(
596             u"Hectometers",
597             u"unit/hectometer",
598             u"unit/hectometer",
599             NumberFormatter::with().unit(MeasureUnit::forIdentifier("hectometer", status)),
600             Locale::getEnglish(),
601             u"87,650 hm",
602             u"8,765 hm",
603             u"876.5 hm",
604             u"87.65 hm",
605             u"8.765 hm",
606             u"0.8765 hm",
607             u"0.08765 hm",
608             u"0.008765 hm",
609             u"0 hm");
610 
611 //    TODO: Implement Measure in C++
612 //    assertFormatSingleMeasure(
613 //            u"Meters with Measure Input",
614 //            NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
615 //            Locale::getEnglish(),
616 //            new Measure(5.43, new MeasureUnit(METER)),
617 //            u"5.43 meters");
618 
619 //    TODO: Implement Measure in C++
620 //    assertFormatSingleMeasure(
621 //            u"Measure format method takes precedence over fluent chain",
622 //            NumberFormatter::with().unit(METER),
623 //            Locale::getEnglish(),
624 //            new Measure(5.43, USD),
625 //            u"$5.43");
626 
627     assertFormatSingle(
628             u"Meters with Negative Sign",
629             u"measure-unit/length-meter",
630             u"unit/meter",
631             NumberFormatter::with().unit(METER),
632             Locale::getEnglish(),
633             -9876543.21,
634             u"-9,876,543.21 m");
635 
636     // The locale string "सान" appears only in brx.txt:
637     assertFormatSingle(
638             u"Interesting Data Fallback 1",
639             u"measure-unit/duration-day unit-width-full-name",
640             u"unit/day unit-width-full-name",
641             NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
642             Locale::createFromName("brx"),
643             5.43,
644             u"5.43 सान");
645 
646     // Requires following the alias from unitsNarrow to unitsShort:
647     assertFormatSingle(
648             u"Interesting Data Fallback 2",
649             u"measure-unit/duration-day unit-width-narrow",
650             u"unit/day unit-width-narrow",
651             NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
652             Locale::createFromName("brx"),
653             5.43,
654             u"5.43 d");
655 
656     // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
657     // requiring fallback to the root.
658     assertFormatSingle(
659             u"Interesting Data Fallback 3",
660             u"measure-unit/area-square-meter unit-width-narrow",
661             u"unit/square-meter unit-width-narrow",
662             NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
663             Locale::createFromName("en-GB"),
664             5.43,
665             u"5.43m²");
666 
667     // Try accessing a narrow unit directly from root.
668     assertFormatSingle(
669             u"Interesting Data Fallback 4",
670             u"measure-unit/area-square-meter unit-width-narrow",
671             u"unit/square-meter unit-width-narrow",
672             NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
673             Locale::createFromName("root"),
674             5.43,
675             u"5.43 m²");
676 
677     // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
678     // NOTE: This example is in the documentation.
679     assertFormatSingle(
680             u"Difference between Narrow and Short (Narrow Version)",
681             u"measure-unit/temperature-fahrenheit unit-width-narrow",
682             u"unit/fahrenheit unit-width-narrow",
683             NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_NARROW),
684             Locale("es-US"),
685             5.43,
686             u"5.43°");
687 
688     assertFormatSingle(
689             u"Difference between Narrow and Short (Short Version)",
690             u"measure-unit/temperature-fahrenheit unit-width-short",
691             u"unit/fahrenheit unit-width-short",
692             NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_SHORT),
693             Locale("es-US"),
694             5.43,
695             u"5.43 °F");
696 
697     assertFormatSingle(
698             u"MeasureUnit form without {0} in CLDR pattern",
699             u"measure-unit/temperature-kelvin unit-width-full-name",
700             u"unit/kelvin unit-width-full-name",
701             NumberFormatter::with().unit(KELVIN).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
702             Locale("es-MX"),
703             1,
704             u"kelvin");
705 
706     assertFormatSingle(
707             u"MeasureUnit form without {0} in CLDR pattern and wide base form",
708             u"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
709             u"unit/kelvin .00000000000000000000 unit-width-full-name",
710             NumberFormatter::with().precision(Precision::fixedFraction(20))
711                     .unit(KELVIN)
712                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
713             Locale("es-MX"),
714             1,
715             u"kelvin");
716 
717     assertFormatSingle(
718             u"Person unit not in short form",
719             u"measure-unit/duration-year-person unit-width-full-name",
720             u"unit/year-person unit-width-full-name",
721             NumberFormatter::with().unit(MeasureUnit::getYearPerson())
722                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
723             Locale("es-MX"),
724             5,
725             u"5 a\u00F1os");
726 
727     assertFormatSingle(
728             u"Hubble Constant - usually expressed in km/s/Mpc",
729             u"unit/kilometer-per-megaparsec-second",
730             u"unit/kilometer-per-megaparsec-second",
731             NumberFormatter::with().unit(MeasureUnit::forIdentifier("kilometer-per-second-per-megaparsec", status)),
732             Locale("en"),
733             74, // Approximate 2019-03-18 measurement
734             u"74 km/Mpc⋅sec");
735 
736     assertFormatSingle(
737             u"Mixed unit",
738             u"unit/yard-and-foot-and-inch",
739             u"unit/yard-and-foot-and-inch",
740             NumberFormatter::with()
741                 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)),
742             Locale("en-US"),
743             3.65,
744             "3 yd, 1 ft, 11.4 in");
745 
746     assertFormatSingle(
747             u"Mixed unit, Scientific",
748             u"unit/yard-and-foot-and-inch E0",
749             u"unit/yard-and-foot-and-inch E0",
750             NumberFormatter::with()
751                 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status))
752                 .notation(Notation::scientific()),
753             Locale("en-US"),
754             3.65,
755             "3 yd, 1 ft, 1.14E1 in");
756 
757     assertFormatSingle(
758             u"Mixed Unit (Narrow Version)",
759             u"unit/tonne-and-kilogram-and-gram unit-width-narrow",
760             u"unit/tonne-and-kilogram-and-gram unit-width-narrow",
761             NumberFormatter::with()
762                 .unit(MeasureUnit::forIdentifier("tonne-and-kilogram-and-gram", status))
763                 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
764             Locale("en-US"),
765             4.28571,
766             u"4t 285kg 710g");
767 
768     assertFormatSingle(
769             u"Mixed Unit (Short Version)",
770             u"unit/tonne-and-kilogram-and-gram unit-width-short",
771             u"unit/tonne-and-kilogram-and-gram unit-width-short",
772             NumberFormatter::with()
773                 .unit(MeasureUnit::forIdentifier("tonne-and-kilogram-and-gram", status))
774                 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
775             Locale("en-US"),
776             4.28571,
777             u"4 t, 285 kg, 710 g");
778 
779     assertFormatSingle(
780             u"Mixed Unit (Full Name Version)",
781             u"unit/tonne-and-kilogram-and-gram unit-width-full-name",
782             u"unit/tonne-and-kilogram-and-gram unit-width-full-name",
783             NumberFormatter::with()
784                 .unit(MeasureUnit::forIdentifier("tonne-and-kilogram-and-gram", status))
785                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
786             Locale("en-US"),
787             4.28571,
788             u"4 metric tons, 285 kilograms, 710 grams");
789 
790     assertFormatSingle(u"Mixed Unit (Not Sorted) [metric]",                               //
791                        u"unit/gram-and-kilogram unit-width-full-name",                    //
792                        u"unit/gram-and-kilogram unit-width-full-name",                    //
793                        NumberFormatter::with()                                            //
794                            .unit(MeasureUnit::forIdentifier("gram-and-kilogram", status)) //
795                            .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                         //
796                        Locale("en-US"),                                                   //
797                        4.28571,                                                           //
798                        u"285.71 grams, 4 kilograms");                                     //
799 
800     assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial]",                                  //
801                        u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
802                        u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
803                        NumberFormatter::with()                                                 //
804                            .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
805                            .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                              //
806                        Locale("en-US"),                                                        //
807                        4.28571,                                                                //
808                        u"10.28556 inches, 4 yards, 0 feet");                                   //
809 
810     assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full]",                             //
811                        u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
812                        u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
813                        NumberFormatter::with()                                                 //
814                            .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
815                            .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                              //
816                        Locale("en-US"),                                                        //
817                        4.38571,                                                                //
818                        u"1.88556 inches, 4 yards, 1 foot");                                    //
819 
820     assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full integers]",                    //
821                        u"unit/inch-and-yard-and-foot @# unit-width-full-name",                 //
822                        u"unit/inch-and-yard-and-foot @# unit-width-full-name",                 //
823                        NumberFormatter::with()                                                 //
824                            .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
825                            .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)                               //
826                            .precision(Precision::maxSignificantDigits(2)),                     //
827                        Locale("en-US"),                                                        //
828                        4.36112,                                                                //
829                        u"1 inch, 4 yards, 1 foot");                                            //
830 
831     assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full] with `And` in the end",       //
832                        u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
833                        u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
834                        NumberFormatter::with()                                                 //
835                            .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
836                            .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                              //
837                        Locale("fr-FR"),                                                        //
838                        4.38571,                                                                //
839                        u"1,88556\u00A0pouce, 4\u00A0yards et 1\u00A0pied");                    //
840 
841     assertFormatSingle(u"Mixed unit, Scientific [Not in Order]",                               //
842                        u"unit/foot-and-inch-and-yard E0",                                      //
843                        u"unit/foot-and-inch-and-yard E0",                                      //
844                        NumberFormatter::with()                                                 //
845                            .unit(MeasureUnit::forIdentifier("foot-and-inch-and-yard", status)) //
846                            .notation(Notation::scientific()),                                  //
847                        Locale("en-US"),                                                        //
848                        3.65,                                                                   //
849                        "1 ft, 1.14E1 in, 3 yd");                                               //
850 
851     assertFormatSingle(
852             u"Testing  \"1 foot 12 inches\"",
853             u"unit/foot-and-inch @### unit-width-full-name",
854             u"unit/foot-and-inch @### unit-width-full-name",
855             NumberFormatter::with()
856                 .unit(MeasureUnit::forIdentifier("foot-and-inch", status))
857                 .precision(Precision::maxSignificantDigits(4))
858                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
859             Locale("en-US"),
860             1.9999,
861             u"2 feet, 0 inches");
862 
863     assertFormatSingle(
864             u"Negative numbers: temperature",
865             u"measure-unit/temperature-celsius",
866             u"unit/celsius",
867             NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)),
868             Locale("nl-NL"),
869             -6.5,
870             u"-6,5°C");
871 
872     assertFormatSingle(
873             u"Negative numbers: time",
874             u"unit/hour-and-minute-and-second",
875             u"unit/hour-and-minute-and-second",
876             NumberFormatter::with().unit(MeasureUnit::forIdentifier("hour-and-minute-and-second", status)),
877             Locale("de-DE"),
878             -1.24,
879             u"-1 Std., 14 Min. und 24 Sek.");
880 
881     assertFormatSingle(
882             u"Zero out the unit field",
883             u"",
884             u"",
885             NumberFormatter::with().unit(KELVIN).unit(MeasureUnit()),
886             Locale("en"),
887             100,
888             u"100");
889 
890     // TODO: desired behaviour for this "pathological" case?
891     // Since this is pointless, we don't test that its behaviour doesn't change.
892     // As of January 2021, the produced result has a missing sign: 23.5 Kelvin
893     // is "23 Kelvin and -272.65 degrees Celsius":
894 //     assertFormatSingle(
895 //             u"Meaningless: kelvin-and-celcius",
896 //             u"unit/kelvin-and-celsius",
897 //             u"unit/kelvin-and-celsius",
898 //             NumberFormatter::with().unit(MeasureUnit::forIdentifier("kelvin-and-celsius", status)),
899 //             Locale("en"),
900 //             23.5,
901 //             u"23 K, 272.65°C");
902 
903     if (uprv_getNaN() != 0.0) {
904         assertFormatSingle(
905                 u"Measured -Inf",
906                 u"measure-unit/electric-ampere",
907                 u"unit/ampere",
908                 NumberFormatter::with().unit(MeasureUnit::getAmpere()),
909                 Locale("en"),
910                 -uprv_getInfinity(),
911                 u"-∞ A");
912 
913         assertFormatSingle(
914                 u"Measured NaN",
915                 u"measure-unit/temperature-celsius",
916                 u"unit/celsius",
917                 NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)),
918                 Locale("en"),
919                 uprv_getNaN(),
920                 u"NaN°C");
921     }
922 }
923 
unitCompoundMeasure()924 void NumberFormatterApiTest::unitCompoundMeasure() {
925     IcuTestErrorCode status(*this, "unitCompoundMeasure()");
926 
927     assertFormatDescending(
928             u"Meters Per Second Short (unit that simplifies) and perUnit method",
929             u"measure-unit/length-meter per-measure-unit/duration-second",
930             u"unit/meter-per-second",
931             NumberFormatter::with().unit(METER).perUnit(SECOND),
932             Locale::getEnglish(),
933             u"87,650 m/s",
934             u"8,765 m/s",
935             u"876.5 m/s",
936             u"87.65 m/s",
937             u"8.765 m/s",
938             u"0.8765 m/s",
939             u"0.08765 m/s",
940             u"0.008765 m/s",
941             u"0 m/s");
942 
943     assertFormatDescending(
944             u"Meters Per Second Short, built-in m/s",
945             u"measure-unit/speed-meter-per-second",
946             u"unit/meter-per-second",
947             NumberFormatter::with().unit(METER_PER_SECOND),
948             Locale::getEnglish(),
949             u"87,650 m/s",
950             u"8,765 m/s",
951             u"876.5 m/s",
952             u"87.65 m/s",
953             u"8.765 m/s",
954             u"0.8765 m/s",
955             u"0.08765 m/s",
956             u"0.008765 m/s",
957             u"0 m/s");
958 
959     assertFormatDescending(
960             u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
961             u"measure-unit/mass-pound per-measure-unit/area-square-mile",
962             u"unit/pound-per-square-mile",
963             NumberFormatter::with().unit(POUND).adoptPerUnit(new MeasureUnit(SQUARE_MILE)),
964             Locale::getEnglish(),
965             u"87,650 lb/mi²",
966             u"8,765 lb/mi²",
967             u"876.5 lb/mi²",
968             u"87.65 lb/mi²",
969             u"8.765 lb/mi²",
970             u"0.8765 lb/mi²",
971             u"0.08765 lb/mi²",
972             u"0.008765 lb/mi²",
973             u"0 lb/mi²");
974 
975     assertFormatDescending(
976             u"Joules Per Furlong Short (unit with no simplifications or special patterns)",
977             u"measure-unit/energy-joule per-measure-unit/length-furlong",
978             u"unit/joule-per-furlong",
979             NumberFormatter::with().unit(JOULE).perUnit(FURLONG),
980             Locale::getEnglish(),
981             u"87,650 J/fur",
982             u"8,765 J/fur",
983             u"876.5 J/fur",
984             u"87.65 J/fur",
985             u"8.765 J/fur",
986             u"0.8765 J/fur",
987             u"0.08765 J/fur",
988             u"0.008765 J/fur",
989             u"0 J/fur");
990 
991     assertFormatDescending(
992             u"Joules Per Furlong Short with unit identifier via API",
993             u"measure-unit/energy-joule per-measure-unit/length-furlong",
994             u"unit/joule-per-furlong",
995             NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)),
996             Locale::getEnglish(),
997             u"87,650 J/fur",
998             u"8,765 J/fur",
999             u"876.5 J/fur",
1000             u"87.65 J/fur",
1001             u"8.765 J/fur",
1002             u"0.8765 J/fur",
1003             u"0.08765 J/fur",
1004             u"0.008765 J/fur",
1005             u"0 J/fur");
1006 
1007     assertFormatDescending(
1008             u"Pounds per Square Inch: composed",
1009             u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
1010             u"unit/pound-force-per-square-inch",
1011             NumberFormatter::with().unit(POUND_FORCE).perUnit(SQUARE_INCH),
1012             Locale::getEnglish(),
1013             u"87,650 psi",
1014             u"8,765 psi",
1015             u"876.5 psi",
1016             u"87.65 psi",
1017             u"8.765 psi",
1018             u"0.8765 psi",
1019             u"0.08765 psi",
1020             u"0.008765 psi",
1021             u"0 psi");
1022 
1023     assertFormatDescending(
1024             u"Pounds per Square Inch: built-in",
1025             u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
1026             u"unit/pound-force-per-square-inch",
1027             NumberFormatter::with().unit(MeasureUnit::getPoundPerSquareInch()),
1028             Locale::getEnglish(),
1029             u"87,650 psi",
1030             u"8,765 psi",
1031             u"876.5 psi",
1032             u"87.65 psi",
1033             u"8.765 psi",
1034             u"0.8765 psi",
1035             u"0.08765 psi",
1036             u"0.008765 psi",
1037             u"0 psi");
1038 
1039     assertFormatSingle(
1040             u"m/s/s simplifies to m/s^2",
1041             u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
1042             u"unit/meter-per-square-second",
1043             NumberFormatter::with().unit(METER_PER_SECOND).perUnit(SECOND),
1044             Locale("en-GB"),
1045             2.4,
1046             u"2.4 m/s\u00B2");
1047 
1048     assertFormatSingle(
1049             u"Negative numbers: acceleration",
1050             u"measure-unit/acceleration-meter-per-square-second",
1051             u"unit/meter-per-second-second",
1052             NumberFormatter::with().unit(MeasureUnit::forIdentifier("meter-per-pow2-second", status)),
1053             Locale("af-ZA"),
1054             -9.81,
1055             u"-9,81 m/s\u00B2");
1056 
1057     // Testing the rejection of invalid specifications
1058 
1059     // If .unit() is not given a built-in type, .perUnit() is not allowed
1060     // (because .unit is now flexible enough to handle compound units,
1061     // .perUnit() is supported for backward compatibility).
1062     LocalizedNumberFormatter nf = NumberFormatter::with()
1063              .unit(MeasureUnit::forIdentifier("furlong-pascal", status))
1064              .perUnit(METER)
1065              .locale("en-GB");
1066     status.assertSuccess(); // Error is only returned once we try to format.
1067     FormattedNumber num = nf.formatDouble(2.4, status);
1068     if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
1069         errln(UnicodeString("Expected failure for unit/furlong-pascal per-unit/length-meter, got: \"") +
1070               nf.formatDouble(2.4, status).toString(status) + "\".");
1071         status.assertSuccess();
1072     }
1073 
1074     // .perUnit() may only be passed a built-in type, or something that combines
1075     // to a built-in type together with .unit().
1076     MeasureUnit SQUARE_SECOND = MeasureUnit::forIdentifier("square-second", status);
1077     nf = NumberFormatter::with().unit(FURLONG).perUnit(SQUARE_SECOND).locale("en-GB");
1078     status.assertSuccess(); // Error is only returned once we try to format.
1079     num = nf.formatDouble(2.4, status);
1080     if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
1081         errln(UnicodeString("Expected failure, got: \"") +
1082               nf.formatDouble(2.4, status).toString(status) + "\".");
1083         status.assertSuccess();
1084     }
1085     // As above, "square-second" is not a built-in type, however this time,
1086     // meter-per-square-second is a built-in type.
1087     assertFormatSingle(
1088             u"meter per square-second works as a composed unit",
1089             u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
1090             u"unit/meter-per-square-second",
1091             NumberFormatter::with().unit(METER).perUnit(SQUARE_SECOND),
1092             Locale("en-GB"),
1093             2.4,
1094             u"2.4 m/s\u00B2");
1095 }
1096 
unitArbitraryMeasureUnits()1097 void NumberFormatterApiTest::unitArbitraryMeasureUnits() {
1098     IcuTestErrorCode status(*this, "unitArbitraryMeasureUnits()");
1099 
1100     // TODO: fix after data bug is resolved? See CLDR-14510.
1101 //     assertFormatSingle(
1102 //             u"Binary unit prefix: kibibyte",
1103 //             u"unit/kibibyte",
1104 //             u"unit/kibibyte",
1105 //             NumberFormatter::with().unit(MeasureUnit::forIdentifier("kibibyte", status)),
1106 //             Locale("en-GB"),
1107 //             2.4,
1108 //             u"2.4 KiB");
1109 
1110     assertFormatSingle(
1111             u"Binary unit prefix: kibibyte full-name",
1112             u"unit/kibibyte unit-width-full-name",
1113             u"unit/kibibyte unit-width-full-name",
1114             NumberFormatter::with()
1115                 .unit(MeasureUnit::forIdentifier("kibibyte", status))
1116                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1117             Locale("en-GB"),
1118             2.4,
1119             u"2.4 kibibytes");
1120 
1121     assertFormatSingle(
1122             u"Binary unit prefix: kibibyte full-name",
1123             u"unit/kibibyte unit-width-full-name",
1124             u"unit/kibibyte unit-width-full-name",
1125             NumberFormatter::with()
1126                 .unit(MeasureUnit::forIdentifier("kibibyte", status))
1127                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1128             Locale("de"),
1129             2.4,
1130             u"2,4 Kibibyte");
1131 
1132     assertFormatSingle(
1133             u"Binary prefix for non-digital units: kibimeter",
1134             u"unit/kibimeter",
1135             u"unit/kibimeter",
1136             NumberFormatter::with()
1137                 .unit(MeasureUnit::forIdentifier("kibimeter", status)),
1138             Locale("en-GB"),
1139             2.4,
1140             u"2.4 Kim");
1141 
1142     assertFormatSingle(
1143             u"Extra-large prefix: exabyte",
1144             u"unit/exabyte",
1145             u"unit/exabyte",
1146             NumberFormatter::with()
1147                 .unit(MeasureUnit::forIdentifier("exabyte", status)),
1148             Locale("en-GB"),
1149             2.4,
1150             u"2.4 Ebyte");
1151 
1152     assertFormatSingle(
1153             u"Extra-large prefix: exabyte (full-name)",
1154             u"unit/exabyte unit-width-full-name",
1155             u"unit/exabyte unit-width-full-name",
1156             NumberFormatter::with()
1157                 .unit(MeasureUnit::forIdentifier("exabyte", status))
1158                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1159             Locale("en-GB"),
1160             2.4,
1161             u"2.4 exabytes");
1162 
1163     assertFormatSingle(
1164             u"SI prefix falling back to root: microohm",
1165             u"unit/microohm",
1166             u"unit/microohm",
1167             NumberFormatter::with()
1168                 .unit(MeasureUnit::forIdentifier("microohm", status)),
1169             Locale("de-CH"),
1170             2.4,
1171             u"2.4 μΩ");
1172 
1173     assertFormatSingle(
1174             u"de-CH fallback to de: microohm unit-width-full-name",
1175             u"unit/microohm unit-width-full-name",
1176             u"unit/microohm unit-width-full-name",
1177             NumberFormatter::with()
1178                 .unit(MeasureUnit::forIdentifier("microohm", status))
1179                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1180             Locale("de-CH"),
1181             2.4,
1182             u"2.4\u00A0Mikroohm");
1183 
1184     assertFormatSingle(
1185             u"No prefixes, 'times' pattern: joule-furlong",
1186             u"unit/joule-furlong",
1187             u"unit/joule-furlong",
1188             NumberFormatter::with()
1189                 .unit(MeasureUnit::forIdentifier("joule-furlong", status)),
1190             Locale("en"),
1191             2.4,
1192             u"2.4 J⋅fur");
1193 
1194     assertFormatSingle(
1195             u"No numeratorUnitString: per-second",
1196             u"unit/per-second",
1197             u"unit/per-second",
1198             NumberFormatter::with()
1199                 .unit(MeasureUnit::forIdentifier("per-second", status)),
1200             Locale("de-CH"),
1201             2.4,
1202             u"2.4/s");
1203 
1204     assertFormatSingle(
1205             u"No numeratorUnitString: per-second unit-width-full-name",
1206             u"unit/per-second unit-width-full-name",
1207             u"unit/per-second unit-width-full-name",
1208             NumberFormatter::with()
1209                 .unit(MeasureUnit::forIdentifier("per-second", status))
1210                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1211             Locale("de-CH"),
1212             2.4,
1213             u"2.4 pro Sekunde");
1214 
1215     assertFormatSingle(
1216             u"Prefix in the denominator: nanogram-per-picobarrel",
1217             u"unit/nanogram-per-picobarrel",
1218             u"unit/nanogram-per-picobarrel",
1219             NumberFormatter::with()
1220                 .unit(MeasureUnit::forIdentifier("nanogram-per-picobarrel", status)),
1221             Locale("en-ZA"),
1222             2.4,
1223             u"2.4 ng/pbbl");
1224 
1225     assertFormatSingle(
1226             u"Prefix in the denominator: nanogram-per-picobarrel unit-width-full-name",
1227             u"unit/nanogram-per-picobarrel unit-width-full-name",
1228             u"unit/nanogram-per-picobarrel unit-width-full-name",
1229             NumberFormatter::with()
1230                 .unit(MeasureUnit::forIdentifier("nanogram-per-picobarrel", status))
1231                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1232             Locale("en-ZA"),
1233             2.4,
1234             u"2.4 nanograms per picobarrel");
1235 
1236     // Valid MeasureUnit, but unformattable, because we only have patterns for
1237     // pow2 and pow3 at this time:
1238     LocalizedNumberFormatter lnf = NumberFormatter::with()
1239               .unit(MeasureUnit::forIdentifier("pow4-mile", status))
1240               .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
1241               .locale("en-ZA");
1242     lnf.operator=(lnf);  // self-assignment should be a no-op
1243     lnf.formatInt(1, status);
1244     status.expectErrorAndReset(U_INTERNAL_PROGRAM_ERROR);
1245 
1246     assertFormatSingle(
1247             u"kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1248             u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1249             u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1250             NumberFormatter::with()
1251                 .unit(MeasureUnit::forIdentifier("kibijoule-foot-per-cubic-gigafurlong-square-second",
1252                                                  status))
1253                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1254             Locale("en-ZA"),
1255             2.4,
1256             u"2.4 kibijoule-feet per cubic gigafurlong-square second");
1257 
1258     assertFormatSingle(
1259             u"kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1260             u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1261             u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1262             NumberFormatter::with()
1263                 .unit(MeasureUnit::forIdentifier("kibijoule-foot-per-cubic-gigafurlong-square-second",
1264                                                  status))
1265                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1266             Locale("de-CH"),
1267             2.4,
1268             u"2.4\u00A0Kibijoule⋅Fuss pro Kubikgigafurlong⋅Quadratsekunde");
1269 
1270     // TODO(ICU-21504): We want to be able to format this, but "100-kilometer"
1271     // is not yet supported when it's not part of liter-per-100-kilometer:
1272     // Actually now in CLDR 40 this is supported directly in data, so change test.
1273     assertFormatSingle(
1274             u"kilowatt-hour-per-100-kilometer unit-width-full-name",
1275             u"unit/kilowatt-hour-per-100-kilometer unit-width-full-name",
1276             u"unit/kilowatt-hour-per-100-kilometer unit-width-full-name",
1277             NumberFormatter::with()
1278                 .unit(MeasureUnit::forIdentifier("kilowatt-hour-per-100-kilometer",
1279                                                  status))
1280                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1281             Locale("en-ZA"),
1282             2.4,
1283             u"2.4 kilowatt-hours per 100 kilometres");
1284 }
1285 
1286 // TODO: merge these tests into numbertest_skeletons.cpp instead of here:
unitSkeletons()1287 void NumberFormatterApiTest::unitSkeletons() {
1288     const struct TestCase {
1289         const char *msg;
1290         const char16_t *inputSkeleton;
1291         const char16_t *normalizedSkeleton;
1292     } cases[] = {
1293         {"old-form built-in compound unit",      //
1294          u"measure-unit/speed-meter-per-second", //
1295          u"unit/meter-per-second"},
1296 
1297         {"old-form compound construction, converts to built-in",        //
1298          u"measure-unit/length-meter per-measure-unit/duration-second", //
1299          u"unit/meter-per-second"},
1300 
1301         {"old-form compound construction which does not simplify to a built-in", //
1302          u"measure-unit/energy-joule per-measure-unit/length-meter",             //
1303          u"unit/joule-per-meter"},
1304 
1305         {"old-form compound-compound ugliness resolves neatly",                   //
1306          u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", //
1307          u"unit/meter-per-square-second"},
1308 
1309         {"short-form built-in units stick with the built-in", //
1310          u"unit/meter-per-second",                            //
1311          u"unit/meter-per-second"},
1312 
1313         {"short-form compound units stay as is", //
1314          u"unit/square-meter-per-square-meter",  //
1315          u"unit/square-meter-per-square-meter"},
1316 
1317         {"short-form compound units stay as is", //
1318          u"unit/joule-per-furlong",              //
1319          u"unit/joule-per-furlong"},
1320 
1321         {"short-form that doesn't consist of built-in units", //
1322          u"unit/hectometer-per-second",                       //
1323          u"unit/hectometer-per-second"},
1324 
1325         {"short-form that doesn't consist of built-in units", //
1326          u"unit/meter-per-hectosecond",                       //
1327          u"unit/meter-per-hectosecond"},
1328 
1329         {"percent compound skeletons handled correctly", //
1330          u"unit/percent-per-meter",                      //
1331          u"unit/percent-per-meter"},
1332 
1333         {"permille compound skeletons handled correctly",                 //
1334          u"measure-unit/concentr-permille per-measure-unit/length-meter", //
1335          u"unit/permille-per-meter"},
1336 
1337         {"percent simple unit is not actually considered a unit", //
1338          u"unit/percent",                                         //
1339          u"percent"},
1340 
1341         {"permille simple unit is not actually considered a unit", //
1342          u"measure-unit/concentr-permille",                        //
1343          u"permille"},
1344 
1345         {"Round-trip example from icu-units#35", //
1346          u"unit/kibijoule-per-furlong",          //
1347          u"unit/kibijoule-per-furlong"},
1348     };
1349     for (const auto& cas : cases) {
1350         IcuTestErrorCode status(*this, cas.msg);
1351         auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1352         if (status.errIfFailureAndReset("NumberFormatter::forSkeleton failed")) {
1353             continue;
1354         }
1355         assertEquals(                                                       //
1356             UnicodeString(true, cas.inputSkeleton, -1) + u" normalization", //
1357             cas.normalizedSkeleton,                                         //
1358             nf.toSkeleton(status));
1359         status.errIfFailureAndReset("NumberFormatter::toSkeleton failed");
1360     }
1361 
1362     const struct FailCase {
1363         const char *msg;
1364         const char16_t *inputSkeleton;
1365         UErrorCode expectedForSkelStatus;
1366         UErrorCode expectedToSkelStatus;
1367     } failCases[] = {
1368         {"Parsing measure-unit/* results in failure if not built-in unit",
1369          u"measure-unit/hectometer",     //
1370          U_NUMBER_SKELETON_SYNTAX_ERROR, //
1371          U_ZERO_ERROR},
1372 
1373         {"Parsing per-measure-unit/* results in failure if not built-in unit",
1374          u"measure-unit/meter per-measure-unit/hectosecond", //
1375          U_NUMBER_SKELETON_SYNTAX_ERROR,                     //
1376          U_ZERO_ERROR},
1377 
1378         {"\"currency/EUR measure-unit/length-meter\" fails, conflicting skeleton.",
1379          u"currency/EUR measure-unit/length-meter", //
1380          U_NUMBER_SKELETON_SYNTAX_ERROR,            //
1381          U_ZERO_ERROR},
1382 
1383         {"\"measure-unit/length-meter currency/EUR\" fails, conflicting skeleton.",
1384          u"measure-unit/length-meter currency/EUR", //
1385          U_NUMBER_SKELETON_SYNTAX_ERROR,            //
1386          U_ZERO_ERROR},
1387 
1388         {"\"currency/EUR per-measure-unit/meter\" fails, conflicting skeleton.",
1389          u"currency/EUR per-measure-unit/length-meter", //
1390          U_NUMBER_SKELETON_SYNTAX_ERROR,                //
1391          U_ZERO_ERROR},
1392     };
1393     for (const auto& cas : failCases) {
1394         IcuTestErrorCode status(*this, cas.msg);
1395         auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1396         if (status.expectErrorAndReset(cas.expectedForSkelStatus, cas.msg)) {
1397             continue;
1398         }
1399         nf.toSkeleton(status);
1400         status.expectErrorAndReset(cas.expectedToSkelStatus, cas.msg);
1401     }
1402 
1403     IcuTestErrorCode status(*this, "unitSkeletons");
1404     assertEquals(                                //
1405         ".unit(METER_PER_SECOND) normalization", //
1406         u"unit/meter-per-second",                //
1407         NumberFormatter::with().unit(METER_PER_SECOND).toSkeleton(status));
1408     assertEquals(                                     //
1409         ".unit(METER).perUnit(SECOND) normalization", //
1410         u"unit/meter-per-second",
1411         NumberFormatter::with().unit(METER).perUnit(SECOND).toSkeleton(status));
1412     assertEquals(                                                                  //
1413         ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1414         u"unit/hectometer",
1415         NumberFormatter::with()
1416             .unit(MeasureUnit::forIdentifier("hectometer", status))
1417             .toSkeleton(status));
1418     assertEquals(                                                                  //
1419         ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1420         u"unit/meter-per-hectosecond",
1421         NumberFormatter::with()
1422             .unit(METER)
1423             .perUnit(MeasureUnit::forIdentifier("hectosecond", status))
1424             .toSkeleton(status));
1425 
1426     status.assertSuccess();
1427     assertEquals(                                                //
1428         ".unit(CURRENCY) produces a currency/CURRENCY skeleton", //
1429         u"currency/GBP",                                         //
1430         NumberFormatter::with().unit(GBP).toSkeleton(status));
1431     status.assertSuccess();
1432     // .unit(CURRENCY).perUnit(ANYTHING) is not supported.
1433     NumberFormatter::with().unit(GBP).perUnit(METER).toSkeleton(status);
1434     status.expectErrorAndReset(U_UNSUPPORTED_ERROR);
1435 }
1436 
unitUsage()1437 void NumberFormatterApiTest::unitUsage() {
1438     IcuTestErrorCode status(*this, "unitUsage()");
1439     UnlocalizedNumberFormatter unloc_formatter;
1440     LocalizedNumberFormatter formatter;
1441     FormattedNumber formattedNum;
1442     UnicodeString uTestCase;
1443 
1444     status.assertSuccess();
1445     formattedNum =
1446         NumberFormatter::with().usage("road").locale(Locale::getEnglish()).formatInt(1, status);
1447     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1448 
1449     unloc_formatter = NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
1450 
1451     uTestCase = u"unitUsage() en-ZA road";
1452     formatter = unloc_formatter.locale("en-ZA");
1453     formattedNum = formatter.formatDouble(321, status);
1454     status.errIfFailureAndReset("unitUsage() en-ZA road formatDouble");
1455     assertTrue(
1456             uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1457             MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
1458     assertEquals(uTestCase, "300 m", formattedNum.toString(status));
1459     {
1460         static const UFieldPosition expectedFieldPositions[] = {
1461                 {UNUM_INTEGER_FIELD, 0, 3},
1462                 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
1463         assertNumberFieldPositions(
1464                 (uTestCase + u" field positions").getTerminatedBuffer(),
1465                 formattedNum,
1466                 expectedFieldPositions,
1467                 UPRV_LENGTHOF(expectedFieldPositions));
1468     }
1469     assertFormatDescendingBig(
1470             uTestCase.getTerminatedBuffer(),
1471             u"measure-unit/length-meter usage/road",
1472             u"unit/meter usage/road",
1473             unloc_formatter,
1474             Locale("en-ZA"),
1475             u"87,650 km",
1476             u"8,765 km",
1477             u"876 km", // 6.5 rounds down, 7.5 rounds up.
1478             u"88 km",
1479             u"8.8 km",
1480             u"900 m",
1481             u"90 m",
1482             u"9 m",
1483             u"0 m");
1484 
1485     uTestCase = u"unitUsage() en-GB road";
1486     formatter = unloc_formatter.locale("en-GB");
1487     formattedNum = formatter.formatDouble(321, status);
1488     status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)");
1489     assertTrue(
1490             uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1491             MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
1492     status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)");
1493     assertEquals(uTestCase, "350 yd", formattedNum.toString(status));
1494     status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)");
1495     {
1496         static const UFieldPosition expectedFieldPositions[] = {
1497                 {UNUM_INTEGER_FIELD, 0, 3},
1498                 {UNUM_MEASURE_UNIT_FIELD, 4, 6}};
1499         assertNumberFieldPositions(
1500                 (uTestCase + u" field positions").getTerminatedBuffer(),
1501                 formattedNum,
1502                 expectedFieldPositions,
1503                 UPRV_LENGTHOF(expectedFieldPositions));
1504     }
1505     assertFormatDescendingBig(
1506             uTestCase.getTerminatedBuffer(),
1507             u"measure-unit/length-meter usage/road",
1508             u"unit/meter usage/road",
1509             unloc_formatter,
1510             Locale("en-GB"),
1511             u"54,463 mi",
1512             u"5,446 mi",
1513             u"545 mi",
1514             u"54 mi",
1515             u"5.4 mi",
1516             u"0.54 mi",
1517             u"100 yd",
1518             u"10 yd",
1519             u"0 yd");
1520 
1521     uTestCase = u"unitUsage() en-US road";
1522     formatter = unloc_formatter.locale("en-US");
1523     formattedNum = formatter.formatDouble(321, status);
1524     status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)");
1525     assertTrue(
1526             uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1527             MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
1528     status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)");
1529     assertEquals(uTestCase, "1,050 ft", formattedNum.toString(status));
1530     status.errIfFailureAndReset("unitUsage() en-US road, toString(...)");
1531     {
1532         static const UFieldPosition expectedFieldPositions[] = {
1533                 {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
1534                 {UNUM_INTEGER_FIELD, 0, 5},
1535                 {UNUM_MEASURE_UNIT_FIELD, 6, 8}};
1536         assertNumberFieldPositions(
1537                 (uTestCase + u" field positions").getTerminatedBuffer(),
1538                 formattedNum,
1539                 expectedFieldPositions,
1540                 UPRV_LENGTHOF(expectedFieldPositions));
1541     }
1542     assertFormatDescendingBig(
1543             uTestCase.getTerminatedBuffer(),
1544             u"measure-unit/length-meter usage/road",
1545             u"unit/meter usage/road",
1546             unloc_formatter,
1547             Locale("en-US"),
1548             u"54,463 mi",
1549             u"5,446 mi",
1550             u"545 mi",
1551             u"54 mi",
1552             u"5.4 mi",
1553             u"0.54 mi",
1554             u"300 ft",
1555             u"30 ft",
1556             u"0 ft");
1557 
1558     unloc_formatter = NumberFormatter::with().usage("person").unit(MeasureUnit::getKilogram());
1559     uTestCase = u"unitUsage() en-GB person";
1560     formatter = unloc_formatter.locale("en-GB");
1561     formattedNum = formatter.formatDouble(80, status);
1562     status.errIfFailureAndReset("unitUsage() en-GB person formatDouble");
1563     assertTrue(
1564         uTestCase + ", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1565         MeasureUnit::forIdentifier("stone-and-pound", status) == formattedNum.getOutputUnit(status));
1566     status.errIfFailureAndReset("unitUsage() en-GB person - formattedNum.getOutputUnit(status)");
1567     assertEquals(uTestCase, "12 st, 8.4 lb", formattedNum.toString(status));
1568     status.errIfFailureAndReset("unitUsage() en-GB person, toString(...)");
1569     {
1570         static const UFieldPosition expectedFieldPositions[] = {
1571                 // // Desired output: TODO(icu-units#67)
1572                 // {UNUM_INTEGER_FIELD, 0, 2},
1573                 // {UNUM_MEASURE_UNIT_FIELD, 3, 5},
1574                 // {ULISTFMT_LITERAL_FIELD, 5, 6},
1575                 // {UNUM_INTEGER_FIELD, 7, 8},
1576                 // {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1577                 // {UNUM_FRACTION_FIELD, 9, 10},
1578                 // {UNUM_MEASURE_UNIT_FIELD, 11, 13}};
1579 
1580                 // Current output: rather no fields than wrong fields
1581                 {UNUM_INTEGER_FIELD, 7, 8},
1582                 {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1583                 {UNUM_FRACTION_FIELD, 9, 10},
1584                 };
1585         assertNumberFieldPositions(
1586                 (uTestCase + u" field positions").getTerminatedBuffer(),
1587                 formattedNum,
1588                 expectedFieldPositions,
1589                 UPRV_LENGTHOF(expectedFieldPositions));
1590     }
1591     assertFormatDescending(
1592             uTestCase.getTerminatedBuffer(),
1593             u"measure-unit/mass-kilogram usage/person",
1594             u"unit/kilogram usage/person",
1595             unloc_formatter,
1596             Locale("en-GB"),
1597             u"13,802 st, 7.2 lb",
1598             u"1,380 st, 3.5 lb",
1599             u"138 st, 0.35 lb",
1600             u"13 st, 11 lb",
1601             u"1 st, 5.3 lb",
1602             u"1 lb, 15 oz",
1603             u"0 lb, 3.1 oz",
1604             u"0 lb, 0.31 oz",
1605             u"0 lb, 0 oz");
1606 
1607    assertFormatDescending(
1608             uTestCase.getTerminatedBuffer(),
1609             u"usage/person unit-width-narrow measure-unit/mass-kilogram",
1610             u"usage/person unit-width-narrow unit/kilogram",
1611             unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_NARROW),
1612             Locale("en-GB"),
1613             u"13,802st 7.2lb",
1614             u"1,380st 3.5lb",
1615             u"138st 0.35lb",
1616             u"13st 11lb",
1617             u"1st 5.3lb",
1618             u"1lb 15oz",
1619             u"0lb 3.1oz",
1620             u"0lb 0.31oz",
1621             u"0lb 0oz");
1622 
1623    assertFormatDescending(
1624             uTestCase.getTerminatedBuffer(),
1625             u"usage/person unit-width-short measure-unit/mass-kilogram",
1626             u"usage/person unit-width-short unit/kilogram",
1627             unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_SHORT),
1628             Locale("en-GB"),
1629             u"13,802 st, 7.2 lb",
1630             u"1,380 st, 3.5 lb",
1631             u"138 st, 0.35 lb",
1632             u"13 st, 11 lb",
1633             u"1 st, 5.3 lb",
1634             u"1 lb, 15 oz",
1635             u"0 lb, 3.1 oz",
1636             u"0 lb, 0.31 oz",
1637             u"0 lb, 0 oz");
1638 
1639    assertFormatDescending(
1640             uTestCase.getTerminatedBuffer(),
1641             u"usage/person unit-width-full-name measure-unit/mass-kilogram",
1642             u"usage/person unit-width-full-name unit/kilogram",
1643             unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1644             Locale("en-GB"),
1645             u"13,802 stone, 7.2 pounds",
1646             u"1,380 stone, 3.5 pounds",
1647             u"138 stone, 0.35 pounds",
1648             u"13 stone, 11 pounds",
1649             u"1 stone, 5.3 pounds",
1650             u"1 pound, 15 ounces",
1651             u"0 pounds, 3.1 ounces",
1652             u"0 pounds, 0.31 ounces",
1653             u"0 pounds, 0 ounces");
1654 
1655     assertFormatDescendingBig(
1656             u"Scientific notation with Usage: possible when using a reasonable Precision",
1657             u"scientific @### usage/default measure-unit/area-square-meter unit-width-full-name",
1658             u"scientific @### usage/default unit/square-meter unit-width-full-name",
1659             NumberFormatter::with()
1660                     .unit(SQUARE_METER)
1661                     .usage("default")
1662                     .notation(Notation::scientific())
1663                     .precision(Precision::minMaxSignificantDigits(1, 4))
1664                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1665             Locale("en-ZA"),
1666             u"8.765E1 square kilometres",
1667             u"8.765E0 square kilometres",
1668             u"8.765E1 hectares",
1669             u"8.765E0 hectares",
1670             u"8.765E3 square metres",
1671             u"8.765E2 square metres",
1672             u"8.765E1 square metres",
1673             u"8.765E0 square metres",
1674             u"0E0 square centimetres");
1675 
1676     assertFormatSingle(
1677             u"Negative Infinity with Unit Preferences",
1678             u"measure-unit/area-acre usage/default",
1679             u"unit/acre usage/default",
1680             NumberFormatter::with().unit(MeasureUnit::getAcre()).usage("default"),
1681             Locale::getEnglish(),
1682             -uprv_getInfinity(),
1683             u"-∞ sq mi");
1684 
1685 //     // TODO(icu-units#131): do we care about NaN?
1686 //     // TODO: on some platforms with MSVC, "-NaN sec" is returned.
1687 //     assertFormatSingle(
1688 //             u"NaN with Unit Preferences",
1689 //             u"measure-unit/area-acre usage/default",
1690 //             u"unit/acre usage/default",
1691 //             NumberFormatter::with().unit(MeasureUnit::getAcre()).usage("default"),
1692 //             Locale::getEnglish(),
1693 //             uprv_getNaN(),
1694 //             u"NaN cm²");
1695 
1696     assertFormatSingle(
1697             u"Negative numbers: minute-and-second",
1698             u"measure-unit/duration-second usage/media",
1699             u"unit/second usage/media",
1700             NumberFormatter::with().unit(SECOND).usage("media"),
1701             Locale("nl-NL"),
1702             -77.7,
1703             u"-1 min, 18 sec");
1704 
1705     assertFormatSingle(
1706             u"Negative numbers: media seconds",
1707             u"measure-unit/duration-second usage/media",
1708             u"unit/second usage/media",
1709             NumberFormatter::with().unit(SECOND).usage("media"),
1710             Locale("nl-NL"),
1711             -2.7,
1712             u"-2,7 sec");
1713 
1714 //     // TODO: on some platforms with MSVC, "-NaN sec" is returned.
1715 //     assertFormatSingle(
1716 //             u"NaN minute-and-second",
1717 //             u"measure-unit/duration-second usage/media",
1718 //             u"unit/second usage/media",
1719 //             NumberFormatter::with().unit(SECOND).usage("media"),
1720 //             Locale("nl-NL"),
1721 //             uprv_getNaN(),
1722 //             u"NaN sec");
1723 
1724     assertFormatSingle(
1725             u"NaN meter-and-centimeter",
1726             u"measure-unit/length-meter usage/person-height",
1727             u"unit/meter usage/person-height",
1728             NumberFormatter::with().unit(METER).usage("person-height"),
1729             Locale("sv-SE"),
1730             uprv_getNaN(),
1731             u"0 m, NaN cm");
1732 
1733     assertFormatSingle(
1734             u"Rounding Mode propagates: rounding down",
1735             u"usage/road measure-unit/length-centimeter rounding-mode-floor",
1736             u"usage/road unit/centimeter rounding-mode-floor",
1737             NumberFormatter::with()
1738                 .unit(MeasureUnit::forIdentifier("centimeter", status))
1739                 .usage("road")
1740                 .roundingMode(UNUM_ROUND_FLOOR),
1741             Locale("en-ZA"),
1742             34500,
1743             u"300 m");
1744 
1745     assertFormatSingle(
1746             u"Rounding Mode propagates: rounding up",
1747             u"usage/road measure-unit/length-centimeter rounding-mode-ceiling",
1748             u"usage/road unit/centimeter rounding-mode-ceiling",
1749             NumberFormatter::with()
1750                 .unit(MeasureUnit::forIdentifier("centimeter", status))
1751                 .usage("road")
1752                 .roundingMode(UNUM_ROUND_CEILING),
1753             Locale("en-ZA"),
1754             30500,
1755             u"350 m");
1756 
1757     assertFormatSingle(u"Fuel consumption: inverted units",                                     //
1758                        u"unit/liter-per-100-kilometer usage/vehicle-fuel",                      //
1759                        u"unit/liter-per-100-kilometer usage/vehicle-fuel",                      //
1760                        NumberFormatter::with()                                                  //
1761                            .unit(MeasureUnit::forIdentifier("liter-per-100-kilometer", status)) //
1762                            .usage("vehicle-fuel"),                                              //
1763                        Locale("en-US"),                                                         //
1764                        6.6,                                                                     //
1765                        "36 mpg");
1766 
1767     assertFormatSingle(u"Fuel consumption: inverted units, divide-by-zero, en-US",              //
1768                        u"unit/liter-per-100-kilometer usage/vehicle-fuel",                      //
1769                        u"unit/liter-per-100-kilometer usage/vehicle-fuel",                      //
1770                        NumberFormatter::with()                                                  //
1771                            .unit(MeasureUnit::forIdentifier("liter-per-100-kilometer", status)) //
1772                            .usage("vehicle-fuel"),                                              //
1773                        Locale("en-US"),                                                         //
1774                        0,                                                                       //
1775                        u"∞ mpg");
1776 
1777     assertFormatSingle(u"Fuel consumption: inverted units, divide-by-zero, en-ZA",      //
1778                        u"unit/mile-per-gallon usage/vehicle-fuel",                      //
1779                        u"unit/mile-per-gallon usage/vehicle-fuel",                      //
1780                        NumberFormatter::with()                                          //
1781                            .unit(MeasureUnit::forIdentifier("mile-per-gallon", status)) //
1782                            .usage("vehicle-fuel"),                                      //
1783                        Locale("en-ZA"),                                                 //
1784                        0,                                                               //
1785                        u"∞ l/100 km");
1786 
1787     assertFormatSingle(u"Fuel consumption: inverted units, divide-by-inf",              //
1788                        u"unit/mile-per-gallon usage/vehicle-fuel",                      //
1789                        u"unit/mile-per-gallon usage/vehicle-fuel",                      //
1790                        NumberFormatter::with()                                          //
1791                            .unit(MeasureUnit::forIdentifier("mile-per-gallon", status)) //
1792                            .usage("vehicle-fuel"),                                      //
1793                        Locale("de-CH"),                                                 //
1794                        uprv_getInfinity(),                                              //
1795                        u"0 L/100 km");
1796 
1797     // Test calling `.usage("")` should unset the existing usage.
1798     // First: without usage
1799     assertFormatSingle(u"Rounding Mode propagates: rounding up",
1800                        u"measure-unit/length-centimeter rounding-mode-ceiling",
1801                        u"unit/centimeter rounding-mode-ceiling",
1802                        NumberFormatter::with()
1803                            .unit(MeasureUnit::forIdentifier("centimeter", status))
1804                            .roundingMode(UNUM_ROUND_CEILING),
1805                        Locale("en-US"), //
1806                        3048,            //
1807                        u"3,048 cm");
1808 
1809     // Second: with "road" usage
1810     assertFormatSingle(u"Rounding Mode propagates: rounding up",
1811                        u"usage/road measure-unit/length-centimeter rounding-mode-ceiling",
1812                        u"usage/road unit/centimeter rounding-mode-ceiling",
1813                        NumberFormatter::with()
1814                            .unit(MeasureUnit::forIdentifier("centimeter", status))
1815                            .usage("road")
1816                            .roundingMode(UNUM_ROUND_CEILING),
1817                        Locale("en-US"), //
1818                        3048,            //
1819                        u"100 ft");
1820 
1821     // Third: with "road" usage, then the usage unsetted by calling .usage("")
1822     assertFormatSingle(u"Rounding Mode propagates: rounding up",
1823                        u"measure-unit/length-centimeter rounding-mode-ceiling",
1824                        u"unit/centimeter rounding-mode-ceiling",
1825                        NumberFormatter::with()
1826                            .unit(MeasureUnit::forIdentifier("centimeter", status))
1827                            .usage("road")
1828                            .roundingMode(UNUM_ROUND_CEILING)
1829                            .usage(""),  // unset
1830                        Locale("en-US"), //
1831                        3048,            //
1832                        u"3,048 cm");
1833 
1834     assertFormatSingle(u"kilometer-per-liter match the correct category",                   //
1835                        u"unit/kilometer-per-liter usage/default",                           //
1836                        u"unit/kilometer-per-liter usage/default",                           //
1837                        NumberFormatter::with()                                              //
1838                            .unit(MeasureUnit::forIdentifier("kilometer-per-liter", status)) //
1839                            .usage("default"),                                               //
1840                        Locale("en-US"),                                                     //
1841                        1,                                                                   //
1842                        u"100 L/100 km");
1843 
1844     assertFormatSingle(u"gallon-per-mile match the correct category",                   //
1845                        u"unit/gallon-per-mile usage/default",                           //
1846                        u"unit/gallon-per-mile usage/default",                           //
1847                        NumberFormatter::with()                                          //
1848                            .unit(MeasureUnit::forIdentifier("gallon-per-mile", status)) //
1849                            .usage("default"),                                           //
1850                        Locale("en-US"),                                                 //
1851                        1,                                                               //
1852                        u"235 L/100 km");
1853 
1854     assertFormatSingle(u"psi match the correct category",                          //
1855                        u"unit/megapascal usage/default",                           //
1856                        u"unit/megapascal usage/default",                           //
1857                        NumberFormatter::with()                                     //
1858                            .unit(MeasureUnit::forIdentifier("megapascal", status)) //
1859                            .usage("default"),                                      //
1860                        Locale("en-US"),                                            //
1861                        1,                                                          //
1862                        "145 psi");
1863 
1864     assertFormatSingle(u"millibar match the correct category",                   //
1865                        u"unit/millibar usage/default",                           //
1866                        u"unit/millibar usage/default",                           //
1867                        NumberFormatter::with()                                   //
1868                            .unit(MeasureUnit::forIdentifier("millibar", status)) //
1869                            .usage("default"),                                    //
1870                        Locale("en-US"),                                          //
1871                        1,                                                        //
1872                        "0.015 psi");
1873 
1874     assertFormatSingle(u"pound-force-per-square-inch match the correct category",                   //
1875                        u"unit/pound-force-per-square-inch usage/default",                           //
1876                        u"unit/pound-force-per-square-inch usage/default",                           //
1877                        NumberFormatter::with()                                                      //
1878                            .unit(MeasureUnit::forIdentifier("pound-force-per-square-inch", status)) //
1879                            .usage("default"),                                                       //
1880                        Locale("en-US"),                                                             //
1881                        1,                                                                           //
1882                        "1 psi");                                                                    //
1883 
1884     assertFormatSingle(u"inch-ofhg match the correct category",                   //
1885                        u"unit/inch-ofhg usage/default",                           //
1886                        u"unit/inch-ofhg usage/default",                           //
1887                        NumberFormatter::with()                                    //
1888                            .unit(MeasureUnit::forIdentifier("inch-ofhg", status)) //
1889                            .usage("default"),                                     //
1890                        Locale("en-US"),                                           //
1891                        1,                                                         //
1892                        "0.49 psi");
1893 
1894     assertFormatSingle(u"millimeter-ofhg match the correct category",                   //
1895                        u"unit/millimeter-ofhg usage/default",                           //
1896                        u"unit/millimeter-ofhg usage/default",                           //
1897                        NumberFormatter::with()                                          //
1898                            .unit(MeasureUnit::forIdentifier("millimeter-ofhg", status)) //
1899                            .usage("default"),                                           //
1900                        Locale("en-US"),                                                 //
1901                        1,                                                               //
1902                        "0.019 psi");
1903 
1904     assertFormatSingle(u"negative temperature conversion",                                 //
1905                        u"measure-unit/temperature-celsius unit-width-short usage/default", //
1906                        u"measure-unit/temperature-celsius unit-width-short usage/default", //
1907                        NumberFormatter::with()                                             //
1908                            .unit(MeasureUnit::forIdentifier("celsius", status))            //
1909                            .usage("default")                                               //
1910                            .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT),            //
1911                        Locale("en-US"),                                                    //
1912                        -1,                                                                 //
1913                        u"30°F");
1914 }
1915 
unitUsageErrorCodes()1916 void NumberFormatterApiTest::unitUsageErrorCodes() {
1917     IcuTestErrorCode status(*this, "unitUsageErrorCodes()");
1918     UnlocalizedNumberFormatter unloc_formatter;
1919 
1920     unloc_formatter = NumberFormatter::forSkeleton(u"unit/foobar", status);
1921     // This gives an error, because foobar is an invalid unit:
1922     status.expectErrorAndReset(U_NUMBER_SKELETON_SYNTAX_ERROR);
1923 
1924     unloc_formatter = NumberFormatter::forSkeleton(u"usage/foobar", status);
1925     // This does not give an error, because usage is not looked up yet.
1926     status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid usage");
1927     unloc_formatter.locale("en-GB").formatInt(1, status);
1928     // Lacking a unit results in a failure. The skeleton is "incomplete", but we
1929     // support adding the unit via the fluent API, so it is not an error until
1930     // we build the formatting pipeline itself.
1931     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1932     // Adding the unit as part of the fluent chain leads to success.
1933     unloc_formatter.unit(MeasureUnit::getMeter()).locale("en-GB").formatInt(1, status);
1934     status.assertSuccess();
1935 
1936     // Setting unit to the "base dimensionless unit" is like clearing unit.
1937     unloc_formatter = NumberFormatter::with().unit(MeasureUnit()).usage("default");
1938     // This does not give an error, because usage-vs-unit isn't resolved yet.
1939     status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid unit");
1940     unloc_formatter.locale("en-GB").formatInt(1, status);
1941     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1942 }
1943 
1944 // Tests for the "skeletons" field in unitPreferenceData, as well as precision
1945 // and notation overrides.
unitUsageSkeletons()1946 void NumberFormatterApiTest::unitUsageSkeletons() {
1947     IcuTestErrorCode status(*this, "unitUsageSkeletons()");
1948 
1949     assertFormatSingle(
1950             u"Default >300m road preference skeletons round to 50m",
1951             u"usage/road measure-unit/length-meter",
1952             u"usage/road unit/meter",
1953             NumberFormatter::with().unit(METER).usage("road"),
1954             Locale("en-ZA"),
1955             321,
1956             u"300 m");
1957 
1958     assertFormatSingle(
1959             u"Precision can be overridden: override takes precedence",
1960             u"usage/road measure-unit/length-meter @#",
1961             u"usage/road unit/meter @#",
1962             NumberFormatter::with()
1963                 .unit(METER)
1964                 .usage("road")
1965                 .precision(Precision::maxSignificantDigits(2)),
1966             Locale("en-ZA"),
1967             321,
1968             u"320 m");
1969 
1970     assertFormatSingle(
1971             u"Compact notation with Usage: bizarre, but possible (short)",
1972             u"compact-short usage/road measure-unit/length-meter",
1973             u"compact-short usage/road unit/meter",
1974             NumberFormatter::with()
1975                .unit(METER)
1976                .usage("road")
1977                .notation(Notation::compactShort()),
1978             Locale("en-ZA"),
1979             987654321,
1980             u"988K km");
1981 
1982     assertFormatSingle(
1983             u"Compact notation with Usage: bizarre, but possible (short, precision override)",
1984             u"compact-short usage/road measure-unit/length-meter @#",
1985             u"compact-short usage/road unit/meter @#",
1986             NumberFormatter::with()
1987                 .unit(METER)
1988                 .usage("road")
1989                 .notation(Notation::compactShort())
1990                 .precision(Precision::maxSignificantDigits(2)),
1991             Locale("en-ZA"),
1992             987654321,
1993             u"990K km");
1994 
1995     assertFormatSingle(
1996             u"Compact notation with Usage: unusual but possible (long)",
1997             u"compact-long usage/road measure-unit/length-meter @#",
1998             u"compact-long usage/road unit/meter @#",
1999             NumberFormatter::with()
2000                 .unit(METER)
2001                 .usage("road")
2002                 .notation(Notation::compactLong())
2003                 .precision(Precision::maxSignificantDigits(2)),
2004             Locale("en-ZA"),
2005             987654321,
2006             u"990 thousand km");
2007 
2008     assertFormatSingle(
2009             u"Compact notation with Usage: unusual but possible (long, precision override)",
2010             u"compact-long usage/road measure-unit/length-meter @#",
2011             u"compact-long usage/road unit/meter @#",
2012             NumberFormatter::with()
2013                 .unit(METER)
2014                 .usage("road")
2015                 .notation(Notation::compactLong())
2016                 .precision(Precision::maxSignificantDigits(2)),
2017             Locale("en-ZA"),
2018             987654321,
2019             u"990 thousand km");
2020 
2021     assertFormatSingle(
2022             u"Scientific notation, not recommended, requires precision override for road",
2023             u"scientific usage/road measure-unit/length-meter",
2024             u"scientific usage/road unit/meter",
2025             NumberFormatter::with().unit(METER).usage("road").notation(Notation::scientific()),
2026             Locale("en-ZA"),
2027             321.45,
2028             // Rounding to the nearest "50" is not exponent-adjusted in scientific notation:
2029             u"0E2 m");
2030 
2031     assertFormatSingle(
2032             u"Scientific notation with Usage: possible when using a reasonable Precision",
2033             u"scientific usage/road measure-unit/length-meter @###",
2034             u"scientific usage/road unit/meter @###",
2035             NumberFormatter::with()
2036                 .unit(METER)
2037                 .usage("road")
2038                 .notation(Notation::scientific())
2039                 .precision(Precision::maxSignificantDigits(4)),
2040             Locale("en-ZA"),
2041             321.45, // 0.45 rounds down, 0.55 rounds up.
2042             u"3.214E2 m");
2043 
2044     assertFormatSingle(
2045             u"Scientific notation with Usage: possible when using a reasonable Precision",
2046             u"scientific usage/default measure-unit/length-astronomical-unit unit-width-full-name",
2047             u"scientific usage/default unit/astronomical-unit unit-width-full-name",
2048             NumberFormatter::with()
2049                 .unit(MeasureUnit::forIdentifier("astronomical-unit", status))
2050                 .usage("default")
2051                 .notation(Notation::scientific())
2052                 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
2053             Locale("en-ZA"),
2054             1e20,
2055             u"1.5E28 kilometres");
2056 
2057     status.assertSuccess();
2058 }
2059 
unitCurrency()2060 void NumberFormatterApiTest::unitCurrency() {
2061     assertFormatDescending(
2062             u"Currency",
2063             u"currency/GBP",
2064             u"currency/GBP",
2065             NumberFormatter::with().unit(GBP),
2066             Locale::getEnglish(),
2067             u"£87,650.00",
2068             u"£8,765.00",
2069             u"£876.50",
2070             u"£87.65",
2071             u"£8.76",
2072             u"£0.88",
2073             u"£0.09",
2074             u"£0.01",
2075             u"£0.00");
2076 
2077     assertFormatDescending(
2078             u"Currency ISO",
2079             u"currency/GBP unit-width-iso-code",
2080             u"currency/GBP unit-width-iso-code",
2081             NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
2082             Locale::getEnglish(),
2083             u"GBP 87,650.00",
2084             u"GBP 8,765.00",
2085             u"GBP 876.50",
2086             u"GBP 87.65",
2087             u"GBP 8.76",
2088             u"GBP 0.88",
2089             u"GBP 0.09",
2090             u"GBP 0.01",
2091             u"GBP 0.00");
2092 
2093     assertFormatDescending(
2094             u"Currency Long Name",
2095             u"currency/GBP unit-width-full-name",
2096             u"currency/GBP unit-width-full-name",
2097             NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
2098             Locale::getEnglish(),
2099             u"87,650.00 British pounds",
2100             u"8,765.00 British pounds",
2101             u"876.50 British pounds",
2102             u"87.65 British pounds",
2103             u"8.76 British pounds",
2104             u"0.88 British pounds",
2105             u"0.09 British pounds",
2106             u"0.01 British pounds",
2107             u"0.00 British pounds");
2108 
2109     assertFormatDescending(
2110             u"Currency Hidden",
2111             u"currency/GBP unit-width-hidden",
2112             u"currency/GBP unit-width-hidden",
2113             NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
2114             Locale::getEnglish(),
2115             u"87,650.00",
2116             u"8,765.00",
2117             u"876.50",
2118             u"87.65",
2119             u"8.76",
2120             u"0.88",
2121             u"0.09",
2122             u"0.01",
2123             u"0.00");
2124 
2125 //    TODO: Implement Measure in C++
2126 //    assertFormatSingleMeasure(
2127 //            u"Currency with CurrencyAmount Input",
2128 //            NumberFormatter::with(),
2129 //            Locale::getEnglish(),
2130 //            new CurrencyAmount(5.43, GBP),
2131 //            u"£5.43");
2132 
2133 //    TODO: Enable this test when DecimalFormat wrapper is done.
2134 //    assertFormatSingle(
2135 //            u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
2136 //                    PatternStringParser.parseToProperties("0 ¤¤¤"),
2137 //                    DecimalFormatSymbols.getInstance(Locale::getEnglish()),
2138 //                    null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
2139 
2140     assertFormatSingle(
2141             u"Currency with Negative Sign",
2142             u"currency/GBP",
2143             u"currency/GBP",
2144             NumberFormatter::with().unit(GBP),
2145             Locale::getEnglish(),
2146             -9876543.21,
2147             u"-£9,876,543.21");
2148 
2149     // The full currency symbol is not shown in NARROW format.
2150     // NOTE: This example is in the documentation.
2151     assertFormatSingle(
2152             u"Currency Difference between Narrow and Short (Narrow Version)",
2153             u"currency/USD unit-width-narrow",
2154             u"currency/USD unit-width-narrow",
2155             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_NARROW),
2156             Locale("en-CA"),
2157             5.43,
2158             u"$5.43");
2159 
2160     assertFormatSingle(
2161             u"Currency Difference between Narrow and Short (Short Version)",
2162             u"currency/USD unit-width-short",
2163             u"currency/USD unit-width-short",
2164             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2165             Locale("en-CA"),
2166             5.43,
2167             u"US$5.43");
2168 
2169     assertFormatSingle(
2170             u"Currency Difference between Formal and Short (Formal Version)",
2171             u"currency/TWD unit-width-formal",
2172             u"currency/TWD unit-width-formal",
2173             NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_FORMAL),
2174             Locale("zh-TW"),
2175             5.43,
2176             u"NT$5.43");
2177 
2178     assertFormatSingle(
2179             u"Currency Difference between Formal and Short (Short Version)",
2180             u"currency/TWD unit-width-short",
2181             u"currency/TWD unit-width-short",
2182             NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2183             Locale("zh-TW"),
2184             5.43,
2185             u"$5.43");
2186 
2187     assertFormatSingle(
2188             u"Currency Difference between Variant and Short (Formal Version)",
2189             u"currency/TRY unit-width-variant",
2190             u"currency/TRY unit-width-variant",
2191             NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_VARIANT),
2192             Locale("tr-TR"),
2193             5.43,
2194             u"TL\u00A05,43");
2195 
2196     assertFormatSingle(
2197             u"Currency Difference between Variant and Short (Short Version)",
2198             u"currency/TRY unit-width-short",
2199             u"currency/TRY unit-width-short",
2200             NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2201             Locale("tr-TR"),
2202             5.43,
2203             u"₺5,43");
2204 
2205     assertFormatSingle(
2206             u"Currency-dependent format (Control)",
2207             u"currency/USD unit-width-short",
2208             u"currency/USD unit-width-short",
2209             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2210             Locale("ca"),
2211             444444.55,
2212             u"444.444,55 USD");
2213 
2214     assertFormatSingle(
2215             u"Currency-dependent format (Test)",
2216             u"currency/ESP unit-width-short",
2217             u"currency/ESP unit-width-short",
2218             NumberFormatter::with().unit(ESP).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2219             Locale("ca"),
2220             444444.55,
2221             u"₧ 444.445");
2222 
2223     assertFormatSingle(
2224             u"Currency-dependent symbols (Control)",
2225             u"currency/USD unit-width-short",
2226             u"currency/USD unit-width-short",
2227             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2228             Locale("pt-PT"),
2229             444444.55,
2230             u"444 444,55 US$");
2231 
2232     // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
2233     // width space), and they set the decimal separator to the $ symbol.
2234     assertFormatSingle(
2235             u"Currency-dependent symbols (Test Short)",
2236             u"currency/PTE unit-width-short",
2237             u"currency/PTE unit-width-short",
2238             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2239             Locale("pt-PT"),
2240             444444.55,
2241             u"444,444$55 \u200B");
2242 
2243     assertFormatSingle(
2244             u"Currency-dependent symbols (Test Narrow)",
2245             u"currency/PTE unit-width-narrow",
2246             u"currency/PTE unit-width-narrow",
2247             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_NARROW),
2248             Locale("pt-PT"),
2249             444444.55,
2250             u"444,444$55 \u200B");
2251 
2252     assertFormatSingle(
2253             u"Currency-dependent symbols (Test ISO Code)",
2254             u"currency/PTE unit-width-iso-code",
2255             u"currency/PTE unit-width-iso-code",
2256             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
2257             Locale("pt-PT"),
2258             444444.55,
2259             u"444,444$55 PTE");
2260 
2261     assertFormatSingle(
2262             u"Plural form depending on visible digits (ICU-20499)",
2263             u"currency/RON unit-width-full-name",
2264             u"currency/RON unit-width-full-name",
2265             NumberFormatter::with().unit(RON).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2266             Locale("ro-RO"),
2267             24,
2268             u"24,00 lei românești");
2269 
2270     assertFormatSingle(
2271             u"Currency spacing in suffix (ICU-20954)",
2272             u"currency/CNY",
2273             u"currency/CNY",
2274             NumberFormatter::with().unit(CNY),
2275             Locale("lu"),
2276             123.12,
2277             u"123,12 CN¥");
2278 
2279     // de-CH has currency pattern "¤ #,##0.00;¤-#,##0.00"
2280     assertFormatSingle(
2281             u"Sign position on negative number with pattern spacing",
2282             u"currency/RON",
2283             u"currency/RON",
2284             NumberFormatter::with().unit(RON),
2285             Locale("de-CH"),
2286             -123.12,
2287             u"RON-123.12");
2288 
2289     // TODO(ICU-21420): Move the sign to the inside of the number
2290     assertFormatSingle(
2291             u"Sign position on negative number with currency spacing",
2292             u"currency/RON",
2293             u"currency/RON",
2294             NumberFormatter::with().unit(RON),
2295             Locale("en"),
2296             -123.12,
2297             u"-RON 123.12");
2298 }
2299 
runUnitInflectionsTestCases(UnlocalizedNumberFormatter unf,UnicodeString skeleton,const UnitInflectionTestCase * cases,int32_t numCases,IcuTestErrorCode & status)2300 void NumberFormatterApiTest::runUnitInflectionsTestCases(UnlocalizedNumberFormatter unf,
2301                                                          UnicodeString skeleton,
2302                                                          const UnitInflectionTestCase *cases,
2303                                                          int32_t numCases,
2304                                                          IcuTestErrorCode &status) {
2305     for (int32_t i = 0; i < numCases; i++) {
2306         UnitInflectionTestCase t = cases[i];
2307         status.assertSuccess();
2308         MeasureUnit mu = MeasureUnit::forIdentifier(t.unitIdentifier, status);
2309         if (status.errIfFailureAndReset("MeasureUnit::forIdentifier(\"%s\", ...) failed",
2310                                         t.unitIdentifier)) {
2311             continue;
2312         };
2313         UnicodeString skelString = UnicodeString("unit/") + t.unitIdentifier + u" " + skeleton;
2314         const char16_t *skel;
2315         if (t.unitDisplayCase == nullptr || t.unitDisplayCase[0] == 0) {
2316             unf = unf.unit(mu).unitDisplayCase("");
2317             skel = skelString.getTerminatedBuffer();
2318         } else {
2319             unf = unf.unit(mu).unitDisplayCase(t.unitDisplayCase);
2320             // No skeleton support for unitDisplayCase yet.
2321             skel = nullptr;
2322         }
2323         assertFormatSingle((UnicodeString("Unit: \"") + t.unitIdentifier + ("\", \"") + skeleton +
2324                             u"\", locale=\"" + t.locale + u"\", case=\"" +
2325                             (t.unitDisplayCase ? t.unitDisplayCase : "") + u"\", value=" + t.value)
2326                                .getTerminatedBuffer(),
2327                            skel, skel, unf, Locale(t.locale), t.value, t.expected);
2328         status.assertSuccess();
2329     }
2330 
2331     for (int32_t i = 0; i < numCases; i++) {
2332         UnitInflectionTestCase t = cases[i];
2333         status.assertSuccess();
2334         MeasureUnit mu = MeasureUnit::forIdentifier(t.unitIdentifier, status);
2335         if (status.errIfFailureAndReset("MeasureUnit::forIdentifier(\"%s\", ...) failed",
2336                                         t.unitIdentifier)) {
2337             continue;
2338         };
2339 
2340         UnicodeString skelString = UnicodeString("unit/") + t.unitIdentifier + u" " + skeleton;
2341         const char16_t *skel;
2342         auto displayOptionsBuilder = DisplayOptions::builder();
2343         if (t.unitDisplayCase == nullptr || t.unitDisplayCase[0] == 0) {
2344             auto displayoptions = displayOptionsBuilder.build();
2345             unf = unf.unit(mu).displayOptions(displayoptions);
2346             skel = skelString.getTerminatedBuffer();
2347         } else {
2348             auto displayoptions =
2349                 displayOptionsBuilder
2350                     .setGrammaticalCase(udispopt_fromGrammaticalCaseIdentifier(t.unitDisplayCase))
2351                     .build();
2352             unf = unf.unit(mu).displayOptions(displayoptions);
2353             // No skeleton support for unitDisplayCase yet.
2354             skel = nullptr;
2355         }
2356         assertFormatSingle((UnicodeString("Unit: \"") + t.unitIdentifier + ("\", \"") + skeleton +
2357                             u"\", locale=\"" + t.locale + u"\", case=\"" +
2358                             (t.unitDisplayCase ? t.unitDisplayCase : "") + u"\", value=" + t.value)
2359                                .getTerminatedBuffer(),
2360                            skel, skel, unf, Locale(t.locale), t.value, t.expected);
2361         status.assertSuccess();
2362     }
2363 }
2364 
unitInflections()2365 void NumberFormatterApiTest::unitInflections() {
2366     IcuTestErrorCode status(*this, "unitInflections");
2367 
2368     UnlocalizedNumberFormatter unf;
2369     const char16_t *skeleton;
2370     {
2371         // Simple inflected form test - test case based on the example in CLDR's
2372         // grammaticalFeatures.xml
2373         unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
2374         skeleton = u"unit-width-full-name";
2375         const UnitInflectionTestCase percentCases[] = {
2376             {"percent", "ru", nullptr, 10, u"10 процентов"},    // many
2377             {"percent", "ru", "genitive", 10, u"10 процентов"}, // many
2378             {"percent", "ru", nullptr, 33, u"33 процента"},     // few
2379             {"percent", "ru", "genitive", 33, u"33 процентов"}, // few
2380             {"percent", "ru", nullptr, 1, u"1 процент"},        // one
2381             {"percent", "ru", "genitive", 1, u"1 процента"},    // one
2382         };
2383         runUnitInflectionsTestCases(unf, skeleton, percentCases, UPRV_LENGTHOF(percentCases), status);
2384     }
2385     {
2386         // General testing of inflection rules
2387         unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
2388         skeleton = u"unit-width-full-name";
2389         const UnitInflectionTestCase testCases[] = {
2390             // Check up on the basic values that the compound patterns below are
2391             // derived from:
2392             {"meter", "de", nullptr, 1, u"1 Meter"},
2393             {"meter", "de", "genitive", 1, u"1 Meters"},
2394             {"meter", "de", nullptr, 2, u"2 Meter"},
2395             {"meter", "de", "dative", 2, u"2 Metern"},
2396             {"mile", "de", nullptr, 1, u"1 Meile"},
2397             {"mile", "de", nullptr, 2, u"2 Meilen"},
2398             {"day", "de", nullptr, 1, u"1 Tag"},
2399             {"day", "de", "genitive", 1, u"1 Tages"},
2400             {"day", "de", nullptr, 2, u"2 Tage"},
2401             {"day", "de", "dative", 2, u"2 Tagen"},
2402             {"decade", "de", nullptr, 1, u"1\u00A0Jahrzehnt"},
2403             {"decade", "de", nullptr, 2, u"2\u00A0Jahrzehnte"},
2404 
2405             // Testing de "per" rules:
2406             //   <deriveComponent feature="case" structure="per" value0="compound" value1="accusative"/>
2407             //   <deriveComponent feature="plural" structure="per" value0="compound" value1="one"/>
2408             // per-patterns use accusative, but since the accusative form
2409             // matches the nominative form, we're not effectively testing value1
2410             // in the "case & per" rule above.
2411 
2412             // We have a perUnitPattern for "day" in de, so "per" rules are not
2413             // applied for these:
2414             {"meter-per-day", "de", nullptr, 1, u"1 Meter pro Tag"},
2415             {"meter-per-day", "de", "genitive", 1, u"1 Meters pro Tag"},
2416             {"meter-per-day", "de", nullptr, 2, u"2 Meter pro Tag"},
2417             {"meter-per-day", "de", "dative", 2, u"2 Metern pro Tag"},
2418 
2419             // testing code path that falls back to "root" grammaticalFeatures
2420             // but does not inflect:
2421             {"meter-per-day", "af", nullptr, 1, u"1 meter per dag"},
2422             {"meter-per-day", "af", "dative", 1, u"1 meter per dag"},
2423 
2424             // Decade does not have a perUnitPattern at this time (CLDR 39 / ICU
2425             // 69), so we can use it to test for selection of correct plural form.
2426             // - Note: fragile test cases, these cases will break when
2427             //   whitespace is more consistently applied.
2428             {"parsec-per-decade", "de", nullptr, 1, u"1\u00A0Parsec pro Jahrzehnt"},
2429             {"parsec-per-decade", "de", "genitive", 1, u"1 Parsec pro Jahrzehnt"},
2430             {"parsec-per-decade", "de", nullptr, 2, u"2\u00A0Parsec pro Jahrzehnt"},
2431             {"parsec-per-decade", "de", "dative", 2, u"2 Parsec pro Jahrzehnt"},
2432 
2433             // Testing de "times", "power" and "prefix" rules:
2434             //
2435             //   <deriveComponent feature="plural" structure="times" value0="one"  value1="compound"/>
2436             //   <deriveComponent feature="case" structure="times" value0="nominative"  value1="compound"/>
2437             //
2438             //   <deriveComponent feature="plural" structure="prefix" value0="one"  value1="compound"/>
2439             //   <deriveComponent feature="case" structure="prefix" value0="nominative"  value1="compound"/>
2440             //
2441             // Prefixes in German don't change with plural or case, so these
2442             // tests can't test value0 of the following two rules:
2443             //   <deriveComponent feature="plural" structure="power" value0="one"  value1="compound"/>
2444             //   <deriveComponent feature="case" structure="power" value0="nominative"  value1="compound"/>
2445             {"square-decimeter-dekameter", "de", nullptr, 1, u"1 Dekameter⋅Quadratdezimeter"},
2446             {"square-decimeter-dekameter", "de", "genitive", 1, u"1 Dekameter⋅Quadratdezimeter"},
2447             {"square-decimeter-dekameter", "de", nullptr, 2, u"2 Dekameter⋅Quadratdezimeter"},
2448             {"square-decimeter-dekameter", "de", "dative", 2, u"2 Dekameter⋅Quadratdezimeter"},
2449             // Feminine "Meile" better demonstrates singular-vs-plural form:
2450             {"cubic-mile-dekamile", "de", nullptr, 1, u"1 Dekameile⋅Kubikmeile"},
2451             {"cubic-mile-dekamile", "de", nullptr, 2, u"2 Dekameile⋅Kubikmeilen"},
2452 
2453             // French handles plural "times" and "power" structures differently:
2454             // plural form impacts all "numerator" units (denominator remains
2455             // singular like German), and "pow2" prefixes have different forms
2456             //   <deriveComponent feature="plural" structure="times" value0="compound"  value1="compound"/>
2457             //   <deriveComponent feature="plural" structure="power" value0="compound"  value1="compound"/>
2458             {"square-decimeter-square-second", "fr", nullptr, 1, u"1\u00A0décimètre carré-seconde carrée"},
2459             {"square-decimeter-square-second", "fr", nullptr, 2, u"2\u00A0décimètres carrés-secondes carrées"},
2460         };
2461         runUnitInflectionsTestCases(unf, skeleton, testCases, UPRV_LENGTHOF(testCases), status);
2462     }
2463     {
2464         // Testing inflection of mixed units:
2465         unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
2466         skeleton = u"unit-width-full-name";
2467         const UnitInflectionTestCase testCases[] = {
2468             {"meter", "de", nullptr, 1, u"1 Meter"},
2469             {"meter", "de", "genitive", 1, u"1 Meters"},
2470             {"meter", "de", "dative", 2, u"2 Metern"},
2471             {"centimeter", "de", nullptr, 1, u"1 Zentimeter"},
2472             {"centimeter", "de", "genitive", 1, u"1 Zentimeters"},
2473             {"centimeter", "de", "dative", 10, u"10 Zentimetern"},
2474             // TODO(CLDR-14582): check that these inflections are correct, and
2475             // whether CLDR needs any rules for them (presumably CLDR spec
2476             // should mention it, if it's a consistent rule):
2477             {"meter-and-centimeter", "de", nullptr, 1.01, u"1 Meter, 1 Zentimeter"},
2478             {"meter-and-centimeter", "de", "genitive", 1.01, u"1 Meters, 1 Zentimeters"},
2479             {"meter-and-centimeter", "de", "genitive", 1.1, u"1 Meters, 10 Zentimeter"},
2480             {"meter-and-centimeter", "de", "dative", 1.1, u"1 Meter, 10 Zentimetern"},
2481             {"meter-and-centimeter", "de", "dative", 2.1, u"2 Metern, 10 Zentimetern"},
2482         };
2483         runUnitInflectionsTestCases(unf, skeleton, testCases, UPRV_LENGTHOF(testCases),
2484                                     status);
2485     }
2486     // TODO: add a usage case that selects between preferences with different
2487     // genders (e.g. year, month, day, hour).
2488     // TODO: look at "↑↑↑" cases: check that inheritance is done right.
2489 }
2490 
unitNounClass()2491 void NumberFormatterApiTest::unitNounClass() {
2492     IcuTestErrorCode status(*this, "unitNounClass");
2493     const struct TestCase {
2494         const char *locale;
2495         const char *unitIdentifier;
2496         const UDisplayOptionsNounClass expectedNounClass;
2497     } cases[] = {
2498         {"de", "inch", UDISPOPT_NOUN_CLASS_MASCULINE},
2499         {"de", "yard", UDISPOPT_NOUN_CLASS_NEUTER},
2500         {"de", "meter", UDISPOPT_NOUN_CLASS_MASCULINE},
2501         {"de", "liter", UDISPOPT_NOUN_CLASS_MASCULINE},
2502         {"de", "second", UDISPOPT_NOUN_CLASS_FEMININE},
2503         {"de", "minute", UDISPOPT_NOUN_CLASS_FEMININE},
2504         {"de", "hour", UDISPOPT_NOUN_CLASS_FEMININE},
2505         {"de", "day", UDISPOPT_NOUN_CLASS_MASCULINE},
2506         {"de", "year", UDISPOPT_NOUN_CLASS_NEUTER},
2507         {"de", "gram", UDISPOPT_NOUN_CLASS_NEUTER},
2508         {"de", "watt", UDISPOPT_NOUN_CLASS_NEUTER},
2509         {"de", "bit", UDISPOPT_NOUN_CLASS_NEUTER},
2510         {"de", "byte", UDISPOPT_NOUN_CLASS_NEUTER},
2511 
2512         {"fr", "inch", UDISPOPT_NOUN_CLASS_MASCULINE},
2513         {"fr", "yard", UDISPOPT_NOUN_CLASS_MASCULINE},
2514         {"fr", "meter", UDISPOPT_NOUN_CLASS_MASCULINE},
2515         {"fr", "liter", UDISPOPT_NOUN_CLASS_MASCULINE},
2516         {"fr", "second", UDISPOPT_NOUN_CLASS_FEMININE},
2517         {"fr", "minute", UDISPOPT_NOUN_CLASS_FEMININE},
2518         {"fr", "hour", UDISPOPT_NOUN_CLASS_FEMININE},
2519         {"fr", "day", UDISPOPT_NOUN_CLASS_MASCULINE},
2520         {"fr", "year", UDISPOPT_NOUN_CLASS_MASCULINE},
2521         {"fr", "gram", UDISPOPT_NOUN_CLASS_MASCULINE},
2522 
2523         // grammaticalFeatures deriveCompound "per" rule takes the gender of the
2524         // numerator unit:
2525         {"de", "meter-per-hour", UDISPOPT_NOUN_CLASS_MASCULINE},
2526         {"fr", "meter-per-hour", UDISPOPT_NOUN_CLASS_MASCULINE},
2527         {"af", "meter-per-hour",
2528          UDISPOPT_NOUN_CLASS_UNDEFINED}, // ungendered language
2529 
2530         // French "times" takes gender from first value, German takes the
2531         // second. Prefix and power does not have impact on gender for these
2532         // languages:
2533         {"de", "square-decimeter-square-second", UDISPOPT_NOUN_CLASS_FEMININE},
2534         {"fr", "square-decimeter-square-second",
2535          UDISPOPT_NOUN_CLASS_MASCULINE},
2536 
2537         // TODO(icu-units#149): percent and permille bypasses LongNameHandler
2538         // when unitWidth is not FULL_NAME:
2539         // // Gender of per-second might be that of percent? TODO(icu-units#28)
2540         // {"de", "percent", UNounClass::UNOUN_CLASS_NEUTER},
2541         // {"fr", "percent", UNounClass::UNOUN_CLASS_MASCULINE},
2542 
2543         // Built-in units whose simple units lack gender in the CLDR data file
2544         {"de", "kilopascal", UDISPOPT_NOUN_CLASS_NEUTER},
2545         {"fr", "kilopascal", UDISPOPT_NOUN_CLASS_MASCULINE},
2546         // {"de", "pascal", UNounClass::UNOUN_CLASS_UNDEFINED},
2547         // {"fr", "pascal", UNounClass::UNOUN_CLASS_UNDEFINED},
2548 
2549         // Built-in units that lack gender in the CLDR data file
2550         // {"de", "revolution", UNounClass::UNOUN_CLASS_UNDEFINED},
2551         // {"de", "radian", UNounClass::UNOUN_CLASS_UNDEFINED},
2552         // {"de", "arc-minute", UNounClass::UNOUN_CLASS_UNDEFINED},
2553         // {"de", "arc-second", UNounClass::UNOUN_CLASS_UNDEFINED},
2554         {"de", "square-yard", UDISPOPT_NOUN_CLASS_NEUTER},    // POWER
2555         {"de", "square-inch", UDISPOPT_NOUN_CLASS_MASCULINE}, // POWER
2556         // {"de", "dunam", UNounClass::UNOUN_CLASS_ UNDEFINED},
2557         // {"de", "karat", UNounClass::UNOUN_CLASS_ UNDEFINED},
2558         // {"de", "milligram-ofglucose-per-deciliter", UNounClass::UNOUN_CLASS_UNDEFINED}, // COMPOUND,
2559         // ofglucose
2560         // {"de", "millimole-per-liter", UNounClass::UNOUN_CLASS_UNDEFINED},               // COMPOUND,
2561         // mole
2562         // {"de", "permillion", UNounClass::UNOUN_CLASS_UNDEFINED},
2563         // {"de", "permille", UNounClass::UNOUN_CLASS_UNDEFINED},
2564         // {"de", "permyriad", UNounClass::UNOUN_CLASS_UNDEFINED},
2565         // {"de", "mole", UNounClass::UNOUN_CLASS_UNDEFINED},
2566         {"de", "liter-per-kilometer",
2567          UDISPOPT_NOUN_CLASS_MASCULINE},                // COMPOUND
2568         {"de", "petabyte", UDISPOPT_NOUN_CLASS_NEUTER}, // PREFIX
2569         {"de", "terabit", UDISPOPT_NOUN_CLASS_NEUTER},  // PREFIX
2570         // {"de", "century", UNounClass::UNOUN_CLASS_UNDEFINED},
2571         // {"de", "decade", UNounClass::UNOUN_CLASS_UNDEFINED},
2572         {"de", "millisecond", UDISPOPT_NOUN_CLASS_FEMININE}, // PREFIX
2573         {"de", "microsecond", UDISPOPT_NOUN_CLASS_FEMININE}, // PREFIX
2574         {"de", "nanosecond", UDISPOPT_NOUN_CLASS_FEMININE},  // PREFIX
2575         // {"de", "ampere", UNounClass::UNOUN_CLASS_UNDEFINED},
2576         // {"de", "milliampere", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, ampere
2577         // {"de", "ohm", UNounClass::UNOUN_CLASS_UNDEFINED},
2578         // {"de", "calorie", UNounClass::UNOUN_CLASS_UNDEFINED},
2579         // {"de", "kilojoule", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, joule
2580         // {"de", "joule", UNounClass::UNOUN_CLASS_ UNDEFINED},
2581         {"de", "kilowatt-hour", UDISPOPT_NOUN_CLASS_FEMININE}, // COMPOUND
2582         // {"de", "electronvolt", UNounClass::UNOUN_CLASS_UNDEFINED},
2583         // {"de", "british-thermal-unit", UNounClass::UNOUN_CLASS_UNDEFINED},
2584         // {"de", "therm-us", UNounClass::UNOUN_CLASS_UNDEFINED},
2585         // {"de", "pound-force", UNounClass::UNOUN_CLASS_UNDEFINED},
2586         // {"de", "newton", UNounClass::UNOUN_CLASS_UNDEFINED},
2587         // {"de", "gigahertz", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, hertz
2588         // {"de", "megahertz", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, hertz
2589         // {"de", "kilohertz", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, hertz
2590         // {"de", "hertz", UNounClass::UNOUN_CLASS_ UNDEFINED},
2591         // {"de", "em", UNounClass::UNOUN_CLASS_UNDEFINED},
2592         // {"de", "pixel", UNounClass::UNOUN_CLASS_ UNDEFINED},
2593         // {"de", "megapixel", UNounClass::UNOUN_CLASS_UNDEFINED},
2594         // {"de", "pixel-per-centimeter", UNounClass::UNOUN_CLASS_UNDEFINED}, // COMPOUND, pixel
2595         // {"de", "pixel-per-inch", UNounClass::UNOUN_CLASS_UNDEFINED},       // COMPOUND, pixel
2596         // {"de", "dot-per-centimeter", UNounClass::UNOUN_CLASS_UNDEFINED},   // COMPOUND, dot
2597         // {"de", "dot-per-inch", UNounClass::UNOUN_CLASS_UNDEFINED},         // COMPOUND, dot
2598         // {"de", "dot", UNounClass::UNOUN_CLASS_UNDEFINED},
2599         // {"de", "earth-radius", UNounClass::UNOUN_CLASS_UNDEFINED},
2600         {"de", "decimeter", UDISPOPT_NOUN_CLASS_MASCULINE},  // PREFIX
2601         {"de", "micrometer", UDISPOPT_NOUN_CLASS_MASCULINE}, // PREFIX
2602         {"de", "nanometer", UDISPOPT_NOUN_CLASS_MASCULINE},  // PREFIX
2603         // {"de", "light-year", UNounClass::UNOUN_CLASS_UNDEFINED},
2604         // {"de", "astronomical-unit", UNounClass::UNOUN_CLASS_UNDEFINED},
2605         // {"de", "furlong", UNounClass::UNOUN_CLASS_UNDEFINED},
2606         // {"de", "fathom", UNounClass::UNOUN_CLASS_UNDEFINED},
2607         // {"de", "nautical-mile", UNounClass::UNOUN_CLASS_ UNDEFINED},
2608         // {"de", "mile-scandinavian", UNounClass::UNOUN_CLASS_UNDEFINED},
2609         // {"de", "point", UNounClass::UNOUN_CLASS_ UNDEFINED},
2610         // {"de", "lux", UNounClass::UNOUN_CLASS_UNDEFINED},
2611         // {"de", "candela", UNounClass::UNOUN_CLASS_UNDEFINED},
2612         // {"de", "lumen", UNounClass::UNOUN_CLASS_ UNDEFINED},
2613         // {"de", "tonne", UNounClass::UNOUN_CLASS_UNDEFINED},
2614         // {"de", "microgram", UNounClass::UNOUN_CLASS_NEUTER}, // PREFIX
2615         // {"de", "ton", UNounClass::UNOUN_CLASS_UNDEFINED},
2616         // {"de", "stone", UNounClass::UNOUN_CLASS_ UNDEFINED},
2617         // {"de", "ounce-troy", UNounClass::UNOUN_CLASS_UNDEFINED},
2618         // {"de", "carat", UNounClass::UNOUN_CLASS_ UNDEFINED},
2619         {"de", "gigawatt", UDISPOPT_NOUN_CLASS_NEUTER},  // PREFIX
2620         {"de", "milliwatt", UDISPOPT_NOUN_CLASS_NEUTER}, // PREFIX
2621         // {"de", "horsepower", UNounClass::UNOUN_CLASS_UNDEFINED},
2622         // {"de", "millimeter-ofhg", UNounClass::UNOUN_CLASS_UNDEFINED},
2623         // {"de", "pound-force-per-square-inch", UNounClass::UNOUN_CLASS_UNDEFINED}, // COMPOUND,
2624         // pound-force
2625         // {"de", "inch-ofhg", UNounClass::UNOUN_CLASS_UNDEFINED},
2626         // {"de", "bar", UNounClass::UNOUN_CLASS_UNDEFINED},
2627         // {"de", "millibar", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, bar
2628         // {"de", "atmosphere", UNounClass::UNOUN_CLASS_UNDEFINED},
2629         // {"de", "pascal", UNounClass::UNOUN_CLASS_UNDEFINED},      // PREFIX, kilopascal? neuter?
2630         // {"de", "hectopascal", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, pascal, neuter?
2631         // {"de", "megapascal", UNounClass::UNOUN_CLASS_UNDEFINED},  // PREFIX, pascal, neuter?
2632         // {"de", "knot", UNounClass::UNOUN_CLASS_UNDEFINED},
2633         {"de", "pound-force-foot", UDISPOPT_NOUN_CLASS_MASCULINE}, // COMPOUND
2634         {"de", "newton-meter", UDISPOPT_NOUN_CLASS_MASCULINE},     // COMPOUND
2635         {"de", "cubic-kilometer", UDISPOPT_NOUN_CLASS_MASCULINE},  // POWER
2636         {"de", "cubic-yard", UDISPOPT_NOUN_CLASS_NEUTER},          // POWER
2637         {"de", "cubic-inch", UDISPOPT_NOUN_CLASS_MASCULINE},       // POWER
2638         {"de", "megaliter", UDISPOPT_NOUN_CLASS_MASCULINE},        // PREFIX
2639         {"de", "hectoliter", UDISPOPT_NOUN_CLASS_MASCULINE},       // PREFIX
2640         // {"de", "pint-metric", UNounClass::UNOUN_CLASS_UNDEFINED},
2641         // {"de", "cup-metric", UNounClass::UNOUN_CLASS_UNDEFINED},
2642         {"de", "acre-foot", UDISPOPT_NOUN_CLASS_MASCULINE}, // COMPOUND
2643         // {"de", "bushel", UNounClass::UNOUN_CLASS_UNDEFINED},
2644         // {"de", "barrel", UNounClass::UNOUN_CLASS_UNDEFINED},
2645         // Units missing gender in German also misses gender in French:
2646         // {"fr", "revolution", UNounClass::UNOUN_CLASS_UNDEFINED},
2647         // {"fr", "radian", UNounClass::UNOUN_CLASS_UNDEFINED},
2648         // {"fr", "arc-minute", UNounClass::UNOUN_CLASS_UNDEFINED},
2649         // {"fr", "arc-second", UNounClass::UNOUN_CLASS_UNDEFINED},
2650         {"fr", "square-yard", UDISPOPT_NOUN_CLASS_MASCULINE}, // POWER
2651         {"fr", "square-inch", UDISPOPT_NOUN_CLASS_MASCULINE}, // POWER
2652         // {"fr", "dunam", UNounClass::UNOUN_CLASS_ UNDEFINED},
2653         // {"fr", "karat", UNounClass::UNOUN_CLASS_ UNDEFINED},
2654         {"fr", "milligram-ofglucose-per-deciliter",
2655          UDISPOPT_NOUN_CLASS_MASCULINE}, // COMPOUND
2656         // {"fr", "millimole-per-liter", UNounClass::UNOUN_CLASS_UNDEFINED},                        //
2657         // COMPOUND, mole
2658         // {"fr", "permillion", UNounClass::UNOUN_CLASS_UNDEFINED},
2659         // {"fr", "permille", UNounClass::UNOUN_CLASS_UNDEFINED},
2660         // {"fr", "permyriad", UNounClass::UNOUN_CLASS_UNDEFINED},
2661         // {"fr", "mole", UNounClass::UNOUN_CLASS_UNDEFINED},
2662         {"fr", "liter-per-kilometer",
2663          UDISPOPT_NOUN_CLASS_MASCULINE}, // COMPOUND
2664         // {"fr", "petabyte", UNounClass::UNOUN_CLASS_UNDEFINED},                     // PREFIX
2665         // {"fr", "terabit", UNounClass::UNOUN_CLASS_UNDEFINED},                      // PREFIX
2666         // {"fr", "century", UNounClass::UNOUN_CLASS_UNDEFINED},
2667         // {"fr", "decade", UNounClass::UNOUN_CLASS_UNDEFINED},
2668         {"fr", "millisecond", UDISPOPT_NOUN_CLASS_FEMININE}, // PREFIX
2669         {"fr", "microsecond", UDISPOPT_NOUN_CLASS_FEMININE}, // PREFIX
2670         {"fr", "nanosecond", UDISPOPT_NOUN_CLASS_FEMININE},  // PREFIX
2671         // {"fr", "ampere", UNounClass::UNOUN_CLASS_UNDEFINED},
2672         // {"fr", "milliampere", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, ampere
2673         // {"fr", "ohm", UNounClass::UNOUN_CLASS_UNDEFINED},
2674         // {"fr", "calorie", UNounClass::UNOUN_CLASS_UNDEFINED},
2675         // {"fr", "kilojoule", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, joule
2676         // {"fr", "joule", UNounClass::UNOUN_CLASS_UNDEFINED},
2677         // {"fr", "kilowatt-hour", UNounClass::UNOUN_CLASS_UNDEFINED}, // COMPOUND
2678         // {"fr", "electronvolt", UNounClass::UNOUN_CLASS_UNDEFINED},
2679         // {"fr", "british-thermal-unit", UNounClass::UNOUN_CLASS_UNDEFINED},
2680         // {"fr", "therm-us", UNounClass::UNOUN_CLASS_UNDEFINED},
2681         // {"fr", "pound-force", UNounClass::UNOUN_CLASS_UNDEFINED},
2682         // {"fr", "newton", UNounClass::UNOUN_CLASS_UNDEFINED},
2683         // {"fr", "gigahertz", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, hertz
2684         // {"fr", "megahertz", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, hertz
2685         // {"fr", "kilohertz", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, hertz
2686         // {"fr", "hertz", UNounClass::UNOUN_CLASS_ UNDEFINED},
2687         // {"fr", "em", UNounClass::UNOUN_CLASS_UNDEFINED},
2688         // {"fr", "pixel", UNounClass::UNOUN_CLASS_ UNDEFINED},
2689         // {"fr", "megapixel", UNounClass::UNOUN_CLASS_UNDEFINED},
2690         // {"fr", "pixel-per-centimeter", UNounClass::UNOUN_CLASS_UNDEFINED}, // COMPOUND, pixel
2691         // {"fr", "pixel-per-inch", UNounClass::UNOUN_CLASS_UNDEFINED},       // COMPOUND, pixel
2692         // {"fr", "dot-per-centimeter", UNounClass::UNOUN_CLASS_UNDEFINED},   // COMPOUND, dot
2693         // {"fr", "dot-per-inch", UNounClass::UNOUN_CLASS_UNDEFINED},         // COMPOUND, dot
2694         // {"fr", "dot", UNounClass::UNOUN_CLASS_UNDEFINED},
2695         // {"fr", "earth-radius", UNounClass::UNOUN_CLASS_UNDEFINED},
2696         {"fr", "decimeter", UDISPOPT_NOUN_CLASS_MASCULINE},  // PREFIX
2697         {"fr", "micrometer", UDISPOPT_NOUN_CLASS_MASCULINE}, // PREFIX
2698         {"fr", "nanometer", UDISPOPT_NOUN_CLASS_MASCULINE},  // PREFIX
2699         // {"fr", "light-year", UNounClass::UNOUN_CLASS_UNDEFINED},
2700         // {"fr", "astronomical-unit", UNounClass::UNOUN_CLASS_UNDEFINED},
2701         // {"fr", "furlong", UNounClass::UNOUN_CLASS_UNDEFINED},
2702         // {"fr", "fathom", UNounClass::UNOUN_CLASS_UNDEFINED},
2703         // {"fr", "nautical-mile", UNounClass::UNOUN_CLASS_ UNDEFINED},
2704         // {"fr", "mile-scandinavian", UNounClass::UNOUN_CLASS_UNDEFINED},
2705         // {"fr", "point", UNounClass::UNOUN_CLASS_ UNDEFINED},
2706         // {"fr", "lux", UNounClass::UNOUN_CLASS_UNDEFINED},
2707         // {"fr", "candela", UNounClass::UNOUN_CLASS_UNDEFINED},
2708         // {"fr", "lumen", UNounClass::UNOUN_CLASS_ UNDEFINED},
2709         // {"fr", "tonne", UNounClass::UNOUN_CLASS_UNDEFINED},
2710         // {"fr", "microgram", UNounClass::UNOUN_CLASS_MASCULINE}, // PREFIX
2711         // {"fr", "ton", UNounClass::UNOUN_CLASS_UNDEFINED},
2712         // {"fr", "stone", UNounClass::UNOUN_CLASS_ UNDEFINED},
2713         // {"fr", "ounce-troy", UNounClass::UNOUN_CLASS_UNDEFINED},
2714         // {"fr", "carat", UNounClass::UNOUN_CLASS_ UNDEFINED},
2715         // {"fr", "gigawatt", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX
2716         // {"fr", "milliwatt", UNounClass::UNOUN_CLASS_UNDEFINED},
2717         // {"fr", "horsepower", UNounClass::UNOUN_CLASS_UNDEFINED},
2718         {"fr", "millimeter-ofhg", UDISPOPT_NOUN_CLASS_MASCULINE},
2719         // {"fr", "pound-force-per-square-inch", UNounClass::UNOUN_CLASS_UNDEFINED}, // COMPOUND,
2720         // pound-force
2721         {"fr", "inch-ofhg", UDISPOPT_NOUN_CLASS_MASCULINE},
2722         // {"fr", "bar", UNounClass::UNOUN_CLASS_UNDEFINED},
2723         // {"fr", "millibar", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, bar
2724         // {"fr", "atmosphere", UNounClass::UNOUN_CLASS_UNDEFINED},
2725         // {"fr", "pascal", UNounClass::UNOUN_CLASS_UNDEFINED},      // PREFIX, kilopascal?
2726         // {"fr", "hectopascal", UNounClass::UNOUN_CLASS_UNDEFINED}, // PREFIX, pascal
2727         // {"fr", "megapascal", UNounClass::UNOUN_CLASS_UNDEFINED},  // PREFIX, pascal
2728         // {"fr", "knot", UNounClass::UNOUN_CLASS_UNDEFINED},
2729         // {"fr", "pound-force-foot", UNounClass::UNOUN_CLASS_UNDEFINED},
2730         // {"fr", "newton-meter", UNounClass::UNOUN_CLASS_UNDEFINED},
2731         {"fr", "cubic-kilometer", UDISPOPT_NOUN_CLASS_MASCULINE}, // POWER
2732         {"fr", "cubic-yard", UDISPOPT_NOUN_CLASS_MASCULINE},      // POWER
2733         {"fr", "cubic-inch", UDISPOPT_NOUN_CLASS_MASCULINE},      // POWER
2734         {"fr", "megaliter", UDISPOPT_NOUN_CLASS_MASCULINE},       // PREFIX
2735         {"fr", "hectoliter", UDISPOPT_NOUN_CLASS_MASCULINE},      // PREFIX
2736         // {"fr", "pint-metric", UNounClass::UNOUN_CLASS_UNDEFINED},
2737         // {"fr", "cup-metric", UNounClass::UNOUN_CLASS_UNDEFINED},
2738         {"fr", "acre-foot", UDISPOPT_NOUN_CLASS_FEMININE}, // COMPOUND
2739         // {"fr", "bushel", UNounClass::UNOUN_CLASS_UNDEFINED},
2740         // {"fr", "barrel", UNounClass::UNOUN_CLASS_UNDEFINED},
2741         // Some more French units missing gender:
2742         // {"fr", "degree", UNounClass::UNOUN_CLASS_UNDEFINED},
2743         {"fr", "square-meter", UDISPOPT_NOUN_CLASS_MASCULINE}, // POWER
2744         // {"fr", "terabyte", UNounClass::UNOUN_CLASS_UNDEFINED},              // PREFIX, byte
2745         // {"fr", "gigabyte", UNounClass::UNOUN_CLASS_UNDEFINED},              // PREFIX, byte
2746         // {"fr", "gigabit", UNounClass::UNOUN_CLASS_UNDEFINED},               // PREFIX, bit
2747         // {"fr", "megabyte", UNounClass::UNOUN_CLASS_UNDEFINED},              // PREFIX, byte
2748         // {"fr", "megabit", UNounClass::UNOUN_CLASS_UNDEFINED},               // PREFIX, bit
2749         // {"fr", "kilobyte", UNounClass::UNOUN_CLASS_UNDEFINED},              // PREFIX, byte
2750         // {"fr", "kilobit", UNounClass::UNOUN_CLASS_UNDEFINED},               // PREFIX, bit
2751         // {"fr", "byte", UNounClass::UNOUN_CLASS_UNDEFINED},
2752         // {"fr", "bit", UNounClass::UNOUN_CLASS_UNDEFINED},
2753         // {"fr", "volt", UNounClass::UNOUN_CLASS_UNDEFINED},
2754         // {"fr", "watt", UNounClass::UNOUN_CLASS_UNDEFINED},
2755         {"fr", "cubic-meter", UDISPOPT_NOUN_CLASS_MASCULINE}, // POWER
2756 
2757         // gender-lacking builtins within compound units
2758         {"de", "newton-meter-per-second", UDISPOPT_NOUN_CLASS_MASCULINE},
2759 
2760         // TODO(ICU-21494): determine whether list genders behave as follows,
2761         // and implement proper getListGender support (covering more than just
2762         // two genders):
2763         // // gender rule for lists of people: de "neutral", fr "maleTaints"
2764         // {"de", "day-and-hour-and-minute", UNounClass::UNOUN_CLASS_NEUTER},
2765         // {"de", "hour-and-minute", UNounClass::UNOUN_CLASS_FEMININE},
2766         // {"fr", "day-and-hour-and-minute", UNounClass::UNOUN_CLASS_MASCULINE},
2767         // {"fr", "hour-and-minute", UNounClass::UNOUN_CLASS_FEMININE},
2768     };
2769 
2770     LocalizedNumberFormatter formatter;
2771     FormattedNumber fn;
2772     for (const TestCase &t : cases) {
2773         formatter = NumberFormatter::with()
2774                         .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status))
2775                         .locale(Locale(t.locale));
2776         fn = formatter.formatDouble(1.1, status);
2777         assertEquals(UnicodeString("Testing NounClass with default width, unit: ") + t.unitIdentifier +
2778                          ", locale: " + t.locale,
2779                      t.expectedNounClass, fn.getNounClass(status));
2780         status.assertSuccess();
2781 
2782         formatter = NumberFormatter::with()
2783                         .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status))
2784                         .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
2785                         .locale(Locale(t.locale));
2786         fn = formatter.formatDouble(1.1, status);
2787         assertEquals(UnicodeString("Testing NounClass with UNUM_UNIT_WIDTH_FULL_NAME, unit: ") +
2788                          t.unitIdentifier + ", locale: " + t.locale,
2789                      t.expectedNounClass, fn.getNounClass(status));
2790         status.assertSuccess();
2791     }
2792 
2793     // Make sure getNounClass does not return garbage for languages without noun classes.
2794     formatter = NumberFormatter::with().locale(Locale::getEnglish());
2795     fn = formatter.formatDouble(1.1, status);
2796     status.assertSuccess();
2797     assertEquals("getNounClasses for a not supported language",
2798                  UDISPOPT_NOUN_CLASS_UNDEFINED, fn.getNounClass(status));
2799 }
2800 
2801 // The following test of getGender (removed in ICU 72) is replaced by the above
2802 // parallel test unitNounClass using getNounClass (getGender replacement).
2803 //void NumberFormatterApiTest::unitGender() {...}
2804 
unitNotConvertible()2805 void NumberFormatterApiTest::unitNotConvertible() {
2806     IcuTestErrorCode status(*this, "unitNotConvertible");
2807     const double randomNumber = 1234;
2808 
2809     NumberFormatter::with()
2810         .unit(MeasureUnit::forIdentifier("meter-and-liter", status))
2811         .locale("en_US")
2812         .formatDouble(randomNumber, status);
2813     assertEquals(u"error must be returned", status.errorName(), u"U_ARGUMENT_TYPE_MISMATCH");
2814 
2815     status.reset();
2816     NumberFormatter::with()
2817         .unit(MeasureUnit::forIdentifier("month-and-week", status))
2818         .locale("en_US")
2819         .formatDouble(randomNumber, status);
2820     assertEquals(u"error must be returned", status.errorName(), u"U_ARGUMENT_TYPE_MISMATCH");
2821 
2822     status.reset();
2823     NumberFormatter::with()
2824         .unit(MeasureUnit::forIdentifier("week-and-day", status))
2825         .locale("en_US")
2826         .formatDouble(randomNumber, status);
2827     assertTrue(u"no error", !U_FAILURE(status));
2828 }
2829 
unitPercent()2830 void NumberFormatterApiTest::unitPercent() {
2831     assertFormatDescending(
2832             u"Percent",
2833             u"percent",
2834             u"%",
2835             NumberFormatter::with().unit(NoUnit::percent()),
2836             Locale::getEnglish(),
2837             u"87,650%",
2838             u"8,765%",
2839             u"876.5%",
2840             u"87.65%",
2841             u"8.765%",
2842             u"0.8765%",
2843             u"0.08765%",
2844             u"0.008765%",
2845             u"0%");
2846 
2847     assertFormatDescending(
2848             u"Permille",
2849             u"permille",
2850             u"permille",
2851             NumberFormatter::with().unit(NoUnit::permille()),
2852             Locale::getEnglish(),
2853             u"87,650‰",
2854             u"8,765‰",
2855             u"876.5‰",
2856             u"87.65‰",
2857             u"8.765‰",
2858             u"0.8765‰",
2859             u"0.08765‰",
2860             u"0.008765‰",
2861             u"0‰");
2862 
2863     assertFormatSingle(
2864             u"NoUnit Base",
2865             u"base-unit",
2866             u"",
2867             NumberFormatter::with().unit(NoUnit::base()),
2868             Locale::getEnglish(),
2869             51423,
2870             u"51,423");
2871 
2872     assertFormatSingle(
2873             u"Percent with Negative Sign",
2874             u"percent",
2875             u"%",
2876             NumberFormatter::with().unit(NoUnit::percent()),
2877             Locale::getEnglish(),
2878             -98.7654321,
2879             u"-98.765432%");
2880 
2881     // ICU-20923
2882     assertFormatDescendingBig(
2883             u"Compact Percent",
2884             u"compact-short percent",
2885             u"K %",
2886             NumberFormatter::with()
2887                     .notation(Notation::compactShort())
2888                     .unit(NoUnit::percent()),
2889             Locale::getEnglish(),
2890             u"88M%",
2891             u"8.8M%",
2892             u"876K%",
2893             u"88K%",
2894             u"8.8K%",
2895             u"876%",
2896             u"88%",
2897             u"8.8%",
2898             u"0%");
2899 
2900     // ICU-20923
2901     assertFormatDescendingBig(
2902             u"Compact Percent with Scale",
2903             u"compact-short percent scale/100",
2904             u"K %x100",
2905             NumberFormatter::with()
2906                     .notation(Notation::compactShort())
2907                     .unit(NoUnit::percent())
2908                     .scale(Scale::powerOfTen(2)),
2909             Locale::getEnglish(),
2910             u"8.8B%",
2911             u"876M%",
2912             u"88M%",
2913             u"8.8M%",
2914             u"876K%",
2915             u"88K%",
2916             u"8.8K%",
2917             u"876%",
2918             u"0%");
2919 
2920     // ICU-20923
2921     assertFormatDescendingBig(
2922             u"Compact Percent Long Name",
2923             u"compact-short percent unit-width-full-name",
2924             u"K % unit-width-full-name",
2925             NumberFormatter::with()
2926                     .notation(Notation::compactShort())
2927                     .unit(NoUnit::percent())
2928                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2929             Locale::getEnglish(),
2930             u"88M percent",
2931             u"8.8M percent",
2932             u"876K percent",
2933             u"88K percent",
2934             u"8.8K percent",
2935             u"876 percent",
2936             u"88 percent",
2937             u"8.8 percent",
2938             u"0 percent");
2939 
2940     assertFormatSingle(
2941             u"Per Percent",
2942             u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
2943             u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
2944             NumberFormatter::with()
2945                     .unit(MeasureUnit::getMeter())
2946                     .perUnit(MeasureUnit::getPercent())
2947                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2948             Locale::getEnglish(),
2949             50,
2950             u"50 meters per percent");
2951 }
2952 
unitLocaleTags()2953 void NumberFormatterApiTest::unitLocaleTags() {
2954     IcuTestErrorCode status(*this, "unitLocaleTags");
2955 
2956     const struct TestCase {
2957         const UnicodeString message;
2958         const char *locale;
2959         const char *inputUnit;
2960         const double inputValue;
2961         const char *usage;
2962         const char *expectedOutputUnit;
2963         const double expectedOutputValue;
2964         const UnicodeString expectedFormattedNumber;
2965     } cases[] = {
2966         // Test without any tag behaviour
2967         {u"Test the locale without any addition and without usage", "en-US", "celsius", 0, nullptr,
2968          "celsius", 0.0, u"0 degrees Celsius"},
2969         {u"Test the locale without any addition and usage", "en-US", "celsius", 0, "default",
2970          "fahrenheit", 32.0, u"32 degrees Fahrenheit"},
2971 
2972         // Test the behaviour of the `mu` tag.
2973         {u"Test the locale with mu = celsius and without usage", "en-US-u-mu-celsius", "fahrenheit", 0,
2974          nullptr, "fahrenheit", 0.0, u"0 degrees Fahrenheit"},
2975         {u"Test the locale with mu = celsius and with usage", "en-US-u-mu-celsius", "fahrenheit", 0,
2976          "default", "celsius", -18.0, u"-18 degrees Celsius"},
2977         {u"Test the locale with mu = calsius (wrong spelling) and with usage", "en-US-u-mu-calsius",
2978          "fahrenheit", 0, "default", "fahrenheit", 0.0, u"0 degrees Fahrenheit"},
2979         {u"Test the locale with mu = meter (only temprature units are supported) and with usage",
2980          "en-US-u-mu-meter", "foot", 0, "default", "inch", 0.0, u"0 inches"},
2981 
2982         // Test the behaviour of the `ms` tag
2983         {u"Test the locale with ms = metric and without usage", "en-US-u-ms-metric", "fahrenheit", 0,
2984          nullptr, "fahrenheit", 0.0, u"0 degrees Fahrenheit"},
2985         {u"Test the locale with ms = metric and with usage", "en-US-u-ms-metric", "fahrenheit", 0,
2986          "default", "celsius", -18, u"-18 degrees Celsius"},
2987         {u"Test the locale with ms = Matric (wrong spelling) and with usage", "en-US-u-ms-Matric",
2988          "fahrenheit", 0, "default", "fahrenheit", 0.0, u"0 degrees Fahrenheit"},
2989 
2990         // Test the behaviour of the `rg` tag
2991         {u"Test the locale with rg = UK and without usage", "en-US-u-rg-ukzzzz", "fahrenheit", 0,
2992          nullptr, "fahrenheit", 0.0, u"0 degrees Fahrenheit"},
2993         {u"Test the locale with rg = UK and with usage", "en-US-u-rg-ukzzzz", "fahrenheit", 0, "default",
2994          "celsius", -18, u"-18 degrees Celsius"},
2995         {"Test the locale with mu = fahrenheit and without usage", "en-US-u-mu-fahrenheit", "celsius", 0,
2996          nullptr, "celsius", 0.0, "0 degrees Celsius"},
2997         {"Test the locale with mu = fahrenheit and with usage", "en-US-u-mu-fahrenheit", "celsius", 0,
2998          "default", "fahrenheit", 32.0, "32 degrees Fahrenheit"},
2999         {u"Test the locale with rg = UKOI and with usage", "en-US-u-rg-ukoi", "fahrenheit", 0,
3000          "default", "celsius", -18.0, u"-18 degrees Celsius"},
3001 
3002         // Test the priorities
3003         {u"Test the locale with mu,ms,rg --> mu tag wins", "en-US-u-mu-celsius-ms-ussystem-rg-uszzzz",
3004          "celsius", 0, "default", "celsius", 0.0, u"0 degrees Celsius"},
3005         {u"Test the locale with ms,rg --> ms tag wins", "en-US-u-ms-metric-rg-uszzzz", "foot", 1,
3006          "default", "centimeter", 30.0, u"30 centimeters"},
3007 
3008         // Test the liklihood of the languages
3009         {"Test the region of `en` --> region should be US", "en", "celsius", 1, "default", "fahrenheit",
3010          34.0, u"34 degrees Fahrenheit"},
3011         {"Test the region of `de` --> region should be DE", "de", "celsius", 1, "default", "celsius",
3012          1.0, u"1 Grad Celsius"},
3013         {"Test the region of `ar` --> region should be EG", "ar", "celsius", 1, "default", "celsius",
3014          1.0, u"١ درجة مئوية"},
3015     };
3016 
3017     for (const auto &testCase : cases) {
3018         UnicodeString message = testCase.message;
3019         Locale locale(testCase.locale);
3020         auto inputUnit = MeasureUnit::forIdentifier(testCase.inputUnit, status);
3021         auto inputValue = testCase.inputValue;
3022         const auto* usage = testCase.usage;
3023         auto expectedOutputUnit = MeasureUnit::forIdentifier(testCase.expectedOutputUnit, status);
3024         UnicodeString expectedFormattedNumber = testCase.expectedFormattedNumber;
3025 
3026         auto nf = NumberFormatter::with()
3027                       .locale(locale)                        //
3028                       .unit(inputUnit)                       //
3029                       .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME); //
3030         if (usage != nullptr) {
3031             nf = nf.usage(usage);
3032         }
3033         auto fn = nf.formatDouble(inputValue, status);
3034         if (status.errIfFailureAndReset()) {
3035             continue;
3036         }
3037 
3038         assertEquals(message, fn.toString(status), expectedFormattedNumber);
3039         // TODO: ICU-22154
3040         // assertEquals(message, fn.getOutputUnit(status).getIdentifier(),
3041         //              expectedOutputUnit.getIdentifier());
3042     }
3043 }
3044 
percentParity()3045 void NumberFormatterApiTest::percentParity() {
3046     IcuTestErrorCode status(*this, "percentParity");
3047     UnlocalizedNumberFormatter uNoUnitPercent = NumberFormatter::with().unit(NoUnit::percent());
3048     UnlocalizedNumberFormatter uNoUnitPermille = NumberFormatter::with().unit(NoUnit::permille());
3049     UnlocalizedNumberFormatter uMeasurePercent = NumberFormatter::with().unit(MeasureUnit::getPercent());
3050     UnlocalizedNumberFormatter uMeasurePermille = NumberFormatter::with().unit(MeasureUnit::getPermille());
3051 
3052     int32_t localeCount;
3053     const auto* locales = Locale::getAvailableLocales(localeCount);
3054     for (int32_t i=0; i<localeCount; i++) {
3055         const auto& locale = locales[i];
3056         UnicodeString sNoUnitPercent = uNoUnitPercent.locale(locale)
3057             .formatDouble(50, status).toString(status);
3058         UnicodeString sNoUnitPermille = uNoUnitPermille.locale(locale)
3059             .formatDouble(50, status).toString(status);
3060         UnicodeString sMeasurePercent = uMeasurePercent.locale(locale)
3061             .formatDouble(50, status).toString(status);
3062         UnicodeString sMeasurePermille = uMeasurePermille.locale(locale)
3063             .formatDouble(50, status).toString(status);
3064 
3065         assertEquals(u"Percent, locale " + UnicodeString(locale.getName()),
3066             sNoUnitPercent, sMeasurePercent);
3067         assertEquals(u"Permille, locale " + UnicodeString(locale.getName()),
3068             sNoUnitPermille, sMeasurePermille);
3069     }
3070 }
3071 
roundingFraction()3072 void NumberFormatterApiTest::roundingFraction() {
3073     assertFormatDescending(
3074             u"Integer",
3075             u"precision-integer",
3076             u".",
3077             NumberFormatter::with().precision(Precision::integer()),
3078             Locale::getEnglish(),
3079             u"87,650",
3080             u"8,765",
3081             u"876",
3082             u"88",
3083             u"9",
3084             u"1",
3085             u"0",
3086             u"0",
3087             u"0");
3088 
3089     assertFormatDescending(
3090             u"Fixed Fraction",
3091             u".000",
3092             u".000",
3093             NumberFormatter::with().precision(Precision::fixedFraction(3)),
3094             Locale::getEnglish(),
3095             u"87,650.000",
3096             u"8,765.000",
3097             u"876.500",
3098             u"87.650",
3099             u"8.765",
3100             u"0.876",
3101             u"0.088",
3102             u"0.009",
3103             u"0.000");
3104 
3105     assertFormatDescending(
3106             u"Min Fraction",
3107             u".0*",
3108             u".0+",
3109             NumberFormatter::with().precision(Precision::minFraction(1)),
3110             Locale::getEnglish(),
3111             u"87,650.0",
3112             u"8,765.0",
3113             u"876.5",
3114             u"87.65",
3115             u"8.765",
3116             u"0.8765",
3117             u"0.08765",
3118             u"0.008765",
3119             u"0.0");
3120 
3121     assertFormatDescending(
3122             u"Max Fraction",
3123             u".#",
3124             u".#",
3125             NumberFormatter::with().precision(Precision::maxFraction(1)),
3126             Locale::getEnglish(),
3127             u"87,650",
3128             u"8,765",
3129             u"876.5",
3130             u"87.6",
3131             u"8.8",
3132             u"0.9",
3133             u"0.1",
3134             u"0",
3135             u"0");
3136 
3137     assertFormatDescending(
3138             u"Min/Max Fraction",
3139             u".0##",
3140             u".0##",
3141             NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
3142             Locale::getEnglish(),
3143             u"87,650.0",
3144             u"8,765.0",
3145             u"876.5",
3146             u"87.65",
3147             u"8.765",
3148             u"0.876",
3149             u"0.088",
3150             u"0.009",
3151             u"0.0");
3152 
3153     assertFormatSingle(
3154             u"Hide If Whole A",
3155             u".00/w",
3156             u".00/w",
3157             NumberFormatter::with().precision(Precision::fixedFraction(2)
3158                 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE)),
3159             Locale::getEnglish(),
3160             1.2,
3161             "1.20");
3162 
3163     assertFormatSingle(
3164             u"Hide If Whole B",
3165             u".00/w",
3166             u".00/w",
3167             NumberFormatter::with().precision(Precision::fixedFraction(2)
3168                 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE)),
3169             Locale::getEnglish(),
3170             1,
3171             "1");
3172 
3173     assertFormatSingle(
3174             u"Hide If Whole with Rounding Mode A (ICU-21881)",
3175             u".00/w rounding-mode-floor",
3176             u".00/w rounding-mode-floor",
3177             NumberFormatter::with().precision(Precision::fixedFraction(2)
3178                 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE))
3179                 .roundingMode(UNUM_ROUND_FLOOR),
3180             Locale::getEnglish(),
3181             3.009,
3182             "3");
3183 
3184     assertFormatSingle(
3185             u"Hide If Whole with Rounding Mode B (ICU-21881)",
3186             u".00/w rounding-mode-half-up",
3187             u".00/w rounding-mode-half-up",
3188             NumberFormatter::with().precision(Precision::fixedFraction(2)
3189                 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE))
3190                 .roundingMode(UNUM_ROUND_HALFUP),
3191             Locale::getEnglish(),
3192             3.001,
3193             "3");
3194 }
3195 
roundingFigures()3196 void NumberFormatterApiTest::roundingFigures() {
3197     assertFormatSingle(
3198             u"Fixed Significant",
3199             u"@@@",
3200             u"@@@",
3201             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
3202             Locale::getEnglish(),
3203             -98,
3204             u"-98.0");
3205 
3206     assertFormatSingle(
3207             u"Fixed Significant Rounding",
3208             u"@@@",
3209             u"@@@",
3210             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
3211             Locale::getEnglish(),
3212             -98.7654321,
3213             u"-98.8");
3214 
3215     assertFormatSingle(
3216             u"Fixed Significant at rounding boundary",
3217             u"@@@",
3218             u"@@@",
3219             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
3220             Locale::getEnglish(),
3221             9.999,
3222             u"10.0");
3223 
3224     assertFormatSingle(
3225             u"Fixed Significant Zero",
3226             u"@@@",
3227             u"@@@",
3228             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
3229             Locale::getEnglish(),
3230             0,
3231             u"0.00");
3232 
3233     assertFormatSingle(
3234             u"Min Significant",
3235             u"@@*",
3236             u"@@+",
3237             NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
3238             Locale::getEnglish(),
3239             -9,
3240             u"-9.0");
3241 
3242     assertFormatSingle(
3243             u"Max Significant",
3244             u"@###",
3245             u"@###",
3246             NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
3247             Locale::getEnglish(),
3248             98.7654321,
3249             u"98.77");
3250 
3251     assertFormatSingle(
3252             u"Min/Max Significant",
3253             u"@@@#",
3254             u"@@@#",
3255             NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
3256             Locale::getEnglish(),
3257             9.99999,
3258             u"10.0");
3259 
3260     assertFormatSingle(
3261             u"Fixed Significant on zero with lots of integer width",
3262             u"@ integer-width/+000",
3263             u"@ 000",
3264             NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
3265                     .integerWidth(IntegerWidth::zeroFillTo(3)),
3266             Locale::getEnglish(),
3267             0,
3268             "000");
3269 
3270     assertFormatSingle(
3271             u"Fixed Significant on zero with zero integer width",
3272             u"@ integer-width/*",
3273             u"@ integer-width/+",
3274             NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
3275                     .integerWidth(IntegerWidth::zeroFillTo(0)),
3276             Locale::getEnglish(),
3277             0,
3278             "0");
3279 }
3280 
roundingFractionFigures()3281 void NumberFormatterApiTest::roundingFractionFigures() {
3282     assertFormatDescending(
3283             u"Basic Significant", // for comparison
3284             u"@#",
3285             u"@#",
3286             NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
3287             Locale::getEnglish(),
3288             u"88,000",
3289             u"8,800",
3290             u"880",
3291             u"88",
3292             u"8.8",
3293             u"0.88",
3294             u"0.088",
3295             u"0.0088",
3296             u"0");
3297 
3298     assertFormatDescending(
3299             u"FracSig minMaxFrac minSig",
3300             u".0#/@@@*",
3301             u".0#/@@@+",
3302             NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
3303             Locale::getEnglish(),
3304             u"87,650.0",
3305             u"8,765.0",
3306             u"876.5",
3307             u"87.65",
3308             u"8.76",
3309             u"0.876", // minSig beats maxFrac
3310             u"0.0876", // minSig beats maxFrac
3311             u"0.00876", // minSig beats maxFrac
3312             u"0.0");
3313 
3314     assertFormatDescending(
3315             u"FracSig minMaxFrac maxSig A",
3316             u".0##/@#",
3317             u".0##/@#",
3318             NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
3319             Locale::getEnglish(),
3320             u"88,000.0", // maxSig beats maxFrac
3321             u"8,800.0", // maxSig beats maxFrac
3322             u"880.0", // maxSig beats maxFrac
3323             u"88.0", // maxSig beats maxFrac
3324             u"8.8", // maxSig beats maxFrac
3325             u"0.88", // maxSig beats maxFrac
3326             u"0.088",
3327             u"0.009",
3328             u"0.0");
3329 
3330     assertFormatDescending(
3331             u"FracSig minMaxFrac maxSig B",
3332             u".00/@#",
3333             u".00/@#",
3334             NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
3335             Locale::getEnglish(),
3336             u"88,000.00", // maxSig beats maxFrac
3337             u"8,800.00", // maxSig beats maxFrac
3338             u"880.00", // maxSig beats maxFrac
3339             u"88.00", // maxSig beats maxFrac
3340             u"8.80", // maxSig beats maxFrac
3341             u"0.88",
3342             u"0.09",
3343             u"0.01",
3344             u"0.00");
3345 
3346     assertFormatSingle(
3347             u"FracSig with trailing zeros A",
3348             u".00/@@@*",
3349             u".00/@@@+",
3350             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
3351             Locale::getEnglish(),
3352             0.1,
3353             u"0.10");
3354 
3355     assertFormatSingle(
3356             u"FracSig with trailing zeros B",
3357             u".00/@@@*",
3358             u".00/@@@+",
3359             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
3360             Locale::getEnglish(),
3361             0.0999999,
3362             u"0.10");
3363 
3364     assertFormatDescending(
3365             u"FracSig withSignificantDigits RELAXED",
3366             u"precision-integer/@#r",
3367             u"./@#r",
3368             NumberFormatter::with().precision(Precision::maxFraction(0)
3369                 .withSignificantDigits(1, 2, UNUM_ROUNDING_PRIORITY_RELAXED)),
3370             Locale::getEnglish(),
3371             u"87,650",
3372             u"8,765",
3373             u"876",
3374             u"88",
3375             u"8.8",
3376             u"0.88",
3377             u"0.088",
3378             u"0.0088",
3379             u"0");
3380 
3381     assertFormatDescending(
3382             u"FracSig withSignificantDigits STRICT",
3383             u"precision-integer/@#s",
3384             u"./@#s",
3385             NumberFormatter::with().precision(Precision::maxFraction(0)
3386                 .withSignificantDigits(1, 2, UNUM_ROUNDING_PRIORITY_STRICT)),
3387             Locale::getEnglish(),
3388             u"88,000",
3389             u"8,800",
3390             u"880",
3391             u"88",
3392             u"9",
3393             u"1",
3394             u"0",
3395             u"0",
3396             u"0");
3397 
3398     assertFormatSingle(
3399             u"FracSig withSignificantDigits Trailing Zeros RELAXED",
3400             u".0/@@@r",
3401             u".0/@@@r",
3402             NumberFormatter::with().precision(Precision::fixedFraction(1)
3403                 .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_RELAXED)),
3404             Locale::getEnglish(),
3405             1,
3406             u"1.00");
3407 
3408     // Trailing zeros follow the strategy that was chosen:
3409     assertFormatSingle(
3410             u"FracSig withSignificantDigits Trailing Zeros STRICT",
3411             u".0/@@@s",
3412             u".0/@@@s",
3413             NumberFormatter::with().precision(Precision::fixedFraction(1)
3414                 .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_STRICT)),
3415             Locale::getEnglish(),
3416             1,
3417             u"1.0");
3418 
3419     assertFormatSingle(
3420             u"FracSig withSignificantDigits at rounding boundary",
3421             u"precision-integer/@@@s",
3422             u"./@@@s",
3423             NumberFormatter::with().precision(Precision::fixedFraction(0)
3424                     .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_STRICT)),
3425             Locale::getEnglish(),
3426             9.99,
3427             u"10");
3428 
3429     assertFormatSingle(
3430             u"FracSig with Trailing Zero Display",
3431             u".00/@@@*/w",
3432             u".00/@@@+/w",
3433             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)
3434                 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE)),
3435             Locale::getEnglish(),
3436             1,
3437             u"1");
3438 }
3439 
roundingOther()3440 void NumberFormatterApiTest::roundingOther() {
3441     assertFormatDescending(
3442             u"Rounding None",
3443             u"precision-unlimited",
3444             u".+",
3445             NumberFormatter::with().precision(Precision::unlimited()),
3446             Locale::getEnglish(),
3447             u"87,650",
3448             u"8,765",
3449             u"876.5",
3450             u"87.65",
3451             u"8.765",
3452             u"0.8765",
3453             u"0.08765",
3454             u"0.008765",
3455             u"0");
3456 
3457     assertFormatDescending(
3458             u"Increment",
3459             u"precision-increment/0.5",
3460             u"precision-increment/0.5",
3461             NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
3462             Locale::getEnglish(),
3463             u"87,650.0",
3464             u"8,765.0",
3465             u"876.5",
3466             u"87.5",
3467             u"9.0",
3468             u"1.0",
3469             u"0.0",
3470             u"0.0",
3471             u"0.0");
3472 
3473     assertFormatDescending(
3474             u"Increment with Min Fraction",
3475             u"precision-increment/0.50",
3476             u"precision-increment/0.50",
3477             NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
3478             Locale::getEnglish(),
3479             u"87,650.00",
3480             u"8,765.00",
3481             u"876.50",
3482             u"87.50",
3483             u"9.00",
3484             u"1.00",
3485             u"0.00",
3486             u"0.00",
3487             u"0.00");
3488 
3489     assertFormatDescending(
3490             u"Strange Increment",
3491             u"precision-increment/3.140",
3492             u"precision-increment/3.140",
3493             NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
3494             Locale::getEnglish(),
3495             u"87,649.960",
3496             u"8,763.740",
3497             u"876.060",
3498             u"87.920",
3499             u"9.420",
3500             u"0.000",
3501             u"0.000",
3502             u"0.000",
3503             u"0.000");
3504 
3505     assertFormatDescending(
3506             u"Medium nickel increment with rounding mode ceiling (ICU-21668)",
3507             u"precision-increment/50 rounding-mode-ceiling",
3508             u"precision-increment/50 rounding-mode-ceiling",
3509             NumberFormatter::with()
3510                 .precision(Precision::increment(50))
3511                 .roundingMode(UNUM_ROUND_CEILING),
3512             Locale::getEnglish(),
3513             u"87,650",
3514             u"8,800",
3515             u"900",
3516             u"100",
3517             u"50",
3518             u"50",
3519             u"50",
3520             u"50",
3521             u"0");
3522 
3523     assertFormatDescending(
3524             u"Large nickel increment with rounding mode up (ICU-21668)",
3525             u"precision-increment/5000 rounding-mode-up",
3526             u"precision-increment/5000 rounding-mode-up",
3527             NumberFormatter::with()
3528                 .precision(Precision::increment(5000))
3529                 .roundingMode(UNUM_ROUND_UP),
3530             Locale::getEnglish(),
3531             u"90,000",
3532             u"10,000",
3533             u"5,000",
3534             u"5,000",
3535             u"5,000",
3536             u"5,000",
3537             u"5,000",
3538             u"5,000",
3539             u"0");
3540 
3541     assertFormatDescending(
3542             u"Large dime increment with rounding mode up (ICU-21668)",
3543             u"precision-increment/10000 rounding-mode-up",
3544             u"precision-increment/10000 rounding-mode-up",
3545             NumberFormatter::with()
3546                 .precision(Precision::increment(10000))
3547                 .roundingMode(UNUM_ROUND_UP),
3548             Locale::getEnglish(),
3549             u"90,000",
3550             u"10,000",
3551             u"10,000",
3552             u"10,000",
3553             u"10,000",
3554             u"10,000",
3555             u"10,000",
3556             u"10,000",
3557             u"0");
3558 
3559     assertFormatDescending(
3560             u"Large non-nickel increment with rounding mode up (ICU-21668)",
3561             u"precision-increment/15000 rounding-mode-up",
3562             u"precision-increment/15000 rounding-mode-up",
3563             NumberFormatter::with()
3564                 .precision(Precision::increment(15000))
3565                 .roundingMode(UNUM_ROUND_UP),
3566             Locale::getEnglish(),
3567             u"90,000",
3568             u"15,000",
3569             u"15,000",
3570             u"15,000",
3571             u"15,000",
3572             u"15,000",
3573             u"15,000",
3574             u"15,000",
3575             u"0");
3576 
3577     assertFormatDescending(
3578             u"Increment Resolving to Power of 10",
3579             u"precision-increment/0.010",
3580             u"precision-increment/0.010",
3581             NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
3582             Locale::getEnglish(),
3583             u"87,650.000",
3584             u"8,765.000",
3585             u"876.500",
3586             u"87.650",
3587             u"8.760",
3588             u"0.880",
3589             u"0.090",
3590             u"0.010",
3591             u"0.000");
3592 
3593     assertFormatDescending(
3594             u"Integer increment with trailing zeros (ICU-21654)",
3595             u"precision-increment/50",
3596             u"precision-increment/50",
3597             NumberFormatter::with().precision(Precision::increment(50)),
3598             Locale::getEnglish(),
3599             u"87,650",
3600             u"8,750",
3601             u"900",
3602             u"100",
3603             u"0",
3604             u"0",
3605             u"0",
3606             u"0",
3607             u"0");
3608 
3609     assertFormatDescending(
3610             u"Integer increment with minFraction (ICU-21654)",
3611             u"precision-increment/5.0",
3612             u"precision-increment/5.0",
3613             NumberFormatter::with().precision(Precision::increment(5).withMinFraction(1)),
3614             Locale::getEnglish(),
3615             u"87,650.0",
3616             u"8,765.0",
3617             u"875.0",
3618             u"90.0",
3619             u"10.0",
3620             u"0.0",
3621             u"0.0",
3622             u"0.0",
3623             u"0.0");
3624 
3625     assertFormatSingle(
3626             u"Large integer increment",
3627             u"precision-increment/24000000000000000000000",
3628             u"precision-increment/24000000000000000000000",
3629             NumberFormatter::with().precision(Precision::incrementExact(24, 21)),
3630             Locale::getEnglish(),
3631             3.1e22,
3632             u"24,000,000,000,000,000,000,000");
3633 
3634     assertFormatSingle(
3635             u"Quarter rounding",
3636             u"precision-increment/250",
3637             u"precision-increment/250",
3638             NumberFormatter::with().precision(Precision::incrementExact(250, 0)),
3639             Locale::getEnglish(),
3640             700,
3641             u"750");
3642 
3643     assertFormatSingle(
3644             u"ECMA-402 limit",
3645             u"precision-increment/.00000000000000000020",
3646             u"precision-increment/.00000000000000000020",
3647             NumberFormatter::with().precision(Precision::incrementExact(20, -20)),
3648             Locale::getEnglish(),
3649             333e-20,
3650             u"0.00000000000000000340");
3651 
3652     assertFormatSingle(
3653             u"ECMA-402 limit with increment = 1",
3654             u"precision-increment/.00000000000000000001",
3655             u"precision-increment/.00000000000000000001",
3656             NumberFormatter::with().precision(Precision::incrementExact(1, -20)),
3657             Locale::getEnglish(),
3658             4321e-21,
3659             u"0.00000000000000000432");
3660 
3661     assertFormatDescending(
3662             u"Currency Standard",
3663             u"currency/CZK precision-currency-standard",
3664             u"currency/CZK precision-currency-standard",
3665             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
3666                     .unit(CZK),
3667             Locale::getEnglish(),
3668             u"CZK 87,650.00",
3669             u"CZK 8,765.00",
3670             u"CZK 876.50",
3671             u"CZK 87.65",
3672             u"CZK 8.76",
3673             u"CZK 0.88",
3674             u"CZK 0.09",
3675             u"CZK 0.01",
3676             u"CZK 0.00");
3677 
3678     assertFormatDescending(
3679             u"Currency Cash",
3680             u"currency/CZK precision-currency-cash",
3681             u"currency/CZK precision-currency-cash",
3682             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
3683                     .unit(CZK),
3684             Locale::getEnglish(),
3685             u"CZK 87,650",
3686             u"CZK 8,765",
3687             u"CZK 876",
3688             u"CZK 88",
3689             u"CZK 9",
3690             u"CZK 1",
3691             u"CZK 0",
3692             u"CZK 0",
3693             u"CZK 0");
3694 
3695     assertFormatDescending(
3696             u"Currency Standard with Trailing Zero Display",
3697             u"currency/CZK precision-currency-standard/w",
3698             u"currency/CZK precision-currency-standard/w",
3699             NumberFormatter::with().precision(
3700                         Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD)
3701                         .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE))
3702                     .unit(CZK),
3703             Locale::getEnglish(),
3704             u"CZK 87,650",
3705             u"CZK 8,765",
3706             u"CZK 876.50",
3707             u"CZK 87.65",
3708             u"CZK 8.76",
3709             u"CZK 0.88",
3710             u"CZK 0.09",
3711             u"CZK 0.01",
3712             u"CZK 0");
3713 
3714     assertFormatDescending(
3715             u"Currency Cash with Nickel Rounding",
3716             u"currency/CAD precision-currency-cash",
3717             u"currency/CAD precision-currency-cash",
3718             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
3719                     .unit(CAD),
3720             Locale::getEnglish(),
3721             u"CA$87,650.00",
3722             u"CA$8,765.00",
3723             u"CA$876.50",
3724             u"CA$87.65",
3725             u"CA$8.75",
3726             u"CA$0.90",
3727             u"CA$0.10",
3728             u"CA$0.00",
3729             u"CA$0.00");
3730 
3731     assertFormatDescending(
3732             u"Currency not in top-level fluent chain",
3733             u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
3734             u".",
3735             NumberFormatter::with().precision(
3736                     Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
3737             Locale::getEnglish(),
3738             u"87,650",
3739             u"8,765",
3740             u"876",
3741             u"88",
3742             u"9",
3743             u"1",
3744             u"0",
3745             u"0",
3746             u"0");
3747 
3748     // NOTE: Other tests cover the behavior of the other rounding modes.
3749     assertFormatDescending(
3750             u"Rounding Mode CEILING",
3751             u"precision-integer rounding-mode-ceiling",
3752             u". rounding-mode-ceiling",
3753             NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING),
3754             Locale::getEnglish(),
3755             u"87,650",
3756             u"8,765",
3757             u"877",
3758             u"88",
3759             u"9",
3760             u"1",
3761             u"1",
3762             u"1",
3763             u"0");
3764 
3765     assertFormatSingle(
3766             u"ICU-20974 Double.MIN_NORMAL",
3767             u"scientific",
3768             u"E0",
3769             NumberFormatter::with().notation(Notation::scientific()),
3770             Locale::getEnglish(),
3771             DBL_MIN,
3772             u"2.225074E-308");
3773 
3774 #ifndef DBL_TRUE_MIN
3775 #define DBL_TRUE_MIN 4.9E-324
3776 #endif
3777 
3778     // Note: this behavior is intentionally different from Java; see
3779     // https://github.com/google/double-conversion/issues/126
3780     assertFormatSingle(
3781             u"ICU-20974 Double.MIN_VALUE",
3782             u"scientific",
3783             u"E0",
3784             NumberFormatter::with().notation(Notation::scientific()),
3785             Locale::getEnglish(),
3786             DBL_TRUE_MIN,
3787             u"5E-324");
3788 }
3789 
3790 /** Test for ICU-21654 and ICU-21668 */
roundingIncrementRegressionTest()3791 void NumberFormatterApiTest::roundingIncrementRegressionTest() {
3792     IcuTestErrorCode status(*this, "roundingIncrementRegressionTest");
3793     Locale locale = Locale::getEnglish();
3794 
3795     for (int min_fraction_digits = 1; min_fraction_digits < 8; min_fraction_digits++) {
3796         // pattern is a snprintf pattern string like "precision-increment/%0.5f"
3797         char pattern[256];
3798         snprintf(pattern, 256, "precision-increment/%%0.%df", min_fraction_digits);
3799         double increment = 0.05;
3800         for (int i = 0; i < 8 ; i++, increment *= 10.0) {
3801             const UnlocalizedNumberFormatter f =
3802                 NumberFormatter::with().precision(
3803                     Precision::increment(increment).withMinFraction(
3804                         min_fraction_digits));
3805             const LocalizedNumberFormatter l = f.locale(locale);
3806 
3807             std::string skeleton;
3808             f.toSkeleton(status).toUTF8String<std::string>(skeleton);
3809 
3810             char message[256];
3811             snprintf(message, 256,
3812                 "ICU-21654: Precision::increment(%0.5f).withMinFraction(%d) '%s'\n",
3813                 increment, min_fraction_digits,
3814                 skeleton.c_str());
3815 
3816             if (increment == 0.05 && min_fraction_digits == 1) {
3817                 // Special case when the number of fraction digits is too low:
3818                 // Precision::increment(0.05000).withMinFraction(1) 'precision-increment/0.05'
3819                 assertEquals(message, "precision-increment/0.05", skeleton.c_str());
3820             } else {
3821                 // All other cases: use the snprintf pattern computed above:
3822                 // Precision::increment(0.50000).withMinFraction(1) 'precision-increment/0.5'
3823                 // Precision::increment(5.00000).withMinFraction(1) 'precision-increment/5.0'
3824                 // Precision::increment(50.00000).withMinFraction(1) 'precision-increment/50.0'
3825                 // ...
3826                 // Precision::increment(0.05000).withMinFraction(2) 'precision-increment/0.05'
3827                 // Precision::increment(0.50000).withMinFraction(2) 'precision-increment/0.50'
3828                 // Precision::increment(5.00000).withMinFraction(2) 'precision-increment/5.00'
3829                 // ...
3830 
3831                 char expected[256];
3832                 snprintf(expected, 256, pattern, increment);
3833                 assertEquals(message, expected, skeleton.c_str());
3834             }
3835         }
3836     }
3837 
3838     auto increment = NumberFormatter::with()
3839         .precision(Precision::increment(5000).withMinFraction(0))
3840         .roundingMode(UNUM_ROUND_UP)
3841         .locale(Locale::getEnglish())
3842         .formatDouble(5.625, status)
3843         .toString(status);
3844     assertEquals("ICU-21668", u"5,000", increment);
3845 }
3846 
roundingPriorityCoverageTest()3847 void NumberFormatterApiTest::roundingPriorityCoverageTest() {
3848     IcuTestErrorCode status(*this, "roundingPriorityCoverageTest");
3849     struct TestCase {
3850         double input;
3851         const char16_t* expectedRelaxed0113;
3852         const char16_t* expectedStrict0113;
3853         const char16_t* expectedRelaxed1133;
3854         const char16_t* expectedStrict1133;
3855     } cases[] = {
3856         { 0.9999, u"1",      u"1",     u"1.00",    u"1.0" },
3857         { 9.9999, u"10",     u"10",    u"10.0",    u"10.0" },
3858         { 99.999, u"100",    u"100",   u"100.0",   u"100" },
3859         { 999.99, u"1000",   u"1000",  u"1000.0",  u"1000" },
3860 
3861         { 0, u"0", u"0", u"0.00", u"0.0" },
3862 
3863         { 9.876,  u"9.88",   u"9.9",   u"9.88",   u"9.9" },
3864         { 9.001,  u"9",      u"9",     u"9.00",   u"9.0" },
3865     };
3866     for (const auto& cas : cases) {
3867         auto precisionRelaxed0113 = Precision::minMaxFraction(0, 1)
3868             .withSignificantDigits(1, 3, UNUM_ROUNDING_PRIORITY_RELAXED);
3869         auto precisionStrict0113 = Precision::minMaxFraction(0, 1)
3870             .withSignificantDigits(1, 3, UNUM_ROUNDING_PRIORITY_STRICT);
3871         auto precisionRelaxed1133 = Precision::minMaxFraction(1, 1)
3872             .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_RELAXED);
3873         auto precisionStrict1133 = Precision::minMaxFraction(1, 1)
3874             .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_STRICT);
3875 
3876         auto messageBase = DoubleToUnicodeString(cas.input);
3877 
3878         auto check = [&](
3879             const char16_t* name,
3880             const UnicodeString& expected,
3881             const Precision& precision
3882         ) {
3883             assertEquals(
3884                 messageBase + name,
3885                 expected,
3886                 NumberFormatter::withLocale(Locale::getEnglish())
3887                     .precision(precision)
3888                     .grouping(UNUM_GROUPING_OFF)
3889                     .formatDouble(cas.input, status)
3890                     .toString(status)
3891             );
3892         };
3893 
3894         check(u" Relaxed 0113", cas.expectedRelaxed0113, precisionRelaxed0113);
3895         if (status.errIfFailureAndReset()) continue;
3896 
3897         check(u" Strict 0113", cas.expectedStrict0113, precisionStrict0113);
3898         if (status.errIfFailureAndReset()) continue;
3899 
3900         check(u" Relaxed 1133", cas.expectedRelaxed1133, precisionRelaxed1133);
3901         if (status.errIfFailureAndReset()) continue;
3902 
3903         check(u" Strict 1133", cas.expectedStrict1133, precisionStrict1133);
3904         if (status.errIfFailureAndReset()) continue;
3905     }
3906 }
3907 
grouping()3908 void NumberFormatterApiTest::grouping() {
3909     assertFormatDescendingBig(
3910             u"Western Grouping",
3911             u"group-auto",
3912             u"",
3913             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
3914             Locale::getEnglish(),
3915             u"87,650,000",
3916             u"8,765,000",
3917             u"876,500",
3918             u"87,650",
3919             u"8,765",
3920             u"876.5",
3921             u"87.65",
3922             u"8.765",
3923             u"0");
3924 
3925     assertFormatDescendingBig(
3926             u"Indic Grouping",
3927             u"group-auto",
3928             u"",
3929             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
3930             Locale("en-IN"),
3931             u"8,76,50,000",
3932             u"87,65,000",
3933             u"8,76,500",
3934             u"87,650",
3935             u"8,765",
3936             u"876.5",
3937             u"87.65",
3938             u"8.765",
3939             u"0");
3940 
3941     assertFormatDescendingBig(
3942             u"Western Grouping, Min 2",
3943             u"group-min2",
3944             u",?",
3945             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
3946             Locale::getEnglish(),
3947             u"87,650,000",
3948             u"8,765,000",
3949             u"876,500",
3950             u"87,650",
3951             u"8765",
3952             u"876.5",
3953             u"87.65",
3954             u"8.765",
3955             u"0");
3956 
3957     assertFormatDescendingBig(
3958             u"Indic Grouping, Min 2",
3959             u"group-min2",
3960             u",?",
3961             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
3962             Locale("en-IN"),
3963             u"8,76,50,000",
3964             u"87,65,000",
3965             u"8,76,500",
3966             u"87,650",
3967             u"8765",
3968             u"876.5",
3969             u"87.65",
3970             u"8.765",
3971             u"0");
3972 
3973     assertFormatDescendingBig(
3974             u"No Grouping",
3975             u"group-off",
3976             u",_",
3977             NumberFormatter::with().grouping(UNUM_GROUPING_OFF),
3978             Locale("en-IN"),
3979             u"87650000",
3980             u"8765000",
3981             u"876500",
3982             u"87650",
3983             u"8765",
3984             u"876.5",
3985             u"87.65",
3986             u"8.765",
3987             u"0");
3988 
3989     assertFormatDescendingBig(
3990             u"Indic locale with THOUSANDS grouping",
3991             u"group-thousands",
3992             u"group-thousands",
3993             NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS),
3994             Locale("en-IN"),
3995             u"87,650,000",
3996             u"8,765,000",
3997             u"876,500",
3998             u"87,650",
3999             u"8,765",
4000             u"876.5",
4001             u"87.65",
4002             u"8.765",
4003             u"0");
4004 
4005     // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data
4006     // (Most locales have either 1 or 2)
4007     // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
4008     assertFormatDescendingBig(
4009             u"Polish Grouping",
4010             u"group-auto",
4011             u"",
4012             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
4013             Locale("pl"),
4014             u"87 650 000",
4015             u"8 765 000",
4016             u"876 500",
4017             u"87 650",
4018             u"8765",
4019             u"876,5",
4020             u"87,65",
4021             u"8,765",
4022             u"0");
4023 
4024     assertFormatDescendingBig(
4025             u"Polish Grouping, Min 2",
4026             u"group-min2",
4027             u",?",
4028             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
4029             Locale("pl"),
4030             u"87 650 000",
4031             u"8 765 000",
4032             u"876 500",
4033             u"87 650",
4034             u"8765",
4035             u"876,5",
4036             u"87,65",
4037             u"8,765",
4038             u"0");
4039 
4040     assertFormatDescendingBig(
4041             u"Polish Grouping, Always",
4042             u"group-on-aligned",
4043             u",!",
4044             NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED),
4045             Locale("pl"),
4046             u"87 650 000",
4047             u"8 765 000",
4048             u"876 500",
4049             u"87 650",
4050             u"8 765",
4051             u"876,5",
4052             u"87,65",
4053             u"8,765",
4054             u"0");
4055 
4056     // NOTE: en_US_POSIX is interesting because it has no grouping in the default currency format.
4057     // If this test breaks due to data changes, find another locale that has no default grouping.
4058     assertFormatDescendingBig(
4059             u"en_US_POSIX Currency Grouping",
4060             u"currency/USD group-auto",
4061             u"currency/USD",
4062             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO).unit(USD),
4063             Locale("en_US_POSIX"),
4064             u"$ 87650000.00",
4065             u"$ 8765000.00",
4066             u"$ 876500.00",
4067             u"$ 87650.00",
4068             u"$ 8765.00",
4069             u"$ 876.50",
4070             u"$ 87.65",
4071             u"$ 8.76",
4072             u"$ 0.00");
4073 
4074     assertFormatDescendingBig(
4075             u"en_US_POSIX Currency Grouping, Always",
4076             u"currency/USD group-on-aligned",
4077             u"currency/USD ,!",
4078             NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED).unit(USD),
4079             Locale("en_US_POSIX"),
4080             u"$ 87,650,000.00",
4081             u"$ 8,765,000.00",
4082             u"$ 876,500.00",
4083             u"$ 87,650.00",
4084             u"$ 8,765.00",
4085             u"$ 876.50",
4086             u"$ 87.65",
4087             u"$ 8.76",
4088             u"$ 0.00");
4089 
4090     MacroProps macros;
4091     macros.grouper = Grouper(4, 1, 3, UNUM_GROUPING_COUNT);
4092     assertFormatDescendingBig(
4093             u"Custom Grouping via Internal API",
4094             nullptr,
4095             nullptr,
4096             NumberFormatter::with().macros(macros),
4097             Locale::getEnglish(),
4098             u"8,7,6,5,0000",
4099             u"8,7,6,5000",
4100             u"876500",
4101             u"87650",
4102             u"8765",
4103             u"876.5",
4104             u"87.65",
4105             u"8.765",
4106             u"0");
4107 }
4108 
padding()4109 void NumberFormatterApiTest::padding() {
4110     assertFormatDescending(
4111             u"Padding",
4112             nullptr,
4113             nullptr,
4114             NumberFormatter::with().padding(Padder::none()),
4115             Locale::getEnglish(),
4116             u"87,650",
4117             u"8,765",
4118             u"876.5",
4119             u"87.65",
4120             u"8.765",
4121             u"0.8765",
4122             u"0.08765",
4123             u"0.008765",
4124             u"0");
4125 
4126     assertFormatDescending(
4127             u"Padding",
4128             nullptr,
4129             nullptr,
4130             NumberFormatter::with().padding(
4131                     Padder::codePoints(
4132                             '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
4133             Locale::getEnglish(),
4134             u"**87,650",
4135             u"***8,765",
4136             u"***876.5",
4137             u"***87.65",
4138             u"***8.765",
4139             u"**0.8765",
4140             u"*0.08765",
4141             u"0.008765",
4142             u"*******0");
4143 
4144     assertFormatDescending(
4145             u"Padding with code points",
4146             nullptr,
4147             nullptr,
4148             NumberFormatter::with().padding(
4149                     Padder::codePoints(
4150                             0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
4151             Locale::getEnglish(),
4152             u"����87,650",
4153             u"������8,765",
4154             u"������876.5",
4155             u"������87.65",
4156             u"������8.765",
4157             u"����0.8765",
4158             u"��0.08765",
4159             u"0.008765",
4160             u"��������������0");
4161 
4162     assertFormatDescending(
4163             u"Padding with wide digits",
4164             nullptr,
4165             nullptr,
4166             NumberFormatter::with().padding(
4167                             Padder::codePoints(
4168                                     '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX))
4169                     .adoptSymbols(new NumberingSystem(MATHSANB)),
4170             Locale::getEnglish(),
4171             u"**����,������",
4172             u"***��,������",
4173             u"***������.��",
4174             u"***����.����",
4175             u"***��.������",
4176             u"**��.��������",
4177             u"*��.����������",
4178             u"��.������������",
4179             u"*******��");
4180 
4181     assertFormatDescending(
4182             u"Padding with currency spacing",
4183             nullptr,
4184             nullptr,
4185             NumberFormatter::with().padding(
4186                             Padder::codePoints(
4187                                     '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX))
4188                     .unit(GBP)
4189                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
4190             Locale::getEnglish(),
4191             u"GBP 87,650.00",
4192             u"GBP 8,765.00",
4193             u"GBP*876.50",
4194             u"GBP**87.65",
4195             u"GBP***8.76",
4196             u"GBP***0.88",
4197             u"GBP***0.09",
4198             u"GBP***0.01",
4199             u"GBP***0.00");
4200 
4201     assertFormatSingle(
4202             u"Pad Before Prefix",
4203             nullptr,
4204             nullptr,
4205             NumberFormatter::with().padding(
4206                     Padder::codePoints(
4207                             '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX)),
4208             Locale::getEnglish(),
4209             -88.88,
4210             u"**-88.88");
4211 
4212     assertFormatSingle(
4213             u"Pad After Prefix",
4214             nullptr,
4215             nullptr,
4216             NumberFormatter::with().padding(
4217                     Padder::codePoints(
4218                             '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
4219             Locale::getEnglish(),
4220             -88.88,
4221             u"-**88.88");
4222 
4223     assertFormatSingle(
4224             u"Pad Before Suffix",
4225             nullptr,
4226             nullptr,
4227             NumberFormatter::with().padding(
4228                     Padder::codePoints(
4229                             '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX)).unit(NoUnit::percent()),
4230             Locale::getEnglish(),
4231             88.88,
4232             u"88.88**%");
4233 
4234     assertFormatSingle(
4235             u"Pad After Suffix",
4236             nullptr,
4237             nullptr,
4238             NumberFormatter::with().padding(
4239                     Padder::codePoints(
4240                             '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX)).unit(NoUnit::percent()),
4241             Locale::getEnglish(),
4242             88.88,
4243             u"88.88%**");
4244 
4245     assertFormatSingle(
4246             u"Currency Spacing with Zero Digit Padding Broken",
4247             nullptr,
4248             nullptr,
4249             NumberFormatter::with().padding(
4250                             Padder::codePoints(
4251                                     '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX))
4252                     .unit(GBP)
4253                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
4254             Locale::getEnglish(),
4255             514.23,
4256             u"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
4257 }
4258 
integerWidth()4259 void NumberFormatterApiTest::integerWidth() {
4260     assertFormatDescending(
4261             u"Integer Width Default",
4262             u"integer-width/+0",
4263             u"0",
4264             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
4265             Locale::getEnglish(),
4266             u"87,650",
4267             u"8,765",
4268             u"876.5",
4269             u"87.65",
4270             u"8.765",
4271             u"0.8765",
4272             u"0.08765",
4273             u"0.008765",
4274             u"0");
4275 
4276     assertFormatDescending(
4277             u"Integer Width Zero Fill 0",
4278             u"integer-width/*",
4279             u"integer-width/+",
4280             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
4281             Locale::getEnglish(),
4282             u"87,650",
4283             u"8,765",
4284             u"876.5",
4285             u"87.65",
4286             u"8.765",
4287             u".8765",
4288             u".08765",
4289             u".008765",
4290             u"0");  // see ICU-20844
4291 
4292     assertFormatDescending(
4293             u"Integer Width Zero Fill 3",
4294             u"integer-width/+000",
4295             u"000",
4296             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
4297             Locale::getEnglish(),
4298             u"87,650",
4299             u"8,765",
4300             u"876.5",
4301             u"087.65",
4302             u"008.765",
4303             u"000.8765",
4304             u"000.08765",
4305             u"000.008765",
4306             u"000");
4307 
4308     assertFormatDescending(
4309             u"Integer Width Max 3",
4310             u"integer-width/##0",
4311             u"integer-width/##0",
4312             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
4313             Locale::getEnglish(),
4314             u"650",
4315             u"765",
4316             u"876.5",
4317             u"87.65",
4318             u"8.765",
4319             u"0.8765",
4320             u"0.08765",
4321             u"0.008765",
4322             u"0");
4323 
4324     assertFormatDescending(
4325             u"Integer Width Fixed 2",
4326             u"integer-width/00",
4327             u"integer-width/00",
4328             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
4329             Locale::getEnglish(),
4330             u"50",
4331             u"65",
4332             u"76.5",
4333             u"87.65",
4334             u"08.765",
4335             u"00.8765",
4336             u"00.08765",
4337             u"00.008765",
4338             u"00");
4339 
4340     assertFormatDescending(
4341             u"Integer Width Compact",
4342             u"compact-short integer-width/000",
4343             u"compact-short integer-width/000",
4344             NumberFormatter::with()
4345                 .notation(Notation::compactShort())
4346                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
4347             Locale::getEnglish(),
4348             u"088K",
4349             u"008.8K",
4350             u"876",
4351             u"088",
4352             u"008.8",
4353             u"000.88",
4354             u"000.088",
4355             u"000.0088",
4356             u"000");
4357 
4358     assertFormatDescending(
4359             u"Integer Width Scientific",
4360             u"scientific integer-width/000",
4361             u"scientific integer-width/000",
4362             NumberFormatter::with()
4363                 .notation(Notation::scientific())
4364                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
4365             Locale::getEnglish(),
4366             u"008.765E4",
4367             u"008.765E3",
4368             u"008.765E2",
4369             u"008.765E1",
4370             u"008.765E0",
4371             u"008.765E-1",
4372             u"008.765E-2",
4373             u"008.765E-3",
4374             u"000E0");
4375 
4376     assertFormatDescending(
4377             u"Integer Width Engineering",
4378             u"engineering integer-width/000",
4379             u"engineering integer-width/000",
4380             NumberFormatter::with()
4381                 .notation(Notation::engineering())
4382                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
4383             Locale::getEnglish(),
4384             u"087.65E3",
4385             u"008.765E3",
4386             u"876.5E0",
4387             u"087.65E0",
4388             u"008.765E0",
4389             u"876.5E-3",
4390             u"087.65E-3",
4391             u"008.765E-3",
4392             u"000E0");
4393 
4394     assertFormatSingle(
4395             u"Integer Width Remove All A",
4396             u"integer-width/00",
4397             u"integer-width/00",
4398             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
4399             "en",
4400             2500,
4401             u"00");
4402 
4403     assertFormatSingle(
4404             u"Integer Width Remove All B",
4405             u"integer-width/00",
4406             u"integer-width/00",
4407             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
4408             "en",
4409             25000,
4410             u"00");
4411 
4412     assertFormatSingle(
4413             u"Integer Width Remove All B, Bytes Mode",
4414             u"integer-width/00",
4415             u"integer-width/00",
4416             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
4417             "en",
4418             // Note: this double produces all 17 significant digits
4419             10000000000000002000.0,
4420             u"00");
4421 
4422     assertFormatDescending(
4423             u"Integer Width Double Zero (ICU-21590)",
4424             u"integer-width-trunc",
4425             u"integer-width-trunc",
4426             NumberFormatter::with()
4427                 .integerWidth(IntegerWidth::zeroFillTo(0).truncateAt(0)),
4428             Locale::getEnglish(),
4429             u"0",
4430             u"0",
4431             u".5",
4432             u".65",
4433             u".765",
4434             u".8765",
4435             u".08765",
4436             u".008765",
4437             u"0");
4438 
4439     assertFormatDescending(
4440             u"Integer Width Double Zero with minFraction (ICU-21590)",
4441             u"integer-width-trunc .0*",
4442             u"integer-width-trunc .0*",
4443             NumberFormatter::with()
4444                 .integerWidth(IntegerWidth::zeroFillTo(0).truncateAt(0))
4445                 .precision(Precision::minFraction(1)),
4446             Locale::getEnglish(),
4447             u".0",
4448             u".0",
4449             u".5",
4450             u".65",
4451             u".765",
4452             u".8765",
4453             u".08765",
4454             u".008765",
4455             u".0");
4456 }
4457 
symbols()4458 void NumberFormatterApiTest::symbols() {
4459     assertFormatDescending(
4460             u"French Symbols with Japanese Data 1",
4461             nullptr,
4462             nullptr,
4463             NumberFormatter::with().symbols(FRENCH_SYMBOLS),
4464             Locale::getJapan(),
4465             u"87\u202F650",
4466             u"8\u202F765",
4467             u"876,5",
4468             u"87,65",
4469             u"8,765",
4470             u"0,8765",
4471             u"0,08765",
4472             u"0,008765",
4473             u"0");
4474 
4475     assertFormatSingle(
4476             u"French Symbols with Japanese Data 2",
4477             nullptr,
4478             nullptr,
4479             NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS),
4480             Locale::getJapan(),
4481             12345,
4482             u"1,2\u4E07");
4483 
4484     assertFormatDescending(
4485             u"Latin Numbering System with Arabic Data",
4486             u"currency/USD latin",
4487             u"currency/USD latin",
4488             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
4489             Locale("ar"),
4490             u"\u200F87,650.00 US$",
4491             u"\u200F8,765.00 US$",
4492             u"\u200F876.50 US$",
4493             u"\u200F87.65 US$",
4494             u"\u200F8.76 US$",
4495             u"\u200F0.88 US$",
4496             u"\u200F0.09 US$",
4497             u"\u200F0.01 US$",
4498             u"\u200F0.00 US$");
4499 
4500     assertFormatDescending(
4501             u"Math Numbering System with French Data",
4502             u"numbering-system/mathsanb",
4503             u"numbering-system/mathsanb",
4504             NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB)),
4505             Locale::getFrench(),
4506             u"����\u202F������",
4507             u"��\u202F������",
4508             u"������,��",
4509             u"����,����",
4510             u"��,������",
4511             u"��,��������",
4512             u"��,����������",
4513             u"��,������������",
4514             u"��");
4515 
4516     assertFormatSingle(
4517             u"Swiss Symbols (used in documentation)",
4518             nullptr,
4519             nullptr,
4520             NumberFormatter::with().symbols(SWISS_SYMBOLS),
4521             Locale::getEnglish(),
4522             12345.67,
4523             u"12’345.67");
4524 
4525     assertFormatSingle(
4526             u"Myanmar Symbols (used in documentation)",
4527             nullptr,
4528             nullptr,
4529             NumberFormatter::with().symbols(MYANMAR_SYMBOLS),
4530             Locale::getEnglish(),
4531             12345.67,
4532             u"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
4533 
4534     // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
4535 
4536     assertFormatSingle(
4537             u"Currency symbol should follow number in ar with NS latn",
4538             u"currency/USD latin",
4539             u"currency/USD latin",
4540             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
4541             Locale("ar"),
4542             12345.67,
4543             u"\u200F12,345.67 US$");
4544 
4545     assertFormatSingle(
4546             u"Currency symbol should follow number in ar@numbers=latn",
4547             u"currency/USD",
4548             u"currency/USD",
4549             NumberFormatter::with().unit(USD),
4550             Locale("ar@numbers=latn"),
4551             12345.67,
4552             u"\u200F12,345.67 US$");
4553 
4554     assertFormatSingle(
4555             u"Currency symbol should follow number in ar-EG with NS arab",
4556             u"currency/USD",
4557             u"currency/USD",
4558             NumberFormatter::with().unit(USD),
4559             Locale("ar-EG"),
4560             12345.67,
4561             u"\u200F١٢٬٣٤٥٫٦٧ US$");
4562 
4563     assertFormatSingle(
4564             u"Currency symbol should follow number in ar@numbers=arab",
4565             u"currency/USD",
4566             u"currency/USD",
4567             NumberFormatter::with().unit(USD),
4568             Locale("ar@numbers=arab"),
4569             12345.67,
4570             u"\u200F١٢٬٣٤٥٫٦٧ US$");
4571 
4572     assertFormatSingle(
4573             u"NumberingSystem in API should win over @numbers keyword",
4574             u"currency/USD latin",
4575             u"currency/USD latin",
4576             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
4577             Locale("ar@numbers=arab"),
4578             12345.67,
4579             u"\u200F12,345.67 US$");
4580 
4581     UErrorCode status = U_ZERO_ERROR;
4582     assertEquals(
4583             "NumberingSystem in API should win over @numbers keyword in reverse order",
4584             u"\u200F12,345.67 US$",
4585             NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN))
4586                     .unit(USD)
4587                     .formatDouble(12345.67, status)
4588                     .toString(status));
4589 
4590     DecimalFormatSymbols symbols = SWISS_SYMBOLS;
4591     UnlocalizedNumberFormatter f = NumberFormatter::with().symbols(symbols);
4592     symbols.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol, u"!", status);
4593     assertFormatSingle(
4594             u"Symbols object should be copied",
4595             nullptr,
4596             nullptr,
4597             f,
4598             Locale::getEnglish(),
4599             12345.67,
4600             u"12’345.67");
4601 
4602     assertFormatSingle(
4603             u"The last symbols setter wins",
4604             u"latin",
4605             u"latin",
4606             NumberFormatter::with().symbols(symbols).adoptSymbols(new NumberingSystem(LATN)),
4607             Locale::getEnglish(),
4608             12345.67,
4609             u"12,345.67");
4610 
4611     assertFormatSingle(
4612             u"The last symbols setter wins",
4613             nullptr,
4614             nullptr,
4615             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).symbols(symbols),
4616             Locale::getEnglish(),
4617             12345.67,
4618             u"12!345.67");
4619 }
4620 
4621 // TODO: Enable if/when currency symbol override is added.
4622 //void NumberFormatterTest::symbolsOverride() {
4623 //    DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
4624 //    dfs.setCurrencySymbol("@");
4625 //    dfs.setInternationalCurrencySymbol("foo");
4626 //    assertFormatSingle(
4627 //            u"Custom Short Currency Symbol",
4628 //            NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
4629 //            Locale::getEnglish(),
4630 //            12.3,
4631 //            u"@ 12.30");
4632 //}
4633 
sign()4634 void NumberFormatterApiTest::sign() {
4635     assertFormatSingle(
4636             u"Sign Auto Positive",
4637             u"sign-auto",
4638             u"",
4639             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
4640             Locale::getEnglish(),
4641             444444,
4642             u"444,444");
4643 
4644     assertFormatSingle(
4645             u"Sign Auto Negative",
4646             u"sign-auto",
4647             u"",
4648             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
4649             Locale::getEnglish(),
4650             -444444,
4651             u"-444,444");
4652 
4653     assertFormatSingle(
4654             u"Sign Auto Zero",
4655             u"sign-auto",
4656             u"",
4657             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
4658             Locale::getEnglish(),
4659             0,
4660             u"0");
4661 
4662     assertFormatSingle(
4663             u"Sign Always Positive",
4664             u"sign-always",
4665             u"+!",
4666             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
4667             Locale::getEnglish(),
4668             444444,
4669             u"+444,444");
4670 
4671     assertFormatSingle(
4672             u"Sign Always Negative",
4673             u"sign-always",
4674             u"+!",
4675             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
4676             Locale::getEnglish(),
4677             -444444,
4678             u"-444,444");
4679 
4680     assertFormatSingle(
4681             u"Sign Always Zero",
4682             u"sign-always",
4683             u"+!",
4684             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
4685             Locale::getEnglish(),
4686             0,
4687             u"+0");
4688 
4689     assertFormatSingle(
4690             u"Sign Never Positive",
4691             u"sign-never",
4692             u"+_",
4693             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
4694             Locale::getEnglish(),
4695             444444,
4696             u"444,444");
4697 
4698     assertFormatSingle(
4699             u"Sign Never Negative",
4700             u"sign-never",
4701             u"+_",
4702             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
4703             Locale::getEnglish(),
4704             -444444,
4705             u"444,444");
4706 
4707     assertFormatSingle(
4708             u"Sign Never Zero",
4709             u"sign-never",
4710             u"+_",
4711             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
4712             Locale::getEnglish(),
4713             0,
4714             u"0");
4715 
4716     assertFormatSingle(
4717             u"Sign Accounting Positive",
4718             u"currency/USD sign-accounting",
4719             u"currency/USD ()",
4720             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
4721             Locale::getEnglish(),
4722             444444,
4723             u"$444,444.00");
4724 
4725     assertFormatSingle(
4726             u"Sign Accounting Negative",
4727             u"currency/USD sign-accounting",
4728             u"currency/USD ()",
4729             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
4730             Locale::getEnglish(),
4731             -444444,
4732             u"($444,444.00)");
4733 
4734     assertFormatSingle(
4735             u"Sign Accounting Zero",
4736             u"currency/USD sign-accounting",
4737             u"currency/USD ()",
4738             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
4739             Locale::getEnglish(),
4740             0,
4741             u"$0.00");
4742 
4743     assertFormatSingle(
4744             u"Sign Accounting-Always Positive",
4745             u"currency/USD sign-accounting-always",
4746             u"currency/USD ()!",
4747             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
4748             Locale::getEnglish(),
4749             444444,
4750             u"+$444,444.00");
4751 
4752     assertFormatSingle(
4753             u"Sign Accounting-Always Negative",
4754             u"currency/USD sign-accounting-always",
4755             u"currency/USD ()!",
4756             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
4757             Locale::getEnglish(),
4758             -444444,
4759             u"($444,444.00)");
4760 
4761     assertFormatSingle(
4762             u"Sign Accounting-Always Zero",
4763             u"currency/USD sign-accounting-always",
4764             u"currency/USD ()!",
4765             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
4766             Locale::getEnglish(),
4767             0,
4768             u"+$0.00");
4769 
4770     assertFormatSingle(
4771             u"Sign Except-Zero Positive",
4772             u"sign-except-zero",
4773             u"+?",
4774             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
4775             Locale::getEnglish(),
4776             444444,
4777             u"+444,444");
4778 
4779     assertFormatSingle(
4780             u"Sign Except-Zero Negative",
4781             u"sign-except-zero",
4782             u"+?",
4783             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
4784             Locale::getEnglish(),
4785             -444444,
4786             u"-444,444");
4787 
4788     assertFormatSingle(
4789             u"Sign Except-Zero Zero",
4790             u"sign-except-zero",
4791             u"+?",
4792             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
4793             Locale::getEnglish(),
4794             0,
4795             u"0");
4796 
4797     assertFormatSingle(
4798             u"Sign Accounting-Except-Zero Positive",
4799             u"currency/USD sign-accounting-except-zero",
4800             u"currency/USD ()?",
4801             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
4802             Locale::getEnglish(),
4803             444444,
4804             u"+$444,444.00");
4805 
4806     assertFormatSingle(
4807             u"Sign Accounting-Except-Zero Negative",
4808             u"currency/USD sign-accounting-except-zero",
4809             u"currency/USD ()?",
4810             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
4811             Locale::getEnglish(),
4812             -444444,
4813             u"($444,444.00)");
4814 
4815     assertFormatSingle(
4816             u"Sign Accounting-Except-Zero Zero",
4817             u"currency/USD sign-accounting-except-zero",
4818             u"currency/USD ()?",
4819             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
4820             Locale::getEnglish(),
4821             0,
4822             u"$0.00");
4823 
4824     assertFormatSingle(
4825             u"Sign Negative Positive",
4826             u"sign-negative",
4827             u"+-",
4828             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEGATIVE),
4829             Locale::getEnglish(),
4830             444444,
4831             u"444,444");
4832 
4833     assertFormatSingle(
4834             u"Sign Negative Negative",
4835             u"sign-negative",
4836             u"+-",
4837             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEGATIVE),
4838             Locale::getEnglish(),
4839             -444444,
4840             u"-444,444");
4841 
4842     assertFormatSingle(
4843             u"Sign Negative Negative Zero",
4844             u"sign-negative",
4845             u"+-",
4846             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEGATIVE),
4847             Locale::getEnglish(),
4848             -0.0000001,
4849             u"0");
4850 
4851     assertFormatSingle(
4852             u"Sign Accounting-Negative Positive",
4853             u"currency/USD sign-accounting-negative",
4854             u"currency/USD ()-",
4855             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE).unit(USD),
4856             Locale::getEnglish(),
4857             444444,
4858             u"$444,444.00");
4859 
4860     assertFormatSingle(
4861             u"Sign Accounting-Negative Negative",
4862             u"currency/USD sign-accounting-negative",
4863             u"currency/USD ()-",
4864             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE).unit(USD),
4865             Locale::getEnglish(),
4866             -444444,
4867             "($444,444.00)");
4868 
4869     assertFormatSingle(
4870             u"Sign Accounting-Negative Negative Zero",
4871             u"currency/USD sign-accounting-negative",
4872             u"currency/USD ()-",
4873             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE).unit(USD),
4874             Locale::getEnglish(),
4875             -0.0000001,
4876             u"$0.00");
4877 
4878     assertFormatSingle(
4879             u"Sign Accounting Negative Hidden",
4880             u"currency/USD unit-width-hidden sign-accounting",
4881             u"currency/USD unit-width-hidden ()",
4882             NumberFormatter::with()
4883                     .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4884                     .unit(USD)
4885                     .unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
4886             Locale::getEnglish(),
4887             -444444,
4888             u"(444,444.00)");
4889 
4890     assertFormatSingle(
4891             u"Sign Accounting Negative Narrow",
4892             u"currency/USD unit-width-narrow sign-accounting",
4893             u"currency/USD unit-width-narrow ()",
4894             NumberFormatter::with()
4895                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4896                 .unit(USD)
4897                 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
4898             Locale::getCanada(),
4899             -444444,
4900             u"($444,444.00)");
4901 
4902     assertFormatSingle(
4903             u"Sign Accounting Negative Short",
4904             u"currency/USD sign-accounting",
4905             u"currency/USD ()",
4906             NumberFormatter::with()
4907                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4908                 .unit(USD)
4909                 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
4910             Locale::getCanada(),
4911             -444444,
4912             u"(US$444,444.00)");
4913 
4914     assertFormatSingle(
4915             u"Sign Accounting Negative Iso Code",
4916             u"currency/USD unit-width-iso-code sign-accounting",
4917             u"currency/USD unit-width-iso-code ()",
4918             NumberFormatter::with()
4919                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4920                 .unit(USD)
4921                 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
4922             Locale::getCanada(),
4923             -444444,
4924             u"(USD 444,444.00)");
4925 
4926     // Note: CLDR does not provide an accounting pattern for long name currency.
4927     // We fall back to normal currency format. This may change in the future.
4928     assertFormatSingle(
4929             u"Sign Accounting Negative Full Name",
4930             u"currency/USD unit-width-full-name sign-accounting",
4931             u"currency/USD unit-width-full-name ()",
4932             NumberFormatter::with()
4933                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4934                 .unit(USD)
4935                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4936             Locale::getCanada(),
4937             -444444,
4938             u"-444,444.00 US dollars");
4939 }
4940 
signNearZero()4941 void NumberFormatterApiTest::signNearZero() {
4942     // https://unicode-org.atlassian.net/browse/ICU-20709
4943     IcuTestErrorCode status(*this, "signNearZero");
4944     const struct TestCase {
4945         UNumberSignDisplay sign;
4946         double input;
4947         const char16_t* expected;
4948     } cases[] = {
4949         { UNUM_SIGN_AUTO,  1.1, u"1" },
4950         { UNUM_SIGN_AUTO,  0.9, u"1" },
4951         { UNUM_SIGN_AUTO,  0.1, u"0" },
4952         { UNUM_SIGN_AUTO, -0.1, u"-0" }, // interesting case
4953         { UNUM_SIGN_AUTO, -0.9, u"-1" },
4954         { UNUM_SIGN_AUTO, -1.1, u"-1" },
4955         { UNUM_SIGN_ALWAYS,  1.1, u"+1" },
4956         { UNUM_SIGN_ALWAYS,  0.9, u"+1" },
4957         { UNUM_SIGN_ALWAYS,  0.1, u"+0" },
4958         { UNUM_SIGN_ALWAYS, -0.1, u"-0" },
4959         { UNUM_SIGN_ALWAYS, -0.9, u"-1" },
4960         { UNUM_SIGN_ALWAYS, -1.1, u"-1" },
4961         { UNUM_SIGN_EXCEPT_ZERO,  1.1, u"+1" },
4962         { UNUM_SIGN_EXCEPT_ZERO,  0.9, u"+1" },
4963         { UNUM_SIGN_EXCEPT_ZERO,  0.1, u"0" }, // interesting case
4964         { UNUM_SIGN_EXCEPT_ZERO, -0.1, u"0" }, // interesting case
4965         { UNUM_SIGN_EXCEPT_ZERO, -0.9, u"-1" },
4966         { UNUM_SIGN_EXCEPT_ZERO, -1.1, u"-1" },
4967         { UNUM_SIGN_NEGATIVE,  1.1, u"1" },
4968         { UNUM_SIGN_NEGATIVE,  0.9, u"1" },
4969         { UNUM_SIGN_NEGATIVE,  0.1, u"0" },
4970         { UNUM_SIGN_NEGATIVE, -0.1, u"0" }, // interesting case
4971         { UNUM_SIGN_NEGATIVE, -0.9, u"-1" },
4972         { UNUM_SIGN_NEGATIVE, -1.1, u"-1" },
4973     };
4974     for (const auto& cas : cases) {
4975         auto sign = cas.sign;
4976         auto input = cas.input;
4977         const auto* expected = cas.expected;
4978         auto actual = NumberFormatter::with()
4979             .sign(sign)
4980             .precision(Precision::integer())
4981             .locale(Locale::getUS())
4982             .formatDouble(input, status)
4983             .toString(status);
4984         assertEquals(
4985             DoubleToUnicodeString(input) + " @ SignDisplay " + Int64ToUnicodeString(sign),
4986             expected, actual);
4987     }
4988 }
4989 
signCoverage()4990 void NumberFormatterApiTest::signCoverage() {
4991     // https://unicode-org.atlassian.net/browse/ICU-20708
4992     IcuTestErrorCode status(*this, "signCoverage");
4993     const struct TestCase {
4994         UNumberSignDisplay sign;
4995         const char16_t* expectedStrings[8];
4996     } cases[] = {
4997         { UNUM_SIGN_AUTO, {        u"-∞", u"-1", u"-0",  u"0",  u"1",  u"∞",  u"NaN", u"-NaN" } },
4998         { UNUM_SIGN_ALWAYS, {      u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } },
4999         { UNUM_SIGN_NEVER, {        u"∞",  u"1",  u"0",  u"0",  u"1",  u"∞",  u"NaN",  u"NaN" } },
5000         { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1",  u"0",  u"0", u"+1", u"+∞",  u"NaN",  u"NaN" } },
5001     };
5002     double negNaN = std::copysign(uprv_getNaN(), -0.0);
5003     const double inputs[] = {
5004         -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
5005     };
5006     for (const auto& cas : cases) {
5007         auto sign = cas.sign;
5008         for (int32_t i = 0; i < UPRV_LENGTHOF(inputs); i++) {
5009             auto input = inputs[i];
5010             const auto* expected = cas.expectedStrings[i];
5011             auto actual = NumberFormatter::with()
5012                 .sign(sign)
5013                 .locale(Locale::getUS())
5014                 .formatDouble(input, status)
5015                 .toString(status);
5016             assertEquals(
5017                 DoubleToUnicodeString(input) + " " + Int64ToUnicodeString(sign),
5018                 expected, actual);
5019         }
5020     }
5021 }
5022 
decimal()5023 void NumberFormatterApiTest::decimal() {
5024     assertFormatDescending(
5025             u"Decimal Default",
5026             u"decimal-auto",
5027             u"",
5028             NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO),
5029             Locale::getEnglish(),
5030             u"87,650",
5031             u"8,765",
5032             u"876.5",
5033             u"87.65",
5034             u"8.765",
5035             u"0.8765",
5036             u"0.08765",
5037             u"0.008765",
5038             u"0");
5039 
5040     assertFormatDescending(
5041             u"Decimal Always Shown",
5042             u"decimal-always",
5043             u"decimal-always",
5044             NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS),
5045             Locale::getEnglish(),
5046             u"87,650.",
5047             u"8,765.",
5048             u"876.5",
5049             u"87.65",
5050             u"8.765",
5051             u"0.8765",
5052             u"0.08765",
5053             u"0.008765",
5054             u"0.");
5055 }
5056 
scale()5057 void NumberFormatterApiTest::scale() {
5058     assertFormatDescending(
5059             u"Multiplier None",
5060             u"scale/1",
5061             u"",
5062             NumberFormatter::with().scale(Scale::none()),
5063             Locale::getEnglish(),
5064             u"87,650",
5065             u"8,765",
5066             u"876.5",
5067             u"87.65",
5068             u"8.765",
5069             u"0.8765",
5070             u"0.08765",
5071             u"0.008765",
5072             u"0");
5073 
5074     assertFormatDescending(
5075             u"Multiplier Power of Ten",
5076             u"scale/1000000",
5077             u"scale/1E6",
5078             NumberFormatter::with().scale(Scale::powerOfTen(6)),
5079             Locale::getEnglish(),
5080             u"87,650,000,000",
5081             u"8,765,000,000",
5082             u"876,500,000",
5083             u"87,650,000",
5084             u"8,765,000",
5085             u"876,500",
5086             u"87,650",
5087             u"8,765",
5088             u"0");
5089 
5090     assertFormatDescending(
5091             u"Multiplier Arbitrary Double",
5092             u"scale/5.2",
5093             u"scale/5.2",
5094             NumberFormatter::with().scale(Scale::byDouble(5.2)),
5095             Locale::getEnglish(),
5096             u"455,780",
5097             u"45,578",
5098             u"4,557.8",
5099             u"455.78",
5100             u"45.578",
5101             u"4.5578",
5102             u"0.45578",
5103             u"0.045578",
5104             u"0");
5105 
5106     assertFormatDescending(
5107             u"Multiplier Arbitrary BigDecimal",
5108             u"scale/5.2",
5109             u"scale/5.2",
5110             NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
5111             Locale::getEnglish(),
5112             u"455,780",
5113             u"45,578",
5114             u"4,557.8",
5115             u"455.78",
5116             u"45.578",
5117             u"4.5578",
5118             u"0.45578",
5119             u"0.045578",
5120             u"0");
5121 
5122     assertFormatDescending(
5123             u"Multiplier Arbitrary Double And Power Of Ten",
5124             u"scale/5200",
5125             u"scale/5200",
5126             NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
5127             Locale::getEnglish(),
5128             u"455,780,000",
5129             u"45,578,000",
5130             u"4,557,800",
5131             u"455,780",
5132             u"45,578",
5133             u"4,557.8",
5134             u"455.78",
5135             u"45.578",
5136             u"0");
5137 
5138     assertFormatDescending(
5139             u"Multiplier Zero",
5140             u"scale/0",
5141             u"scale/0",
5142             NumberFormatter::with().scale(Scale::byDouble(0)),
5143             Locale::getEnglish(),
5144             u"0",
5145             u"0",
5146             u"0",
5147             u"0",
5148             u"0",
5149             u"0",
5150             u"0",
5151             u"0",
5152             u"0");
5153 
5154     assertFormatSingle(
5155             u"Multiplier Skeleton Scientific Notation and Percent",
5156             u"percent scale/1E2",
5157             u"%x100",
5158             NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
5159             Locale::getEnglish(),
5160             0.5,
5161             u"50%");
5162 
5163     assertFormatSingle(
5164             u"Negative Multiplier",
5165             u"scale/-5.2",
5166             u"scale/-5.2",
5167             NumberFormatter::with().scale(Scale::byDouble(-5.2)),
5168             Locale::getEnglish(),
5169             2,
5170             u"-10.4");
5171 
5172     assertFormatSingle(
5173             u"Negative One Multiplier",
5174             u"scale/-1",
5175             u"scale/-1",
5176             NumberFormatter::with().scale(Scale::byDouble(-1)),
5177             Locale::getEnglish(),
5178             444444,
5179             u"-444,444");
5180 
5181     assertFormatSingle(
5182             u"Two-Type Multiplier with Overlap",
5183             u"scale/10000",
5184             u"scale/1E4",
5185             NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
5186             Locale::getEnglish(),
5187             2,
5188             u"20,000");
5189 }
5190 
locale()5191 void NumberFormatterApiTest::locale() {
5192     // Coverage for the locale setters.
5193     UErrorCode status = U_ZERO_ERROR;
5194     UnicodeString actual = NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status)
5195             .toString(status);
5196     assertEquals("Locale withLocale()", u"1\u202f234", actual);
5197 
5198     LocalizedNumberFormatter lnf1 = NumberFormatter::withLocale("en").unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
5199             .scale(Scale::powerOfTen(2));
5200     LocalizedNumberFormatter lnf2 = NumberFormatter::with()
5201             .notation(Notation::compactLong()).locale("fr").unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
5202     UnlocalizedNumberFormatter unf1 = lnf1.withoutLocale();
5203     UnlocalizedNumberFormatter unf2 = std::move(lnf2).withoutLocale();
5204 
5205     assertFormatSingle(
5206             u"Formatter after withoutLocale A",
5207             u"unit/meter unit-width-full-name scale/100",
5208             u"unit/meter unit-width-full-name scale/100",
5209             unf1.unit(METER),
5210             "it-IT",
5211             2,
5212             u"200 metri");
5213 
5214     assertFormatSingle(
5215             u"Formatter after withoutLocale B",
5216             u"compact-long unit/meter unit-width-full-name",
5217             u"compact-long unit/meter unit-width-full-name",
5218             unf2.unit(METER),
5219             "ja-JP",
5220             2,
5221             u"2 メートル");
5222 }
5223 
skeletonUserGuideExamples()5224 void NumberFormatterApiTest::skeletonUserGuideExamples() {
5225     IcuTestErrorCode status(*this, "skeletonUserGuideExamples");
5226 
5227     // Test the skeleton examples in userguide/format_parse/numbers/skeletons.md
5228     struct TestCase {
5229         const char16_t* skeleton;
5230         const char16_t* conciseSkeleton;
5231         double input;
5232         const char16_t* expected;
5233     } cases[] = {
5234         {u"percent", u"%", 25, u"25%"},
5235         {u".00", u".00", 25, u"25.00"},
5236         {u"percent .00", u"% .00", 25, u"25.00%"},
5237         {u"scale/100", u"scale/100", 0.3, u"30"},
5238         {u"percent scale/100", u"%x100", 0.3, u"30%"},
5239         {u"measure-unit/length-meter", u"unit/meter", 5, u"5 m"},
5240         {u"measure-unit/length-meter unit-width-full-name", u"unit/meter unit-width-full-name", 5, u"5 meters"},
5241         {u"currency/CAD", u"currency/CAD", 10, u"CA$10.00"},
5242         {u"currency/CAD unit-width-narrow", u"currency/CAD unit-width-narrow", 10, u"$10.00"},
5243         {u"compact-short", u"K", 5000, u"5K"},
5244         {u"compact-long", u"KK", 5000, u"5 thousand"},
5245         {u"compact-short currency/CAD", u"K currency/CAD", 5000, u"CA$5K"},
5246         {u"", u"", 5000, u"5,000"},
5247         {u"group-min2", u",?", 5000, u"5000"},
5248         {u"group-min2", u",?", 15000, u"15,000"},
5249         {u"sign-always", u"+!", 60, u"+60"},
5250         {u"sign-always", u"+!", 0, u"+0"},
5251         {u"sign-except-zero", u"+?", 60, u"+60"},
5252         {u"sign-except-zero", u"+?", 0, u"0"},
5253         {u"sign-accounting currency/CAD", u"() currency/CAD", -40, u"(CA$40.00)"}
5254     };
5255 
5256     for (const auto& cas : cases) {
5257         status.setScope(cas.skeleton);
5258         FormattedNumber actual = NumberFormatter::forSkeleton(cas.skeleton, status)
5259             .locale("en-US")
5260             .formatDouble(cas.input, status);
5261         assertEquals(cas.skeleton, cas.expected, actual.toTempString(status));
5262         status.errIfFailureAndReset();
5263         FormattedNumber actualConcise = NumberFormatter::forSkeleton(cas.conciseSkeleton, status)
5264             .locale("en-US")
5265             .formatDouble(cas.input, status);
5266         assertEquals(cas.conciseSkeleton, cas.expected, actualConcise.toTempString(status));
5267         status.errIfFailureAndReset();
5268     }
5269 }
5270 
formatTypes()5271 void NumberFormatterApiTest::formatTypes() {
5272     UErrorCode status = U_ZERO_ERROR;
5273     LocalizedNumberFormatter formatter = NumberFormatter::withLocale(Locale::getEnglish());
5274 
5275     // Double
5276     assertEquals("Format double", "514.23", formatter.formatDouble(514.23, status).toString(status));
5277 
5278     // Int64
5279     assertEquals("Format int64", "51,423", formatter.formatDouble(51423L, status).toString(status));
5280 
5281     // decNumber
5282     UnicodeString actual = formatter.formatDecimal("98765432123456789E1", status).toString(status);
5283     assertEquals("Format decNumber", u"987,654,321,234,567,890", actual);
5284 
5285     // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
5286     // The number needs to have exactly 40 digits, which is the size of the default buffer.
5287     // (issue discovered by the address sanitizer in C++)
5288     static const char* str = "0.009876543210987654321098765432109876543211";
5289     actual = formatter.precision(Precision::unlimited()).formatDecimal(str, status).toString(status);
5290     assertEquals("Format decNumber to 40 digits", str, actual);
5291 }
5292 
fieldPositionLogic()5293 void NumberFormatterApiTest::fieldPositionLogic() {
5294     IcuTestErrorCode status(*this, "fieldPositionLogic");
5295 
5296     const char16_t* message = u"Field position logic test";
5297 
5298     FormattedNumber fmtd = assertFormatSingle(
5299             message,
5300             u"",
5301             u"",
5302             NumberFormatter::with(),
5303             Locale::getEnglish(),
5304             -9876543210.12,
5305             u"-9,876,543,210.12");
5306 
5307     static const UFieldPosition expectedFieldPositions[] = {
5308             // field, begin index, end index
5309             {UNUM_SIGN_FIELD, 0, 1},
5310             {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
5311             {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
5312             {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
5313             {UNUM_INTEGER_FIELD, 1, 14},
5314             {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
5315             {UNUM_FRACTION_FIELD, 15, 17}};
5316 
5317     assertNumberFieldPositions(
5318             message,
5319             fmtd,
5320             expectedFieldPositions,
5321             UPRV_LENGTHOF(expectedFieldPositions));
5322 
5323     // Test the iteration functionality of nextFieldPosition
5324     ConstrainedFieldPosition actual;
5325     actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
5326     int32_t i = 1;
5327     while (fmtd.nextPosition(actual, status)) {
5328         UFieldPosition expected = expectedFieldPositions[i++];
5329         assertEquals(
5330                 UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
5331                 expected.field,
5332                 actual.getField());
5333         assertEquals(
5334                 UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
5335                 expected.beginIndex,
5336                 actual.getStart());
5337         assertEquals(
5338                 UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
5339                 expected.endIndex,
5340                 actual.getLimit());
5341     }
5342     assertEquals(u"Should have seen all grouping separators", 4, i);
5343 
5344     // Make sure strings without fraction do not contain fraction field
5345     actual.reset();
5346     actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD);
5347     fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
5348     assertFalse(u"No fraction part in an integer", fmtd.nextPosition(actual, status));
5349 }
5350 
fieldPositionCoverage()5351 void NumberFormatterApiTest::fieldPositionCoverage() {
5352     IcuTestErrorCode status(*this, "fieldPositionCoverage");
5353 
5354     {
5355         const char16_t* message = u"Measure unit field position basic";
5356         FormattedNumber result = assertFormatSingle(
5357                 message,
5358                 u"measure-unit/temperature-fahrenheit",
5359                 u"unit/fahrenheit",
5360                 NumberFormatter::with().unit(FAHRENHEIT),
5361                 Locale::getEnglish(),
5362                 68,
5363                 u"68°F");
5364         static const UFieldPosition expectedFieldPositions[] = {
5365                 // field, begin index, end index
5366                 {UNUM_INTEGER_FIELD, 0, 2},
5367                 {UNUM_MEASURE_UNIT_FIELD, 2, 4}};
5368         assertNumberFieldPositions(
5369                 message,
5370                 result,
5371                 expectedFieldPositions,
5372                 UPRV_LENGTHOF(expectedFieldPositions));
5373     }
5374 
5375     {
5376         const char16_t* message = u"Measure unit field position with compound unit";
5377         FormattedNumber result = assertFormatSingle(
5378                 message,
5379                 u"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
5380                 u"unit/fahrenheit-per-day",
5381                 NumberFormatter::with().unit(FAHRENHEIT).perUnit(DAY),
5382                 Locale::getEnglish(),
5383                 68,
5384                 u"68°F/d");
5385         static const UFieldPosition expectedFieldPositions[] = {
5386                 // field, begin index, end index
5387                 {UNUM_INTEGER_FIELD, 0, 2},
5388                 // coverage for old enum:
5389                 {DecimalFormat::kMeasureUnitField, 2, 6}};
5390         assertNumberFieldPositions(
5391                 message,
5392                 result,
5393                 expectedFieldPositions,
5394                 UPRV_LENGTHOF(expectedFieldPositions));
5395     }
5396 
5397     {
5398         const char16_t* message = u"Measure unit field position with spaces";
5399         FormattedNumber result = assertFormatSingle(
5400                 message,
5401                 u"measure-unit/length-meter unit-width-full-name",
5402                 u"unit/meter unit-width-full-name",
5403                 NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
5404                 Locale::getEnglish(),
5405                 68,
5406                 u"68 meters");
5407         static const UFieldPosition expectedFieldPositions[] = {
5408                 // field, begin index, end index
5409                 {UNUM_INTEGER_FIELD, 0, 2},
5410                 // note: field starts after the space
5411                 {UNUM_MEASURE_UNIT_FIELD, 3, 9}};
5412         assertNumberFieldPositions(
5413                 message,
5414                 result,
5415                 expectedFieldPositions,
5416                 UPRV_LENGTHOF(expectedFieldPositions));
5417     }
5418 
5419     {
5420         const char16_t* message = u"Measure unit field position with prefix and suffix, composed m/s";
5421         FormattedNumber result = assertFormatSingle(
5422                 message,
5423                 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
5424                 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
5425                 NumberFormatter::with().unit(METER).perUnit(SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
5426                 "ky", // locale with the interesting data
5427                 68,
5428                 u"секундасына 68 метр");
5429         static const UFieldPosition expectedFieldPositions[] = {
5430                 // field, begin index, end index
5431                 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
5432                 {UNUM_INTEGER_FIELD, 12, 14},
5433                 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
5434         assertNumberFieldPositions(
5435                 message,
5436                 result,
5437                 expectedFieldPositions,
5438                 UPRV_LENGTHOF(expectedFieldPositions));
5439     }
5440 
5441     {
5442         const char16_t* message = u"Measure unit field position with prefix and suffix, built-in m/s";
5443         FormattedNumber result = assertFormatSingle(
5444                 message,
5445                 u"measure-unit/speed-meter-per-second unit-width-full-name",
5446                 u"unit/meter-per-second unit-width-full-name",
5447                 NumberFormatter::with().unit(METER_PER_SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
5448                 "ky", // locale with the interesting data
5449                 68,
5450                 u"секундасына 68 метр");
5451         static const UFieldPosition expectedFieldPositions[] = {
5452                 // field, begin index, end index
5453                 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
5454                 {UNUM_INTEGER_FIELD, 12, 14},
5455                 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
5456         assertNumberFieldPositions(
5457                 message,
5458                 result,
5459                 expectedFieldPositions,
5460                 UPRV_LENGTHOF(expectedFieldPositions));
5461     }
5462 
5463     {
5464         const char16_t* message = u"Measure unit field position with inner spaces";
5465         FormattedNumber result = assertFormatSingle(
5466                 message,
5467                 u"measure-unit/temperature-fahrenheit unit-width-full-name",
5468                 u"unit/fahrenheit unit-width-full-name",
5469                 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
5470                 "vi", // locale with the interesting data
5471                 68,
5472                 u"68 độ F");
5473         static const UFieldPosition expectedFieldPositions[] = {
5474                 // field, begin index, end index
5475                 {UNUM_INTEGER_FIELD, 0, 2},
5476                 // Should trim leading/trailing spaces, but not inner spaces:
5477                 {UNUM_MEASURE_UNIT_FIELD, 3, 7}};
5478         assertNumberFieldPositions(
5479                 message,
5480                 result,
5481                 expectedFieldPositions,
5482                 UPRV_LENGTHOF(expectedFieldPositions));
5483     }
5484 
5485     {
5486         // Data: other{"‎{0} K"} == "\u200E{0} K"
5487         // If that data changes, try to find another example of a non-empty unit prefix/suffix
5488         // that is also all ignorables (whitespace and bidi control marks).
5489         const char16_t* message = u"Measure unit field position with fully ignorable prefix";
5490         FormattedNumber result = assertFormatSingle(
5491                 message,
5492                 u"measure-unit/temperature-kelvin",
5493                 u"unit/kelvin",
5494                 NumberFormatter::with().unit(KELVIN),
5495                 "fa", // locale with the interesting data
5496                 68,
5497                 u"‎۶۸ K");
5498         static const UFieldPosition expectedFieldPositions[] = {
5499                 // field, begin index, end index
5500                 {UNUM_INTEGER_FIELD, 1, 3},
5501                 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
5502         assertNumberFieldPositions(
5503                 message,
5504                 result,
5505                 expectedFieldPositions,
5506                 UPRV_LENGTHOF(expectedFieldPositions));
5507     }
5508 
5509     {
5510         const char16_t* message = u"Compact field basic";
5511         FormattedNumber result = assertFormatSingle(
5512                 message,
5513                 u"compact-short",
5514                 u"K",
5515                 NumberFormatter::with().notation(Notation::compactShort()),
5516                 Locale::getUS(),
5517                 65000,
5518                 u"65K");
5519         static const UFieldPosition expectedFieldPositions[] = {
5520                 // field, begin index, end index
5521                 {UNUM_INTEGER_FIELD, 0, 2},
5522                 {UNUM_COMPACT_FIELD, 2, 3}};
5523         assertNumberFieldPositions(
5524                 message,
5525                 result,
5526                 expectedFieldPositions,
5527                 UPRV_LENGTHOF(expectedFieldPositions));
5528     }
5529 
5530     {
5531         const char16_t* message = u"Compact field with spaces";
5532         FormattedNumber result = assertFormatSingle(
5533                 message,
5534                 u"compact-long",
5535                 u"KK",
5536                 NumberFormatter::with().notation(Notation::compactLong()),
5537                 Locale::getUS(),
5538                 65000,
5539                 u"65 thousand");
5540         static const UFieldPosition expectedFieldPositions[] = {
5541                 // field, begin index, end index
5542                 {UNUM_INTEGER_FIELD, 0, 2},
5543                 {UNUM_COMPACT_FIELD, 3, 11}};
5544         assertNumberFieldPositions(
5545                 message,
5546                 result,
5547                 expectedFieldPositions,
5548                 UPRV_LENGTHOF(expectedFieldPositions));
5549     }
5550 
5551     {
5552         const char16_t* message = u"Compact field with inner space";
5553         FormattedNumber result = assertFormatSingle(
5554                 message,
5555                 u"compact-long",
5556                 u"KK",
5557                 NumberFormatter::with().notation(Notation::compactLong()),
5558                 "fil",  // locale with interesting data
5559                 6000,
5560                 u"6 na libo");
5561         static const UFieldPosition expectedFieldPositions[] = {
5562                 // field, begin index, end index
5563                 {UNUM_INTEGER_FIELD, 0, 1},
5564                 {UNUM_COMPACT_FIELD, 2, 9}};
5565         assertNumberFieldPositions(
5566                 message,
5567                 result,
5568                 expectedFieldPositions,
5569                 UPRV_LENGTHOF(expectedFieldPositions));
5570     }
5571 
5572     {
5573         const char16_t* message = u"Compact field with bidi mark";
5574         FormattedNumber result = assertFormatSingle(
5575                 message,
5576                 u"compact-long",
5577                 u"KK",
5578                 NumberFormatter::with().notation(Notation::compactLong()),
5579                 "he",  // locale with interesting data
5580                 6000,
5581                 u"\u200F6 אלף");
5582         static const UFieldPosition expectedFieldPositions[] = {
5583                 // field, begin index, end index
5584                 {UNUM_INTEGER_FIELD, 1, 2},
5585                 {UNUM_COMPACT_FIELD, 3, 6}};
5586         assertNumberFieldPositions(
5587                 message,
5588                 result,
5589                 expectedFieldPositions,
5590                 UPRV_LENGTHOF(expectedFieldPositions));
5591     }
5592 
5593     {
5594         const char16_t* message = u"Compact with currency fields";
5595         FormattedNumber result = assertFormatSingle(
5596                 message,
5597                 u"compact-short currency/USD",
5598                 u"K currency/USD",
5599                 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
5600                 "sr_Latn",  // locale with interesting data
5601                 65000,
5602                 u"65 hilj. US$");
5603         static const UFieldPosition expectedFieldPositions[] = {
5604                 // field, begin index, end index
5605                 {UNUM_INTEGER_FIELD, 0, 2},
5606                 {UNUM_COMPACT_FIELD, 3, 8},
5607                 {UNUM_CURRENCY_FIELD, 9, 12}};
5608         assertNumberFieldPositions(
5609                 message,
5610                 result,
5611                 expectedFieldPositions,
5612                 UPRV_LENGTHOF(expectedFieldPositions));
5613     }
5614 
5615     {
5616         const char16_t* message = u"Currency long name fields";
5617         FormattedNumber result = assertFormatSingle(
5618                 message,
5619                 u"currency/USD unit-width-full-name",
5620                 u"currency/USD unit-width-full-name",
5621                 NumberFormatter::with().unit(USD)
5622                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
5623                 "en",
5624                 12345,
5625                 u"12,345.00 US dollars");
5626         static const UFieldPosition expectedFieldPositions[] = {
5627                 // field, begin index, end index
5628                 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
5629                 {UNUM_INTEGER_FIELD, 0, 6},
5630                 {UNUM_DECIMAL_SEPARATOR_FIELD, 6, 7},
5631                 {UNUM_FRACTION_FIELD, 7, 9},
5632                 {UNUM_CURRENCY_FIELD, 10, 20}};
5633         assertNumberFieldPositions(
5634                 message,
5635                 result,
5636                 expectedFieldPositions,
5637                 UPRV_LENGTHOF(expectedFieldPositions));
5638     }
5639 
5640     {
5641         const char16_t* message = u"Compact with measure unit fields";
5642         FormattedNumber result = assertFormatSingle(
5643                 message,
5644                 u"compact-long measure-unit/length-meter unit-width-full-name",
5645                 u"KK unit/meter unit-width-full-name",
5646                 NumberFormatter::with().notation(Notation::compactLong())
5647                     .unit(METER)
5648                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
5649                 Locale::getUS(),
5650                 65000,
5651                 u"65 thousand meters");
5652         static const UFieldPosition expectedFieldPositions[] = {
5653                 // field, begin index, end index
5654                 {UNUM_INTEGER_FIELD, 0, 2},
5655                 {UNUM_COMPACT_FIELD, 3, 11},
5656                 {UNUM_MEASURE_UNIT_FIELD, 12, 18}};
5657         assertNumberFieldPositions(
5658                 message,
5659                 result,
5660                 expectedFieldPositions,
5661                 UPRV_LENGTHOF(expectedFieldPositions));
5662     }
5663 }
5664 
toFormat()5665 void NumberFormatterApiTest::toFormat() {
5666     IcuTestErrorCode status(*this, "icuFormat");
5667     LocalizedNumberFormatter lnf = NumberFormatter::withLocale("fr")
5668             .precision(Precision::fixedFraction(3));
5669     LocalPointer<Format> format(lnf.toFormat(status), status);
5670     FieldPosition fpos(UNUM_DECIMAL_SEPARATOR_FIELD);
5671     UnicodeString sb;
5672     format->format(514.23, sb, fpos, status);
5673     assertEquals("Should correctly format number", u"514,230", sb);
5674     assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
5675     assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
5676     assertEquals(
5677             "ICU Format should round-trip",
5678             lnf.toSkeleton(status),
5679             dynamic_cast<LocalizedNumberFormatterAsFormat*>(format.getAlias())->getNumberFormatter()
5680                     .toSkeleton(status));
5681 
5682     UFormattedNumberData result;
5683     result.quantity.setToDouble(514.23);
5684     lnf.formatImpl(&result, status);
5685     FieldPositionIterator fpi1;
5686     {
5687         FieldPositionIteratorHandler fpih(&fpi1, status);
5688         result.getAllFieldPositions(fpih, status);
5689     }
5690 
5691     FieldPositionIterator fpi2;
5692     format->format(514.23, sb.remove(), &fpi2, status);
5693 
5694     assertTrue("Should produce same field position iterator", fpi1 == fpi2);
5695 }
5696 
errors()5697 void NumberFormatterApiTest::errors() {
5698     LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
5699             Precision::fixedFraction(
5700                     -1));
5701 
5702     // formatInt
5703     UErrorCode status = U_ZERO_ERROR;
5704     FormattedNumber fn = lnf.formatInt(1, status);
5705     assertEquals(
5706             "Should fail in formatInt method with error code for rounding",
5707             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5708             status);
5709 
5710     // formatDouble
5711     status = U_ZERO_ERROR;
5712     fn = lnf.formatDouble(1.0, status);
5713     assertEquals(
5714             "Should fail in formatDouble method with error code for rounding",
5715             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5716             status);
5717 
5718     // formatDecimal (decimal error)
5719     status = U_ZERO_ERROR;
5720     fn = NumberFormatter::withLocale("en").formatDecimal("1x2", status);
5721     assertEquals(
5722             "Should fail in formatDecimal method with error code for decimal number syntax",
5723             U_DECIMAL_NUMBER_SYNTAX_ERROR,
5724             status);
5725 
5726     // formatDecimal (setting error)
5727     status = U_ZERO_ERROR;
5728     fn = lnf.formatDecimal("1.0", status);
5729     assertEquals(
5730             "Should fail in formatDecimal method with error code for rounding",
5731             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5732             status);
5733 
5734     // Skeleton string
5735     status = U_ZERO_ERROR;
5736     UnicodeString output = lnf.toSkeleton(status);
5737     assertEquals(
5738             "Should fail on toSkeleton terminal method with correct error code",
5739             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5740             status);
5741     assertTrue(
5742             "Terminal toSkeleton on error object should be bogus",
5743             output.isBogus());
5744 
5745     // FieldPosition (constrained category)
5746     status = U_ZERO_ERROR;
5747     ConstrainedFieldPosition fp;
5748     fp.constrainCategory(UFIELD_CATEGORY_NUMBER);
5749     fn.nextPosition(fp, status);
5750     assertEquals(
5751             "Should fail on FieldPosition terminal method with correct error code",
5752             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5753             status);
5754 
5755     // FieldPositionIterator (no constraints)
5756     status = U_ZERO_ERROR;
5757     fp.reset();
5758     fn.nextPosition(fp, status);
5759     assertEquals(
5760             "Should fail on FieldPositoinIterator terminal method with correct error code",
5761             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5762             status);
5763 
5764     // Appendable
5765     status = U_ZERO_ERROR;
5766     UnicodeStringAppendable appendable(output.remove());
5767     fn.appendTo(appendable, status);
5768     assertEquals(
5769             "Should fail on Appendable terminal method with correct error code",
5770             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5771             status);
5772 
5773     // UnicodeString
5774     status = U_ZERO_ERROR;
5775     output = fn.toString(status);
5776     assertEquals(
5777             "Should fail on UnicodeString terminal method with correct error code",
5778             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5779             status);
5780     assertTrue(
5781             "Terminal UnicodeString on error object should be bogus",
5782             output.isBogus());
5783 
5784     // CopyErrorTo
5785     status = U_ZERO_ERROR;
5786     lnf.copyErrorTo(status);
5787     assertEquals(
5788             "Should fail since rounder is not legal with correct error code",
5789             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
5790             status);
5791 }
5792 
validRanges()5793 void NumberFormatterApiTest::validRanges() {
5794 
5795 #define EXPECTED_MAX_INT_FRAC_SIG 999
5796 
5797 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) UPRV_BLOCK_MACRO_BEGIN { \
5798     UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
5799         ? U_ZERO_ERROR \
5800         : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
5801     assertEquals( \
5802         UnicodeString(u"Incorrect status for " #method " on input ") \
5803             + Int64ToUnicodeString(argument), \
5804         expectedStatus, \
5805         status); \
5806 } UPRV_BLOCK_MACRO_END
5807 
5808 #define VALID_RANGE_ONEARG(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
5809     for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
5810         UErrorCode status = U_ZERO_ERROR; \
5811         NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
5812         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
5813     } \
5814 } UPRV_BLOCK_MACRO_END
5815 
5816 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
5817     for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
5818         UErrorCode status = U_ZERO_ERROR; \
5819         /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
5820         NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
5821         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
5822         status = U_ZERO_ERROR; \
5823         /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
5824         NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
5825         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
5826         /* Check that first argument must be less than or equal to second argument */ \
5827         NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
5828         assertEquals("Incorrect status for " #method " on max < min input", \
5829             U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
5830             status); \
5831     } \
5832 } UPRV_BLOCK_MACRO_END
5833 
5834     VALID_RANGE_ONEARG(precision, Precision::fixedFraction, 0);
5835     VALID_RANGE_ONEARG(precision, Precision::minFraction, 0);
5836     VALID_RANGE_ONEARG(precision, Precision::maxFraction, 0);
5837     VALID_RANGE_TWOARGS(precision, Precision::minMaxFraction, 0);
5838     VALID_RANGE_ONEARG(precision, Precision::fixedSignificantDigits, 1);
5839     VALID_RANGE_ONEARG(precision, Precision::minSignificantDigits, 1);
5840     VALID_RANGE_ONEARG(precision, Precision::maxSignificantDigits, 1);
5841     VALID_RANGE_TWOARGS(precision, Precision::minMaxSignificantDigits, 1);
5842     VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMinDigits, 1);
5843     VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMaxDigits, 1);
5844     VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1);
5845     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0);
5846     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1);
5847 }
5848 
copyMove()5849 void NumberFormatterApiTest::copyMove() {
5850     IcuTestErrorCode status(*this, "copyMove");
5851 
5852     // Default constructors
5853     LocalizedNumberFormatter l1;
5854     assertEquals("Initial behavior", u"10", l1.formatInt(10, status).toString(status), true);
5855     if (status.errDataIfFailureAndReset()) { return; }
5856     assertEquals("Initial call count", 1, l1.getCallCount());
5857     assertTrue("Initial compiled", l1.getCompiled() == nullptr);
5858 
5859     // Setup
5860     l1 = NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
5861     assertEquals("Initial behavior", u"10%", l1.formatInt(10, status).toString(status));
5862     assertEquals("Initial call count", 1, l1.getCallCount());
5863     assertTrue("Initial compiled", l1.getCompiled() == nullptr);
5864     l1.formatInt(123, status);
5865     assertEquals("Still not compiled", 2, l1.getCallCount());
5866     assertTrue("Still not compiled", l1.getCompiled() == nullptr);
5867     l1.formatInt(123, status);
5868     assertEquals("Compiled", u"10%", l1.formatInt(10, status).toString(status));
5869     assertEquals("Compiled", INT32_MIN, l1.getCallCount());
5870     assertTrue("Compiled", l1.getCompiled() != nullptr);
5871 
5872     // Copy constructor
5873     LocalizedNumberFormatter l2 = l1;
5874     assertEquals("[constructor] Copy behavior", u"10%", l2.formatInt(10, status).toString(status));
5875     assertEquals("[constructor] Copy should not have compiled state", 1, l2.getCallCount());
5876     assertTrue("[constructor] Copy should not have compiled state", l2.getCompiled() == nullptr);
5877 
5878     // Move constructor
5879     LocalizedNumberFormatter l3 = std::move(l1);
5880     assertEquals("[constructor] Move behavior", u"10%", l3.formatInt(10, status).toString(status));
5881     assertEquals("[constructor] Move *should* have compiled state", INT32_MIN, l3.getCallCount());
5882     assertTrue("[constructor] Move *should* have compiled state", l3.getCompiled() != nullptr);
5883     assertEquals("[constructor] Source should be reset after move", 0, l1.getCallCount());
5884     assertTrue("[constructor] Source should be reset after move", l1.getCompiled() == nullptr);
5885 
5886     // Reset l1 and l2 to check for macro-props copying for behavior testing
5887     // Make the test more interesting: also warm them up with a compiled formatter.
5888     l1 = NumberFormatter::withLocale("en");
5889     l1.formatInt(1, status);
5890     l1.formatInt(1, status);
5891     l1.formatInt(1, status);
5892     l2 = NumberFormatter::withLocale("en");
5893     l2.formatInt(1, status);
5894     l2.formatInt(1, status);
5895     l2.formatInt(1, status);
5896 
5897     // Copy assignment
5898     l1 = l3;
5899     assertEquals("[assignment] Copy behavior", u"10%", l1.formatInt(10, status).toString(status));
5900     assertEquals("[assignment] Copy should not have compiled state", 1, l1.getCallCount());
5901     assertTrue("[assignment] Copy should not have compiled state", l1.getCompiled() == nullptr);
5902 
5903     // Move assignment
5904     l2 = std::move(l3);
5905     assertEquals("[assignment] Move behavior", u"10%", l2.formatInt(10, status).toString(status));
5906     assertEquals("[assignment] Move *should* have compiled state", INT32_MIN, l2.getCallCount());
5907     assertTrue("[assignment] Move *should* have compiled state", l2.getCompiled() != nullptr);
5908     assertEquals("[assignment] Source should be reset after move", 0, l3.getCallCount());
5909     assertTrue("[assignment] Source should be reset after move", l3.getCompiled() == nullptr);
5910 
5911     // Coverage tests for UnlocalizedNumberFormatter
5912     UnlocalizedNumberFormatter u1;
5913     assertEquals("Default behavior", u"10", u1.locale("en").formatInt(10, status).toString(status));
5914     u1 = u1.unit(NoUnit::percent());
5915     assertEquals("Copy assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
5916     UnlocalizedNumberFormatter u2 = u1;
5917     assertEquals("Copy constructor", u"10%", u2.locale("en").formatInt(10, status).toString(status));
5918     UnlocalizedNumberFormatter u3 = std::move(u1);
5919     assertEquals("Move constructor", u"10%", u3.locale("en").formatInt(10, status).toString(status));
5920     u1 = NumberFormatter::with();
5921     u1 = std::move(u2);
5922     assertEquals("Move assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
5923 
5924     // FormattedNumber move operators
5925     FormattedNumber result = l1.formatInt(10, status);
5926     assertEquals("FormattedNumber move constructor", u"10%", result.toString(status));
5927     result = l1.formatInt(20, status);
5928     assertEquals("FormattedNumber move assignment", u"20%", result.toString(status));
5929 }
5930 
localPointerCAPI()5931 void NumberFormatterApiTest::localPointerCAPI() {
5932     // NOTE: This is also the sample code in unumberformatter.h
5933     UErrorCode ec = U_ZERO_ERROR;
5934 
5935     // Setup:
5936     LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(u"percent", -1, "en", &ec));
5937     LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
5938     if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
5939 
5940     // Format a decimal number:
5941     unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
5942     if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
5943 
5944     // Get the location of the percent sign:
5945     UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
5946     unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
5947     assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
5948     assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
5949 
5950     // No need to do any cleanup since we are using LocalPointer.
5951 }
5952 
toObject()5953 void NumberFormatterApiTest::toObject() {
5954     IcuTestErrorCode status(*this, "toObject");
5955 
5956     // const lvalue version
5957     {
5958         LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en")
5959                 .precision(Precision::fixedFraction(2));
5960         LocalPointer<LocalizedNumberFormatter> lnf2(lnf.clone());
5961         assertFalse("should create successfully, const lvalue", lnf2.isNull());
5962         assertEquals("object API test, const lvalue", u"1,000.00",
5963             lnf2->formatDouble(1000, status).toString(status));
5964     }
5965 
5966     // rvalue reference version
5967     {
5968         LocalPointer<LocalizedNumberFormatter> lnf(
5969             NumberFormatter::withLocale("en")
5970                 .precision(Precision::fixedFraction(2))
5971                 .clone());
5972         assertFalse("should create successfully, rvalue reference", lnf.isNull());
5973         assertEquals("object API test, rvalue reference", u"1,000.00",
5974             lnf->formatDouble(1000, status).toString(status));
5975     }
5976 
5977     // to std::unique_ptr via constructor
5978     {
5979         std::unique_ptr<LocalizedNumberFormatter> lnf(
5980             NumberFormatter::withLocale("en")
5981                 .precision(Precision::fixedFraction(2))
5982                 .clone());
5983         assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf));
5984         assertEquals("object API test, unique_ptr", u"1,000.00",
5985             lnf->formatDouble(1000, status).toString(status));
5986     }
5987 
5988     // to std::unique_ptr via assignment
5989     {
5990         std::unique_ptr<LocalizedNumberFormatter> lnf =
5991             NumberFormatter::withLocale("en")
5992                 .precision(Precision::fixedFraction(2))
5993                 .clone();
5994         assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
5995         assertEquals("object API test, unique_ptr B", u"1,000.00",
5996             lnf->formatDouble(1000, status).toString(status));
5997     }
5998 
5999     // to LocalPointer via assignment
6000     {
6001         LocalPointer<UnlocalizedNumberFormatter> f =
6002             NumberFormatter::with().clone();
6003     }
6004 
6005     // make sure no memory leaks
6006     {
6007         NumberFormatter::with().clone();
6008     }
6009 }
6010 
toDecimalNumber()6011 void NumberFormatterApiTest::toDecimalNumber() {
6012     IcuTestErrorCode status(*this, "toDecimalNumber");
6013     FormattedNumber fn = NumberFormatter::withLocale("bn-BD")
6014         .scale(Scale::powerOfTen(2))
6015         .precision(Precision::maxSignificantDigits(5))
6016         .formatDouble(9.87654321e12, status);
6017     assertEquals("Should have expected localized string result",
6018         u"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn.toString(status));
6019     assertEquals(u"Should have expected toDecimalNumber string result",
6020         "9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
6021 
6022     fn = NumberFormatter::withLocale("bn-BD").formatDouble(0, status);
6023     assertEquals("Should have expected localized string result",
6024         u"০", fn.toString(status));
6025     assertEquals(u"Should have expected toDecimalNumber string result",
6026         "0", fn.toDecimalNumber<std::string>(status).c_str());
6027 }
6028 
microPropsInternals()6029 void NumberFormatterApiTest::microPropsInternals() {
6030     // Verify copy construction and assignment operators.
6031     int64_t testValues[2] = {4, 61};
6032 
6033     MicroProps mp;
6034     assertEquals("capacity", 2, mp.mixedMeasures.getCapacity());
6035     mp.mixedMeasures[0] = testValues[0];
6036     mp.mixedMeasures[1] = testValues[1];
6037     MicroProps copyConstructed(mp);
6038     MicroProps copyAssigned;
6039     int64_t *resizeResult = mp.mixedMeasures.resize(4, 4);
6040     assertTrue("Resize success", resizeResult != nullptr);
6041     copyAssigned = mp;
6042 
6043     assertTrue("MicroProps success status", U_SUCCESS(mp.mixedMeasures.status));
6044     assertTrue("Copy Constructed success status", U_SUCCESS(copyConstructed.mixedMeasures.status));
6045     assertTrue("Copy Assigned success status", U_SUCCESS(copyAssigned.mixedMeasures.status));
6046     assertEquals("Original values[0]", testValues[0], mp.mixedMeasures[0]);
6047     assertEquals("Original values[1]", testValues[1], mp.mixedMeasures[1]);
6048     assertEquals("Copy Constructed[0]", testValues[0], copyConstructed.mixedMeasures[0]);
6049     assertEquals("Copy Constructed[1]", testValues[1], copyConstructed.mixedMeasures[1]);
6050     assertEquals("Copy Assigned[0]", testValues[0], copyAssigned.mixedMeasures[0]);
6051     assertEquals("Copy Assigned[1]", testValues[1], copyAssigned.mixedMeasures[1]);
6052     assertEquals("Original capacity", 4, mp.mixedMeasures.getCapacity());
6053     assertEquals("Copy Constructed capacity", 2, copyConstructed.mixedMeasures.getCapacity());
6054     assertEquals("Copy Assigned capacity", 4, copyAssigned.mixedMeasures.getCapacity());
6055 }
6056 
formatUnitsAliases()6057 void NumberFormatterApiTest::formatUnitsAliases() {
6058     IcuTestErrorCode status(*this, "formatUnitsAliases");
6059 
6060     struct TestCase {
6061         const MeasureUnit measureUnit;
6062         const UnicodeString expectedFormat;
6063     } testCases[]{
6064         // Aliases
6065         {MeasureUnit::getMilligramPerDeciliter(), u"2 milligrams per deciliter"},
6066         {MeasureUnit::getLiterPer100Kilometers(), u"2 liters per 100 kilometers"},
6067         {MeasureUnit::getPartPerMillion(), u"2 parts per million"},
6068         {MeasureUnit::getMillimeterOfMercury(), u"2 millimeters of mercury"},
6069 
6070         // Replacements
6071         {MeasureUnit::getMilligramOfglucosePerDeciliter(), u"2 milligrams per deciliter"},
6072         {MeasureUnit::forIdentifier("millimeter-ofhg", status), u"2 millimeters of mercury"},
6073         {MeasureUnit::forIdentifier("liter-per-100-kilometer", status), u"2 liters per 100 kilometers"},
6074         {MeasureUnit::forIdentifier("permillion", status), u"2 parts per million"},
6075     };
6076 
6077     for (const auto &testCase : testCases) {
6078         UnicodeString actualFormat = NumberFormatter::withLocale(icu::Locale::getEnglish())
6079                                          .unit(testCase.measureUnit)
6080                                          .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME)
6081                                          .formatDouble(2.0, status)
6082                                          .toString(status);
6083 
6084         assertEquals("test unit aliases", testCase.expectedFormat, actualFormat);
6085     }
6086 }
6087 
testIssue22378()6088 void NumberFormatterApiTest::testIssue22378() {
6089     IcuTestErrorCode status(*this, "testIssue22378");
6090 
6091     // I checked the results before the fix and everything works the same except
6092     // "fr-FR-u-mu-fahrenhe" and "fr_FR@mu=fahrenhe"
6093     struct TestCase {
6094         const std::string localeId;
6095         const UnicodeString expectedFormat;
6096     } testCases[]{
6097         {"en-US", u"73\u00B0F"},
6098         {"en-US-u-mu-fahrenhe", u"73\u00B0F"},
6099         // Unlike ULocale, forLanguageTag fails wih U_ILLEGAL_ARGUMENT_ERROR
6100         // because fahrenheit is not valid value for -u-mu-
6101         // {"en-US-u-mu-fahrenheit", u"73\u00B0F"},
6102         {"en-US-u-mu-celsius", u"23\u00B0C"},
6103         {"en-US-u-mu-badvalue", u"73\u00B0F"},
6104         {"en_US@mu=fahrenhe", u"73\u00B0F"},
6105         {"en_US@mu=fahrenheit", u"73\u00B0F"},
6106         {"en_US@mu=celsius", u"23\u00B0C"},
6107         {"en_US@mu=badvalue", u"73\u00B0F"},
6108 
6109         {"fr-FR", u"23\u202F\u00B0C"},
6110         {"fr-FR-u-mu-fahrenhe", u"73\u202F\u00B0F"},
6111         // Unlike ULocale, forLanguageTag fails wih U_ILLEGAL_ARGUMENT_ERROR
6112         // because fahrenheit is not valid value for -u-mu-
6113         // {"fr-FR-u-mu-fahrenheit", u"23\u202F\u00B0C"},
6114         {"fr-FR-u-mu-celsius", u"23\u202F\u00B0C"},
6115         {"fr-FR-u-mu-badvalue", u"23\u202F\u00B0C"},
6116         {"fr_FR@mu=fahrenhe", u"73\u202F\u00B0F"},
6117         {"fr_FR@mu=fahrenheit", u"73\u202F\u00B0F"},
6118         {"fr_FR@mu=celsius", u"23\u202F\u00B0C"},
6119         {"fr_FR@mu=badvalue", u"23\u202F\u00B0C"},
6120     };
6121 
6122     UnlocalizedNumberFormatter formatter = NumberFormatter::with()
6123             .usage("weather")
6124             .unit(MeasureUnit::getCelsius());
6125     double value = 23.0;
6126 
6127     for (const auto &testCase : testCases) {
6128         std::string localeId = testCase.localeId;
6129         const Locale locale = (localeId.find("@") != std::string::npos)
6130                 ? Locale(localeId.c_str())
6131                 : Locale::forLanguageTag(localeId, status);
6132         UnicodeString actualFormat = formatter.locale(locale)
6133                 .formatDouble(value, status)
6134                 .toString(status);
6135         assertEquals(u"-u-mu- honored (" + UnicodeString(localeId.c_str()) + u")",
6136                 testCase.expectedFormat, actualFormat);
6137     }
6138 
6139     UnicodeString result = formatter.locale("en-US").formatDouble(value, status).getOutputUnit(status).getIdentifier();
6140     assertEquals("Testing default -u-mu- for en-US", MeasureUnit::getFahrenheit().getIdentifier(), result);
6141     result = formatter.locale("fr-FR").formatDouble(value, status).getOutputUnit(status).getIdentifier();
6142     assertEquals("Testing default -u-mu- for fr-FR", MeasureUnit::getCelsius().getIdentifier(), result);
6143 }
6144 
6145 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
6146  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
6147  * not round-trip uskeleton itself.
6148  *
6149  * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
6150  *
6151  * If `uskeleton` is nullptr, toSkeleton is expected to return an
6152  * U_UNSUPPORTED_ERROR.
6153  */
assertFormatDescending(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)6154 void NumberFormatterApiTest::assertFormatDescending(
6155         const char16_t* umessage,
6156         const char16_t* uskeleton,
6157         const char16_t* conciseSkeleton,
6158         const UnlocalizedNumberFormatter& f,
6159         Locale locale,
6160         ...) {
6161     va_list args;
6162     va_start(args, locale);
6163     UnicodeString message(true, umessage, -1);
6164     static double inputs[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
6165     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
6166     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
6167     IcuTestErrorCode status(*this, "assertFormatDescending");
6168     status.setScope(message);
6169     UnicodeString expecteds[10];
6170     for (int16_t i = 0; i < 9; i++) {
6171         char16_t caseNumber = u'0' + i;
6172         double d = inputs[i];
6173         UnicodeString expected = va_arg(args, const char16_t*);
6174         expecteds[i] = expected;
6175         UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
6176         assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
6177         assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
6178         UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
6179         assertSuccess(message + u": Safe Path: " + caseNumber, status);
6180         assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
6181     }
6182     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
6183         UnicodeString skeleton(true, uskeleton, -1);
6184         // Only compare normalized skeletons: the tests need not provide the normalized forms.
6185         // Use the normalized form to construct the testing formatter to guarantee no loss of info.
6186         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
6187         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
6188         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
6189         for (int32_t i = 0; i < 9; i++) {
6190             double d = inputs[i];
6191             UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
6192             assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
6193         }
6194         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
6195         // If the concise skeleton starts with '~', disable the round-trip check.
6196         bool shouldRoundTrip = true;
6197         if (conciseSkeleton[0] == u'~') {
6198             conciseSkeleton++;
6199             shouldRoundTrip = false;
6200         }
6201         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
6202         if (shouldRoundTrip) {
6203             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
6204         }
6205         for (int32_t i = 0; i < 9; i++) {
6206             double d = inputs[i];
6207             UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
6208             assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
6209         }
6210     } else {
6211         assertUndefinedSkeleton(f);
6212     }
6213 }
6214 
6215 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
6216  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
6217  * not round-trip uskeleton itself.
6218  *
6219  * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
6220  *
6221  * If `uskeleton` is nullptr, toSkeleton is expected to return an
6222  * U_UNSUPPORTED_ERROR.
6223  */
assertFormatDescendingBig(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)6224 void NumberFormatterApiTest::assertFormatDescendingBig(
6225         const char16_t* umessage,
6226         const char16_t* uskeleton,
6227         const char16_t* conciseSkeleton,
6228         const UnlocalizedNumberFormatter& f,
6229         Locale locale,
6230         ...) {
6231     va_list args;
6232     va_start(args, locale);
6233     UnicodeString message(true, umessage, -1);
6234     static double inputs[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
6235     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
6236     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
6237     IcuTestErrorCode status(*this, "assertFormatDescendingBig");
6238     status.setScope(message);
6239     UnicodeString expecteds[10];
6240     for (int16_t i = 0; i < 9; i++) {
6241         char16_t caseNumber = u'0' + i;
6242         double d = inputs[i];
6243         UnicodeString expected = va_arg(args, const char16_t*);
6244         expecteds[i] = expected;
6245         UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
6246         assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
6247         assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
6248         UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
6249         assertSuccess(message + u": Safe Path: " + caseNumber, status);
6250         assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
6251     }
6252     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
6253         UnicodeString skeleton(true, uskeleton, -1);
6254         // Only compare normalized skeletons: the tests need not provide the normalized forms.
6255         // Use the normalized form to construct the testing formatter to guarantee no loss of info.
6256         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
6257         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
6258         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
6259         for (int32_t i = 0; i < 9; i++) {
6260             double d = inputs[i];
6261             UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
6262             assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
6263         }
6264         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
6265         // If the concise skeleton starts with '~', disable the round-trip check.
6266         bool shouldRoundTrip = true;
6267         if (conciseSkeleton[0] == u'~') {
6268             conciseSkeleton++;
6269             shouldRoundTrip = false;
6270         }
6271         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
6272         if (shouldRoundTrip) {
6273             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
6274         }
6275         for (int32_t i = 0; i < 9; i++) {
6276             double d = inputs[i];
6277             UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
6278             assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
6279         }
6280     } else {
6281         assertUndefinedSkeleton(f);
6282     }
6283 }
6284 
6285 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
6286  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
6287  * not round-trip uskeleton itself.
6288  *
6289  * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
6290  *
6291  * If `uskeleton` is nullptr, toSkeleton is expected to return an
6292  * U_UNSUPPORTED_ERROR.
6293  */
6294 FormattedNumber
assertFormatSingle(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,double input,const UnicodeString & expected)6295 NumberFormatterApiTest::assertFormatSingle(
6296         const char16_t* umessage,
6297         const char16_t* uskeleton,
6298         const char16_t* conciseSkeleton,
6299         const UnlocalizedNumberFormatter& f,
6300         Locale locale,
6301         double input,
6302         const UnicodeString& expected) {
6303     UnicodeString message(true, umessage, -1);
6304     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
6305     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
6306     IcuTestErrorCode status(*this, "assertFormatSingle");
6307     status.setScope(message);
6308     FormattedNumber result1 = l1.formatDouble(input, status);
6309     UnicodeString actual1 = result1.toString(status);
6310     assertSuccess(message + u": Unsafe Path", status);
6311     assertEquals(message + u": Unsafe Path", expected, actual1);
6312     UnicodeString actual2 = l2.formatDouble(input, status).toString(status);
6313     assertSuccess(message + u": Safe Path", status);
6314     assertEquals(message + u": Safe Path", expected, actual2);
6315     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
6316         UnicodeString skeleton(true, uskeleton, -1);
6317         // Only compare normalized skeletons: the tests need not provide the normalized forms.
6318         // Use the normalized form to construct the testing formatter to ensure no loss of info.
6319         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
6320         assertEquals(message + ": Skeleton", normalized, f.toSkeleton(status));
6321         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
6322         UnicodeString actual3 = l3.formatDouble(input, status).toString(status);
6323         assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
6324         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
6325         // If the concise skeleton starts with '~', disable the round-trip check.
6326         bool shouldRoundTrip = true;
6327         if (conciseSkeleton[0] == u'~') {
6328             conciseSkeleton++;
6329             shouldRoundTrip = false;
6330         }
6331         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
6332         if (shouldRoundTrip) {
6333             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
6334         }
6335         UnicodeString actual4 = l4.formatDouble(input, status).toString(status);
6336         assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
6337     } else {
6338         assertUndefinedSkeleton(f);
6339     }
6340     return result1;
6341 }
6342 
assertUndefinedSkeleton(const UnlocalizedNumberFormatter & f)6343 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f) {
6344     UErrorCode status = U_ZERO_ERROR;
6345     UnicodeString skeleton = f.toSkeleton(status);
6346     assertEquals(
6347             u"Expect toSkeleton to fail, but passed, producing: " + skeleton,
6348             U_UNSUPPORTED_ERROR,
6349             status);
6350 }
6351 
assertNumberFieldPositions(const char16_t * message,const FormattedNumber & formattedNumber,const UFieldPosition * expectedFieldPositions,int32_t length)6352 void NumberFormatterApiTest::assertNumberFieldPositions(
6353         const char16_t* message,
6354         const FormattedNumber& formattedNumber,
6355         const UFieldPosition* expectedFieldPositions,
6356         int32_t length) {
6357     IcuTestErrorCode status(*this, "assertNumberFieldPositions");
6358 
6359     // Check FormattedValue functions
6360     checkFormattedValue(
6361         message,
6362         static_cast<const FormattedValue&>(formattedNumber),
6363         formattedNumber.toString(status),
6364         UFIELD_CATEGORY_NUMBER,
6365         expectedFieldPositions,
6366         length);
6367 }
6368 
6369 
6370 #endif /* #if !UCONFIG_NO_FORMATTING */
6371