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