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