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(unitSkeletons);
84         TESTCASE_AUTO(unitUsage);
85         TESTCASE_AUTO(unitUsageErrorCodes);
86         TESTCASE_AUTO(unitUsageSkeletons);
87         TESTCASE_AUTO(unitCurrency);
88         TESTCASE_AUTO(unitPercent);
89         if (!quick) {
90             // Slow test: run in exhaustive mode only
91             TESTCASE_AUTO(percentParity);
92         }
93         TESTCASE_AUTO(roundingFraction);
94         TESTCASE_AUTO(roundingFigures);
95         TESTCASE_AUTO(roundingFractionFigures);
96         TESTCASE_AUTO(roundingOther);
97         TESTCASE_AUTO(grouping);
98         TESTCASE_AUTO(padding);
99         TESTCASE_AUTO(integerWidth);
100         TESTCASE_AUTO(symbols);
101         // TODO: Add this method if currency symbols override support is added.
102         //TESTCASE_AUTO(symbolsOverride);
103         TESTCASE_AUTO(sign);
104         TESTCASE_AUTO(signNearZero);
105         TESTCASE_AUTO(signCoverage);
106         TESTCASE_AUTO(decimal);
107         TESTCASE_AUTO(scale);
108         TESTCASE_AUTO(locale);
109         TESTCASE_AUTO(skeletonUserGuideExamples);
110         TESTCASE_AUTO(formatTypes);
111         TESTCASE_AUTO(fieldPositionLogic);
112         TESTCASE_AUTO(fieldPositionCoverage);
113         TESTCASE_AUTO(toFormat);
114         TESTCASE_AUTO(errors);
115         if (!quick) {
116             // Slow test: run in exhaustive mode only
117             // (somewhat slow to check all permutations of settings)
118             TESTCASE_AUTO(validRanges);
119         }
120         TESTCASE_AUTO(copyMove);
121         TESTCASE_AUTO(localPointerCAPI);
122         TESTCASE_AUTO(toObject);
123         TESTCASE_AUTO(toDecimalNumber);
124         TESTCASE_AUTO(microPropsInternals);
125     TESTCASE_AUTO_END;
126 }
127 
notationSimple()128 void NumberFormatterApiTest::notationSimple() {
129     assertFormatDescending(
130             u"Basic",
131             u"",
132             u"",
133             NumberFormatter::with(),
134             Locale::getEnglish(),
135             u"87,650",
136             u"8,765",
137             u"876.5",
138             u"87.65",
139             u"8.765",
140             u"0.8765",
141             u"0.08765",
142             u"0.008765",
143             u"0");
144 
145     assertFormatDescendingBig(
146             u"Big Simple",
147             u"notation-simple",
148             u"",
149             NumberFormatter::with().notation(Notation::simple()),
150             Locale::getEnglish(),
151             u"87,650,000",
152             u"8,765,000",
153             u"876,500",
154             u"87,650",
155             u"8,765",
156             u"876.5",
157             u"87.65",
158             u"8.765",
159             u"0");
160 
161     assertFormatSingle(
162             u"Basic with Negative Sign",
163             u"",
164             u"",
165             NumberFormatter::with(),
166             Locale::getEnglish(),
167             -9876543.21,
168             u"-9,876,543.21");
169 }
170 
171 
notationScientific()172 void NumberFormatterApiTest::notationScientific() {
173     assertFormatDescending(
174             u"Scientific",
175             u"scientific",
176             u"E0",
177             NumberFormatter::with().notation(Notation::scientific()),
178             Locale::getEnglish(),
179             u"8.765E4",
180             u"8.765E3",
181             u"8.765E2",
182             u"8.765E1",
183             u"8.765E0",
184             u"8.765E-1",
185             u"8.765E-2",
186             u"8.765E-3",
187             u"0E0");
188 
189     assertFormatDescending(
190             u"Engineering",
191             u"engineering",
192             u"EE0",
193             NumberFormatter::with().notation(Notation::engineering()),
194             Locale::getEnglish(),
195             u"87.65E3",
196             u"8.765E3",
197             u"876.5E0",
198             u"87.65E0",
199             u"8.765E0",
200             u"876.5E-3",
201             u"87.65E-3",
202             u"8.765E-3",
203             u"0E0");
204 
205     assertFormatDescending(
206             u"Scientific sign always shown",
207             u"scientific/sign-always",
208             u"E+!0",
209             NumberFormatter::with().notation(
210                     Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)),
211             Locale::getEnglish(),
212             u"8.765E+4",
213             u"8.765E+3",
214             u"8.765E+2",
215             u"8.765E+1",
216             u"8.765E+0",
217             u"8.765E-1",
218             u"8.765E-2",
219             u"8.765E-3",
220             u"0E+0");
221 
222     assertFormatDescending(
223             u"Scientific min exponent digits",
224             u"scientific/*ee",
225             u"E00",
226             NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
227             Locale::getEnglish(),
228             u"8.765E04",
229             u"8.765E03",
230             u"8.765E02",
231             u"8.765E01",
232             u"8.765E00",
233             u"8.765E-01",
234             u"8.765E-02",
235             u"8.765E-03",
236             u"0E00");
237 
238     assertFormatSingle(
239             u"Scientific Negative",
240             u"scientific",
241             u"E0",
242             NumberFormatter::with().notation(Notation::scientific()),
243             Locale::getEnglish(),
244             -1000000,
245             u"-1E6");
246 
247     assertFormatSingle(
248             u"Scientific Infinity",
249             u"scientific",
250             u"E0",
251             NumberFormatter::with().notation(Notation::scientific()),
252             Locale::getEnglish(),
253             -uprv_getInfinity(),
254             u"-∞");
255 
256     assertFormatSingle(
257             u"Scientific NaN",
258             u"scientific",
259             u"E0",
260             NumberFormatter::with().notation(Notation::scientific()),
261             Locale::getEnglish(),
262             uprv_getNaN(),
263             u"NaN");
264 }
265 
notationCompact()266 void NumberFormatterApiTest::notationCompact() {
267     assertFormatDescending(
268             u"Compact Short",
269             u"compact-short",
270             u"K",
271             NumberFormatter::with().notation(Notation::compactShort()),
272             Locale::getEnglish(),
273             u"88K",
274             u"8.8K",
275             u"876",
276             u"88",
277             u"8.8",
278             u"0.88",
279             u"0.088",
280             u"0.0088",
281             u"0");
282 
283     assertFormatDescending(
284             u"Compact Long",
285             u"compact-long",
286             u"KK",
287             NumberFormatter::with().notation(Notation::compactLong()),
288             Locale::getEnglish(),
289             u"88 thousand",
290             u"8.8 thousand",
291             u"876",
292             u"88",
293             u"8.8",
294             u"0.88",
295             u"0.088",
296             u"0.0088",
297             u"0");
298 
299     assertFormatDescending(
300             u"Compact Short Currency",
301             u"compact-short currency/USD",
302             u"K currency/USD",
303             NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
304             Locale::getEnglish(),
305             u"$88K",
306             u"$8.8K",
307             u"$876",
308             u"$88",
309             u"$8.8",
310             u"$0.88",
311             u"$0.088",
312             u"$0.0088",
313             u"$0");
314 
315     assertFormatDescending(
316             u"Compact Short with ISO Currency",
317             u"compact-short currency/USD unit-width-iso-code",
318             u"K currency/USD unit-width-iso-code",
319             NumberFormatter::with().notation(Notation::compactShort())
320                     .unit(USD)
321                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
322             Locale::getEnglish(),
323             u"USD 88K",
324             u"USD 8.8K",
325             u"USD 876",
326             u"USD 88",
327             u"USD 8.8",
328             u"USD 0.88",
329             u"USD 0.088",
330             u"USD 0.0088",
331             u"USD 0");
332 
333     assertFormatDescending(
334             u"Compact Short with Long Name Currency",
335             u"compact-short currency/USD unit-width-full-name",
336             u"K currency/USD unit-width-full-name",
337             NumberFormatter::with().notation(Notation::compactShort())
338                     .unit(USD)
339                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
340             Locale::getEnglish(),
341             u"88K US dollars",
342             u"8.8K US dollars",
343             u"876 US dollars",
344             u"88 US dollars",
345             u"8.8 US dollars",
346             u"0.88 US dollars",
347             u"0.088 US dollars",
348             u"0.0088 US dollars",
349             u"0 US dollars");
350 
351     // Note: Most locales don't have compact long currency, so this currently falls back to short.
352     // This test case should be fixed when proper compact long currency patterns are added.
353     assertFormatDescending(
354             u"Compact Long Currency",
355             u"compact-long currency/USD",
356             u"KK currency/USD",
357             NumberFormatter::with().notation(Notation::compactLong()).unit(USD),
358             Locale::getEnglish(),
359             u"$88K", // should be something like "$88 thousand"
360             u"$8.8K",
361             u"$876",
362             u"$88",
363             u"$8.8",
364             u"$0.88",
365             u"$0.088",
366             u"$0.0088",
367             u"$0");
368 
369     // Note: Most locales don't have compact long currency, so this currently falls back to short.
370     // This test case should be fixed when proper compact long currency patterns are added.
371     assertFormatDescending(
372             u"Compact Long with ISO Currency",
373             u"compact-long currency/USD unit-width-iso-code",
374             u"KK currency/USD unit-width-iso-code",
375             NumberFormatter::with().notation(Notation::compactLong())
376                     .unit(USD)
377                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
378             Locale::getEnglish(),
379             u"USD 88K", // should be something like "USD 88 thousand"
380             u"USD 8.8K",
381             u"USD 876",
382             u"USD 88",
383             u"USD 8.8",
384             u"USD 0.88",
385             u"USD 0.088",
386             u"USD 0.0088",
387             u"USD 0");
388 
389     // TODO: This behavior could be improved and should be revisited.
390     assertFormatDescending(
391             u"Compact Long with Long Name Currency",
392             u"compact-long currency/USD unit-width-full-name",
393             u"KK currency/USD unit-width-full-name",
394             NumberFormatter::with().notation(Notation::compactLong())
395                     .unit(USD)
396                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
397             Locale::getEnglish(),
398             u"88 thousand US dollars",
399             u"8.8 thousand US dollars",
400             u"876 US dollars",
401             u"88 US dollars",
402             u"8.8 US dollars",
403             u"0.88 US dollars",
404             u"0.088 US dollars",
405             u"0.0088 US dollars",
406             u"0 US dollars");
407 
408     assertFormatSingle(
409             u"Compact Plural One",
410             u"compact-long",
411             u"KK",
412             NumberFormatter::with().notation(Notation::compactLong()),
413             Locale::createFromName("es"),
414             1000000,
415             u"1 millón");
416 
417     assertFormatSingle(
418             u"Compact Plural Other",
419             u"compact-long",
420             u"KK",
421             NumberFormatter::with().notation(Notation::compactLong()),
422             Locale::createFromName("es"),
423             2000000,
424             u"2 millones");
425 
426     assertFormatSingle(
427             u"Compact with Negative Sign",
428             u"compact-short",
429             u"K",
430             NumberFormatter::with().notation(Notation::compactShort()),
431             Locale::getEnglish(),
432             -9876543.21,
433             u"-9.9M");
434 
435     assertFormatSingle(
436             u"Compact Rounding",
437             u"compact-short",
438             u"K",
439             NumberFormatter::with().notation(Notation::compactShort()),
440             Locale::getEnglish(),
441             990000,
442             u"990K");
443 
444     assertFormatSingle(
445             u"Compact Rounding",
446             u"compact-short",
447             u"K",
448             NumberFormatter::with().notation(Notation::compactShort()),
449             Locale::getEnglish(),
450             999000,
451             u"999K");
452 
453     assertFormatSingle(
454             u"Compact Rounding",
455             u"compact-short",
456             u"K",
457             NumberFormatter::with().notation(Notation::compactShort()),
458             Locale::getEnglish(),
459             999900,
460             u"1M");
461 
462     assertFormatSingle(
463             u"Compact Rounding",
464             u"compact-short",
465             u"K",
466             NumberFormatter::with().notation(Notation::compactShort()),
467             Locale::getEnglish(),
468             9900000,
469             u"9.9M");
470 
471     assertFormatSingle(
472             u"Compact Rounding",
473             u"compact-short",
474             u"K",
475             NumberFormatter::with().notation(Notation::compactShort()),
476             Locale::getEnglish(),
477             9990000,
478             u"10M");
479 
480     assertFormatSingle(
481             u"Compact in zh-Hant-HK",
482             u"compact-short",
483             u"K",
484             NumberFormatter::with().notation(Notation::compactShort()),
485             Locale("zh-Hant-HK"),
486             1e7,
487             u"10M");
488 
489     assertFormatSingle(
490             u"Compact in zh-Hant",
491             u"compact-short",
492             u"K",
493             NumberFormatter::with().notation(Notation::compactShort()),
494             Locale("zh-Hant"),
495             1e7,
496             u"1000\u842C");
497 
498     if (!logKnownIssue("21258", "StandardPlural cannot handle keywords 1, 0")) {
499         assertFormatSingle(
500                 u"Compact with plural form =1 (ICU-21258)",
501                 u"compact-long",
502                 u"K",
503                 NumberFormatter::with().notation(Notation::compactLong()),
504                 Locale("fr-FR"),
505                 1e3,
506                 u"mille");
507     }
508 
509     assertFormatSingle(
510             u"Compact Infinity",
511             u"compact-short",
512             u"K",
513             NumberFormatter::with().notation(Notation::compactShort()),
514             Locale::getEnglish(),
515             -uprv_getInfinity(),
516             u"-∞");
517 
518     assertFormatSingle(
519             u"Compact NaN",
520             u"compact-short",
521             u"K",
522             NumberFormatter::with().notation(Notation::compactShort()),
523             Locale::getEnglish(),
524             uprv_getNaN(),
525             u"NaN");
526 
527     // NOTE: There is no API for compact custom data in C++
528     // and thus no "Compact Somali No Figure" test
529 }
530 
unitMeasure()531 void NumberFormatterApiTest::unitMeasure() {
532     IcuTestErrorCode status(*this, "unitMeasure()");
533 
534     assertFormatDescending(
535             u"Meters Short and unit() method",
536             u"measure-unit/length-meter",
537             u"unit/meter",
538             NumberFormatter::with().unit(MeasureUnit::getMeter()),
539             Locale::getEnglish(),
540             u"87,650 m",
541             u"8,765 m",
542             u"876.5 m",
543             u"87.65 m",
544             u"8.765 m",
545             u"0.8765 m",
546             u"0.08765 m",
547             u"0.008765 m",
548             u"0 m");
549 
550     assertFormatDescending(
551             u"Meters Long and adoptUnit() method",
552             u"measure-unit/length-meter unit-width-full-name",
553             u"unit/meter unit-width-full-name",
554             NumberFormatter::with().adoptUnit(new MeasureUnit(METER))
555                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
556             Locale::getEnglish(),
557             u"87,650 meters",
558             u"8,765 meters",
559             u"876.5 meters",
560             u"87.65 meters",
561             u"8.765 meters",
562             u"0.8765 meters",
563             u"0.08765 meters",
564             u"0.008765 meters",
565             u"0 meters");
566 
567     assertFormatDescending(
568             u"Compact Meters Long",
569             u"compact-long measure-unit/length-meter unit-width-full-name",
570             u"KK unit/meter unit-width-full-name",
571             NumberFormatter::with().notation(Notation::compactLong())
572                     .unit(METER)
573                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
574             Locale::getEnglish(),
575             u"88 thousand meters",
576             u"8.8 thousand meters",
577             u"876 meters",
578             u"88 meters",
579             u"8.8 meters",
580             u"0.88 meters",
581             u"0.088 meters",
582             u"0.0088 meters",
583             u"0 meters");
584 
585 //     // TODO(ICU-20941): Support formatting for not-built-in units
586 //     assertFormatDescending(
587 //             u"Hectometers",
588 //             u"measure-unit/length-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"Mixed unit",
720             u"unit/yard-and-foot-and-inch",
721             u"unit/yard-and-foot-and-inch",
722             NumberFormatter::with()
723                 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)),
724             Locale("en-US"),
725             3.65,
726             "3 yd, 1 ft, 11.4 in");
727 
728     assertFormatSingle(
729             u"Mixed unit, Scientific",
730             u"unit/yard-and-foot-and-inch E0",
731             u"unit/yard-and-foot-and-inch E0",
732             NumberFormatter::with()
733                 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status))
734                 .notation(Notation::scientific()),
735             Locale("en-US"),
736             3.65,
737             "3 yd, 1 ft, 1.14E1 in");
738 
739     assertFormatSingle(
740             u"Mixed Unit (Narrow Version)",
741             u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
742             u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
743             NumberFormatter::with()
744                 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
745                 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
746             Locale("en-US"),
747             4.28571,
748             u"4t 285kg 710g");
749 
750     assertFormatSingle(
751             u"Mixed Unit (Short Version)",
752             u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
753             u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
754             NumberFormatter::with()
755                 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
756                 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
757             Locale("en-US"),
758             4.28571,
759             u"4 t, 285 kg, 710 g");
760 
761     assertFormatSingle(
762             u"Mixed Unit (Full Name Version)",
763             u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
764             u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
765             NumberFormatter::with()
766                 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
767                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
768             Locale("en-US"),
769             4.28571,
770             u"4 metric tons, 285 kilograms, 710 grams");
771 
772     assertFormatSingle(
773             u"Testing  \"1 foot 12 inches\"",
774             u"unit/foot-and-inch @### unit-width-full-name",
775             u"unit/foot-and-inch @### unit-width-full-name",
776             NumberFormatter::with()
777                 .unit(MeasureUnit::forIdentifier("foot-and-inch", status))
778                 .precision(Precision::maxSignificantDigits(4))
779                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
780             Locale("en-US"),
781             1.9999,
782             u"2 feet, 0 inches");
783 
784     assertFormatSingle(
785             u"Negative numbers: temperature",
786             u"measure-unit/temperature-celsius",
787             u"unit/celsius",
788             NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)),
789             Locale("nl-NL"),
790             -6.5,
791             u"-6,5\u00B0C");
792 
793     assertFormatSingle(
794             u"Negative numbers: time",
795             u"unit/hour-and-minute-and-second",
796             u"unit/hour-and-minute-and-second",
797             NumberFormatter::with().unit(MeasureUnit::forIdentifier("hour-and-minute-and-second", status)),
798             Locale("de-DE"),
799             -1.24,
800             u"-1 Std., 14 Min. und 24 Sek.");
801 
802     assertFormatSingle(
803             u"Zero out the unit field",
804             u"",
805             u"",
806             NumberFormatter::with().unit(KELVIN).unit(MeasureUnit()),
807             Locale("en"),
808             100,
809             u"100");
810 }
811 
unitCompoundMeasure()812 void NumberFormatterApiTest::unitCompoundMeasure() {
813     IcuTestErrorCode status(*this, "unitCompoundMeasure()");
814 
815     assertFormatDescending(
816             u"Meters Per Second Short (unit that simplifies) and perUnit method",
817             u"measure-unit/length-meter per-measure-unit/duration-second",
818             u"unit/meter-per-second",
819             NumberFormatter::with().unit(METER).perUnit(SECOND),
820             Locale::getEnglish(),
821             u"87,650 m/s",
822             u"8,765 m/s",
823             u"876.5 m/s",
824             u"87.65 m/s",
825             u"8.765 m/s",
826             u"0.8765 m/s",
827             u"0.08765 m/s",
828             u"0.008765 m/s",
829             u"0 m/s");
830 
831     assertFormatDescending(
832             u"Meters Per Second Short, built-in m/s",
833             u"measure-unit/speed-meter-per-second",
834             u"unit/meter-per-second",
835             NumberFormatter::with().unit(METER_PER_SECOND),
836             Locale::getEnglish(),
837             u"87,650 m/s",
838             u"8,765 m/s",
839             u"876.5 m/s",
840             u"87.65 m/s",
841             u"8.765 m/s",
842             u"0.8765 m/s",
843             u"0.08765 m/s",
844             u"0.008765 m/s",
845             u"0 m/s");
846 
847     assertFormatDescending(
848             u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
849             u"measure-unit/mass-pound per-measure-unit/area-square-mile",
850             u"unit/pound-per-square-mile",
851             NumberFormatter::with().unit(POUND).adoptPerUnit(new MeasureUnit(SQUARE_MILE)),
852             Locale::getEnglish(),
853             u"87,650 lb/mi²",
854             u"8,765 lb/mi²",
855             u"876.5 lb/mi²",
856             u"87.65 lb/mi²",
857             u"8.765 lb/mi²",
858             u"0.8765 lb/mi²",
859             u"0.08765 lb/mi²",
860             u"0.008765 lb/mi²",
861             u"0 lb/mi²");
862 
863     assertFormatDescending(
864             u"Joules Per Furlong Short (unit with no simplifications or special patterns)",
865             u"measure-unit/energy-joule per-measure-unit/length-furlong",
866             u"unit/joule-per-furlong",
867             NumberFormatter::with().unit(JOULE).perUnit(FURLONG),
868             Locale::getEnglish(),
869             u"87,650 J/fur",
870             u"8,765 J/fur",
871             u"876.5 J/fur",
872             u"87.65 J/fur",
873             u"8.765 J/fur",
874             u"0.8765 J/fur",
875             u"0.08765 J/fur",
876             u"0.008765 J/fur",
877             u"0 J/fur");
878 
879     // TODO(ICU-20941): Support constructions such as this one.
880     // assertFormatDescending(
881     //         u"Joules Per Furlong Short with unit identifier via API",
882     //         u"measure-unit/energy-joule per-measure-unit/length-furlong",
883     //         u"unit/joule-per-furlong",
884     //         NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)),
885     //         Locale::getEnglish(),
886     //         u"87,650 J/fur",
887     //         u"8,765 J/fur",
888     //         u"876.5 J/fur",
889     //         u"87.65 J/fur",
890     //         u"8.765 J/fur",
891     //         u"0.8765 J/fur",
892     //         u"0.08765 J/fur",
893     //         u"0.008765 J/fur",
894     //         u"0 J/fur");
895 
896     assertFormatDescending(
897             u"Pounds per Square Inch: composed",
898             u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
899             u"unit/pound-force-per-square-inch",
900             NumberFormatter::with().unit(POUND_FORCE).perUnit(SQUARE_INCH),
901             Locale::getEnglish(),
902             u"87,650 psi",
903             u"8,765 psi",
904             u"876.5 psi",
905             u"87.65 psi",
906             u"8.765 psi",
907             u"0.8765 psi",
908             u"0.08765 psi",
909             u"0.008765 psi",
910             u"0 psi");
911 
912     assertFormatDescending(
913             u"Pounds per Square Inch: built-in",
914             u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
915             u"unit/pound-force-per-square-inch",
916             NumberFormatter::with().unit(MeasureUnit::getPoundPerSquareInch()),
917             Locale::getEnglish(),
918             u"87,650 psi",
919             u"8,765 psi",
920             u"876.5 psi",
921             u"87.65 psi",
922             u"8.765 psi",
923             u"0.8765 psi",
924             u"0.08765 psi",
925             u"0.008765 psi",
926             u"0 psi");
927 
928     assertFormatSingle(
929             u"m/s/s simplifies to m/s^2",
930             u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
931             u"unit/meter-per-square-second",
932             NumberFormatter::with().unit(METER_PER_SECOND).perUnit(SECOND),
933             Locale("en-GB"),
934             2.4,
935             u"2.4 m/s\u00B2");
936 
937     assertFormatSingle(
938             u"Negative numbers: acceleration",
939             u"measure-unit/acceleration-meter-per-square-second",
940             u"unit/meter-per-second-second",
941             NumberFormatter::with().unit(MeasureUnit::forIdentifier("meter-per-pow2-second", status)),
942             Locale("af-ZA"),
943             -9.81,
944             u"-9,81 m/s\u00B2");
945 
946     // Testing the rejection of invalid specifications
947 
948     // If .unit() is not given a built-in type, .perUnit() is not allowed
949     // (because .unit is now flexible enough to handle compound units,
950     // .perUnit() is supported for backward compatibility).
951     LocalizedNumberFormatter nf = NumberFormatter::with()
952              .unit(MeasureUnit::forIdentifier("furlong-pascal", status))
953              .perUnit(METER)
954              .locale("en-GB");
955     status.assertSuccess(); // Error is only returned once we try to format.
956     FormattedNumber num = nf.formatDouble(2.4, status);
957     if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
958         errln(UnicodeString("Expected failure, got: \"") +
959               nf.formatDouble(2.4, status).toString(status) + "\".");
960         status.assertSuccess();
961     }
962 
963     // .perUnit() may only be passed a built-in type, or something that combines
964     // to a built-in type together with .unit().
965     MeasureUnit SQUARE_SECOND = MeasureUnit::forIdentifier("square-second", status);
966     nf = NumberFormatter::with().unit(FURLONG).perUnit(SQUARE_SECOND).locale("en-GB");
967     status.assertSuccess(); // Error is only returned once we try to format.
968     num = nf.formatDouble(2.4, status);
969     if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
970         errln(UnicodeString("Expected failure, got: \"") +
971               nf.formatDouble(2.4, status).toString(status) + "\".");
972         status.assertSuccess();
973     }
974     // As above, "square-second" is not a built-in type, however this time,
975     // meter-per-square-second is a built-in type.
976     assertFormatSingle(
977             u"meter per square-second works as a composed unit",
978             u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
979             u"unit/meter-per-square-second",
980             NumberFormatter::with().unit(METER).perUnit(SQUARE_SECOND),
981             Locale("en-GB"),
982             2.4,
983             u"2.4 m/s\u00B2");
984 }
985 
986 // TODO: merge these tests into numbertest_skeletons.cpp instead of here:
unitSkeletons()987 void NumberFormatterApiTest::unitSkeletons() {
988     const struct TestCase {
989         const char *msg;
990         const char16_t *inputSkeleton;
991         const char16_t *normalizedSkeleton;
992     } cases[] = {
993         {"old-form built-in compound unit",      //
994          u"measure-unit/speed-meter-per-second", //
995          u"unit/meter-per-second"},
996 
997         {"old-form compound construction, converts to built-in",        //
998          u"measure-unit/length-meter per-measure-unit/duration-second", //
999          u"unit/meter-per-second"},
1000 
1001         {"old-form compound construction which does not simplify to a built-in", //
1002          u"measure-unit/energy-joule per-measure-unit/length-meter",             //
1003          u"unit/joule-per-meter"},
1004 
1005         {"old-form compound-compound ugliness resolves neatly",                   //
1006          u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", //
1007          u"unit/meter-per-square-second"},
1008 
1009         {"short-form built-in units stick with the built-in", //
1010          u"unit/meter-per-second",                            //
1011          u"unit/meter-per-second"},
1012 
1013         {"short-form compound units stay as is", //
1014          u"unit/square-meter-per-square-meter",  //
1015          u"unit/square-meter-per-square-meter"},
1016 
1017         {"short-form compound units stay as is", //
1018          u"unit/joule-per-furlong",              //
1019          u"unit/joule-per-furlong"},
1020 
1021         {"short-form that doesn't consist of built-in units", //
1022          u"unit/hectometer-per-second",                       //
1023          u"unit/hectometer-per-second"},
1024 
1025         {"short-form that doesn't consist of built-in units", //
1026          u"unit/meter-per-hectosecond",                       //
1027          u"unit/meter-per-hectosecond"},
1028 
1029         {"percent compound skeletons handled correctly", //
1030          u"unit/percent-per-meter",                      //
1031          u"unit/percent-per-meter"},
1032 
1033         {"permille compound skeletons handled correctly",                 //
1034          u"measure-unit/concentr-permille per-measure-unit/length-meter", //
1035          u"unit/permille-per-meter"},
1036 
1037         {"percent simple unit is not actually considered a unit", //
1038          u"unit/percent",                                         //
1039          u"percent"},
1040 
1041         {"permille simple unit is not actually considered a unit", //
1042          u"measure-unit/concentr-permille",                        //
1043          u"permille"},
1044 
1045         // // TODO: binary prefixes not supported yet!
1046         // {"Round-trip example from icu-units#35", //
1047         //  u"unit/kibijoule-per-furlong",          //
1048         //  u"unit/kibijoule-per-furlong"},
1049     };
1050     for (auto &cas : cases) {
1051         IcuTestErrorCode status(*this, cas.msg);
1052         auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1053         if (status.errIfFailureAndReset("NumberFormatter::forSkeleton failed")) {
1054             continue;
1055         }
1056         assertEquals(                                                       //
1057             UnicodeString(TRUE, cas.inputSkeleton, -1) + u" normalization", //
1058             cas.normalizedSkeleton,                                         //
1059             nf.toSkeleton(status));
1060         status.errIfFailureAndReset("NumberFormatter::toSkeleton failed");
1061     }
1062 
1063     const struct FailCase {
1064         const char *msg;
1065         const char16_t *inputSkeleton;
1066         UErrorCode expectedForSkelStatus;
1067         UErrorCode expectedToSkelStatus;
1068     } failCases[] = {
1069         {"Parsing measure-unit/* results in failure if not built-in unit",
1070          u"measure-unit/hectometer",     //
1071          U_NUMBER_SKELETON_SYNTAX_ERROR, //
1072          U_ZERO_ERROR},
1073 
1074         {"Parsing per-measure-unit/* results in failure if not built-in unit",
1075          u"measure-unit/meter per-measure-unit/hectosecond", //
1076          U_NUMBER_SKELETON_SYNTAX_ERROR,                     //
1077          U_ZERO_ERROR},
1078 
1079         {"\"currency/EUR measure-unit/length-meter\" fails, conflicting skeleton.",
1080          u"currency/EUR measure-unit/length-meter", //
1081          U_NUMBER_SKELETON_SYNTAX_ERROR,            //
1082          U_ZERO_ERROR},
1083 
1084         {"\"measure-unit/length-meter currency/EUR\" fails, conflicting skeleton.",
1085          u"measure-unit/length-meter currency/EUR", //
1086          U_NUMBER_SKELETON_SYNTAX_ERROR,            //
1087          U_ZERO_ERROR},
1088 
1089         {"\"currency/EUR per-measure-unit/meter\" fails, conflicting skeleton.",
1090          u"currency/EUR per-measure-unit/length-meter", //
1091          U_NUMBER_SKELETON_SYNTAX_ERROR,                //
1092          U_ZERO_ERROR},
1093     };
1094     for (auto &cas : failCases) {
1095         IcuTestErrorCode status(*this, cas.msg);
1096         auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1097         if (status.expectErrorAndReset(cas.expectedForSkelStatus, cas.msg)) {
1098             continue;
1099         }
1100         nf.toSkeleton(status);
1101         status.expectErrorAndReset(cas.expectedToSkelStatus, cas.msg);
1102     }
1103 
1104     IcuTestErrorCode status(*this, "unitSkeletons");
1105     assertEquals(                                //
1106         ".unit(METER_PER_SECOND) normalization", //
1107         u"unit/meter-per-second",                //
1108         NumberFormatter::with().unit(METER_PER_SECOND).toSkeleton(status));
1109     assertEquals(                                     //
1110         ".unit(METER).perUnit(SECOND) normalization", //
1111         u"unit/meter-per-second",
1112         NumberFormatter::with().unit(METER).perUnit(SECOND).toSkeleton(status));
1113     assertEquals(                                                                  //
1114         ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1115         u"unit/hectometer",
1116         NumberFormatter::with()
1117             .unit(MeasureUnit::forIdentifier("hectometer", status))
1118             .toSkeleton(status));
1119     assertEquals(                                                                  //
1120         ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1121         u"unit/meter-per-hectosecond",
1122         NumberFormatter::with()
1123             .unit(METER)
1124             .perUnit(MeasureUnit::forIdentifier("hectosecond", status))
1125             .toSkeleton(status));
1126 
1127     status.assertSuccess();
1128     assertEquals(                                                //
1129         ".unit(CURRENCY) produces a currency/CURRENCY skeleton", //
1130         u"currency/GBP",                                         //
1131         NumberFormatter::with().unit(GBP).toSkeleton(status));
1132     status.assertSuccess();
1133     // .unit(CURRENCY).perUnit(ANYTHING) is not supported.
1134     NumberFormatter::with().unit(GBP).perUnit(METER).toSkeleton(status);
1135     status.expectErrorAndReset(U_UNSUPPORTED_ERROR);
1136 }
1137 
unitUsage()1138 void NumberFormatterApiTest::unitUsage() {
1139     IcuTestErrorCode status(*this, "unitUsage()");
1140     UnlocalizedNumberFormatter unloc_formatter;
1141     LocalizedNumberFormatter formatter;
1142     FormattedNumber formattedNum;
1143     UnicodeString uTestCase;
1144 
1145     status.assertSuccess();
1146     formattedNum =
1147         NumberFormatter::with().usage("road").locale(Locale::getEnglish()).formatInt(1, status);
1148     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1149 
1150     unloc_formatter = NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
1151 
1152     uTestCase = u"unitUsage() en-ZA road";
1153     formatter = unloc_formatter.locale("en-ZA");
1154     formattedNum = formatter.formatDouble(321, status);
1155     status.errIfFailureAndReset("unitUsage() en-ZA road formatDouble");
1156     assertTrue(
1157             uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1158             MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
1159     assertEquals(uTestCase, "300 m", formattedNum.toString(status));
1160     {
1161         static const UFieldPosition expectedFieldPositions[] = {
1162                 {UNUM_INTEGER_FIELD, 0, 3},
1163                 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
1164         assertNumberFieldPositions(
1165                 (uTestCase + u" field positions").getTerminatedBuffer(),
1166                 formattedNum,
1167                 expectedFieldPositions,
1168                 UPRV_LENGTHOF(expectedFieldPositions));
1169     }
1170     assertFormatDescendingBig(
1171             uTestCase.getTerminatedBuffer(),
1172             u"measure-unit/length-meter usage/road",
1173             u"unit/meter usage/road",
1174             unloc_formatter,
1175             Locale("en-ZA"),
1176             u"87\u00A0650 km",
1177             u"8\u00A0765 km",
1178             u"876 km", // 6.5 rounds down, 7.5 rounds up.
1179             u"88 km",
1180             u"8,8 km",
1181             u"900 m",
1182             u"90 m",
1183             u"10 m",
1184             u"0 m");
1185 
1186     uTestCase = u"unitUsage() en-GB road";
1187     formatter = unloc_formatter.locale("en-GB");
1188     formattedNum = formatter.formatDouble(321, status);
1189     status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)");
1190     assertTrue(
1191             uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1192             MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
1193     status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)");
1194     assertEquals(uTestCase, "350 yd", formattedNum.toString(status));
1195     status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)");
1196     {
1197         static const UFieldPosition expectedFieldPositions[] = {
1198                 {UNUM_INTEGER_FIELD, 0, 3},
1199                 {UNUM_MEASURE_UNIT_FIELD, 4, 6}};
1200         assertNumberFieldPositions(
1201                 (uTestCase + u" field positions").getTerminatedBuffer(),
1202                 formattedNum,
1203                 expectedFieldPositions,
1204                 UPRV_LENGTHOF(expectedFieldPositions));
1205     }
1206     assertFormatDescendingBig(
1207             uTestCase.getTerminatedBuffer(),
1208             u"measure-unit/length-meter usage/road",
1209             u"unit/meter usage/road",
1210             unloc_formatter,
1211             Locale("en-GB"),
1212             u"54,463 mi",
1213             u"5,446 mi",
1214             u"545 mi",
1215             u"54 mi",
1216             u"5.4 mi",
1217             u"0.54 mi",
1218             u"96 yd",
1219             u"9.6 yd",
1220             u"0 yd");
1221 
1222     uTestCase = u"unitUsage() en-US road";
1223     formatter = unloc_formatter.locale("en-US");
1224     formattedNum = formatter.formatDouble(321, status);
1225     status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)");
1226     assertTrue(
1227             uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1228             MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
1229     status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)");
1230     assertEquals(uTestCase, "1,050 ft", formattedNum.toString(status));
1231     status.errIfFailureAndReset("unitUsage() en-US road, toString(...)");
1232     {
1233         static const UFieldPosition expectedFieldPositions[] = {
1234                 {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
1235                 {UNUM_INTEGER_FIELD, 0, 5},
1236                 {UNUM_MEASURE_UNIT_FIELD, 6, 8}};
1237         assertNumberFieldPositions(
1238                 (uTestCase + u" field positions").getTerminatedBuffer(),
1239                 formattedNum,
1240                 expectedFieldPositions,
1241                 UPRV_LENGTHOF(expectedFieldPositions));
1242     }
1243     assertFormatDescendingBig(
1244             uTestCase.getTerminatedBuffer(),
1245             u"measure-unit/length-meter usage/road",
1246             u"unit/meter usage/road",
1247             unloc_formatter,
1248             Locale("en-US"),
1249             u"54,463 mi",
1250             u"5,446 mi",
1251             u"545 mi",
1252             u"54 mi",
1253             u"5.4 mi",
1254             u"0.54 mi",
1255             u"300 ft",
1256             u"30 ft",
1257             u"0 ft");
1258 
1259     unloc_formatter = NumberFormatter::with().usage("person").unit(MeasureUnit::getKilogram());
1260     uTestCase = u"unitUsage() en-GB person";
1261     formatter = unloc_formatter.locale("en-GB");
1262     formattedNum = formatter.formatDouble(80, status);
1263     status.errIfFailureAndReset("unitUsage() en-GB person formatDouble");
1264     assertTrue(
1265         uTestCase + ", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1266         MeasureUnit::forIdentifier("stone-and-pound", status) == formattedNum.getOutputUnit(status));
1267     status.errIfFailureAndReset("unitUsage() en-GB person - formattedNum.getOutputUnit(status)");
1268     assertEquals(uTestCase, "12 st, 8.4 lb", formattedNum.toString(status));
1269     status.errIfFailureAndReset("unitUsage() en-GB person, toString(...)");
1270     {
1271         static const UFieldPosition expectedFieldPositions[] = {
1272                 // // Desired output: TODO(icu-units#67)
1273                 // {UNUM_INTEGER_FIELD, 0, 2},
1274                 // {UNUM_MEASURE_UNIT_FIELD, 3, 5},
1275                 // {ULISTFMT_LITERAL_FIELD, 5, 6},
1276                 // {UNUM_INTEGER_FIELD, 7, 8},
1277                 // {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1278                 // {UNUM_FRACTION_FIELD, 9, 10},
1279                 // {UNUM_MEASURE_UNIT_FIELD, 11, 13}};
1280 
1281                 // Current output: rather no fields than wrong fields
1282                 {UNUM_INTEGER_FIELD, 7, 8},
1283                 {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1284                 {UNUM_FRACTION_FIELD, 9, 10},
1285                 };
1286         assertNumberFieldPositions(
1287                 (uTestCase + u" field positions").getTerminatedBuffer(),
1288                 formattedNum,
1289                 expectedFieldPositions,
1290                 UPRV_LENGTHOF(expectedFieldPositions));
1291     }
1292     assertFormatDescending(
1293             uTestCase.getTerminatedBuffer(),
1294             u"measure-unit/mass-kilogram usage/person",
1295             u"unit/kilogram usage/person",
1296             unloc_formatter,
1297             Locale("en-GB"),
1298             u"13,802 st, 7.2 lb",
1299             u"1,380 st, 3.5 lb",
1300             u"138 st, 0.35 lb",
1301             u"13 st, 11 lb",
1302             u"1 st, 5.3 lb",
1303             u"1 lb, 15 oz",
1304             u"0 lb, 3.1 oz",
1305             u"0 lb, 0.31 oz",
1306             u"0 lb, 0 oz");
1307 
1308    assertFormatDescending(
1309             uTestCase.getTerminatedBuffer(),
1310             u"usage/person unit-width-narrow measure-unit/mass-kilogram",
1311             u"usage/person unit-width-narrow unit/kilogram",
1312             unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_NARROW),
1313             Locale("en-GB"),
1314             u"13,802st 7.2lb",
1315             u"1,380st 3.5lb",
1316             u"138st 0.35lb",
1317             u"13st 11lb",
1318             u"1st 5.3lb",
1319             u"1lb 15oz",
1320             u"0lb 3.1oz",
1321             u"0lb 0.31oz",
1322             u"0lb 0oz");
1323 
1324    assertFormatDescending(
1325             uTestCase.getTerminatedBuffer(),
1326             u"usage/person unit-width-short measure-unit/mass-kilogram",
1327             u"usage/person unit-width-short unit/kilogram",
1328             unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_SHORT),
1329             Locale("en-GB"),
1330             u"13,802 st, 7.2 lb",
1331             u"1,380 st, 3.5 lb",
1332             u"138 st, 0.35 lb",
1333             u"13 st, 11 lb",
1334             u"1 st, 5.3 lb",
1335             u"1 lb, 15 oz",
1336             u"0 lb, 3.1 oz",
1337             u"0 lb, 0.31 oz",
1338             u"0 lb, 0 oz");
1339 
1340    assertFormatDescending(
1341             uTestCase.getTerminatedBuffer(),
1342             u"usage/person unit-width-full-name measure-unit/mass-kilogram",
1343             u"usage/person unit-width-full-name unit/kilogram",
1344             unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1345             Locale("en-GB"),
1346             u"13,802 stone, 7.2 pounds",
1347             u"1,380 stone, 3.5 pounds",
1348             u"138 stone, 0.35 pounds",
1349             u"13 stone, 11 pounds",
1350             u"1 stone, 5.3 pounds",
1351             u"1 pound, 15 ounces",
1352             u"0 pounds, 3.1 ounces",
1353             u"0 pounds, 0.31 ounces",
1354             u"0 pounds, 0 ounces");
1355 
1356     assertFormatDescendingBig(
1357             u"Scientific notation with Usage: possible when using a reasonable Precision",
1358             u"scientific @### usage/default measure-unit/area-square-meter unit-width-full-name",
1359             u"scientific @### usage/default unit/square-meter unit-width-full-name",
1360             NumberFormatter::with()
1361                     .unit(SQUARE_METER)
1362                     .usage("default")
1363                     .notation(Notation::scientific())
1364                     .precision(Precision::minMaxSignificantDigits(1, 4))
1365                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1366             Locale("en-ZA"),
1367             u"8,765E1 square kilometres",
1368             u"8,765E0 square kilometres",
1369             u"8,765E1 hectares",
1370             u"8,765E0 hectares",
1371             u"8,765E3 square metres",
1372             u"8,765E2 square metres",
1373             u"8,765E1 square metres",
1374             u"8,765E0 square metres",
1375             u"0E0 square centimetres");
1376 
1377     assertFormatSingle(
1378             u"Negative numbers: minute-and-second",
1379             u"measure-unit/duration-second usage/media",
1380             u"unit/second usage/media",
1381             NumberFormatter::with().unit(SECOND).usage("media"),
1382             Locale("nl-NL"),
1383             -77.7,
1384             u"-1 min, 18 sec");
1385 
1386     assertFormatSingle(
1387             u"Rounding Mode propagates: rounding down",
1388             u"usage/road measure-unit/length-centimeter rounding-mode-floor",
1389             u"usage/road unit/centimeter rounding-mode-floor",
1390             NumberFormatter::with()
1391                 .unit(MeasureUnit::forIdentifier("centimeter", status))
1392                 .usage("road")
1393                 .roundingMode(UNUM_ROUND_FLOOR),
1394             Locale("en-ZA"),
1395             34500,
1396             u"300 m");
1397 
1398     assertFormatSingle(
1399             u"Rounding Mode propagates: rounding up",
1400             u"usage/road measure-unit/length-centimeter rounding-mode-ceiling",
1401             u"usage/road unit/centimeter rounding-mode-ceiling",
1402             NumberFormatter::with()
1403                 .unit(MeasureUnit::forIdentifier("centimeter", status))
1404                 .usage("road")
1405                 .roundingMode(UNUM_ROUND_CEILING),
1406             Locale("en-ZA"),
1407             30500,
1408             u"350 m");
1409 
1410     // TODO(icu-units#38): improve unit testing coverage. E.g. add vehicle-fuel
1411     // triggering inversion conversion code. Test with 0 too, to see
1412     // divide-by-zero behaviour.
1413 }
1414 
unitUsageErrorCodes()1415 void NumberFormatterApiTest::unitUsageErrorCodes() {
1416     IcuTestErrorCode status(*this, "unitUsageErrorCodes()");
1417     UnlocalizedNumberFormatter unloc_formatter;
1418 
1419     unloc_formatter = NumberFormatter::forSkeleton(u"unit/foobar", status);
1420     // This gives an error, because foobar is an invalid unit:
1421     status.expectErrorAndReset(U_NUMBER_SKELETON_SYNTAX_ERROR);
1422 
1423     unloc_formatter = NumberFormatter::forSkeleton(u"usage/foobar", status);
1424     // This does not give an error, because usage is not looked up yet.
1425     status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid usage");
1426     unloc_formatter.locale("en-GB").formatInt(1, status);
1427     // Lacking a unit results in a failure. The skeleton is "incomplete", but we
1428     // support adding the unit via the fluent API, so it is not an error until
1429     // we build the formatting pipeline itself.
1430     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1431     // Adding the unit as part of the fluent chain leads to success.
1432     unloc_formatter.unit(MeasureUnit::getMeter()).locale("en-GB").formatInt(1, status);
1433     status.assertSuccess();
1434 }
1435 
1436 // Tests for the "skeletons" field in unitPreferenceData, as well as precision
1437 // and notation overrides.
unitUsageSkeletons()1438 void NumberFormatterApiTest::unitUsageSkeletons() {
1439     IcuTestErrorCode status(*this, "unitUsageSkeletons()");
1440 
1441     assertFormatSingle(
1442             u"Default >300m road preference skeletons round to 50m",
1443             u"usage/road measure-unit/length-meter",
1444             u"usage/road unit/meter",
1445             NumberFormatter::with().unit(METER).usage("road"),
1446             Locale("en-ZA"),
1447             321,
1448             u"300 m");
1449 
1450     assertFormatSingle(
1451             u"Precision can be overridden: override takes precedence",
1452             u"usage/road measure-unit/length-meter @#",
1453             u"usage/road unit/meter @#",
1454             NumberFormatter::with()
1455                 .unit(METER)
1456                 .usage("road")
1457                 .precision(Precision::maxSignificantDigits(2)),
1458             Locale("en-ZA"),
1459             321,
1460             u"320 m");
1461 
1462     assertFormatSingle(
1463             u"Compact notation with Usage: bizarre, but possible (short)",
1464             u"compact-short usage/road measure-unit/length-meter",
1465             u"compact-short usage/road unit/meter",
1466             NumberFormatter::with()
1467                .unit(METER)
1468                .usage("road")
1469                .notation(Notation::compactShort()),
1470             Locale("en-ZA"),
1471             987654321,
1472             u"988K km");
1473 
1474     assertFormatSingle(
1475             u"Compact notation with Usage: bizarre, but possible (short, precision override)",
1476             u"compact-short usage/road measure-unit/length-meter @#",
1477             u"compact-short usage/road unit/meter @#",
1478             NumberFormatter::with()
1479                 .unit(METER)
1480                 .usage("road")
1481                 .notation(Notation::compactShort())
1482                 .precision(Precision::maxSignificantDigits(2)),
1483             Locale("en-ZA"),
1484             987654321,
1485             u"990K km");
1486 
1487     assertFormatSingle(
1488             u"Compact notation with Usage: unusual but possible (long)",
1489             u"compact-long usage/road measure-unit/length-meter @#",
1490             u"compact-long usage/road unit/meter @#",
1491             NumberFormatter::with()
1492                 .unit(METER)
1493                 .usage("road")
1494                 .notation(Notation::compactLong())
1495                 .precision(Precision::maxSignificantDigits(2)),
1496             Locale("en-ZA"),
1497             987654321,
1498             u"990 thousand km");
1499 
1500     assertFormatSingle(
1501             u"Compact notation with Usage: unusual but possible (long, precision override)",
1502             u"compact-long usage/road measure-unit/length-meter @#",
1503             u"compact-long usage/road unit/meter @#",
1504             NumberFormatter::with()
1505                 .unit(METER)
1506                 .usage("road")
1507                 .notation(Notation::compactLong())
1508                 .precision(Precision::maxSignificantDigits(2)),
1509             Locale("en-ZA"),
1510             987654321,
1511             u"990 thousand km");
1512 
1513     assertFormatSingle(
1514             u"Scientific notation, not recommended, requires precision override for road",
1515             u"scientific usage/road measure-unit/length-meter",
1516             u"scientific usage/road unit/meter",
1517             NumberFormatter::with().unit(METER).usage("road").notation(Notation::scientific()),
1518             Locale("en-ZA"),
1519             321.45,
1520             // Rounding to the nearest "50" is not exponent-adjusted in scientific notation:
1521             u"0E2 m");
1522 
1523     assertFormatSingle(
1524             u"Scientific notation with Usage: possible when using a reasonable Precision",
1525             u"scientific usage/road measure-unit/length-meter @###",
1526             u"scientific usage/road unit/meter @###",
1527             NumberFormatter::with()
1528                 .unit(METER)
1529                 .usage("road")
1530                 .notation(Notation::scientific())
1531                 .precision(Precision::maxSignificantDigits(4)),
1532             Locale("en-ZA"),
1533             321.45, // 0.45 rounds down, 0.55 rounds up.
1534             u"3,214E2 m");
1535 
1536     assertFormatSingle(
1537             u"Scientific notation with Usage: possible when using a reasonable Precision",
1538             u"scientific usage/default measure-unit/length-astronomical-unit unit-width-full-name",
1539             u"scientific usage/default unit/astronomical-unit unit-width-full-name",
1540             NumberFormatter::with()
1541                 .unit(MeasureUnit::forIdentifier("astronomical-unit", status))
1542                 .usage("default")
1543                 .notation(Notation::scientific())
1544                 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1545             Locale("en-ZA"),
1546             1e20,
1547             u"1,5E28 kilometres");
1548 
1549     status.assertSuccess();
1550 }
1551 
unitCurrency()1552 void NumberFormatterApiTest::unitCurrency() {
1553     assertFormatDescending(
1554             u"Currency",
1555             u"currency/GBP",
1556             u"currency/GBP",
1557             NumberFormatter::with().unit(GBP),
1558             Locale::getEnglish(),
1559             u"£87,650.00",
1560             u"£8,765.00",
1561             u"£876.50",
1562             u"£87.65",
1563             u"£8.76",
1564             u"£0.88",
1565             u"£0.09",
1566             u"£0.01",
1567             u"£0.00");
1568 
1569     assertFormatDescending(
1570             u"Currency ISO",
1571             u"currency/GBP unit-width-iso-code",
1572             u"currency/GBP unit-width-iso-code",
1573             NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
1574             Locale::getEnglish(),
1575             u"GBP 87,650.00",
1576             u"GBP 8,765.00",
1577             u"GBP 876.50",
1578             u"GBP 87.65",
1579             u"GBP 8.76",
1580             u"GBP 0.88",
1581             u"GBP 0.09",
1582             u"GBP 0.01",
1583             u"GBP 0.00");
1584 
1585     assertFormatDescending(
1586             u"Currency Long Name",
1587             u"currency/GBP unit-width-full-name",
1588             u"currency/GBP unit-width-full-name",
1589             NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1590             Locale::getEnglish(),
1591             u"87,650.00 British pounds",
1592             u"8,765.00 British pounds",
1593             u"876.50 British pounds",
1594             u"87.65 British pounds",
1595             u"8.76 British pounds",
1596             u"0.88 British pounds",
1597             u"0.09 British pounds",
1598             u"0.01 British pounds",
1599             u"0.00 British pounds");
1600 
1601     assertFormatDescending(
1602             u"Currency Hidden",
1603             u"currency/GBP unit-width-hidden",
1604             u"currency/GBP unit-width-hidden",
1605             NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
1606             Locale::getEnglish(),
1607             u"87,650.00",
1608             u"8,765.00",
1609             u"876.50",
1610             u"87.65",
1611             u"8.76",
1612             u"0.88",
1613             u"0.09",
1614             u"0.01",
1615             u"0.00");
1616 
1617 //    TODO: Implement Measure in C++
1618 //    assertFormatSingleMeasure(
1619 //            u"Currency with CurrencyAmount Input",
1620 //            NumberFormatter::with(),
1621 //            Locale::getEnglish(),
1622 //            new CurrencyAmount(5.43, GBP),
1623 //            u"£5.43");
1624 
1625 //    TODO: Enable this test when DecimalFormat wrapper is done.
1626 //    assertFormatSingle(
1627 //            u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
1628 //                    PatternStringParser.parseToProperties("0 ¤¤¤"),
1629 //                    DecimalFormatSymbols.getInstance(Locale::getEnglish()),
1630 //                    null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
1631 
1632     assertFormatSingle(
1633             u"Currency with Negative Sign",
1634             u"currency/GBP",
1635             u"currency/GBP",
1636             NumberFormatter::with().unit(GBP),
1637             Locale::getEnglish(),
1638             -9876543.21,
1639             u"-£9,876,543.21");
1640 
1641     // The full currency symbol is not shown in NARROW format.
1642     // NOTE: This example is in the documentation.
1643     assertFormatSingle(
1644             u"Currency Difference between Narrow and Short (Narrow Version)",
1645             u"currency/USD unit-width-narrow",
1646             u"currency/USD unit-width-narrow",
1647             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_NARROW),
1648             Locale("en-CA"),
1649             5.43,
1650             u"$5.43");
1651 
1652     assertFormatSingle(
1653             u"Currency Difference between Narrow and Short (Short Version)",
1654             u"currency/USD unit-width-short",
1655             u"currency/USD unit-width-short",
1656             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1657             Locale("en-CA"),
1658             5.43,
1659             u"US$5.43");
1660 
1661     assertFormatSingle(
1662             u"Currency Difference between Formal and Short (Formal Version)",
1663             u"currency/TWD unit-width-formal",
1664             u"currency/TWD unit-width-formal",
1665             NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_FORMAL),
1666             Locale("zh-TW"),
1667             5.43,
1668             u"NT$5.43");
1669 
1670     assertFormatSingle(
1671             u"Currency Difference between Formal and Short (Short Version)",
1672             u"currency/TWD unit-width-short",
1673             u"currency/TWD unit-width-short",
1674             NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1675             Locale("zh-TW"),
1676             5.43,
1677             u"$5.43");
1678 
1679     assertFormatSingle(
1680             u"Currency Difference between Variant and Short (Formal Version)",
1681             u"currency/TRY unit-width-variant",
1682             u"currency/TRY unit-width-variant",
1683             NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_VARIANT),
1684             Locale("tr-TR"),
1685             5.43,
1686             u"TL\u00A05,43");
1687 
1688     assertFormatSingle(
1689             u"Currency Difference between Variant and Short (Short Version)",
1690             u"currency/TRY unit-width-short",
1691             u"currency/TRY unit-width-short",
1692             NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1693             Locale("tr-TR"),
1694             5.43,
1695             u"₺5,43");
1696 
1697     assertFormatSingle(
1698             u"Currency-dependent format (Control)",
1699             u"currency/USD unit-width-short",
1700             u"currency/USD unit-width-short",
1701             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1702             Locale("ca"),
1703             444444.55,
1704             u"444.444,55 USD");
1705 
1706     assertFormatSingle(
1707             u"Currency-dependent format (Test)",
1708             u"currency/ESP unit-width-short",
1709             u"currency/ESP unit-width-short",
1710             NumberFormatter::with().unit(ESP).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1711             Locale("ca"),
1712             444444.55,
1713             u"₧ 444.445");
1714 
1715     assertFormatSingle(
1716             u"Currency-dependent symbols (Control)",
1717             u"currency/USD unit-width-short",
1718             u"currency/USD unit-width-short",
1719             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1720             Locale("pt-PT"),
1721             444444.55,
1722             u"444 444,55 US$");
1723 
1724     // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
1725     // width space), and they set the decimal separator to the $ symbol.
1726     assertFormatSingle(
1727             u"Currency-dependent symbols (Test Short)",
1728             u"currency/PTE unit-width-short",
1729             u"currency/PTE unit-width-short",
1730             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1731             Locale("pt-PT"),
1732             444444.55,
1733             u"444,444$55 \u200B");
1734 
1735     assertFormatSingle(
1736             u"Currency-dependent symbols (Test Narrow)",
1737             u"currency/PTE unit-width-narrow",
1738             u"currency/PTE unit-width-narrow",
1739             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_NARROW),
1740             Locale("pt-PT"),
1741             444444.55,
1742             u"444,444$55 \u200B");
1743 
1744     assertFormatSingle(
1745             u"Currency-dependent symbols (Test ISO Code)",
1746             u"currency/PTE unit-width-iso-code",
1747             u"currency/PTE unit-width-iso-code",
1748             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
1749             Locale("pt-PT"),
1750             444444.55,
1751             u"444,444$55 PTE");
1752 
1753     assertFormatSingle(
1754             u"Plural form depending on visible digits (ICU-20499)",
1755             u"currency/RON unit-width-full-name",
1756             u"currency/RON unit-width-full-name",
1757             NumberFormatter::with().unit(RON).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1758             Locale("ro-RO"),
1759             24,
1760             u"24,00 lei românești");
1761 
1762     assertFormatSingle(
1763             u"Currency spacing in suffix (ICU-20954)",
1764             u"currency/CNY",
1765             u"currency/CNY",
1766             NumberFormatter::with().unit(CNY),
1767             Locale("lu"),
1768             123.12,
1769             u"123,12 CN¥");
1770 }
1771 
unitPercent()1772 void NumberFormatterApiTest::unitPercent() {
1773     assertFormatDescending(
1774             u"Percent",
1775             u"percent",
1776             u"%",
1777             NumberFormatter::with().unit(NoUnit::percent()),
1778             Locale::getEnglish(),
1779             u"87,650%",
1780             u"8,765%",
1781             u"876.5%",
1782             u"87.65%",
1783             u"8.765%",
1784             u"0.8765%",
1785             u"0.08765%",
1786             u"0.008765%",
1787             u"0%");
1788 
1789     assertFormatDescending(
1790             u"Permille",
1791             u"permille",
1792             u"permille",
1793             NumberFormatter::with().unit(NoUnit::permille()),
1794             Locale::getEnglish(),
1795             u"87,650‰",
1796             u"8,765‰",
1797             u"876.5‰",
1798             u"87.65‰",
1799             u"8.765‰",
1800             u"0.8765‰",
1801             u"0.08765‰",
1802             u"0.008765‰",
1803             u"0‰");
1804 
1805     assertFormatSingle(
1806             u"NoUnit Base",
1807             u"base-unit",
1808             u"",
1809             NumberFormatter::with().unit(NoUnit::base()),
1810             Locale::getEnglish(),
1811             51423,
1812             u"51,423");
1813 
1814     assertFormatSingle(
1815             u"Percent with Negative Sign",
1816             u"percent",
1817             u"%",
1818             NumberFormatter::with().unit(NoUnit::percent()),
1819             Locale::getEnglish(),
1820             -98.7654321,
1821             u"-98.765432%");
1822 
1823     // ICU-20923
1824     assertFormatDescendingBig(
1825             u"Compact Percent",
1826             u"compact-short percent",
1827             u"K %",
1828             NumberFormatter::with()
1829                     .notation(Notation::compactShort())
1830                     .unit(NoUnit::percent()),
1831             Locale::getEnglish(),
1832             u"88M%",
1833             u"8.8M%",
1834             u"876K%",
1835             u"88K%",
1836             u"8.8K%",
1837             u"876%",
1838             u"88%",
1839             u"8.8%",
1840             u"0%");
1841 
1842     // ICU-20923
1843     assertFormatDescendingBig(
1844             u"Compact Percent with Scale",
1845             u"compact-short percent scale/100",
1846             u"K %x100",
1847             NumberFormatter::with()
1848                     .notation(Notation::compactShort())
1849                     .unit(NoUnit::percent())
1850                     .scale(Scale::powerOfTen(2)),
1851             Locale::getEnglish(),
1852             u"8.8B%",
1853             u"876M%",
1854             u"88M%",
1855             u"8.8M%",
1856             u"876K%",
1857             u"88K%",
1858             u"8.8K%",
1859             u"876%",
1860             u"0%");
1861 
1862     // ICU-20923
1863     assertFormatDescendingBig(
1864             u"Compact Percent Long Name",
1865             u"compact-short percent unit-width-full-name",
1866             u"K % unit-width-full-name",
1867             NumberFormatter::with()
1868                     .notation(Notation::compactShort())
1869                     .unit(NoUnit::percent())
1870                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1871             Locale::getEnglish(),
1872             u"88M percent",
1873             u"8.8M percent",
1874             u"876K percent",
1875             u"88K percent",
1876             u"8.8K percent",
1877             u"876 percent",
1878             u"88 percent",
1879             u"8.8 percent",
1880             u"0 percent");
1881 
1882     assertFormatSingle(
1883             u"Per Percent",
1884             u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
1885             u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
1886             NumberFormatter::with()
1887                     .unit(MeasureUnit::getMeter())
1888                     .perUnit(MeasureUnit::getPercent())
1889                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1890             Locale::getEnglish(),
1891             50,
1892             u"50 meters per percent");
1893 }
1894 
percentParity()1895 void NumberFormatterApiTest::percentParity() {
1896     IcuTestErrorCode status(*this, "percentParity");
1897     UnlocalizedNumberFormatter uNoUnitPercent = NumberFormatter::with().unit(NoUnit::percent());
1898     UnlocalizedNumberFormatter uNoUnitPermille = NumberFormatter::with().unit(NoUnit::permille());
1899     UnlocalizedNumberFormatter uMeasurePercent = NumberFormatter::with().unit(MeasureUnit::getPercent());
1900     UnlocalizedNumberFormatter uMeasurePermille = NumberFormatter::with().unit(MeasureUnit::getPermille());
1901 
1902     int32_t localeCount;
1903     auto locales = Locale::getAvailableLocales(localeCount);
1904     for (int32_t i=0; i<localeCount; i++) {
1905         auto& locale = locales[i];
1906         UnicodeString sNoUnitPercent = uNoUnitPercent.locale(locale)
1907             .formatDouble(50, status).toString(status);
1908         UnicodeString sNoUnitPermille = uNoUnitPermille.locale(locale)
1909             .formatDouble(50, status).toString(status);
1910         UnicodeString sMeasurePercent = uMeasurePercent.locale(locale)
1911             .formatDouble(50, status).toString(status);
1912         UnicodeString sMeasurePermille = uMeasurePermille.locale(locale)
1913             .formatDouble(50, status).toString(status);
1914 
1915         assertEquals(u"Percent, locale " + UnicodeString(locale.getName()),
1916             sNoUnitPercent, sMeasurePercent);
1917         assertEquals(u"Permille, locale " + UnicodeString(locale.getName()),
1918             sNoUnitPermille, sMeasurePermille);
1919     }
1920 }
1921 
roundingFraction()1922 void NumberFormatterApiTest::roundingFraction() {
1923     assertFormatDescending(
1924             u"Integer",
1925             u"precision-integer",
1926             u".",
1927             NumberFormatter::with().precision(Precision::integer()),
1928             Locale::getEnglish(),
1929             u"87,650",
1930             u"8,765",
1931             u"876",
1932             u"88",
1933             u"9",
1934             u"1",
1935             u"0",
1936             u"0",
1937             u"0");
1938 
1939     assertFormatDescending(
1940             u"Fixed Fraction",
1941             u".000",
1942             u".000",
1943             NumberFormatter::with().precision(Precision::fixedFraction(3)),
1944             Locale::getEnglish(),
1945             u"87,650.000",
1946             u"8,765.000",
1947             u"876.500",
1948             u"87.650",
1949             u"8.765",
1950             u"0.876",
1951             u"0.088",
1952             u"0.009",
1953             u"0.000");
1954 
1955     assertFormatDescending(
1956             u"Min Fraction",
1957             u".0*",
1958             u".0+",
1959             NumberFormatter::with().precision(Precision::minFraction(1)),
1960             Locale::getEnglish(),
1961             u"87,650.0",
1962             u"8,765.0",
1963             u"876.5",
1964             u"87.65",
1965             u"8.765",
1966             u"0.8765",
1967             u"0.08765",
1968             u"0.008765",
1969             u"0.0");
1970 
1971     assertFormatDescending(
1972             u"Max Fraction",
1973             u".#",
1974             u".#",
1975             NumberFormatter::with().precision(Precision::maxFraction(1)),
1976             Locale::getEnglish(),
1977             u"87,650",
1978             u"8,765",
1979             u"876.5",
1980             u"87.6",
1981             u"8.8",
1982             u"0.9",
1983             u"0.1",
1984             u"0",
1985             u"0");
1986 
1987     assertFormatDescending(
1988             u"Min/Max Fraction",
1989             u".0##",
1990             u".0##",
1991             NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
1992             Locale::getEnglish(),
1993             u"87,650.0",
1994             u"8,765.0",
1995             u"876.5",
1996             u"87.65",
1997             u"8.765",
1998             u"0.876",
1999             u"0.088",
2000             u"0.009",
2001             u"0.0");
2002 }
2003 
roundingFigures()2004 void NumberFormatterApiTest::roundingFigures() {
2005     assertFormatSingle(
2006             u"Fixed Significant",
2007             u"@@@",
2008             u"@@@",
2009             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2010             Locale::getEnglish(),
2011             -98,
2012             u"-98.0");
2013 
2014     assertFormatSingle(
2015             u"Fixed Significant Rounding",
2016             u"@@@",
2017             u"@@@",
2018             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2019             Locale::getEnglish(),
2020             -98.7654321,
2021             u"-98.8");
2022 
2023     assertFormatSingle(
2024             u"Fixed Significant Zero",
2025             u"@@@",
2026             u"@@@",
2027             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2028             Locale::getEnglish(),
2029             0,
2030             u"0.00");
2031 
2032     assertFormatSingle(
2033             u"Min Significant",
2034             u"@@*",
2035             u"@@+",
2036             NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
2037             Locale::getEnglish(),
2038             -9,
2039             u"-9.0");
2040 
2041     assertFormatSingle(
2042             u"Max Significant",
2043             u"@###",
2044             u"@###",
2045             NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
2046             Locale::getEnglish(),
2047             98.7654321,
2048             u"98.77");
2049 
2050     assertFormatSingle(
2051             u"Min/Max Significant",
2052             u"@@@#",
2053             u"@@@#",
2054             NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
2055             Locale::getEnglish(),
2056             9.99999,
2057             u"10.0");
2058 
2059     assertFormatSingle(
2060             u"Fixed Significant on zero with lots of integer width",
2061             u"@ integer-width/+000",
2062             u"@ 000",
2063             NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
2064                     .integerWidth(IntegerWidth::zeroFillTo(3)),
2065             Locale::getEnglish(),
2066             0,
2067             "000");
2068 
2069     assertFormatSingle(
2070             u"Fixed Significant on zero with zero integer width",
2071             u"@ integer-width/*",
2072             u"@ integer-width/+",
2073             NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
2074                     .integerWidth(IntegerWidth::zeroFillTo(0)),
2075             Locale::getEnglish(),
2076             0,
2077             "0");
2078 }
2079 
roundingFractionFigures()2080 void NumberFormatterApiTest::roundingFractionFigures() {
2081     assertFormatDescending(
2082             u"Basic Significant", // for comparison
2083             u"@#",
2084             u"@#",
2085             NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
2086             Locale::getEnglish(),
2087             u"88,000",
2088             u"8,800",
2089             u"880",
2090             u"88",
2091             u"8.8",
2092             u"0.88",
2093             u"0.088",
2094             u"0.0088",
2095             u"0");
2096 
2097     assertFormatDescending(
2098             u"FracSig minMaxFrac minSig",
2099             u".0#/@@@*",
2100             u".0#/@@@+",
2101             NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
2102             Locale::getEnglish(),
2103             u"87,650.0",
2104             u"8,765.0",
2105             u"876.5",
2106             u"87.65",
2107             u"8.76",
2108             u"0.876", // minSig beats maxFrac
2109             u"0.0876", // minSig beats maxFrac
2110             u"0.00876", // minSig beats maxFrac
2111             u"0.0");
2112 
2113     assertFormatDescending(
2114             u"FracSig minMaxFrac maxSig A",
2115             u".0##/@#",
2116             u".0##/@#",
2117             NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
2118             Locale::getEnglish(),
2119             u"88,000.0", // maxSig beats maxFrac
2120             u"8,800.0", // maxSig beats maxFrac
2121             u"880.0", // maxSig beats maxFrac
2122             u"88.0", // maxSig beats maxFrac
2123             u"8.8", // maxSig beats maxFrac
2124             u"0.88", // maxSig beats maxFrac
2125             u"0.088",
2126             u"0.009",
2127             u"0.0");
2128 
2129     assertFormatDescending(
2130             u"FracSig minMaxFrac maxSig B",
2131             u".00/@#",
2132             u".00/@#",
2133             NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
2134             Locale::getEnglish(),
2135             u"88,000.00", // maxSig beats maxFrac
2136             u"8,800.00", // maxSig beats maxFrac
2137             u"880.00", // maxSig beats maxFrac
2138             u"88.00", // maxSig beats maxFrac
2139             u"8.80", // maxSig beats maxFrac
2140             u"0.88",
2141             u"0.09",
2142             u"0.01",
2143             u"0.00");
2144 
2145     assertFormatSingle(
2146             u"FracSig with trailing zeros A",
2147             u".00/@@@*",
2148             u".00/@@@+",
2149             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
2150             Locale::getEnglish(),
2151             0.1,
2152             u"0.10");
2153 
2154     assertFormatSingle(
2155             u"FracSig with trailing zeros B",
2156             u".00/@@@*",
2157             u".00/@@@+",
2158             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
2159             Locale::getEnglish(),
2160             0.0999999,
2161             u"0.10");
2162 }
2163 
roundingOther()2164 void NumberFormatterApiTest::roundingOther() {
2165     assertFormatDescending(
2166             u"Rounding None",
2167             u"precision-unlimited",
2168             u".+",
2169             NumberFormatter::with().precision(Precision::unlimited()),
2170             Locale::getEnglish(),
2171             u"87,650",
2172             u"8,765",
2173             u"876.5",
2174             u"87.65",
2175             u"8.765",
2176             u"0.8765",
2177             u"0.08765",
2178             u"0.008765",
2179             u"0");
2180 
2181     assertFormatDescending(
2182             u"Increment",
2183             u"precision-increment/0.5",
2184             u"precision-increment/0.5",
2185             NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
2186             Locale::getEnglish(),
2187             u"87,650.0",
2188             u"8,765.0",
2189             u"876.5",
2190             u"87.5",
2191             u"9.0",
2192             u"1.0",
2193             u"0.0",
2194             u"0.0",
2195             u"0.0");
2196 
2197     assertFormatDescending(
2198             u"Increment with Min Fraction",
2199             u"precision-increment/0.50",
2200             u"precision-increment/0.50",
2201             NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
2202             Locale::getEnglish(),
2203             u"87,650.00",
2204             u"8,765.00",
2205             u"876.50",
2206             u"87.50",
2207             u"9.00",
2208             u"1.00",
2209             u"0.00",
2210             u"0.00",
2211             u"0.00");
2212 
2213     assertFormatDescending(
2214             u"Strange Increment",
2215             u"precision-increment/3.140",
2216             u"precision-increment/3.140",
2217             NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
2218             Locale::getEnglish(),
2219             u"87,649.960",
2220             u"8,763.740",
2221             u"876.060",
2222             u"87.920",
2223             u"9.420",
2224             u"0.000",
2225             u"0.000",
2226             u"0.000",
2227             u"0.000");
2228 
2229     assertFormatDescending(
2230             u"Increment Resolving to Power of 10",
2231             u"precision-increment/0.010",
2232             u"precision-increment/0.010",
2233             NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
2234             Locale::getEnglish(),
2235             u"87,650.000",
2236             u"8,765.000",
2237             u"876.500",
2238             u"87.650",
2239             u"8.760",
2240             u"0.880",
2241             u"0.090",
2242             u"0.010",
2243             u"0.000");
2244 
2245     assertFormatDescending(
2246             u"Currency Standard",
2247             u"currency/CZK precision-currency-standard",
2248             u"currency/CZK precision-currency-standard",
2249             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
2250                     .unit(CZK),
2251             Locale::getEnglish(),
2252             u"CZK 87,650.00",
2253             u"CZK 8,765.00",
2254             u"CZK 876.50",
2255             u"CZK 87.65",
2256             u"CZK 8.76",
2257             u"CZK 0.88",
2258             u"CZK 0.09",
2259             u"CZK 0.01",
2260             u"CZK 0.00");
2261 
2262     assertFormatDescending(
2263             u"Currency Cash",
2264             u"currency/CZK precision-currency-cash",
2265             u"currency/CZK precision-currency-cash",
2266             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
2267                     .unit(CZK),
2268             Locale::getEnglish(),
2269             u"CZK 87,650",
2270             u"CZK 8,765",
2271             u"CZK 876",
2272             u"CZK 88",
2273             u"CZK 9",
2274             u"CZK 1",
2275             u"CZK 0",
2276             u"CZK 0",
2277             u"CZK 0");
2278 
2279     assertFormatDescending(
2280             u"Currency Cash with Nickel Rounding",
2281             u"currency/CAD precision-currency-cash",
2282             u"currency/CAD precision-currency-cash",
2283             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
2284                     .unit(CAD),
2285             Locale::getEnglish(),
2286             u"CA$87,650.00",
2287             u"CA$8,765.00",
2288             u"CA$876.50",
2289             u"CA$87.65",
2290             u"CA$8.75",
2291             u"CA$0.90",
2292             u"CA$0.10",
2293             u"CA$0.00",
2294             u"CA$0.00");
2295 
2296     assertFormatDescending(
2297             u"Currency not in top-level fluent chain",
2298             u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
2299             u".",
2300             NumberFormatter::with().precision(
2301                     Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
2302             Locale::getEnglish(),
2303             u"87,650",
2304             u"8,765",
2305             u"876",
2306             u"88",
2307             u"9",
2308             u"1",
2309             u"0",
2310             u"0",
2311             u"0");
2312 
2313     // NOTE: Other tests cover the behavior of the other rounding modes.
2314     assertFormatDescending(
2315             u"Rounding Mode CEILING",
2316             u"precision-integer rounding-mode-ceiling",
2317             u". rounding-mode-ceiling",
2318             NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING),
2319             Locale::getEnglish(),
2320             u"87,650",
2321             u"8,765",
2322             u"877",
2323             u"88",
2324             u"9",
2325             u"1",
2326             u"1",
2327             u"1",
2328             u"0");
2329 
2330     assertFormatSingle(
2331             u"ICU-20974 Double.MIN_NORMAL",
2332             u"scientific",
2333             u"E0",
2334             NumberFormatter::with().notation(Notation::scientific()),
2335             Locale::getEnglish(),
2336             DBL_MIN,
2337             u"2.225074E-308");
2338 
2339 #ifndef DBL_TRUE_MIN
2340 #define DBL_TRUE_MIN 4.9E-324
2341 #endif
2342 
2343     // Note: this behavior is intentionally different from Java; see
2344     // https://github.com/google/double-conversion/issues/126
2345     assertFormatSingle(
2346             u"ICU-20974 Double.MIN_VALUE",
2347             u"scientific",
2348             u"E0",
2349             NumberFormatter::with().notation(Notation::scientific()),
2350             Locale::getEnglish(),
2351             DBL_TRUE_MIN,
2352             u"5E-324");
2353 }
2354 
grouping()2355 void NumberFormatterApiTest::grouping() {
2356     assertFormatDescendingBig(
2357             u"Western Grouping",
2358             u"group-auto",
2359             u"",
2360             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
2361             Locale::getEnglish(),
2362             u"87,650,000",
2363             u"8,765,000",
2364             u"876,500",
2365             u"87,650",
2366             u"8,765",
2367             u"876.5",
2368             u"87.65",
2369             u"8.765",
2370             u"0");
2371 
2372     assertFormatDescendingBig(
2373             u"Indic Grouping",
2374             u"group-auto",
2375             u"",
2376             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
2377             Locale("en-IN"),
2378             u"8,76,50,000",
2379             u"87,65,000",
2380             u"8,76,500",
2381             u"87,650",
2382             u"8,765",
2383             u"876.5",
2384             u"87.65",
2385             u"8.765",
2386             u"0");
2387 
2388     assertFormatDescendingBig(
2389             u"Western Grouping, Min 2",
2390             u"group-min2",
2391             u",?",
2392             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
2393             Locale::getEnglish(),
2394             u"87,650,000",
2395             u"8,765,000",
2396             u"876,500",
2397             u"87,650",
2398             u"8765",
2399             u"876.5",
2400             u"87.65",
2401             u"8.765",
2402             u"0");
2403 
2404     assertFormatDescendingBig(
2405             u"Indic Grouping, Min 2",
2406             u"group-min2",
2407             u",?",
2408             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
2409             Locale("en-IN"),
2410             u"8,76,50,000",
2411             u"87,65,000",
2412             u"8,76,500",
2413             u"87,650",
2414             u"8765",
2415             u"876.5",
2416             u"87.65",
2417             u"8.765",
2418             u"0");
2419 
2420     assertFormatDescendingBig(
2421             u"No Grouping",
2422             u"group-off",
2423             u",_",
2424             NumberFormatter::with().grouping(UNUM_GROUPING_OFF),
2425             Locale("en-IN"),
2426             u"87650000",
2427             u"8765000",
2428             u"876500",
2429             u"87650",
2430             u"8765",
2431             u"876.5",
2432             u"87.65",
2433             u"8.765",
2434             u"0");
2435 
2436     assertFormatDescendingBig(
2437             u"Indic locale with THOUSANDS grouping",
2438             u"group-thousands",
2439             u"group-thousands",
2440             NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS),
2441             Locale("en-IN"),
2442             u"87,650,000",
2443             u"8,765,000",
2444             u"876,500",
2445             u"87,650",
2446             u"8,765",
2447             u"876.5",
2448             u"87.65",
2449             u"8.765",
2450             u"0");
2451 
2452     // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data
2453     // (Most locales have either 1 or 2)
2454     // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
2455     assertFormatDescendingBig(
2456             u"Polish Grouping",
2457             u"group-auto",
2458             u"",
2459             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
2460             Locale("pl"),
2461             u"87 650 000",
2462             u"8 765 000",
2463             u"876 500",
2464             u"87 650",
2465             u"8765",
2466             u"876,5",
2467             u"87,65",
2468             u"8,765",
2469             u"0");
2470 
2471     assertFormatDescendingBig(
2472             u"Polish Grouping, Min 2",
2473             u"group-min2",
2474             u",?",
2475             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
2476             Locale("pl"),
2477             u"87 650 000",
2478             u"8 765 000",
2479             u"876 500",
2480             u"87 650",
2481             u"8765",
2482             u"876,5",
2483             u"87,65",
2484             u"8,765",
2485             u"0");
2486 
2487     assertFormatDescendingBig(
2488             u"Polish Grouping, Always",
2489             u"group-on-aligned",
2490             u",!",
2491             NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED),
2492             Locale("pl"),
2493             u"87 650 000",
2494             u"8 765 000",
2495             u"876 500",
2496             u"87 650",
2497             u"8 765",
2498             u"876,5",
2499             u"87,65",
2500             u"8,765",
2501             u"0");
2502 
2503     // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
2504     // If this test breaks due to data changes, find another locale that has no default grouping.
2505     assertFormatDescendingBig(
2506             u"Bulgarian Currency Grouping",
2507             u"currency/USD group-auto",
2508             u"currency/USD",
2509             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO).unit(USD),
2510             Locale("bg"),
2511             u"87650000,00 щ.д.",
2512             u"8765000,00 щ.д.",
2513             u"876500,00 щ.д.",
2514             u"87650,00 щ.д.",
2515             u"8765,00 щ.д.",
2516             u"876,50 щ.д.",
2517             u"87,65 щ.д.",
2518             u"8,76 щ.д.",
2519             u"0,00 щ.д.");
2520 
2521     assertFormatDescendingBig(
2522             u"Bulgarian Currency Grouping, Always",
2523             u"currency/USD group-on-aligned",
2524             u"currency/USD ,!",
2525             NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED).unit(USD),
2526             Locale("bg"),
2527             u"87 650 000,00 щ.д.",
2528             u"8 765 000,00 щ.д.",
2529             u"876 500,00 щ.д.",
2530             u"87 650,00 щ.д.",
2531             u"8 765,00 щ.д.",
2532             u"876,50 щ.д.",
2533             u"87,65 щ.д.",
2534             u"8,76 щ.д.",
2535             u"0,00 щ.д.");
2536 
2537     MacroProps macros;
2538     macros.grouper = Grouper(4, 1, 3, UNUM_GROUPING_COUNT);
2539     assertFormatDescendingBig(
2540             u"Custom Grouping via Internal API",
2541             nullptr,
2542             nullptr,
2543             NumberFormatter::with().macros(macros),
2544             Locale::getEnglish(),
2545             u"8,7,6,5,0000",
2546             u"8,7,6,5000",
2547             u"876500",
2548             u"87650",
2549             u"8765",
2550             u"876.5",
2551             u"87.65",
2552             u"8.765",
2553             u"0");
2554 }
2555 
padding()2556 void NumberFormatterApiTest::padding() {
2557     assertFormatDescending(
2558             u"Padding",
2559             nullptr,
2560             nullptr,
2561             NumberFormatter::with().padding(Padder::none()),
2562             Locale::getEnglish(),
2563             u"87,650",
2564             u"8,765",
2565             u"876.5",
2566             u"87.65",
2567             u"8.765",
2568             u"0.8765",
2569             u"0.08765",
2570             u"0.008765",
2571             u"0");
2572 
2573     assertFormatDescending(
2574             u"Padding",
2575             nullptr,
2576             nullptr,
2577             NumberFormatter::with().padding(
2578                     Padder::codePoints(
2579                             '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
2580             Locale::getEnglish(),
2581             u"**87,650",
2582             u"***8,765",
2583             u"***876.5",
2584             u"***87.65",
2585             u"***8.765",
2586             u"**0.8765",
2587             u"*0.08765",
2588             u"0.008765",
2589             u"*******0");
2590 
2591     assertFormatDescending(
2592             u"Padding with code points",
2593             nullptr,
2594             nullptr,
2595             NumberFormatter::with().padding(
2596                     Padder::codePoints(
2597                             0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
2598             Locale::getEnglish(),
2599             u"����87,650",
2600             u"������8,765",
2601             u"������876.5",
2602             u"������87.65",
2603             u"������8.765",
2604             u"����0.8765",
2605             u"��0.08765",
2606             u"0.008765",
2607             u"��������������0");
2608 
2609     assertFormatDescending(
2610             u"Padding with wide digits",
2611             nullptr,
2612             nullptr,
2613             NumberFormatter::with().padding(
2614                             Padder::codePoints(
2615                                     '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX))
2616                     .adoptSymbols(new NumberingSystem(MATHSANB)),
2617             Locale::getEnglish(),
2618             u"**����,������",
2619             u"***��,������",
2620             u"***������.��",
2621             u"***����.����",
2622             u"***��.������",
2623             u"**��.��������",
2624             u"*��.����������",
2625             u"��.������������",
2626             u"*******��");
2627 
2628     assertFormatDescending(
2629             u"Padding with currency spacing",
2630             nullptr,
2631             nullptr,
2632             NumberFormatter::with().padding(
2633                             Padder::codePoints(
2634                                     '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX))
2635                     .unit(GBP)
2636                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
2637             Locale::getEnglish(),
2638             u"GBP 87,650.00",
2639             u"GBP 8,765.00",
2640             u"GBP*876.50",
2641             u"GBP**87.65",
2642             u"GBP***8.76",
2643             u"GBP***0.88",
2644             u"GBP***0.09",
2645             u"GBP***0.01",
2646             u"GBP***0.00");
2647 
2648     assertFormatSingle(
2649             u"Pad Before Prefix",
2650             nullptr,
2651             nullptr,
2652             NumberFormatter::with().padding(
2653                     Padder::codePoints(
2654                             '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX)),
2655             Locale::getEnglish(),
2656             -88.88,
2657             u"**-88.88");
2658 
2659     assertFormatSingle(
2660             u"Pad After Prefix",
2661             nullptr,
2662             nullptr,
2663             NumberFormatter::with().padding(
2664                     Padder::codePoints(
2665                             '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
2666             Locale::getEnglish(),
2667             -88.88,
2668             u"-**88.88");
2669 
2670     assertFormatSingle(
2671             u"Pad Before Suffix",
2672             nullptr,
2673             nullptr,
2674             NumberFormatter::with().padding(
2675                     Padder::codePoints(
2676                             '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX)).unit(NoUnit::percent()),
2677             Locale::getEnglish(),
2678             88.88,
2679             u"88.88**%");
2680 
2681     assertFormatSingle(
2682             u"Pad After Suffix",
2683             nullptr,
2684             nullptr,
2685             NumberFormatter::with().padding(
2686                     Padder::codePoints(
2687                             '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX)).unit(NoUnit::percent()),
2688             Locale::getEnglish(),
2689             88.88,
2690             u"88.88%**");
2691 
2692     assertFormatSingle(
2693             u"Currency Spacing with Zero Digit Padding Broken",
2694             nullptr,
2695             nullptr,
2696             NumberFormatter::with().padding(
2697                             Padder::codePoints(
2698                                     '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX))
2699                     .unit(GBP)
2700                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
2701             Locale::getEnglish(),
2702             514.23,
2703             u"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
2704 }
2705 
integerWidth()2706 void NumberFormatterApiTest::integerWidth() {
2707     assertFormatDescending(
2708             u"Integer Width Default",
2709             u"integer-width/+0",
2710             u"0",
2711             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
2712             Locale::getEnglish(),
2713             u"87,650",
2714             u"8,765",
2715             u"876.5",
2716             u"87.65",
2717             u"8.765",
2718             u"0.8765",
2719             u"0.08765",
2720             u"0.008765",
2721             u"0");
2722 
2723     assertFormatDescending(
2724             u"Integer Width Zero Fill 0",
2725             u"integer-width/*",
2726             u"integer-width/+",
2727             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
2728             Locale::getEnglish(),
2729             u"87,650",
2730             u"8,765",
2731             u"876.5",
2732             u"87.65",
2733             u"8.765",
2734             u".8765",
2735             u".08765",
2736             u".008765",
2737             u"0");  // see ICU-20844
2738 
2739     assertFormatDescending(
2740             u"Integer Width Zero Fill 3",
2741             u"integer-width/+000",
2742             u"000",
2743             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
2744             Locale::getEnglish(),
2745             u"87,650",
2746             u"8,765",
2747             u"876.5",
2748             u"087.65",
2749             u"008.765",
2750             u"000.8765",
2751             u"000.08765",
2752             u"000.008765",
2753             u"000");
2754 
2755     assertFormatDescending(
2756             u"Integer Width Max 3",
2757             u"integer-width/##0",
2758             u"integer-width/##0",
2759             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
2760             Locale::getEnglish(),
2761             u"650",
2762             u"765",
2763             u"876.5",
2764             u"87.65",
2765             u"8.765",
2766             u"0.8765",
2767             u"0.08765",
2768             u"0.008765",
2769             u"0");
2770 
2771     assertFormatDescending(
2772             u"Integer Width Fixed 2",
2773             u"integer-width/00",
2774             u"integer-width/00",
2775             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2776             Locale::getEnglish(),
2777             u"50",
2778             u"65",
2779             u"76.5",
2780             u"87.65",
2781             u"08.765",
2782             u"00.8765",
2783             u"00.08765",
2784             u"00.008765",
2785             u"00");
2786 
2787     assertFormatDescending(
2788             u"Integer Width Compact",
2789             u"compact-short integer-width/000",
2790             u"compact-short integer-width/000",
2791             NumberFormatter::with()
2792                 .notation(Notation::compactShort())
2793                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
2794             Locale::getEnglish(),
2795             u"088K",
2796             u"008.8K",
2797             u"876",
2798             u"088",
2799             u"008.8",
2800             u"000.88",
2801             u"000.088",
2802             u"000.0088",
2803             u"000");
2804 
2805     assertFormatDescending(
2806             u"Integer Width Scientific",
2807             u"scientific integer-width/000",
2808             u"scientific integer-width/000",
2809             NumberFormatter::with()
2810                 .notation(Notation::scientific())
2811                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
2812             Locale::getEnglish(),
2813             u"008.765E4",
2814             u"008.765E3",
2815             u"008.765E2",
2816             u"008.765E1",
2817             u"008.765E0",
2818             u"008.765E-1",
2819             u"008.765E-2",
2820             u"008.765E-3",
2821             u"000E0");
2822 
2823     assertFormatDescending(
2824             u"Integer Width Engineering",
2825             u"engineering integer-width/000",
2826             u"engineering integer-width/000",
2827             NumberFormatter::with()
2828                 .notation(Notation::engineering())
2829                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
2830             Locale::getEnglish(),
2831             u"087.65E3",
2832             u"008.765E3",
2833             u"876.5E0",
2834             u"087.65E0",
2835             u"008.765E0",
2836             u"876.5E-3",
2837             u"087.65E-3",
2838             u"008.765E-3",
2839             u"000E0");
2840 
2841     assertFormatSingle(
2842             u"Integer Width Remove All A",
2843             u"integer-width/00",
2844             u"integer-width/00",
2845             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2846             "en",
2847             2500,
2848             u"00");
2849 
2850     assertFormatSingle(
2851             u"Integer Width Remove All B",
2852             u"integer-width/00",
2853             u"integer-width/00",
2854             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2855             "en",
2856             25000,
2857             u"00");
2858 
2859     assertFormatSingle(
2860             u"Integer Width Remove All B, Bytes Mode",
2861             u"integer-width/00",
2862             u"integer-width/00",
2863             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2864             "en",
2865             // Note: this double produces all 17 significant digits
2866             10000000000000002000.0,
2867             u"00");
2868 }
2869 
symbols()2870 void NumberFormatterApiTest::symbols() {
2871     assertFormatDescending(
2872             u"French Symbols with Japanese Data 1",
2873             nullptr,
2874             nullptr,
2875             NumberFormatter::with().symbols(FRENCH_SYMBOLS),
2876             Locale::getJapan(),
2877             u"87\u202F650",
2878             u"8\u202F765",
2879             u"876,5",
2880             u"87,65",
2881             u"8,765",
2882             u"0,8765",
2883             u"0,08765",
2884             u"0,008765",
2885             u"0");
2886 
2887     assertFormatSingle(
2888             u"French Symbols with Japanese Data 2",
2889             nullptr,
2890             nullptr,
2891             NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS),
2892             Locale::getJapan(),
2893             12345,
2894             u"1,2\u4E07");
2895 
2896     assertFormatDescending(
2897             u"Latin Numbering System with Arabic Data",
2898             u"currency/USD latin",
2899             u"currency/USD latin",
2900             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2901             Locale("ar"),
2902             u"US$ 87,650.00",
2903             u"US$ 8,765.00",
2904             u"US$ 876.50",
2905             u"US$ 87.65",
2906             u"US$ 8.76",
2907             u"US$ 0.88",
2908             u"US$ 0.09",
2909             u"US$ 0.01",
2910             u"US$ 0.00");
2911 
2912     assertFormatDescending(
2913             u"Math Numbering System with French Data",
2914             u"numbering-system/mathsanb",
2915             u"numbering-system/mathsanb",
2916             NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB)),
2917             Locale::getFrench(),
2918             u"����\u202F������",
2919             u"��\u202F������",
2920             u"������,��",
2921             u"����,����",
2922             u"��,������",
2923             u"��,��������",
2924             u"��,����������",
2925             u"��,������������",
2926             u"��");
2927 
2928     assertFormatSingle(
2929             u"Swiss Symbols (used in documentation)",
2930             nullptr,
2931             nullptr,
2932             NumberFormatter::with().symbols(SWISS_SYMBOLS),
2933             Locale::getEnglish(),
2934             12345.67,
2935             u"12’345.67");
2936 
2937     assertFormatSingle(
2938             u"Myanmar Symbols (used in documentation)",
2939             nullptr,
2940             nullptr,
2941             NumberFormatter::with().symbols(MYANMAR_SYMBOLS),
2942             Locale::getEnglish(),
2943             12345.67,
2944             u"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
2945 
2946     // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
2947 
2948     assertFormatSingle(
2949             u"Currency symbol should precede number in ar with NS latn",
2950             u"currency/USD latin",
2951             u"currency/USD latin",
2952             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2953             Locale("ar"),
2954             12345.67,
2955             u"US$ 12,345.67");
2956 
2957     assertFormatSingle(
2958             u"Currency symbol should precede number in ar@numbers=latn",
2959             u"currency/USD",
2960             u"currency/USD",
2961             NumberFormatter::with().unit(USD),
2962             Locale("ar@numbers=latn"),
2963             12345.67,
2964             u"US$ 12,345.67");
2965 
2966     assertFormatSingle(
2967             u"Currency symbol should follow number in ar-EG with NS arab",
2968             u"currency/USD",
2969             u"currency/USD",
2970             NumberFormatter::with().unit(USD),
2971             Locale("ar-EG"),
2972             12345.67,
2973             u"١٢٬٣٤٥٫٦٧ US$");
2974 
2975     assertFormatSingle(
2976             u"Currency symbol should follow number in ar@numbers=arab",
2977             u"currency/USD",
2978             u"currency/USD",
2979             NumberFormatter::with().unit(USD),
2980             Locale("ar@numbers=arab"),
2981             12345.67,
2982             u"١٢٬٣٤٥٫٦٧ US$");
2983 
2984     assertFormatSingle(
2985             u"NumberingSystem in API should win over @numbers keyword",
2986             u"currency/USD latin",
2987             u"currency/USD latin",
2988             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2989             Locale("ar@numbers=arab"),
2990             12345.67,
2991             u"US$ 12,345.67");
2992 
2993     UErrorCode status = U_ZERO_ERROR;
2994     assertEquals(
2995             "NumberingSystem in API should win over @numbers keyword in reverse order",
2996             u"US$ 12,345.67",
2997             NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN))
2998                     .unit(USD)
2999                     .formatDouble(12345.67, status)
3000                     .toString(status));
3001 
3002     DecimalFormatSymbols symbols = SWISS_SYMBOLS;
3003     UnlocalizedNumberFormatter f = NumberFormatter::with().symbols(symbols);
3004     symbols.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol, u"!", status);
3005     assertFormatSingle(
3006             u"Symbols object should be copied",
3007             nullptr,
3008             nullptr,
3009             f,
3010             Locale::getEnglish(),
3011             12345.67,
3012             u"12’345.67");
3013 
3014     assertFormatSingle(
3015             u"The last symbols setter wins",
3016             u"latin",
3017             u"latin",
3018             NumberFormatter::with().symbols(symbols).adoptSymbols(new NumberingSystem(LATN)),
3019             Locale::getEnglish(),
3020             12345.67,
3021             u"12,345.67");
3022 
3023     assertFormatSingle(
3024             u"The last symbols setter wins",
3025             nullptr,
3026             nullptr,
3027             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).symbols(symbols),
3028             Locale::getEnglish(),
3029             12345.67,
3030             u"12!345.67");
3031 }
3032 
3033 // TODO: Enable if/when currency symbol override is added.
3034 //void NumberFormatterTest::symbolsOverride() {
3035 //    DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
3036 //    dfs.setCurrencySymbol("@");
3037 //    dfs.setInternationalCurrencySymbol("foo");
3038 //    assertFormatSingle(
3039 //            u"Custom Short Currency Symbol",
3040 //            NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
3041 //            Locale::getEnglish(),
3042 //            12.3,
3043 //            u"@ 12.30");
3044 //}
3045 
sign()3046 void NumberFormatterApiTest::sign() {
3047     assertFormatSingle(
3048             u"Sign Auto Positive",
3049             u"sign-auto",
3050             u"",
3051             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3052             Locale::getEnglish(),
3053             444444,
3054             u"444,444");
3055 
3056     assertFormatSingle(
3057             u"Sign Auto Negative",
3058             u"sign-auto",
3059             u"",
3060             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3061             Locale::getEnglish(),
3062             -444444,
3063             u"-444,444");
3064 
3065     assertFormatSingle(
3066             u"Sign Auto Zero",
3067             u"sign-auto",
3068             u"",
3069             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3070             Locale::getEnglish(),
3071             0,
3072             u"0");
3073 
3074     assertFormatSingle(
3075             u"Sign Always Positive",
3076             u"sign-always",
3077             u"+!",
3078             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3079             Locale::getEnglish(),
3080             444444,
3081             u"+444,444");
3082 
3083     assertFormatSingle(
3084             u"Sign Always Negative",
3085             u"sign-always",
3086             u"+!",
3087             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3088             Locale::getEnglish(),
3089             -444444,
3090             u"-444,444");
3091 
3092     assertFormatSingle(
3093             u"Sign Always Zero",
3094             u"sign-always",
3095             u"+!",
3096             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3097             Locale::getEnglish(),
3098             0,
3099             u"+0");
3100 
3101     assertFormatSingle(
3102             u"Sign Never Positive",
3103             u"sign-never",
3104             u"+_",
3105             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3106             Locale::getEnglish(),
3107             444444,
3108             u"444,444");
3109 
3110     assertFormatSingle(
3111             u"Sign Never Negative",
3112             u"sign-never",
3113             u"+_",
3114             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3115             Locale::getEnglish(),
3116             -444444,
3117             u"444,444");
3118 
3119     assertFormatSingle(
3120             u"Sign Never Zero",
3121             u"sign-never",
3122             u"+_",
3123             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3124             Locale::getEnglish(),
3125             0,
3126             u"0");
3127 
3128     assertFormatSingle(
3129             u"Sign Accounting Positive",
3130             u"currency/USD sign-accounting",
3131             u"currency/USD ()",
3132             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3133             Locale::getEnglish(),
3134             444444,
3135             u"$444,444.00");
3136 
3137     assertFormatSingle(
3138             u"Sign Accounting Negative",
3139             u"currency/USD sign-accounting",
3140             u"currency/USD ()",
3141             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3142             Locale::getEnglish(),
3143             -444444,
3144             u"($444,444.00)");
3145 
3146     assertFormatSingle(
3147             u"Sign Accounting Zero",
3148             u"currency/USD sign-accounting",
3149             u"currency/USD ()",
3150             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3151             Locale::getEnglish(),
3152             0,
3153             u"$0.00");
3154 
3155     assertFormatSingle(
3156             u"Sign Accounting-Always Positive",
3157             u"currency/USD sign-accounting-always",
3158             u"currency/USD ()!",
3159             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3160             Locale::getEnglish(),
3161             444444,
3162             u"+$444,444.00");
3163 
3164     assertFormatSingle(
3165             u"Sign Accounting-Always Negative",
3166             u"currency/USD sign-accounting-always",
3167             u"currency/USD ()!",
3168             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3169             Locale::getEnglish(),
3170             -444444,
3171             u"($444,444.00)");
3172 
3173     assertFormatSingle(
3174             u"Sign Accounting-Always Zero",
3175             u"currency/USD sign-accounting-always",
3176             u"currency/USD ()!",
3177             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3178             Locale::getEnglish(),
3179             0,
3180             u"+$0.00");
3181 
3182     assertFormatSingle(
3183             u"Sign Except-Zero Positive",
3184             u"sign-except-zero",
3185             u"+?",
3186             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3187             Locale::getEnglish(),
3188             444444,
3189             u"+444,444");
3190 
3191     assertFormatSingle(
3192             u"Sign Except-Zero Negative",
3193             u"sign-except-zero",
3194             u"+?",
3195             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3196             Locale::getEnglish(),
3197             -444444,
3198             u"-444,444");
3199 
3200     assertFormatSingle(
3201             u"Sign Except-Zero Zero",
3202             u"sign-except-zero",
3203             u"+?",
3204             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3205             Locale::getEnglish(),
3206             0,
3207             u"0");
3208 
3209     assertFormatSingle(
3210             u"Sign Accounting-Except-Zero Positive",
3211             u"currency/USD sign-accounting-except-zero",
3212             u"currency/USD ()?",
3213             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3214             Locale::getEnglish(),
3215             444444,
3216             u"+$444,444.00");
3217 
3218     assertFormatSingle(
3219             u"Sign Accounting-Except-Zero Negative",
3220             u"currency/USD sign-accounting-except-zero",
3221             u"currency/USD ()?",
3222             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3223             Locale::getEnglish(),
3224             -444444,
3225             u"($444,444.00)");
3226 
3227     assertFormatSingle(
3228             u"Sign Accounting-Except-Zero Zero",
3229             u"currency/USD sign-accounting-except-zero",
3230             u"currency/USD ()?",
3231             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3232             Locale::getEnglish(),
3233             0,
3234             u"$0.00");
3235 
3236     assertFormatSingle(
3237             u"Sign Accounting Negative Hidden",
3238             u"currency/USD unit-width-hidden sign-accounting",
3239             u"currency/USD unit-width-hidden ()",
3240             NumberFormatter::with()
3241                     .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3242                     .unit(USD)
3243                     .unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
3244             Locale::getEnglish(),
3245             -444444,
3246             u"(444,444.00)");
3247 
3248     assertFormatSingle(
3249             u"Sign Accounting Negative Narrow",
3250             u"currency/USD unit-width-narrow sign-accounting",
3251             u"currency/USD unit-width-narrow ()",
3252             NumberFormatter::with()
3253                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3254                 .unit(USD)
3255                 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
3256             Locale::getCanada(),
3257             -444444,
3258             u"($444,444.00)");
3259 
3260     assertFormatSingle(
3261             u"Sign Accounting Negative Short",
3262             u"currency/USD sign-accounting",
3263             u"currency/USD ()",
3264             NumberFormatter::with()
3265                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3266                 .unit(USD)
3267                 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
3268             Locale::getCanada(),
3269             -444444,
3270             u"(US$444,444.00)");
3271 
3272     assertFormatSingle(
3273             u"Sign Accounting Negative Iso Code",
3274             u"currency/USD unit-width-iso-code sign-accounting",
3275             u"currency/USD unit-width-iso-code ()",
3276             NumberFormatter::with()
3277                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3278                 .unit(USD)
3279                 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
3280             Locale::getCanada(),
3281             -444444,
3282             u"(USD 444,444.00)");
3283 
3284     // Note: CLDR does not provide an accounting pattern for long name currency.
3285     // We fall back to normal currency format. This may change in the future.
3286     assertFormatSingle(
3287             u"Sign Accounting Negative Full Name",
3288             u"currency/USD unit-width-full-name sign-accounting",
3289             u"currency/USD unit-width-full-name ()",
3290             NumberFormatter::with()
3291                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3292                 .unit(USD)
3293                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3294             Locale::getCanada(),
3295             -444444,
3296             u"-444,444.00 US dollars");
3297 }
3298 
signNearZero()3299 void NumberFormatterApiTest::signNearZero() {
3300     // https://unicode-org.atlassian.net/browse/ICU-20709
3301     IcuTestErrorCode status(*this, "signNearZero");
3302     const struct TestCase {
3303         UNumberSignDisplay sign;
3304         double input;
3305         const char16_t* expected;
3306     } cases[] = {
3307         { UNUM_SIGN_AUTO,  1.1, u"1" },
3308         { UNUM_SIGN_AUTO,  0.9, u"1" },
3309         { UNUM_SIGN_AUTO,  0.1, u"0" },
3310         { UNUM_SIGN_AUTO, -0.1, u"-0" }, // interesting case
3311         { UNUM_SIGN_AUTO, -0.9, u"-1" },
3312         { UNUM_SIGN_AUTO, -1.1, u"-1" },
3313         { UNUM_SIGN_ALWAYS,  1.1, u"+1" },
3314         { UNUM_SIGN_ALWAYS,  0.9, u"+1" },
3315         { UNUM_SIGN_ALWAYS,  0.1, u"+0" },
3316         { UNUM_SIGN_ALWAYS, -0.1, u"-0" },
3317         { UNUM_SIGN_ALWAYS, -0.9, u"-1" },
3318         { UNUM_SIGN_ALWAYS, -1.1, u"-1" },
3319         { UNUM_SIGN_EXCEPT_ZERO,  1.1, u"+1" },
3320         { UNUM_SIGN_EXCEPT_ZERO,  0.9, u"+1" },
3321         { UNUM_SIGN_EXCEPT_ZERO,  0.1, u"0" }, // interesting case
3322         { UNUM_SIGN_EXCEPT_ZERO, -0.1, u"0" }, // interesting case
3323         { UNUM_SIGN_EXCEPT_ZERO, -0.9, u"-1" },
3324         { UNUM_SIGN_EXCEPT_ZERO, -1.1, u"-1" },
3325     };
3326     for (auto& cas : cases) {
3327         auto sign = cas.sign;
3328         auto input = cas.input;
3329         auto expected = cas.expected;
3330         auto actual = NumberFormatter::with()
3331             .sign(sign)
3332             .precision(Precision::integer())
3333             .locale(Locale::getUS())
3334             .formatDouble(input, status)
3335             .toString(status);
3336         assertEquals(
3337             DoubleToUnicodeString(input) + " @ SignDisplay " + Int64ToUnicodeString(sign),
3338             expected, actual);
3339     }
3340 }
3341 
signCoverage()3342 void NumberFormatterApiTest::signCoverage() {
3343     // https://unicode-org.atlassian.net/browse/ICU-20708
3344     IcuTestErrorCode status(*this, "signCoverage");
3345     const struct TestCase {
3346         UNumberSignDisplay sign;
3347         const char16_t* expectedStrings[8];
3348     } cases[] = {
3349         { UNUM_SIGN_AUTO, {        u"-∞", u"-1", u"-0",  u"0",  u"1",  u"∞",  u"NaN", u"-NaN" } },
3350         { UNUM_SIGN_ALWAYS, {      u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } },
3351         { UNUM_SIGN_NEVER, {        u"∞",  u"1",  u"0",  u"0",  u"1",  u"∞",  u"NaN",  u"NaN" } },
3352         { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1",  u"0",  u"0", u"+1", u"+∞",  u"NaN",  u"NaN" } },
3353     };
3354     double negNaN = std::copysign(uprv_getNaN(), -0.0);
3355     const double inputs[] = {
3356         -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
3357     };
3358     for (auto& cas : cases) {
3359         auto sign = cas.sign;
3360         for (int32_t i = 0; i < UPRV_LENGTHOF(inputs); i++) {
3361             auto input = inputs[i];
3362             auto expected = cas.expectedStrings[i];
3363             auto actual = NumberFormatter::with()
3364                 .sign(sign)
3365                 .locale(Locale::getUS())
3366                 .formatDouble(input, status)
3367                 .toString(status);
3368             assertEquals(
3369                 DoubleToUnicodeString(input) + " " + Int64ToUnicodeString(sign),
3370                 expected, actual);
3371         }
3372     }
3373 }
3374 
decimal()3375 void NumberFormatterApiTest::decimal() {
3376     assertFormatDescending(
3377             u"Decimal Default",
3378             u"decimal-auto",
3379             u"",
3380             NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO),
3381             Locale::getEnglish(),
3382             u"87,650",
3383             u"8,765",
3384             u"876.5",
3385             u"87.65",
3386             u"8.765",
3387             u"0.8765",
3388             u"0.08765",
3389             u"0.008765",
3390             u"0");
3391 
3392     assertFormatDescending(
3393             u"Decimal Always Shown",
3394             u"decimal-always",
3395             u"decimal-always",
3396             NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS),
3397             Locale::getEnglish(),
3398             u"87,650.",
3399             u"8,765.",
3400             u"876.5",
3401             u"87.65",
3402             u"8.765",
3403             u"0.8765",
3404             u"0.08765",
3405             u"0.008765",
3406             u"0.");
3407 }
3408 
scale()3409 void NumberFormatterApiTest::scale() {
3410     assertFormatDescending(
3411             u"Multiplier None",
3412             u"scale/1",
3413             u"",
3414             NumberFormatter::with().scale(Scale::none()),
3415             Locale::getEnglish(),
3416             u"87,650",
3417             u"8,765",
3418             u"876.5",
3419             u"87.65",
3420             u"8.765",
3421             u"0.8765",
3422             u"0.08765",
3423             u"0.008765",
3424             u"0");
3425 
3426     assertFormatDescending(
3427             u"Multiplier Power of Ten",
3428             u"scale/1000000",
3429             u"scale/1E6",
3430             NumberFormatter::with().scale(Scale::powerOfTen(6)),
3431             Locale::getEnglish(),
3432             u"87,650,000,000",
3433             u"8,765,000,000",
3434             u"876,500,000",
3435             u"87,650,000",
3436             u"8,765,000",
3437             u"876,500",
3438             u"87,650",
3439             u"8,765",
3440             u"0");
3441 
3442     assertFormatDescending(
3443             u"Multiplier Arbitrary Double",
3444             u"scale/5.2",
3445             u"scale/5.2",
3446             NumberFormatter::with().scale(Scale::byDouble(5.2)),
3447             Locale::getEnglish(),
3448             u"455,780",
3449             u"45,578",
3450             u"4,557.8",
3451             u"455.78",
3452             u"45.578",
3453             u"4.5578",
3454             u"0.45578",
3455             u"0.045578",
3456             u"0");
3457 
3458     assertFormatDescending(
3459             u"Multiplier Arbitrary BigDecimal",
3460             u"scale/5.2",
3461             u"scale/5.2",
3462             NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
3463             Locale::getEnglish(),
3464             u"455,780",
3465             u"45,578",
3466             u"4,557.8",
3467             u"455.78",
3468             u"45.578",
3469             u"4.5578",
3470             u"0.45578",
3471             u"0.045578",
3472             u"0");
3473 
3474     assertFormatDescending(
3475             u"Multiplier Arbitrary Double And Power Of Ten",
3476             u"scale/5200",
3477             u"scale/5200",
3478             NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
3479             Locale::getEnglish(),
3480             u"455,780,000",
3481             u"45,578,000",
3482             u"4,557,800",
3483             u"455,780",
3484             u"45,578",
3485             u"4,557.8",
3486             u"455.78",
3487             u"45.578",
3488             u"0");
3489 
3490     assertFormatDescending(
3491             u"Multiplier Zero",
3492             u"scale/0",
3493             u"scale/0",
3494             NumberFormatter::with().scale(Scale::byDouble(0)),
3495             Locale::getEnglish(),
3496             u"0",
3497             u"0",
3498             u"0",
3499             u"0",
3500             u"0",
3501             u"0",
3502             u"0",
3503             u"0",
3504             u"0");
3505 
3506     assertFormatSingle(
3507             u"Multiplier Skeleton Scientific Notation and Percent",
3508             u"percent scale/1E2",
3509             u"%x100",
3510             NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
3511             Locale::getEnglish(),
3512             0.5,
3513             u"50%");
3514 
3515     assertFormatSingle(
3516             u"Negative Multiplier",
3517             u"scale/-5.2",
3518             u"scale/-5.2",
3519             NumberFormatter::with().scale(Scale::byDouble(-5.2)),
3520             Locale::getEnglish(),
3521             2,
3522             u"-10.4");
3523 
3524     assertFormatSingle(
3525             u"Negative One Multiplier",
3526             u"scale/-1",
3527             u"scale/-1",
3528             NumberFormatter::with().scale(Scale::byDouble(-1)),
3529             Locale::getEnglish(),
3530             444444,
3531             u"-444,444");
3532 
3533     assertFormatSingle(
3534             u"Two-Type Multiplier with Overlap",
3535             u"scale/10000",
3536             u"scale/1E4",
3537             NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
3538             Locale::getEnglish(),
3539             2,
3540             u"20,000");
3541 }
3542 
locale()3543 void NumberFormatterApiTest::locale() {
3544     // Coverage for the locale setters.
3545     UErrorCode status = U_ZERO_ERROR;
3546     UnicodeString actual = NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status)
3547             .toString(status);
3548     assertEquals("Locale withLocale()", u"1\u202f234", actual);
3549 }
3550 
skeletonUserGuideExamples()3551 void NumberFormatterApiTest::skeletonUserGuideExamples() {
3552     IcuTestErrorCode status(*this, "skeletonUserGuideExamples");
3553 
3554     // Test the skeleton examples in userguide/format_parse/numbers/skeletons.md
3555     struct TestCase {
3556         const char16_t* skeleton;
3557         const char16_t* conciseSkeleton;
3558         double input;
3559         const char16_t* expected;
3560     } cases[] = {
3561         {u"percent", u"%", 25, u"25%"},
3562         {u".00", u".00", 25, u"25.00"},
3563         {u"percent .00", u"% .00", 25, u"25.00%"},
3564         {u"scale/100", u"scale/100", 0.3, u"30"},
3565         {u"percent scale/100", u"%x100", 0.3, u"30%"},
3566         {u"measure-unit/length-meter", u"unit/meter", 5, u"5 m"},
3567         {u"measure-unit/length-meter unit-width-full-name", u"unit/meter unit-width-full-name", 5, u"5 meters"},
3568         {u"currency/CAD", u"currency/CAD", 10, u"CA$10.00"},
3569         {u"currency/CAD unit-width-narrow", u"currency/CAD unit-width-narrow", 10, u"$10.00"},
3570         {u"compact-short", u"K", 5000, u"5K"},
3571         {u"compact-long", u"KK", 5000, u"5 thousand"},
3572         {u"compact-short currency/CAD", u"K currency/CAD", 5000, u"CA$5K"},
3573         {u"", u"", 5000, u"5,000"},
3574         {u"group-min2", u",?", 5000, u"5000"},
3575         {u"group-min2", u",?", 15000, u"15,000"},
3576         {u"sign-always", u"+!", 60, u"+60"},
3577         {u"sign-always", u"+!", 0, u"+0"},
3578         {u"sign-except-zero", u"+?", 60, u"+60"},
3579         {u"sign-except-zero", u"+?", 0, u"0"},
3580         {u"sign-accounting currency/CAD", u"() currency/CAD", -40, u"(CA$40.00)"}
3581     };
3582 
3583     for (const auto& cas : cases) {
3584         status.setScope(cas.skeleton);
3585         FormattedNumber actual = NumberFormatter::forSkeleton(cas.skeleton, status)
3586             .locale("en-US")
3587             .formatDouble(cas.input, status);
3588         assertEquals(cas.skeleton, cas.expected, actual.toTempString(status));
3589         status.errIfFailureAndReset();
3590         FormattedNumber actualConcise = NumberFormatter::forSkeleton(cas.conciseSkeleton, status)
3591             .locale("en-US")
3592             .formatDouble(cas.input, status);
3593         assertEquals(cas.conciseSkeleton, cas.expected, actualConcise.toTempString(status));
3594         status.errIfFailureAndReset();
3595     }
3596 }
3597 
formatTypes()3598 void NumberFormatterApiTest::formatTypes() {
3599     UErrorCode status = U_ZERO_ERROR;
3600     LocalizedNumberFormatter formatter = NumberFormatter::withLocale(Locale::getEnglish());
3601 
3602     // Double
3603     assertEquals("Format double", "514.23", formatter.formatDouble(514.23, status).toString(status));
3604 
3605     // Int64
3606     assertEquals("Format int64", "51,423", formatter.formatDouble(51423L, status).toString(status));
3607 
3608     // decNumber
3609     UnicodeString actual = formatter.formatDecimal("98765432123456789E1", status).toString(status);
3610     assertEquals("Format decNumber", u"987,654,321,234,567,890", actual);
3611 
3612     // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
3613     // The number needs to have exactly 40 digits, which is the size of the default buffer.
3614     // (issue discovered by the address sanitizer in C++)
3615     static const char* str = "0.009876543210987654321098765432109876543211";
3616     actual = formatter.precision(Precision::unlimited()).formatDecimal(str, status).toString(status);
3617     assertEquals("Format decNumber to 40 digits", str, actual);
3618 }
3619 
fieldPositionLogic()3620 void NumberFormatterApiTest::fieldPositionLogic() {
3621     IcuTestErrorCode status(*this, "fieldPositionLogic");
3622 
3623     const char16_t* message = u"Field position logic test";
3624 
3625     FormattedNumber fmtd = assertFormatSingle(
3626             message,
3627             u"",
3628             u"",
3629             NumberFormatter::with(),
3630             Locale::getEnglish(),
3631             -9876543210.12,
3632             u"-9,876,543,210.12");
3633 
3634     static const UFieldPosition expectedFieldPositions[] = {
3635             // field, begin index, end index
3636             {UNUM_SIGN_FIELD, 0, 1},
3637             {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
3638             {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
3639             {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
3640             {UNUM_INTEGER_FIELD, 1, 14},
3641             {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
3642             {UNUM_FRACTION_FIELD, 15, 17}};
3643 
3644     assertNumberFieldPositions(
3645             message,
3646             fmtd,
3647             expectedFieldPositions,
3648             UPRV_LENGTHOF(expectedFieldPositions));
3649 
3650     // Test the iteration functionality of nextFieldPosition
3651     ConstrainedFieldPosition actual;
3652     actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
3653     int32_t i = 1;
3654     while (fmtd.nextPosition(actual, status)) {
3655         UFieldPosition expected = expectedFieldPositions[i++];
3656         assertEquals(
3657                 UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
3658                 expected.field,
3659                 actual.getField());
3660         assertEquals(
3661                 UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
3662                 expected.beginIndex,
3663                 actual.getStart());
3664         assertEquals(
3665                 UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
3666                 expected.endIndex,
3667                 actual.getLimit());
3668     }
3669     assertEquals(u"Should have seen all grouping separators", 4, i);
3670 
3671     // Make sure strings without fraction do not contain fraction field
3672     actual.reset();
3673     actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD);
3674     fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
3675     assertFalse(u"No fraction part in an integer", fmtd.nextPosition(actual, status));
3676 }
3677 
fieldPositionCoverage()3678 void NumberFormatterApiTest::fieldPositionCoverage() {
3679     IcuTestErrorCode status(*this, "fieldPositionCoverage");
3680 
3681     {
3682         const char16_t* message = u"Measure unit field position basic";
3683         FormattedNumber result = assertFormatSingle(
3684                 message,
3685                 u"measure-unit/temperature-fahrenheit",
3686                 u"unit/fahrenheit",
3687                 NumberFormatter::with().unit(FAHRENHEIT),
3688                 Locale::getEnglish(),
3689                 68,
3690                 u"68°F");
3691         static const UFieldPosition expectedFieldPositions[] = {
3692                 // field, begin index, end index
3693                 {UNUM_INTEGER_FIELD, 0, 2},
3694                 {UNUM_MEASURE_UNIT_FIELD, 2, 4}};
3695         assertNumberFieldPositions(
3696                 message,
3697                 result,
3698                 expectedFieldPositions,
3699                 UPRV_LENGTHOF(expectedFieldPositions));
3700     }
3701 
3702     {
3703         const char16_t* message = u"Measure unit field position with compound unit";
3704         FormattedNumber result = assertFormatSingle(
3705                 message,
3706                 u"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
3707                 u"unit/fahrenheit-per-day",
3708                 NumberFormatter::with().unit(FAHRENHEIT).perUnit(DAY),
3709                 Locale::getEnglish(),
3710                 68,
3711                 u"68°F/d");
3712         static const UFieldPosition expectedFieldPositions[] = {
3713                 // field, begin index, end index
3714                 {UNUM_INTEGER_FIELD, 0, 2},
3715                 // coverage for old enum:
3716                 {DecimalFormat::kMeasureUnitField, 2, 6}};
3717         assertNumberFieldPositions(
3718                 message,
3719                 result,
3720                 expectedFieldPositions,
3721                 UPRV_LENGTHOF(expectedFieldPositions));
3722     }
3723 
3724     {
3725         const char16_t* message = u"Measure unit field position with spaces";
3726         FormattedNumber result = assertFormatSingle(
3727                 message,
3728                 u"measure-unit/length-meter unit-width-full-name",
3729                 u"unit/meter unit-width-full-name",
3730                 NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3731                 Locale::getEnglish(),
3732                 68,
3733                 u"68 meters");
3734         static const UFieldPosition expectedFieldPositions[] = {
3735                 // field, begin index, end index
3736                 {UNUM_INTEGER_FIELD, 0, 2},
3737                 // note: field starts after the space
3738                 {UNUM_MEASURE_UNIT_FIELD, 3, 9}};
3739         assertNumberFieldPositions(
3740                 message,
3741                 result,
3742                 expectedFieldPositions,
3743                 UPRV_LENGTHOF(expectedFieldPositions));
3744     }
3745 
3746     {
3747         const char16_t* message = u"Measure unit field position with prefix and suffix, composed m/s";
3748         FormattedNumber result = assertFormatSingle(
3749                 message,
3750                 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
3751                 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
3752                 NumberFormatter::with().unit(METER).perUnit(SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3753                 "ky", // locale with the interesting data
3754                 68,
3755                 u"секундасына 68 метр");
3756         static const UFieldPosition expectedFieldPositions[] = {
3757                 // field, begin index, end index
3758                 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
3759                 {UNUM_INTEGER_FIELD, 12, 14},
3760                 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
3761         assertNumberFieldPositions(
3762                 message,
3763                 result,
3764                 expectedFieldPositions,
3765                 UPRV_LENGTHOF(expectedFieldPositions));
3766     }
3767 
3768     {
3769         const char16_t* message = u"Measure unit field position with prefix and suffix, built-in m/s";
3770         FormattedNumber result = assertFormatSingle(
3771                 message,
3772                 u"measure-unit/speed-meter-per-second unit-width-full-name",
3773                 u"unit/meter-per-second unit-width-full-name",
3774                 NumberFormatter::with().unit(METER_PER_SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3775                 "ky", // locale with the interesting data
3776                 68,
3777                 u"секундасына 68 метр");
3778         static const UFieldPosition expectedFieldPositions[] = {
3779                 // field, begin index, end index
3780                 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
3781                 {UNUM_INTEGER_FIELD, 12, 14},
3782                 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
3783         assertNumberFieldPositions(
3784                 message,
3785                 result,
3786                 expectedFieldPositions,
3787                 UPRV_LENGTHOF(expectedFieldPositions));
3788     }
3789 
3790     {
3791         const char16_t* message = u"Measure unit field position with inner spaces";
3792         FormattedNumber result = assertFormatSingle(
3793                 message,
3794                 u"measure-unit/temperature-fahrenheit unit-width-full-name",
3795                 u"unit/fahrenheit unit-width-full-name",
3796                 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3797                 "vi", // locale with the interesting data
3798                 68,
3799                 u"68 độ F");
3800         static const UFieldPosition expectedFieldPositions[] = {
3801                 // field, begin index, end index
3802                 {UNUM_INTEGER_FIELD, 0, 2},
3803                 // Should trim leading/trailing spaces, but not inner spaces:
3804                 {UNUM_MEASURE_UNIT_FIELD, 3, 7}};
3805         assertNumberFieldPositions(
3806                 message,
3807                 result,
3808                 expectedFieldPositions,
3809                 UPRV_LENGTHOF(expectedFieldPositions));
3810     }
3811 
3812     {
3813         // Data: other{"‎{0} K"} == "\u200E{0} K"
3814         // If that data changes, try to find another example of a non-empty unit prefix/suffix
3815         // that is also all ignorables (whitespace and bidi control marks).
3816         const char16_t* message = u"Measure unit field position with fully ignorable prefix";
3817         FormattedNumber result = assertFormatSingle(
3818                 message,
3819                 u"measure-unit/temperature-kelvin",
3820                 u"unit/kelvin",
3821                 NumberFormatter::with().unit(KELVIN),
3822                 "fa", // locale with the interesting data
3823                 68,
3824                 u"‎۶۸ K");
3825         static const UFieldPosition expectedFieldPositions[] = {
3826                 // field, begin index, end index
3827                 {UNUM_INTEGER_FIELD, 1, 3},
3828                 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
3829         assertNumberFieldPositions(
3830                 message,
3831                 result,
3832                 expectedFieldPositions,
3833                 UPRV_LENGTHOF(expectedFieldPositions));
3834     }
3835 
3836     {
3837         const char16_t* message = u"Compact field basic";
3838         FormattedNumber result = assertFormatSingle(
3839                 message,
3840                 u"compact-short",
3841                 u"K",
3842                 NumberFormatter::with().notation(Notation::compactShort()),
3843                 Locale::getUS(),
3844                 65000,
3845                 u"65K");
3846         static const UFieldPosition expectedFieldPositions[] = {
3847                 // field, begin index, end index
3848                 {UNUM_INTEGER_FIELD, 0, 2},
3849                 {UNUM_COMPACT_FIELD, 2, 3}};
3850         assertNumberFieldPositions(
3851                 message,
3852                 result,
3853                 expectedFieldPositions,
3854                 UPRV_LENGTHOF(expectedFieldPositions));
3855     }
3856 
3857     {
3858         const char16_t* message = u"Compact field with spaces";
3859         FormattedNumber result = assertFormatSingle(
3860                 message,
3861                 u"compact-long",
3862                 u"KK",
3863                 NumberFormatter::with().notation(Notation::compactLong()),
3864                 Locale::getUS(),
3865                 65000,
3866                 u"65 thousand");
3867         static const UFieldPosition expectedFieldPositions[] = {
3868                 // field, begin index, end index
3869                 {UNUM_INTEGER_FIELD, 0, 2},
3870                 {UNUM_COMPACT_FIELD, 3, 11}};
3871         assertNumberFieldPositions(
3872                 message,
3873                 result,
3874                 expectedFieldPositions,
3875                 UPRV_LENGTHOF(expectedFieldPositions));
3876     }
3877 
3878     {
3879         const char16_t* message = u"Compact field with inner space";
3880         FormattedNumber result = assertFormatSingle(
3881                 message,
3882                 u"compact-long",
3883                 u"KK",
3884                 NumberFormatter::with().notation(Notation::compactLong()),
3885                 "fil",  // locale with interesting data
3886                 6000,
3887                 u"6 na libo");
3888         static const UFieldPosition expectedFieldPositions[] = {
3889                 // field, begin index, end index
3890                 {UNUM_INTEGER_FIELD, 0, 1},
3891                 {UNUM_COMPACT_FIELD, 2, 9}};
3892         assertNumberFieldPositions(
3893                 message,
3894                 result,
3895                 expectedFieldPositions,
3896                 UPRV_LENGTHOF(expectedFieldPositions));
3897     }
3898 
3899     {
3900         const char16_t* message = u"Compact field with bidi mark";
3901         FormattedNumber result = assertFormatSingle(
3902                 message,
3903                 u"compact-long",
3904                 u"KK",
3905                 NumberFormatter::with().notation(Notation::compactLong()),
3906                 "he",  // locale with interesting data
3907                 6000,
3908                 u"\u200F6 אלף");
3909         static const UFieldPosition expectedFieldPositions[] = {
3910                 // field, begin index, end index
3911                 {UNUM_INTEGER_FIELD, 1, 2},
3912                 {UNUM_COMPACT_FIELD, 3, 6}};
3913         assertNumberFieldPositions(
3914                 message,
3915                 result,
3916                 expectedFieldPositions,
3917                 UPRV_LENGTHOF(expectedFieldPositions));
3918     }
3919 
3920     {
3921         const char16_t* message = u"Compact with currency fields";
3922         FormattedNumber result = assertFormatSingle(
3923                 message,
3924                 u"compact-short currency/USD",
3925                 u"K currency/USD",
3926                 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
3927                 "sr_Latn",  // locale with interesting data
3928                 65000,
3929                 u"65 hilj. US$");
3930         static const UFieldPosition expectedFieldPositions[] = {
3931                 // field, begin index, end index
3932                 {UNUM_INTEGER_FIELD, 0, 2},
3933                 {UNUM_COMPACT_FIELD, 3, 8},
3934                 {UNUM_CURRENCY_FIELD, 9, 12}};
3935         assertNumberFieldPositions(
3936                 message,
3937                 result,
3938                 expectedFieldPositions,
3939                 UPRV_LENGTHOF(expectedFieldPositions));
3940     }
3941 
3942     {
3943         const char16_t* message = u"Currency long name fields";
3944         FormattedNumber result = assertFormatSingle(
3945                 message,
3946                 u"currency/USD unit-width-full-name",
3947                 u"currency/USD unit-width-full-name",
3948                 NumberFormatter::with().unit(USD)
3949                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
3950                 "en",
3951                 12345,
3952                 u"12,345.00 US dollars");
3953         static const UFieldPosition expectedFieldPositions[] = {
3954                 // field, begin index, end index
3955                 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
3956                 {UNUM_INTEGER_FIELD, 0, 6},
3957                 {UNUM_DECIMAL_SEPARATOR_FIELD, 6, 7},
3958                 {UNUM_FRACTION_FIELD, 7, 9},
3959                 {UNUM_CURRENCY_FIELD, 10, 20}};
3960         assertNumberFieldPositions(
3961                 message,
3962                 result,
3963                 expectedFieldPositions,
3964                 UPRV_LENGTHOF(expectedFieldPositions));
3965     }
3966 
3967     {
3968         const char16_t* message = u"Compact with measure unit fields";
3969         FormattedNumber result = assertFormatSingle(
3970                 message,
3971                 u"compact-long measure-unit/length-meter unit-width-full-name",
3972                 u"KK unit/meter unit-width-full-name",
3973                 NumberFormatter::with().notation(Notation::compactLong())
3974                     .unit(METER)
3975                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3976                 Locale::getUS(),
3977                 65000,
3978                 u"65 thousand meters");
3979         static const UFieldPosition expectedFieldPositions[] = {
3980                 // field, begin index, end index
3981                 {UNUM_INTEGER_FIELD, 0, 2},
3982                 {UNUM_COMPACT_FIELD, 3, 11},
3983                 {UNUM_MEASURE_UNIT_FIELD, 12, 18}};
3984         assertNumberFieldPositions(
3985                 message,
3986                 result,
3987                 expectedFieldPositions,
3988                 UPRV_LENGTHOF(expectedFieldPositions));
3989     }
3990 }
3991 
toFormat()3992 void NumberFormatterApiTest::toFormat() {
3993     IcuTestErrorCode status(*this, "icuFormat");
3994     LocalizedNumberFormatter lnf = NumberFormatter::withLocale("fr")
3995             .precision(Precision::fixedFraction(3));
3996     LocalPointer<Format> format(lnf.toFormat(status), status);
3997     FieldPosition fpos(UNUM_DECIMAL_SEPARATOR_FIELD);
3998     UnicodeString sb;
3999     format->format(514.23, sb, fpos, status);
4000     assertEquals("Should correctly format number", u"514,230", sb);
4001     assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
4002     assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
4003     assertEquals(
4004             "ICU Format should round-trip",
4005             lnf.toSkeleton(status),
4006             dynamic_cast<LocalizedNumberFormatterAsFormat*>(format.getAlias())->getNumberFormatter()
4007                     .toSkeleton(status));
4008 
4009     UFormattedNumberData result;
4010     result.quantity.setToDouble(514.23);
4011     lnf.formatImpl(&result, status);
4012     FieldPositionIterator fpi1;
4013     {
4014         FieldPositionIteratorHandler fpih(&fpi1, status);
4015         result.getAllFieldPositions(fpih, status);
4016     }
4017 
4018     FieldPositionIterator fpi2;
4019     format->format(514.23, sb.remove(), &fpi2, status);
4020 
4021     assertTrue("Should produce same field position iterator", fpi1 == fpi2);
4022 }
4023 
errors()4024 void NumberFormatterApiTest::errors() {
4025     LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
4026             Precision::fixedFraction(
4027                     -1));
4028 
4029     // formatInt
4030     UErrorCode status = U_ZERO_ERROR;
4031     FormattedNumber fn = lnf.formatInt(1, status);
4032     assertEquals(
4033             "Should fail in formatInt method with error code for rounding",
4034             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4035             status);
4036 
4037     // formatDouble
4038     status = U_ZERO_ERROR;
4039     fn = lnf.formatDouble(1.0, status);
4040     assertEquals(
4041             "Should fail in formatDouble method with error code for rounding",
4042             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4043             status);
4044 
4045     // formatDecimal (decimal error)
4046     status = U_ZERO_ERROR;
4047     fn = NumberFormatter::withLocale("en").formatDecimal("1x2", status);
4048     assertEquals(
4049             "Should fail in formatDecimal method with error code for decimal number syntax",
4050             U_DECIMAL_NUMBER_SYNTAX_ERROR,
4051             status);
4052 
4053     // formatDecimal (setting error)
4054     status = U_ZERO_ERROR;
4055     fn = lnf.formatDecimal("1.0", status);
4056     assertEquals(
4057             "Should fail in formatDecimal method with error code for rounding",
4058             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4059             status);
4060 
4061     // Skeleton string
4062     status = U_ZERO_ERROR;
4063     UnicodeString output = lnf.toSkeleton(status);
4064     assertEquals(
4065             "Should fail on toSkeleton terminal method with correct error code",
4066             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4067             status);
4068     assertTrue(
4069             "Terminal toSkeleton on error object should be bogus",
4070             output.isBogus());
4071 
4072     // FieldPosition (constrained category)
4073     status = U_ZERO_ERROR;
4074     ConstrainedFieldPosition fp;
4075     fp.constrainCategory(UFIELD_CATEGORY_NUMBER);
4076     fn.nextPosition(fp, status);
4077     assertEquals(
4078             "Should fail on FieldPosition terminal method with correct error code",
4079             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4080             status);
4081 
4082     // FieldPositionIterator (no constraints)
4083     status = U_ZERO_ERROR;
4084     fp.reset();
4085     fn.nextPosition(fp, status);
4086     assertEquals(
4087             "Should fail on FieldPositoinIterator terminal method with correct error code",
4088             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4089             status);
4090 
4091     // Appendable
4092     status = U_ZERO_ERROR;
4093     UnicodeStringAppendable appendable(output.remove());
4094     fn.appendTo(appendable, status);
4095     assertEquals(
4096             "Should fail on Appendable terminal method with correct error code",
4097             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4098             status);
4099 
4100     // UnicodeString
4101     status = U_ZERO_ERROR;
4102     output = fn.toString(status);
4103     assertEquals(
4104             "Should fail on UnicodeString terminal method with correct error code",
4105             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4106             status);
4107     assertTrue(
4108             "Terminal UnicodeString on error object should be bogus",
4109             output.isBogus());
4110 
4111     // CopyErrorTo
4112     status = U_ZERO_ERROR;
4113     lnf.copyErrorTo(status);
4114     assertEquals(
4115             "Should fail since rounder is not legal with correct error code",
4116             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4117             status);
4118 }
4119 
validRanges()4120 void NumberFormatterApiTest::validRanges() {
4121 
4122 #define EXPECTED_MAX_INT_FRAC_SIG 999
4123 
4124 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) UPRV_BLOCK_MACRO_BEGIN { \
4125     UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
4126         ? U_ZERO_ERROR \
4127         : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
4128     assertEquals( \
4129         UnicodeString(u"Incorrect status for " #method " on input ") \
4130             + Int64ToUnicodeString(argument), \
4131         expectedStatus, \
4132         status); \
4133 } UPRV_BLOCK_MACRO_END
4134 
4135 #define VALID_RANGE_ONEARG(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
4136     for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
4137         UErrorCode status = U_ZERO_ERROR; \
4138         NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
4139         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4140     } \
4141 } UPRV_BLOCK_MACRO_END
4142 
4143 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
4144     for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
4145         UErrorCode status = U_ZERO_ERROR; \
4146         /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
4147         NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
4148         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4149         status = U_ZERO_ERROR; \
4150         /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
4151         NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
4152         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4153         /* Check that first argument must be less than or equal to second argument */ \
4154         NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
4155         assertEquals("Incorrect status for " #method " on max < min input", \
4156             U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
4157             status); \
4158     } \
4159 } UPRV_BLOCK_MACRO_END
4160 
4161     VALID_RANGE_ONEARG(precision, Precision::fixedFraction, 0);
4162     VALID_RANGE_ONEARG(precision, Precision::minFraction, 0);
4163     VALID_RANGE_ONEARG(precision, Precision::maxFraction, 0);
4164     VALID_RANGE_TWOARGS(precision, Precision::minMaxFraction, 0);
4165     VALID_RANGE_ONEARG(precision, Precision::fixedSignificantDigits, 1);
4166     VALID_RANGE_ONEARG(precision, Precision::minSignificantDigits, 1);
4167     VALID_RANGE_ONEARG(precision, Precision::maxSignificantDigits, 1);
4168     VALID_RANGE_TWOARGS(precision, Precision::minMaxSignificantDigits, 1);
4169     VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMinDigits, 1);
4170     VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMaxDigits, 1);
4171     VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1);
4172     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0);
4173     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1);
4174 }
4175 
copyMove()4176 void NumberFormatterApiTest::copyMove() {
4177     IcuTestErrorCode status(*this, "copyMove");
4178 
4179     // Default constructors
4180     LocalizedNumberFormatter l1;
4181     assertEquals("Initial behavior", u"10", l1.formatInt(10, status).toString(status), true);
4182     if (status.errDataIfFailureAndReset()) { return; }
4183     assertEquals("Initial call count", 1, l1.getCallCount());
4184     assertTrue("Initial compiled", l1.getCompiled() == nullptr);
4185 
4186     // Setup
4187     l1 = NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
4188     assertEquals("Initial behavior", u"10%", l1.formatInt(10, status).toString(status));
4189     assertEquals("Initial call count", 1, l1.getCallCount());
4190     assertTrue("Initial compiled", l1.getCompiled() == nullptr);
4191     l1.formatInt(123, status);
4192     assertEquals("Still not compiled", 2, l1.getCallCount());
4193     assertTrue("Still not compiled", l1.getCompiled() == nullptr);
4194     l1.formatInt(123, status);
4195     assertEquals("Compiled", u"10%", l1.formatInt(10, status).toString(status));
4196     assertEquals("Compiled", INT32_MIN, l1.getCallCount());
4197     assertTrue("Compiled", l1.getCompiled() != nullptr);
4198 
4199     // Copy constructor
4200     LocalizedNumberFormatter l2 = l1;
4201     assertEquals("[constructor] Copy behavior", u"10%", l2.formatInt(10, status).toString(status));
4202     assertEquals("[constructor] Copy should not have compiled state", 1, l2.getCallCount());
4203     assertTrue("[constructor] Copy should not have compiled state", l2.getCompiled() == nullptr);
4204 
4205     // Move constructor
4206     LocalizedNumberFormatter l3 = std::move(l1);
4207     assertEquals("[constructor] Move behavior", u"10%", l3.formatInt(10, status).toString(status));
4208     assertEquals("[constructor] Move *should* have compiled state", INT32_MIN, l3.getCallCount());
4209     assertTrue("[constructor] Move *should* have compiled state", l3.getCompiled() != nullptr);
4210     assertEquals("[constructor] Source should be reset after move", 0, l1.getCallCount());
4211     assertTrue("[constructor] Source should be reset after move", l1.getCompiled() == nullptr);
4212 
4213     // Reset l1 and l2 to check for macro-props copying for behavior testing
4214     // Make the test more interesting: also warm them up with a compiled formatter.
4215     l1 = NumberFormatter::withLocale("en");
4216     l1.formatInt(1, status);
4217     l1.formatInt(1, status);
4218     l1.formatInt(1, status);
4219     l2 = NumberFormatter::withLocale("en");
4220     l2.formatInt(1, status);
4221     l2.formatInt(1, status);
4222     l2.formatInt(1, status);
4223 
4224     // Copy assignment
4225     l1 = l3;
4226     assertEquals("[assignment] Copy behavior", u"10%", l1.formatInt(10, status).toString(status));
4227     assertEquals("[assignment] Copy should not have compiled state", 1, l1.getCallCount());
4228     assertTrue("[assignment] Copy should not have compiled state", l1.getCompiled() == nullptr);
4229 
4230     // Move assignment
4231     l2 = std::move(l3);
4232     assertEquals("[assignment] Move behavior", u"10%", l2.formatInt(10, status).toString(status));
4233     assertEquals("[assignment] Move *should* have compiled state", INT32_MIN, l2.getCallCount());
4234     assertTrue("[assignment] Move *should* have compiled state", l2.getCompiled() != nullptr);
4235     assertEquals("[assignment] Source should be reset after move", 0, l3.getCallCount());
4236     assertTrue("[assignment] Source should be reset after move", l3.getCompiled() == nullptr);
4237 
4238     // Coverage tests for UnlocalizedNumberFormatter
4239     UnlocalizedNumberFormatter u1;
4240     assertEquals("Default behavior", u"10", u1.locale("en").formatInt(10, status).toString(status));
4241     u1 = u1.unit(NoUnit::percent());
4242     assertEquals("Copy assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
4243     UnlocalizedNumberFormatter u2 = u1;
4244     assertEquals("Copy constructor", u"10%", u2.locale("en").formatInt(10, status).toString(status));
4245     UnlocalizedNumberFormatter u3 = std::move(u1);
4246     assertEquals("Move constructor", u"10%", u3.locale("en").formatInt(10, status).toString(status));
4247     u1 = NumberFormatter::with();
4248     u1 = std::move(u2);
4249     assertEquals("Move assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
4250 
4251     // FormattedNumber move operators
4252     FormattedNumber result = l1.formatInt(10, status);
4253     assertEquals("FormattedNumber move constructor", u"10%", result.toString(status));
4254     result = l1.formatInt(20, status);
4255     assertEquals("FormattedNumber move assignment", u"20%", result.toString(status));
4256 }
4257 
localPointerCAPI()4258 void NumberFormatterApiTest::localPointerCAPI() {
4259     // NOTE: This is also the sample code in unumberformatter.h
4260     UErrorCode ec = U_ZERO_ERROR;
4261 
4262     // Setup:
4263     LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(u"percent", -1, "en", &ec));
4264     LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
4265     if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
4266 
4267     // Format a decimal number:
4268     unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
4269     if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
4270 
4271     // Get the location of the percent sign:
4272     UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
4273     unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
4274     assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
4275     assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
4276 
4277     // No need to do any cleanup since we are using LocalPointer.
4278 }
4279 
toObject()4280 void NumberFormatterApiTest::toObject() {
4281     IcuTestErrorCode status(*this, "toObject");
4282 
4283     // const lvalue version
4284     {
4285         LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en")
4286                 .precision(Precision::fixedFraction(2));
4287         LocalPointer<LocalizedNumberFormatter> lnf2(lnf.clone());
4288         assertFalse("should create successfully, const lvalue", lnf2.isNull());
4289         assertEquals("object API test, const lvalue", u"1,000.00",
4290             lnf2->formatDouble(1000, status).toString(status));
4291     }
4292 
4293     // rvalue reference version
4294     {
4295         LocalPointer<LocalizedNumberFormatter> lnf(
4296             NumberFormatter::withLocale("en")
4297                 .precision(Precision::fixedFraction(2))
4298                 .clone());
4299         assertFalse("should create successfully, rvalue reference", lnf.isNull());
4300         assertEquals("object API test, rvalue reference", u"1,000.00",
4301             lnf->formatDouble(1000, status).toString(status));
4302     }
4303 
4304     // to std::unique_ptr via constructor
4305     {
4306         std::unique_ptr<LocalizedNumberFormatter> lnf(
4307             NumberFormatter::withLocale("en")
4308                 .precision(Precision::fixedFraction(2))
4309                 .clone());
4310         assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf));
4311         assertEquals("object API test, unique_ptr", u"1,000.00",
4312             lnf->formatDouble(1000, status).toString(status));
4313     }
4314 
4315     // to std::unique_ptr via assignment
4316     {
4317         std::unique_ptr<LocalizedNumberFormatter> lnf =
4318             NumberFormatter::withLocale("en")
4319                 .precision(Precision::fixedFraction(2))
4320                 .clone();
4321         assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
4322         assertEquals("object API test, unique_ptr B", u"1,000.00",
4323             lnf->formatDouble(1000, status).toString(status));
4324     }
4325 
4326     // to LocalPointer via assignment
4327     {
4328         LocalPointer<UnlocalizedNumberFormatter> f =
4329             NumberFormatter::with().clone();
4330     }
4331 
4332     // make sure no memory leaks
4333     {
4334         NumberFormatter::with().clone();
4335     }
4336 }
4337 
toDecimalNumber()4338 void NumberFormatterApiTest::toDecimalNumber() {
4339     IcuTestErrorCode status(*this, "toDecimalNumber");
4340     FormattedNumber fn = NumberFormatter::withLocale("bn-BD")
4341         .scale(Scale::powerOfTen(2))
4342         .precision(Precision::maxSignificantDigits(5))
4343         .formatDouble(9.87654321e12, status);
4344     assertEquals("Should have expected localized string result",
4345         u"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn.toString(status));
4346     assertEquals(u"Should have expected toDecimalNumber string result",
4347         "9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
4348 }
4349 
microPropsInternals()4350 void NumberFormatterApiTest::microPropsInternals() {
4351     // Verify copy construction and assignment operators.
4352     int64_t testValues[2] = {4, 61};
4353 
4354     MicroProps mp;
4355     assertEquals("capacity", 2, mp.mixedMeasures.getCapacity());
4356     mp.mixedMeasures[0] = testValues[0];
4357     mp.mixedMeasures[1] = testValues[1];
4358     MicroProps copyConstructed(mp);
4359     MicroProps copyAssigned;
4360     int64_t *resizeResult = mp.mixedMeasures.resize(4, 4);
4361     assertTrue("Resize success", resizeResult != NULL);
4362     copyAssigned = mp;
4363 
4364     assertTrue("MicroProps success status", U_SUCCESS(mp.mixedMeasures.status));
4365     assertTrue("Copy Constructed success status", U_SUCCESS(copyConstructed.mixedMeasures.status));
4366     assertTrue("Copy Assigned success status", U_SUCCESS(copyAssigned.mixedMeasures.status));
4367     assertEquals("Original values[0]", testValues[0], mp.mixedMeasures[0]);
4368     assertEquals("Original values[1]", testValues[1], mp.mixedMeasures[1]);
4369     assertEquals("Copy Constructed[0]", testValues[0], copyConstructed.mixedMeasures[0]);
4370     assertEquals("Copy Constructed[1]", testValues[1], copyConstructed.mixedMeasures[1]);
4371     assertEquals("Copy Assigned[0]", testValues[0], copyAssigned.mixedMeasures[0]);
4372     assertEquals("Copy Assigned[1]", testValues[1], copyAssigned.mixedMeasures[1]);
4373     assertEquals("Original capacity", 4, mp.mixedMeasures.getCapacity());
4374     assertEquals("Copy Constructed capacity", 2, copyConstructed.mixedMeasures.getCapacity());
4375     assertEquals("Copy Assigned capacity", 4, copyAssigned.mixedMeasures.getCapacity());
4376 }
4377 
4378 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
4379  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
4380  * not round-trip uskeleton itself.
4381  *
4382  * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
4383  *
4384  * If `uskeleton` is nullptr, toSkeleton is expected to return an
4385  * U_UNSUPPORTED_ERROR.
4386  */
assertFormatDescending(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)4387 void NumberFormatterApiTest::assertFormatDescending(
4388         const char16_t* umessage,
4389         const char16_t* uskeleton,
4390         const char16_t* conciseSkeleton,
4391         const UnlocalizedNumberFormatter& f,
4392         Locale locale,
4393         ...) {
4394     va_list args;
4395     va_start(args, locale);
4396     UnicodeString message(TRUE, umessage, -1);
4397     static double inputs[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
4398     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
4399     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
4400     IcuTestErrorCode status(*this, "assertFormatDescending");
4401     status.setScope(message);
4402     UnicodeString expecteds[10];
4403     for (int16_t i = 0; i < 9; i++) {
4404         char16_t caseNumber = u'0' + i;
4405         double d = inputs[i];
4406         UnicodeString expected = va_arg(args, const char16_t*);
4407         expecteds[i] = expected;
4408         UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
4409         assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
4410         assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
4411         UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
4412         assertSuccess(message + u": Safe Path: " + caseNumber, status);
4413         assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
4414     }
4415     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
4416         UnicodeString skeleton(TRUE, uskeleton, -1);
4417         // Only compare normalized skeletons: the tests need not provide the normalized forms.
4418         // Use the normalized form to construct the testing formatter to guarantee no loss of info.
4419         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
4420         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
4421         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
4422         for (int32_t i = 0; i < 9; i++) {
4423             double d = inputs[i];
4424             UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
4425             assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
4426         }
4427         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
4428         // If the concise skeleton starts with '~', disable the round-trip check.
4429         bool shouldRoundTrip = true;
4430         if (conciseSkeleton[0] == u'~') {
4431             conciseSkeleton++;
4432             shouldRoundTrip = false;
4433         }
4434         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
4435         if (shouldRoundTrip) {
4436             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
4437         }
4438         for (int32_t i = 0; i < 9; i++) {
4439             double d = inputs[i];
4440             UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
4441             assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
4442         }
4443     } else {
4444         assertUndefinedSkeleton(f);
4445     }
4446 }
4447 
4448 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
4449  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
4450  * not round-trip uskeleton itself.
4451  *
4452  * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
4453  *
4454  * If `uskeleton` is nullptr, toSkeleton is expected to return an
4455  * U_UNSUPPORTED_ERROR.
4456  */
assertFormatDescendingBig(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)4457 void NumberFormatterApiTest::assertFormatDescendingBig(
4458         const char16_t* umessage,
4459         const char16_t* uskeleton,
4460         const char16_t* conciseSkeleton,
4461         const UnlocalizedNumberFormatter& f,
4462         Locale locale,
4463         ...) {
4464     va_list args;
4465     va_start(args, locale);
4466     UnicodeString message(TRUE, umessage, -1);
4467     static double inputs[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
4468     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
4469     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
4470     IcuTestErrorCode status(*this, "assertFormatDescendingBig");
4471     status.setScope(message);
4472     UnicodeString expecteds[10];
4473     for (int16_t i = 0; i < 9; i++) {
4474         char16_t caseNumber = u'0' + i;
4475         double d = inputs[i];
4476         UnicodeString expected = va_arg(args, const char16_t*);
4477         expecteds[i] = expected;
4478         UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
4479         assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
4480         assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
4481         UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
4482         assertSuccess(message + u": Safe Path: " + caseNumber, status);
4483         assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
4484     }
4485     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
4486         UnicodeString skeleton(TRUE, uskeleton, -1);
4487         // Only compare normalized skeletons: the tests need not provide the normalized forms.
4488         // Use the normalized form to construct the testing formatter to guarantee no loss of info.
4489         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
4490         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
4491         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
4492         for (int32_t i = 0; i < 9; i++) {
4493             double d = inputs[i];
4494             UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
4495             assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
4496         }
4497         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
4498         // If the concise skeleton starts with '~', disable the round-trip check.
4499         bool shouldRoundTrip = true;
4500         if (conciseSkeleton[0] == u'~') {
4501             conciseSkeleton++;
4502             shouldRoundTrip = false;
4503         }
4504         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
4505         if (shouldRoundTrip) {
4506             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
4507         }
4508         for (int32_t i = 0; i < 9; i++) {
4509             double d = inputs[i];
4510             UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
4511             assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
4512         }
4513     } else {
4514         assertUndefinedSkeleton(f);
4515     }
4516 }
4517 
4518 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
4519  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
4520  * not round-trip uskeleton itself.
4521  *
4522  * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
4523  *
4524  * If `uskeleton` is nullptr, toSkeleton is expected to return an
4525  * U_UNSUPPORTED_ERROR.
4526  */
4527 FormattedNumber
assertFormatSingle(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,double input,const UnicodeString & expected)4528 NumberFormatterApiTest::assertFormatSingle(
4529         const char16_t* umessage,
4530         const char16_t* uskeleton,
4531         const char16_t* conciseSkeleton,
4532         const UnlocalizedNumberFormatter& f,
4533         Locale locale,
4534         double input,
4535         const UnicodeString& expected) {
4536     UnicodeString message(TRUE, umessage, -1);
4537     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
4538     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
4539     IcuTestErrorCode status(*this, "assertFormatSingle");
4540     status.setScope(message);
4541     FormattedNumber result1 = l1.formatDouble(input, status);
4542     UnicodeString actual1 = result1.toString(status);
4543     assertSuccess(message + u": Unsafe Path", status);
4544     assertEquals(message + u": Unsafe Path", expected, actual1);
4545     UnicodeString actual2 = l2.formatDouble(input, status).toString(status);
4546     assertSuccess(message + u": Safe Path", status);
4547     assertEquals(message + u": Safe Path", expected, actual2);
4548     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
4549         UnicodeString skeleton(TRUE, uskeleton, -1);
4550         // Only compare normalized skeletons: the tests need not provide the normalized forms.
4551         // Use the normalized form to construct the testing formatter to ensure no loss of info.
4552         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
4553         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
4554         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
4555         UnicodeString actual3 = l3.formatDouble(input, status).toString(status);
4556         assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
4557         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
4558         // If the concise skeleton starts with '~', disable the round-trip check.
4559         bool shouldRoundTrip = true;
4560         if (conciseSkeleton[0] == u'~') {
4561             conciseSkeleton++;
4562             shouldRoundTrip = false;
4563         }
4564         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
4565         if (shouldRoundTrip) {
4566             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
4567         }
4568         UnicodeString actual4 = l4.formatDouble(input, status).toString(status);
4569         assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
4570     } else {
4571         assertUndefinedSkeleton(f);
4572     }
4573     return result1;
4574 }
4575 
assertUndefinedSkeleton(const UnlocalizedNumberFormatter & f)4576 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f) {
4577     UErrorCode status = U_ZERO_ERROR;
4578     UnicodeString skeleton = f.toSkeleton(status);
4579     assertEquals(
4580             u"Expect toSkeleton to fail, but passed, producing: " + skeleton,
4581             U_UNSUPPORTED_ERROR,
4582             status);
4583 }
4584 
assertNumberFieldPositions(const char16_t * message,const FormattedNumber & formattedNumber,const UFieldPosition * expectedFieldPositions,int32_t length)4585 void NumberFormatterApiTest::assertNumberFieldPositions(
4586         const char16_t* message,
4587         const FormattedNumber& formattedNumber,
4588         const UFieldPosition* expectedFieldPositions,
4589         int32_t length) {
4590     IcuTestErrorCode status(*this, "assertNumberFieldPositions");
4591 
4592     // Check FormattedValue functions
4593     checkFormattedValue(
4594         message,
4595         static_cast<const FormattedValue&>(formattedNumber),
4596         formattedNumber.toString(status),
4597         UFIELD_CATEGORY_NUMBER,
4598         expectedFieldPositions,
4599         length);
4600 }
4601 
4602 
4603 #endif /* #if !UCONFIG_NO_FORMATTING */
4604