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