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