1 // © 2018 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 "numbertest.h"
9 #include "unicode/numberrangeformatter.h"
10
11 #include <cmath>
12 #include <numparse_affixes.h>
13
14 // Horrible workaround for the lack of a status code in the constructor...
15 // (Also affects numbertest_api.cpp)
16 UErrorCode globalNumberRangeFormatterTestStatus = U_ZERO_ERROR;
17
NumberRangeFormatterTest()18 NumberRangeFormatterTest::NumberRangeFormatterTest()
19 : NumberRangeFormatterTest(globalNumberRangeFormatterTestStatus) {
20 }
21
NumberRangeFormatterTest(UErrorCode & status)22 NumberRangeFormatterTest::NumberRangeFormatterTest(UErrorCode& status)
23 : USD(u"USD", status),
24 CHF(u"CHF", status),
25 GBP(u"GBP", status),
26 PTE(u"PTE", status) {
27
28 // Check for error on the first MeasureUnit in case there is no data
29 LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
30 if (U_FAILURE(status)) {
31 dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
32 return;
33 }
34 METER = *unit;
35
36 KILOMETER = *LocalPointer<MeasureUnit>(MeasureUnit::createKilometer(status));
37 FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
38 KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
39 }
40
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)41 void NumberRangeFormatterTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
42 if (exec) {
43 logln("TestSuite NumberRangeFormatterTest: ");
44 }
45 TESTCASE_AUTO_BEGIN;
46 TESTCASE_AUTO(testSanity);
47 TESTCASE_AUTO(testBasic);
48 TESTCASE_AUTO(testCollapse);
49 TESTCASE_AUTO(testIdentity);
50 TESTCASE_AUTO(testDifferentFormatters);
51 TESTCASE_AUTO(testNaNInfinity);
52 TESTCASE_AUTO(testPlurals);
53 TESTCASE_AUTO(testFieldPositions);
54 TESTCASE_AUTO(testCopyMove);
55 TESTCASE_AUTO(toObject);
56 TESTCASE_AUTO(locale);
57 TESTCASE_AUTO(testGetDecimalNumbers);
58 TESTCASE_AUTO(test21684_Performance);
59 TESTCASE_AUTO(test21358_SignPosition);
60 TESTCASE_AUTO(test21683_StateLeak);
61 TESTCASE_AUTO(testCreateLNRFFromNumberingSystemInSkeleton);
62 TESTCASE_AUTO(test22288_DifferentStartEndSettings);
63 TESTCASE_AUTO_END;
64 }
65
testSanity()66 void NumberRangeFormatterTest::testSanity() {
67 IcuTestErrorCode status(*this, "testSanity");
68 LocalizedNumberRangeFormatter lnrf1 = NumberRangeFormatter::withLocale("en-us");
69 LocalizedNumberRangeFormatter lnrf2 = NumberRangeFormatter::with().locale("en-us");
70 assertEquals("Formatters should have same behavior 1",
71 lnrf1.formatFormattableRange(4, 6, status).toString(status),
72 lnrf2.formatFormattableRange(4, 6, status).toString(status));
73 }
74
testBasic()75 void NumberRangeFormatterTest::testBasic() {
76 assertFormatRange(
77 u"Basic",
78 NumberRangeFormatter::with(),
79 Locale("en-us"),
80 u"1–5",
81 u"~5",
82 u"~5",
83 u"0–3",
84 u"~0",
85 u"3–3,000",
86 u"3,000–5,000",
87 u"4,999–5,001",
88 u"~5,000",
89 u"5,000–5,000,000");
90
91 assertFormatRange(
92 u"Basic with units",
93 NumberRangeFormatter::with()
94 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
95 Locale("en-us"),
96 u"1–5 m",
97 u"~5 m",
98 u"~5 m",
99 u"0–3 m",
100 u"~0 m",
101 u"3–3,000 m",
102 u"3,000–5,000 m",
103 u"4,999–5,001 m",
104 u"~5,000 m",
105 u"5,000–5,000,000 m");
106
107 assertFormatRange(
108 u"Basic with different units",
109 NumberRangeFormatter::with()
110 .numberFormatterFirst(NumberFormatter::with().unit(METER))
111 .numberFormatterSecond(NumberFormatter::with().unit(KILOMETER)),
112 Locale("en-us"),
113 u"1 m – 5 km",
114 u"5 m – 5 km",
115 u"5 m – 5 km",
116 u"0 m – 3 km",
117 u"0 m – 0 km",
118 u"3 m – 3,000 km",
119 u"3,000 m – 5,000 km",
120 u"4,999 m – 5,001 km",
121 u"5,000 m – 5,000 km",
122 u"5,000 m – 5,000,000 km");
123
124 assertFormatRange(
125 u"Basic long unit",
126 NumberRangeFormatter::with()
127 .numberFormatterBoth(NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)),
128 Locale("en-us"),
129 u"1–5 meters",
130 u"~5 meters",
131 u"~5 meters",
132 u"0–3 meters",
133 u"~0 meters",
134 u"3–3,000 meters",
135 u"3,000–5,000 meters",
136 u"4,999–5,001 meters",
137 u"~5,000 meters",
138 u"5,000–5,000,000 meters");
139
140 assertFormatRange(
141 u"Non-English locale and unit",
142 NumberRangeFormatter::with()
143 .numberFormatterBoth(NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)),
144 Locale("fr-FR"),
145 u"1–5\u00A0degrés Fahrenheit",
146 u"≃5\u00A0degrés Fahrenheit",
147 u"≃5\u00A0degrés Fahrenheit",
148 u"0–3\u00A0degrés Fahrenheit",
149 u"≃0\u00A0degré Fahrenheit",
150 u"3–3\u202F000\u00A0degrés Fahrenheit",
151 u"3\u202F000–5\u202F000\u00A0degrés Fahrenheit",
152 u"4\u202F999–5\u202F001\u00A0degrés Fahrenheit",
153 u"≃5\u202F000\u00A0degrés Fahrenheit",
154 u"5\u202F000–5\u202F000\u202F000\u00A0degrés Fahrenheit");
155
156 assertFormatRange(
157 u"Locale with custom range separator",
158 NumberRangeFormatter::with(),
159 Locale("ja"),
160 u"1~5",
161 u"約5",
162 u"約5",
163 u"0~3",
164 u"約0",
165 u"3~3,000",
166 u"3,000~5,000",
167 u"4,999~5,001",
168 u"約5,000",
169 u"5,000~5,000,000");
170
171 assertFormatRange(
172 u"Locale that already has spaces around range separator",
173 NumberRangeFormatter::with()
174 .collapse(UNUM_RANGE_COLLAPSE_NONE)
175 .numberFormatterBoth(NumberFormatter::with().unit(KELVIN)),
176 Locale("hr"),
177 u"1 K – 5 K",
178 u"~5 K",
179 u"~5 K",
180 u"0 K – 3 K",
181 u"~0 K",
182 u"3 K – 3.000 K",
183 u"3.000 K – 5.000 K",
184 u"4.999 K – 5.001 K",
185 u"~5.000 K",
186 u"5.000 K – 5.000.000 K");
187
188 assertFormatRange(
189 u"Locale with custom numbering system and no plural ranges data",
190 NumberRangeFormatter::with(),
191 Locale("shn@numbers=beng"),
192 // 012459 = ০১৩৪৫৯
193 u"১–৫",
194 u"~৫",
195 u"~৫",
196 u"০–৩",
197 u"~০",
198 u"৩–৩,০০০",
199 u"৩,০০০–৫,০০০",
200 u"৪,৯৯৯–৫,০০১",
201 u"~৫,০০০",
202 u"৫,০০০–৫,০০০,০০০");
203
204 assertFormatRange(
205 u"Portuguese currency",
206 NumberRangeFormatter::with()
207 .numberFormatterBoth(NumberFormatter::with().unit(PTE)),
208 Locale("pt-PT"),
209 u"1$00 - 5$00 \u200B",
210 u"~5$00 \u200B",
211 u"~5$00 \u200B",
212 u"0$00 - 3$00 \u200B",
213 u"~0$00 \u200B",
214 u"3$00 - 3000$00 \u200B",
215 u"3000$00 - 5000$00 \u200B",
216 u"4999$00 - 5001$00 \u200B",
217 u"~5000$00 \u200B",
218 u"5000$00 - 5,000,000$00 \u200B");
219 }
220
testCollapse()221 void NumberRangeFormatterTest::testCollapse() {
222 assertFormatRange(
223 u"Default collapse on currency (default rounding)",
224 NumberRangeFormatter::with()
225 .numberFormatterBoth(NumberFormatter::with().unit(USD)),
226 Locale("en-us"),
227 u"$1.00 – $5.00",
228 u"~$5.00",
229 u"~$5.00",
230 u"$0.00 – $3.00",
231 u"~$0.00",
232 u"$3.00 – $3,000.00",
233 u"$3,000.00 – $5,000.00",
234 u"$4,999.00 – $5,001.00",
235 u"~$5,000.00",
236 u"$5,000.00 – $5,000,000.00");
237
238 assertFormatRange(
239 u"Default collapse on currency",
240 NumberRangeFormatter::with()
241 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
242 Locale("en-us"),
243 u"$1 – $5",
244 u"~$5",
245 u"~$5",
246 u"$0 – $3",
247 u"~$0",
248 u"$3 – $3,000",
249 u"$3,000 – $5,000",
250 u"$4,999 – $5,001",
251 u"~$5,000",
252 u"$5,000 – $5,000,000");
253
254 assertFormatRange(
255 u"No collapse on currency",
256 NumberRangeFormatter::with()
257 .collapse(UNUM_RANGE_COLLAPSE_NONE)
258 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
259 Locale("en-us"),
260 u"$1 – $5",
261 u"~$5",
262 u"~$5",
263 u"$0 – $3",
264 u"~$0",
265 u"$3 – $3,000",
266 u"$3,000 – $5,000",
267 u"$4,999 – $5,001",
268 u"~$5,000",
269 u"$5,000 – $5,000,000");
270
271 assertFormatRange(
272 u"Unit collapse on currency",
273 NumberRangeFormatter::with()
274 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
275 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
276 Locale("en-us"),
277 u"$1–5",
278 u"~$5",
279 u"~$5",
280 u"$0–3",
281 u"~$0",
282 u"$3–3,000",
283 u"$3,000–5,000",
284 u"$4,999–5,001",
285 u"~$5,000",
286 u"$5,000–5,000,000");
287
288 assertFormatRange(
289 u"All collapse on currency",
290 NumberRangeFormatter::with()
291 .collapse(UNUM_RANGE_COLLAPSE_ALL)
292 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
293 Locale("en-us"),
294 u"$1–5",
295 u"~$5",
296 u"~$5",
297 u"$0–3",
298 u"~$0",
299 u"$3–3,000",
300 u"$3,000–5,000",
301 u"$4,999–5,001",
302 u"~$5,000",
303 u"$5,000–5,000,000");
304
305 assertFormatRange(
306 u"Default collapse on currency ISO code",
307 NumberRangeFormatter::with()
308 .numberFormatterBoth(NumberFormatter::with()
309 .unit(GBP)
310 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
311 .precision(Precision::integer())),
312 Locale("en-us"),
313 u"GBP 1–5",
314 u"~GBP 5", // TODO: Fix this at some point
315 u"~GBP 5",
316 u"GBP 0–3",
317 u"~GBP 0",
318 u"GBP 3–3,000",
319 u"GBP 3,000–5,000",
320 u"GBP 4,999–5,001",
321 u"~GBP 5,000",
322 u"GBP 5,000–5,000,000");
323
324 assertFormatRange(
325 u"No collapse on currency ISO code",
326 NumberRangeFormatter::with()
327 .collapse(UNUM_RANGE_COLLAPSE_NONE)
328 .numberFormatterBoth(NumberFormatter::with()
329 .unit(GBP)
330 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
331 .precision(Precision::integer())),
332 Locale("en-us"),
333 u"GBP 1 – GBP 5",
334 u"~GBP 5", // TODO: Fix this at some point
335 u"~GBP 5",
336 u"GBP 0 – GBP 3",
337 u"~GBP 0",
338 u"GBP 3 – GBP 3,000",
339 u"GBP 3,000 – GBP 5,000",
340 u"GBP 4,999 – GBP 5,001",
341 u"~GBP 5,000",
342 u"GBP 5,000 – GBP 5,000,000");
343
344 assertFormatRange(
345 u"Unit collapse on currency ISO code",
346 NumberRangeFormatter::with()
347 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
348 .numberFormatterBoth(NumberFormatter::with()
349 .unit(GBP)
350 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
351 .precision(Precision::integer())),
352 Locale("en-us"),
353 u"GBP 1–5",
354 u"~GBP 5", // TODO: Fix this at some point
355 u"~GBP 5",
356 u"GBP 0–3",
357 u"~GBP 0",
358 u"GBP 3–3,000",
359 u"GBP 3,000–5,000",
360 u"GBP 4,999–5,001",
361 u"~GBP 5,000",
362 u"GBP 5,000–5,000,000");
363
364 assertFormatRange(
365 u"All collapse on currency ISO code",
366 NumberRangeFormatter::with()
367 .collapse(UNUM_RANGE_COLLAPSE_ALL)
368 .numberFormatterBoth(NumberFormatter::with()
369 .unit(GBP)
370 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
371 .precision(Precision::integer())),
372 Locale("en-us"),
373 u"GBP 1–5",
374 u"~GBP 5", // TODO: Fix this at some point
375 u"~GBP 5",
376 u"GBP 0–3",
377 u"~GBP 0",
378 u"GBP 3–3,000",
379 u"GBP 3,000–5,000",
380 u"GBP 4,999–5,001",
381 u"~GBP 5,000",
382 u"GBP 5,000–5,000,000");
383
384 // Default collapse on measurement unit is in testBasic()
385
386 assertFormatRange(
387 u"No collapse on measurement unit",
388 NumberRangeFormatter::with()
389 .collapse(UNUM_RANGE_COLLAPSE_NONE)
390 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
391 Locale("en-us"),
392 u"1 m – 5 m",
393 u"~5 m",
394 u"~5 m",
395 u"0 m – 3 m",
396 u"~0 m",
397 u"3 m – 3,000 m",
398 u"3,000 m – 5,000 m",
399 u"4,999 m – 5,001 m",
400 u"~5,000 m",
401 u"5,000 m – 5,000,000 m");
402
403 assertFormatRange(
404 u"Unit collapse on measurement unit",
405 NumberRangeFormatter::with()
406 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
407 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
408 Locale("en-us"),
409 u"1–5 m",
410 u"~5 m",
411 u"~5 m",
412 u"0–3 m",
413 u"~0 m",
414 u"3–3,000 m",
415 u"3,000–5,000 m",
416 u"4,999–5,001 m",
417 u"~5,000 m",
418 u"5,000–5,000,000 m");
419
420 assertFormatRange(
421 u"All collapse on measurement unit",
422 NumberRangeFormatter::with()
423 .collapse(UNUM_RANGE_COLLAPSE_ALL)
424 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
425 Locale("en-us"),
426 u"1–5 m",
427 u"~5 m",
428 u"~5 m",
429 u"0–3 m",
430 u"~0 m",
431 u"3–3,000 m",
432 u"3,000–5,000 m",
433 u"4,999–5,001 m",
434 u"~5,000 m",
435 u"5,000–5,000,000 m");
436
437 assertFormatRange(
438 u"Default collapse, long-form compact notation",
439 NumberRangeFormatter::with()
440 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactLong())),
441 Locale("de-CH"),
442 u"1–5",
443 u"≈5",
444 u"≈5",
445 u"0–3",
446 u"≈0",
447 u"3–3 Tausend",
448 u"3–5 Tausend",
449 u"≈5 Tausend",
450 u"≈5 Tausend",
451 u"5 Tausend – 5 Millionen");
452
453 assertFormatRange(
454 u"Unit collapse, long-form compact notation",
455 NumberRangeFormatter::with()
456 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
457 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactLong())),
458 Locale("de-CH"),
459 u"1–5",
460 u"≈5",
461 u"≈5",
462 u"0–3",
463 u"≈0",
464 u"3–3 Tausend",
465 u"3 Tausend – 5 Tausend",
466 u"≈5 Tausend",
467 u"≈5 Tausend",
468 u"5 Tausend – 5 Millionen");
469
470 assertFormatRange(
471 u"Default collapse on measurement unit with compact-short notation",
472 NumberRangeFormatter::with()
473 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
474 Locale("en-us"),
475 u"1–5 m",
476 u"~5 m",
477 u"~5 m",
478 u"0–3 m",
479 u"~0 m",
480 u"3–3K m",
481 u"3K – 5K m",
482 u"~5K m",
483 u"~5K m",
484 u"5K – 5M m");
485
486 assertFormatRange(
487 u"No collapse on measurement unit with compact-short notation",
488 NumberRangeFormatter::with()
489 .collapse(UNUM_RANGE_COLLAPSE_NONE)
490 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
491 Locale("en-us"),
492 u"1 m – 5 m",
493 u"~5 m",
494 u"~5 m",
495 u"0 m – 3 m",
496 u"~0 m",
497 u"3 m – 3K m",
498 u"3K m – 5K m",
499 u"~5K m",
500 u"~5K m",
501 u"5K m – 5M m");
502
503 assertFormatRange(
504 u"Unit collapse on measurement unit with compact-short notation",
505 NumberRangeFormatter::with()
506 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
507 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
508 Locale("en-us"),
509 u"1–5 m",
510 u"~5 m",
511 u"~5 m",
512 u"0–3 m",
513 u"~0 m",
514 u"3–3K m",
515 u"3K – 5K m",
516 u"~5K m",
517 u"~5K m",
518 u"5K – 5M m");
519
520 assertFormatRange(
521 u"All collapse on measurement unit with compact-short notation",
522 NumberRangeFormatter::with()
523 .collapse(UNUM_RANGE_COLLAPSE_ALL)
524 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
525 Locale("en-us"),
526 u"1–5 m",
527 u"~5 m",
528 u"~5 m",
529 u"0–3 m",
530 u"~0 m",
531 u"3–3K m",
532 u"3–5K m", // this one is the key use case for ALL
533 u"~5K m",
534 u"~5K m",
535 u"5K – 5M m");
536
537 assertFormatRange(
538 u"No collapse on scientific notation",
539 NumberRangeFormatter::with()
540 .collapse(UNUM_RANGE_COLLAPSE_NONE)
541 .numberFormatterBoth(NumberFormatter::with().notation(Notation::scientific())),
542 Locale("en-us"),
543 u"1E0 – 5E0",
544 u"~5E0",
545 u"~5E0",
546 u"0E0 – 3E0",
547 u"~0E0",
548 u"3E0 – 3E3",
549 u"3E3 – 5E3",
550 u"4.999E3 – 5.001E3",
551 u"~5E3",
552 u"5E3 – 5E6");
553
554 assertFormatRange(
555 u"All collapse on scientific notation",
556 NumberRangeFormatter::with()
557 .collapse(UNUM_RANGE_COLLAPSE_ALL)
558 .numberFormatterBoth(NumberFormatter::with().notation(Notation::scientific())),
559 Locale("en-us"),
560 u"1–5E0",
561 u"~5E0",
562 u"~5E0",
563 u"0–3E0",
564 u"~0E0",
565 u"3E0 – 3E3",
566 u"3–5E3",
567 u"4.999–5.001E3",
568 u"~5E3",
569 u"5E3 – 5E6");
570
571 // TODO: Test compact currency?
572 // The code is not smart enough to differentiate the notation from the unit.
573 }
574
testIdentity()575 void NumberRangeFormatterTest::testIdentity() {
576 assertFormatRange(
577 u"Identity fallback Range",
578 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_RANGE),
579 Locale("en-us"),
580 u"1–5",
581 u"5–5",
582 u"5–5",
583 u"0–3",
584 u"0–0",
585 u"3–3,000",
586 u"3,000–5,000",
587 u"4,999–5,001",
588 u"5,000–5,000",
589 u"5,000–5,000,000");
590
591 assertFormatRange(
592 u"Identity fallback Approximately or Single Value",
593 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE),
594 Locale("en-us"),
595 u"1–5",
596 u"~5",
597 u"5",
598 u"0–3",
599 u"0",
600 u"3–3,000",
601 u"3,000–5,000",
602 u"4,999–5,001",
603 u"5,000",
604 u"5,000–5,000,000");
605
606 assertFormatRange(
607 u"Identity fallback Single Value",
608 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE),
609 Locale("en-us"),
610 u"1–5",
611 u"5",
612 u"5",
613 u"0–3",
614 u"0",
615 u"3–3,000",
616 u"3,000–5,000",
617 u"4,999–5,001",
618 u"5,000",
619 u"5,000–5,000,000");
620
621 assertFormatRange(
622 u"Identity fallback Approximately or Single Value with compact notation",
623 NumberRangeFormatter::with()
624 .identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE)
625 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort())),
626 Locale("en-us"),
627 u"1–5",
628 u"~5",
629 u"5",
630 u"0–3",
631 u"0",
632 u"3–3K",
633 u"3K – 5K",
634 u"~5K",
635 u"5K",
636 u"5K – 5M");
637
638 assertFormatRange(
639 u"Approximately in middle of unit string",
640 NumberRangeFormatter::with().numberFormatterBoth(
641 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)),
642 Locale("zh-Hant"),
643 u"華氏 1-5 度",
644 u"華氏 ~5 度",
645 u"華氏 ~5 度",
646 u"華氏 0-3 度",
647 u"華氏 ~0 度",
648 u"華氏 3-3,000 度",
649 u"華氏 3,000-5,000 度",
650 u"華氏 4,999-5,001 度",
651 u"華氏 ~5,000 度",
652 u"華氏 5,000-5,000,000 度");
653 }
654
testDifferentFormatters()655 void NumberRangeFormatterTest::testDifferentFormatters() {
656 assertFormatRange(
657 u"Different rounding rules",
658 NumberRangeFormatter::with()
659 .numberFormatterFirst(NumberFormatter::with().precision(Precision::integer()))
660 .numberFormatterSecond(NumberFormatter::with().precision(Precision::fixedSignificantDigits(2))),
661 Locale("en-us"),
662 u"1–5.0",
663 u"5–5.0",
664 u"5–5.0",
665 u"0–3.0",
666 u"0–0.0",
667 u"3–3,000",
668 u"3,000–5,000",
669 u"4,999–5,000",
670 u"5,000–5,000", // TODO: Should this one be ~5,000?
671 u"5,000–5,000,000");
672 }
673
testNaNInfinity()674 void NumberRangeFormatterTest::testNaNInfinity() {
675 IcuTestErrorCode status(*this, "testNaNInfinity");
676
677 auto lnf = NumberRangeFormatter::withLocale("en");
678 auto result1 = lnf.formatFormattableRange(-uprv_getInfinity(), 0, status);
679 auto result2 = lnf.formatFormattableRange(0, uprv_getInfinity(), status);
680 auto result3 = lnf.formatFormattableRange(-uprv_getInfinity(), uprv_getInfinity(), status);
681 auto result4 = lnf.formatFormattableRange(uprv_getNaN(), 0, status);
682 auto result5 = lnf.formatFormattableRange(0, uprv_getNaN(), status);
683 auto result6 = lnf.formatFormattableRange(uprv_getNaN(), uprv_getNaN(), status);
684 auto result7 = lnf.formatFormattableRange({"1000", status}, {"Infinity", status}, status);
685 auto result8 = lnf.formatFormattableRange({"-Infinity", status}, {"NaN", status}, status);
686
687 assertEquals("0 - inf", u"-∞ – 0", result1.toTempString(status));
688 assertEquals("-inf - 0", u"0–∞", result2.toTempString(status));
689 assertEquals("-inf - inf", u"-∞ – ∞", result3.toTempString(status));
690 assertEquals("NaN - 0", u"NaN–0", result4.toTempString(status));
691 assertEquals("0 - NaN", u"0–NaN", result5.toTempString(status));
692 assertEquals("NaN - NaN", u"~NaN", result6.toTempString(status));
693 assertEquals("1000 - inf", u"1,000–∞", result7.toTempString(status));
694 assertEquals("-inf - NaN", u"-∞ – NaN", result8.toTempString(status));
695 }
696
testPlurals()697 void NumberRangeFormatterTest::testPlurals() {
698 IcuTestErrorCode status(*this, "testPlurals");
699
700 // Locale sl has interesting plural forms:
701 // GBP{
702 // one{"britanski funt"}
703 // two{"britanska funta"}
704 // few{"britanski funti"}
705 // other{"britanskih funtov"}
706 // }
707 Locale locale("sl");
708
709 UnlocalizedNumberFormatter unf = NumberFormatter::with()
710 .unit(GBP)
711 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
712 .precision(Precision::integer());
713 LocalizedNumberFormatter lnf = unf.locale(locale);
714
715 // For comparison, run the non-range version of the formatter
716 assertEquals(Int64ToUnicodeString(1), u"1 britanski funt", lnf.formatDouble(1, status).toString(status));
717 assertEquals(Int64ToUnicodeString(2), u"2 britanska funta", lnf.formatDouble(2, status).toString(status));
718 assertEquals(Int64ToUnicodeString(3), u"3 britanski funti", lnf.formatDouble(3, status).toString(status));
719 assertEquals(Int64ToUnicodeString(5), u"5 britanskih funtov", lnf.formatDouble(5, status).toString(status));
720 if (status.errIfFailureAndReset()) { return; }
721
722 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::with()
723 .numberFormatterBoth(unf)
724 .identityFallback(UNUM_IDENTITY_FALLBACK_RANGE)
725 .locale(locale);
726
727 struct TestCase {
728 double first;
729 double second;
730 const char16_t* expected;
731 } cases[] = {
732 {1, 1, u"1–1 britanski funti"}, // one + one -> few
733 {1, 2, u"1–2 britanska funta"}, // one + two -> two
734 {1, 3, u"1–3 britanski funti"}, // one + few -> few
735 {1, 5, u"1–5 britanskih funtov"}, // one + other -> other
736 {2, 1, u"2–1 britanski funti"}, // two + one -> few
737 {2, 2, u"2–2 britanska funta"}, // two + two -> two
738 {2, 3, u"2–3 britanski funti"}, // two + few -> few
739 {2, 5, u"2–5 britanskih funtov"}, // two + other -> other
740 {3, 1, u"3–1 britanski funti"}, // few + one -> few
741 {3, 2, u"3–2 britanska funta"}, // few + two -> two
742 {3, 3, u"3–3 britanski funti"}, // few + few -> few
743 {3, 5, u"3–5 britanskih funtov"}, // few + other -> other
744 {5, 1, u"5–1 britanski funti"}, // other + one -> few
745 {5, 2, u"5–2 britanska funta"}, // other + two -> two
746 {5, 3, u"5–3 britanski funti"}, // other + few -> few
747 {5, 5, u"5–5 britanskih funtov"}, // other + other -> other
748 };
749 for (auto& cas : cases) {
750 UnicodeString message = Int64ToUnicodeString(static_cast<int64_t>(cas.first));
751 message += u" ";
752 message += Int64ToUnicodeString(static_cast<int64_t>(cas.second));
753 status.setScope(message);
754 UnicodeString actual = lnrf.formatFormattableRange(cas.first, cas.second, status).toString(status);
755 assertEquals(message, cas.expected, actual);
756 status.errIfFailureAndReset();
757 }
758 }
759
testFieldPositions()760 void NumberRangeFormatterTest::testFieldPositions() {
761 {
762 const char16_t* message = u"Field position test 1";
763 const char16_t* expectedString = u"3K – 5K m";
764 FormattedNumberRange result = assertFormattedRangeEquals(
765 message,
766 NumberRangeFormatter::with()
767 .numberFormatterBoth(NumberFormatter::with()
768 .unit(METER)
769 .notation(Notation::compactShort()))
770 .locale("en-us"),
771 3000,
772 5000,
773 expectedString);
774 static const UFieldPositionWithCategory expectedFieldPositions[] = {
775 // category, field, begin index, end index
776 {UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 0, 0, 2},
777 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, 0, 1},
778 {UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD, 1, 2},
779 {UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 1, 5, 7},
780 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, 5, 6},
781 {UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD, 6, 7},
782 {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD, 8, 9}};
783 checkMixedFormattedValue(
784 message,
785 result,
786 expectedString,
787 expectedFieldPositions,
788 UPRV_LENGTHOF(expectedFieldPositions));
789 }
790
791 {
792 const char16_t* message = u"Field position test 2";
793 const char16_t* expectedString = u"87,654,321–98,765,432";
794 FormattedNumberRange result = assertFormattedRangeEquals(
795 message,
796 NumberRangeFormatter::withLocale("en-us"),
797 87654321,
798 98765432,
799 expectedString);
800 static const UFieldPositionWithCategory expectedFieldPositions[] = {
801 // category, field, begin index, end index
802 {UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 0, 0, 10},
803 {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
804 {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
805 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, 0, 10},
806 {UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 1, 11, 21},
807 {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD, 13, 14},
808 {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD, 17, 18},
809 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, 11, 21}};
810 checkMixedFormattedValue(
811 message,
812 result,
813 expectedString,
814 expectedFieldPositions,
815 UPRV_LENGTHOF(expectedFieldPositions));
816 }
817
818 {
819 const char16_t* message = u"Field position with approximately sign";
820 const char16_t* expectedString = u"~-100";
821 FormattedNumberRange result = assertFormattedRangeEquals(
822 message,
823 NumberRangeFormatter::withLocale("en-us"),
824 -100,
825 -100,
826 expectedString);
827 static const UFieldPositionWithCategory expectedFieldPositions[] = {
828 // category, field, begin index, end index
829 {UFIELD_CATEGORY_NUMBER, UNUM_APPROXIMATELY_SIGN_FIELD, 0, 1},
830 {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD, 1, 2},
831 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, 2, 5}};
832 checkMixedFormattedValue(
833 message,
834 result,
835 expectedString,
836 expectedFieldPositions,
837 UPRV_LENGTHOF(expectedFieldPositions));
838 }
839 }
840
testCopyMove()841 void NumberRangeFormatterTest::testCopyMove() {
842 IcuTestErrorCode status(*this, "testCopyMove");
843
844 // Default constructors
845 LocalizedNumberRangeFormatter l1;
846 assertEquals("Initial behavior", u"1–5", l1.formatFormattableRange(1, 5, status).toString(status));
847 if (status.errDataIfFailureAndReset()) { return; }
848
849 // Setup
850 l1 = NumberRangeFormatter::withLocale("fr-FR")
851 .numberFormatterBoth(NumberFormatter::with().unit(USD));
852 assertEquals("Currency behavior", u"1,00–5,00 $US", l1.formatFormattableRange(1, 5, status).toString(status));
853
854 // Copy constructor
855 LocalizedNumberRangeFormatter l2 = l1;
856 assertEquals("Copy constructor", u"1,00–5,00 $US", l2.formatFormattableRange(1, 5, status).toString(status));
857
858 // Move constructor
859 LocalizedNumberRangeFormatter l3 = std::move(l1);
860 assertEquals("Move constructor", u"1,00–5,00 $US", l3.formatFormattableRange(1, 5, status).toString(status));
861
862 // Reset objects for assignment tests
863 l1 = NumberRangeFormatter::withLocale("en-us");
864 l2 = NumberRangeFormatter::withLocale("en-us");
865 assertEquals("Rest behavior, l1", u"1–5", l1.formatFormattableRange(1, 5, status).toString(status));
866 assertEquals("Rest behavior, l2", u"1–5", l2.formatFormattableRange(1, 5, status).toString(status));
867
868 // Copy assignment
869 l1 = l3;
870 assertEquals("Copy constructor", u"1,00–5,00 $US", l1.formatFormattableRange(1, 5, status).toString(status));
871
872 // Move assignment
873 l2 = std::move(l3);
874 assertEquals("Copy constructor", u"1,00–5,00 $US", l2.formatFormattableRange(1, 5, status).toString(status));
875
876 // FormattedNumberRange
877 FormattedNumberRange result = l1.formatFormattableRange(1, 5, status);
878 assertEquals("FormattedNumberRange move constructor", u"1,00–5,00 $US", result.toString(status));
879 result = l1.formatFormattableRange(3, 6, status);
880 assertEquals("FormattedNumberRange move assignment", u"3,00–6,00 $US", result.toString(status));
881 FormattedNumberRange fnrdefault;
882 fnrdefault.toString(status);
883 status.expectErrorAndReset(U_INVALID_STATE_ERROR);
884 }
885
toObject()886 void NumberRangeFormatterTest::toObject() {
887 IcuTestErrorCode status(*this, "toObject");
888
889 // const lvalue version
890 {
891 LocalizedNumberRangeFormatter lnf = NumberRangeFormatter::withLocale("en");
892 LocalPointer<LocalizedNumberRangeFormatter> lnf2(lnf.clone());
893 assertFalse("should create successfully, const lvalue", lnf2.isNull());
894 assertEquals("object API test, const lvalue", u"5–7",
895 lnf2->formatFormattableRange(5, 7, status).toString(status));
896 }
897
898 // rvalue reference version
899 {
900 LocalPointer<LocalizedNumberRangeFormatter> lnf(
901 NumberRangeFormatter::withLocale("en").clone());
902 assertFalse("should create successfully, rvalue reference", lnf.isNull());
903 assertEquals("object API test, rvalue reference", u"5–7",
904 lnf->formatFormattableRange(5, 7, status).toString(status));
905 }
906
907 // to std::unique_ptr via assignment
908 {
909 std::unique_ptr<LocalizedNumberRangeFormatter> lnf =
910 NumberRangeFormatter::withLocale("en").clone();
911 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
912 assertEquals("object API test, unique_ptr B", u"5–7",
913 lnf->formatFormattableRange(5, 7, status).toString(status));
914 }
915
916 // make sure no memory leaks
917 {
918 NumberRangeFormatter::with().clone();
919 }
920 }
921
locale()922 void NumberRangeFormatterTest::locale() {
923 IcuTestErrorCode status(*this, "locale");
924
925 LocalizedNumberRangeFormatter lnf = NumberRangeFormatter::withLocale("en")
926 .identityFallback(UNUM_IDENTITY_FALLBACK_RANGE);
927 UnlocalizedNumberRangeFormatter unf1 = lnf.withoutLocale();
928 UnlocalizedNumberRangeFormatter unf2 = NumberRangeFormatter::with()
929 .identityFallback(UNUM_IDENTITY_FALLBACK_RANGE)
930 .locale("ar-EG")
931 .withoutLocale();
932
933 FormattedNumberRange res1 = unf1.locale("bn").formatFormattableRange(5, 5, status);
934 assertEquals(u"res1", u"\u09EB\u2013\u09EB", res1.toTempString(status));
935 FormattedNumberRange res2 = unf2.locale("ja-JP").formatFormattableRange(5, 5, status);
936 assertEquals(u"res2", u"5\uFF5E5", res2.toTempString(status));
937 }
938
testGetDecimalNumbers()939 void NumberRangeFormatterTest::testGetDecimalNumbers() {
940 IcuTestErrorCode status(*this, "testGetDecimalNumbers");
941
942 LocalizedNumberRangeFormatter lnf = NumberRangeFormatter::withLocale("en")
943 .numberFormatterBoth(NumberFormatter::with().unit(USD));
944
945 // Range of numbers
946 {
947 FormattedNumberRange range = lnf.formatFormattableRange(1, 5, status);
948 assertEquals("Range: Formatted string should be as expected",
949 u"$1.00 \u2013 $5.00",
950 range.toString(status));
951 auto decimalNumbers = range.getDecimalNumbers<std::string>(status);
952 // TODO(ICU-21281): DecNum doesn't retain trailing zeros. Is that a problem?
953 if (logKnownIssue("ICU-21281")) {
954 assertEquals("First decimal number", "1", decimalNumbers.first.c_str());
955 assertEquals("Second decimal number", "5", decimalNumbers.second.c_str());
956 } else {
957 assertEquals("First decimal number", "1.00", decimalNumbers.first.c_str());
958 assertEquals("Second decimal number", "5.00", decimalNumbers.second.c_str());
959 }
960 }
961
962 // Identity fallback
963 {
964 FormattedNumberRange range = lnf.formatFormattableRange(3, 3, status);
965 assertEquals("Identity: Formatted string should be as expected",
966 u"~$3.00",
967 range.toString(status));
968 auto decimalNumbers = range.getDecimalNumbers<std::string>(status);
969 // NOTE: DecNum doesn't retain trailing zeros. Is that a problem?
970 // TODO(ICU-21281): DecNum doesn't retain trailing zeros. Is that a problem?
971 if (logKnownIssue("ICU-21281")) {
972 assertEquals("First decimal number", "3", decimalNumbers.first.c_str());
973 assertEquals("Second decimal number", "3", decimalNumbers.second.c_str());
974 } else {
975 assertEquals("First decimal number", "3.00", decimalNumbers.first.c_str());
976 assertEquals("Second decimal number", "3.00", decimalNumbers.second.c_str());
977 }
978 }
979 }
980
test21684_Performance()981 void NumberRangeFormatterTest::test21684_Performance() {
982 IcuTestErrorCode status(*this, "test21684_Performance");
983 LocalizedNumberRangeFormatter lnf = NumberRangeFormatter::withLocale("en");
984 // The following two lines of code should finish quickly.
985 lnf.formatFormattableRange({"-1e99999", status}, {"0", status}, status);
986 lnf.formatFormattableRange({"0", status}, {"1e99999", status}, status);
987 }
988
test21358_SignPosition()989 void NumberRangeFormatterTest::test21358_SignPosition() {
990 IcuTestErrorCode status(*this, "test21358_SignPosition");
991
992 // de-CH has currency pattern "¤ #,##0.00;¤-#,##0.00"
993 assertFormatRange(
994 u"Approximately sign position with spacing from pattern",
995 NumberRangeFormatter::with()
996 .numberFormatterBoth(NumberFormatter::with().unit(CHF)),
997 Locale("de-CH"),
998 u"CHF 1.00–5.00",
999 u"CHF≈5.00",
1000 u"CHF≈5.00",
1001 u"CHF 0.00–3.00",
1002 u"CHF≈0.00",
1003 u"CHF 3.00–3’000.00",
1004 u"CHF 3’000.00–5’000.00",
1005 u"CHF 4’999.00–5’001.00",
1006 u"CHF≈5’000.00",
1007 u"CHF 5’000.00–5’000’000.00");
1008
1009 // TODO(ICU-21420): Move the sign to the inside of the number
1010 assertFormatRange(
1011 u"Approximately sign position with currency spacing",
1012 NumberRangeFormatter::with()
1013 .numberFormatterBoth(NumberFormatter::with().unit(CHF)),
1014 Locale("en-US"),
1015 u"CHF 1.00–5.00",
1016 u"~CHF 5.00",
1017 u"~CHF 5.00",
1018 u"CHF 0.00–3.00",
1019 u"~CHF 0.00",
1020 u"CHF 3.00–3,000.00",
1021 u"CHF 3,000.00–5,000.00",
1022 u"CHF 4,999.00–5,001.00",
1023 u"~CHF 5,000.00",
1024 u"CHF 5,000.00–5,000,000.00");
1025
1026 {
1027 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("de-CH");
1028 UnicodeString actual = lnrf.formatFormattableRange(-2, 3, status).toString(status);
1029 assertEquals("Negative to positive range", u"-2 – 3", actual);
1030 }
1031
1032 {
1033 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("de-CH")
1034 .numberFormatterBoth(NumberFormatter::forSkeleton(u"%", status));
1035 UnicodeString actual = lnrf.formatFormattableRange(-2, 3, status).toString(status);
1036 assertEquals("Negative to positive percent", u"-2% – 3%", actual);
1037 }
1038
1039 {
1040 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("de-CH");
1041 UnicodeString actual = lnrf.formatFormattableRange(2, -3, status).toString(status);
1042 assertEquals("Positive to negative range", u"2–-3", actual);
1043 }
1044
1045 {
1046 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("de-CH")
1047 .numberFormatterBoth(NumberFormatter::forSkeleton(u"%", status));
1048 UnicodeString actual = lnrf.formatFormattableRange(2, -3, status).toString(status);
1049 assertEquals("Positive to negative percent", u"2% – -3%", actual);
1050 }
1051 }
1052
testCreateLNRFFromNumberingSystemInSkeleton()1053 void NumberRangeFormatterTest::testCreateLNRFFromNumberingSystemInSkeleton() {
1054 IcuTestErrorCode status(*this, "testCreateLNRFFromNumberingSystemInSkeleton");
1055 {
1056 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("en")
1057 .numberFormatterBoth(NumberFormatter::forSkeleton(
1058 u".### rounding-mode-half-up", status));
1059 UnicodeString actual = lnrf.formatFormattableRange(1, 234, status).toString(status);
1060 assertEquals("default numbering system", u"1–234", actual);
1061 status.errIfFailureAndReset("default numbering system");
1062 }
1063 {
1064 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("th")
1065 .numberFormatterBoth(NumberFormatter::forSkeleton(
1066 u".### rounding-mode-half-up numbering-system/thai", status));
1067 UnicodeString actual = lnrf.formatFormattableRange(1, 234, status).toString(status);
1068 assertEquals("Thai numbering system", u"๑-๒๓๔", actual);
1069 status.errIfFailureAndReset("thai numbering system");
1070 }
1071 {
1072 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("en")
1073 .numberFormatterBoth(NumberFormatter::forSkeleton(
1074 u".### rounding-mode-half-up numbering-system/arab", status));
1075 UnicodeString actual = lnrf.formatFormattableRange(1, 234, status).toString(status);
1076 assertEquals("Arabic numbering system", u"١–٢٣٤", actual);
1077 status.errIfFailureAndReset("arab numbering system");
1078 }
1079 {
1080 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("en")
1081 .numberFormatterFirst(NumberFormatter::forSkeleton(u"numbering-system/arab", status))
1082 .numberFormatterSecond(NumberFormatter::forSkeleton(u"numbering-system/arab", status));
1083 UnicodeString actual = lnrf.formatFormattableRange(1, 234, status).toString(status);
1084 assertEquals("Double Arabic numbering system", u"١–٢٣٤", actual);
1085 status.errIfFailureAndReset("double arab numbering system");
1086 }
1087 {
1088 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::withLocale("en")
1089 .numberFormatterFirst(NumberFormatter::forSkeleton(u"numbering-system/arab", status))
1090 .numberFormatterSecond(NumberFormatter::forSkeleton(u"numbering-system/latn", status));
1091 // Note: The error is not set until `formatFormattableRange` because this is where the
1092 // formatter object gets built.
1093 lnrf.formatFormattableRange(1, 234, status);
1094 status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1095 }
1096 }
1097
test21683_StateLeak()1098 void NumberRangeFormatterTest::test21683_StateLeak() {
1099 IcuTestErrorCode status(*this, "test21683_StateLeak");
1100 UNumberRangeFormatter* nrf = nullptr;
1101 UFormattedNumberRange* result = nullptr;
1102 UConstrainedFieldPosition* fpos = nullptr;
1103
1104 struct Range {
1105 double start;
1106 double end;
1107 const char16_t* expected;
1108 int numFields;
1109 } ranges[] = {
1110 {1, 2, u"1\u20132", 4},
1111 {1, 1, u"~1", 2},
1112 };
1113
1114 UParseError* perror = nullptr;
1115 nrf = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
1116 u"", -1,
1117 UNUM_RANGE_COLLAPSE_AUTO,
1118 UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
1119 "en", perror, status);
1120 if (status.errIfFailureAndReset("unumrf_openForSkeletonWithCollapseAndIdentityFallback")) {
1121 goto cleanup;
1122 }
1123
1124 result = unumrf_openResult(status);
1125 if (status.errIfFailureAndReset("unumrf_openResult")) { goto cleanup; }
1126
1127 for (auto range : ranges) {
1128 unumrf_formatDoubleRange(nrf, range.start, range.end, result, status);
1129 if (status.errIfFailureAndReset("unumrf_formatDoubleRange")) { goto cleanup; }
1130
1131 const auto* formattedValue = unumrf_resultAsValue(result, status);
1132 if (status.errIfFailureAndReset("unumrf_resultAsValue")) { goto cleanup; }
1133
1134 int32_t utf16Length;
1135 const char16_t* utf16Str = ufmtval_getString(formattedValue, &utf16Length, status);
1136 if (status.errIfFailureAndReset("ufmtval_getString")) { goto cleanup; }
1137
1138 assertEquals("Format", range.expected, utf16Str);
1139
1140 ucfpos_close(fpos);
1141 fpos = ucfpos_open(status);
1142 if (status.errIfFailureAndReset("ucfpos_open")) { goto cleanup; }
1143
1144 int numFields = 0;
1145 while (true) {
1146 bool hasMore = ufmtval_nextPosition(formattedValue, fpos, status);
1147 if (status.errIfFailureAndReset("ufmtval_nextPosition")) { goto cleanup; }
1148 if (!hasMore) {
1149 break;
1150 }
1151 numFields++;
1152 }
1153 assertEquals("numFields", range.numFields, numFields);
1154 }
1155
1156 cleanup:
1157 unumrf_close(nrf);
1158 unumrf_closeResult(result);
1159 ucfpos_close(fpos);
1160 }
1161
test22288_DifferentStartEndSettings()1162 void NumberRangeFormatterTest::test22288_DifferentStartEndSettings() {
1163 IcuTestErrorCode status(*this, "test22288_DifferentStartEndSettings");
1164 LocalizedNumberRangeFormatter lnrf(NumberRangeFormatter
1165 ::withLocale("en")
1166 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
1167 .numberFormatterFirst(
1168 NumberFormatter::with()
1169 .unit(CurrencyUnit("USD", status))
1170 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
1171 .precision(Precision::integer())
1172 .roundingMode(UNUM_ROUND_FLOOR))
1173 .numberFormatterSecond(
1174 NumberFormatter::with()
1175 .unit(CurrencyUnit("USD", status))
1176 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
1177 .precision(Precision::integer())
1178 .roundingMode(UNUM_ROUND_CEILING)));
1179 FormattedNumberRange result = lnrf.formatFormattableRange(2.5, 2.5, status);
1180 assertEquals("Should format successfully", u"2–3 US dollars", result.toString(status));
1181 }
1182
assertFormatRange(const char16_t * message,const UnlocalizedNumberRangeFormatter & f,Locale locale,const char16_t * expected_10_50,const char16_t * expected_49_51,const char16_t * expected_50_50,const char16_t * expected_00_30,const char16_t * expected_00_00,const char16_t * expected_30_3K,const char16_t * expected_30K_50K,const char16_t * expected_49K_51K,const char16_t * expected_50K_50K,const char16_t * expected_50K_50M)1183 void NumberRangeFormatterTest::assertFormatRange(
1184 const char16_t* message,
1185 const UnlocalizedNumberRangeFormatter& f,
1186 Locale locale,
1187 const char16_t* expected_10_50,
1188 const char16_t* expected_49_51,
1189 const char16_t* expected_50_50,
1190 const char16_t* expected_00_30,
1191 const char16_t* expected_00_00,
1192 const char16_t* expected_30_3K,
1193 const char16_t* expected_30K_50K,
1194 const char16_t* expected_49K_51K,
1195 const char16_t* expected_50K_50K,
1196 const char16_t* expected_50K_50M) {
1197 LocalizedNumberRangeFormatter l = f.locale(locale);
1198 assertFormattedRangeEquals(message, l, 1, 5, expected_10_50);
1199 assertFormattedRangeEquals(message, l, 4.9999999, 5.0000001, expected_49_51);
1200 assertFormattedRangeEquals(message, l, 5, 5, expected_50_50);
1201 assertFormattedRangeEquals(message, l, 0, 3, expected_00_30);
1202 assertFormattedRangeEquals(message, l, 0, 0, expected_00_00);
1203 assertFormattedRangeEquals(message, l, 3, 3000, expected_30_3K);
1204 assertFormattedRangeEquals(message, l, 3000, 5000, expected_30K_50K);
1205 assertFormattedRangeEquals(message, l, 4999, 5001, expected_49K_51K);
1206 assertFormattedRangeEquals(message, l, 5000, 5000, expected_50K_50K);
1207 assertFormattedRangeEquals(message, l, 5e3, 5e6, expected_50K_50M);
1208 }
1209
assertFormattedRangeEquals(const char16_t * message,const LocalizedNumberRangeFormatter & l,double first,double second,const char16_t * expected)1210 FormattedNumberRange NumberRangeFormatterTest::assertFormattedRangeEquals(
1211 const char16_t* message,
1212 const LocalizedNumberRangeFormatter& l,
1213 double first,
1214 double second,
1215 const char16_t* expected) {
1216 IcuTestErrorCode status(*this, "assertFormattedRangeEquals");
1217 UnicodeString fullMessage = UnicodeString(message) + u": " + DoubleToUnicodeString(first) + u", " + DoubleToUnicodeString(second);
1218 status.setScope(fullMessage);
1219 FormattedNumberRange fnr = l.formatFormattableRange(first, second, status);
1220 UnicodeString actual = fnr.toString(status);
1221 assertEquals(fullMessage, expected, actual);
1222 return fnr;
1223 }
1224
1225
1226 #endif
1227