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