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