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 GBP(u"GBP", status),
25 PTE(u"PTE", status) {
26
27 // Check for error on the first MeasureUnit in case there is no data
28 LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
29 if (U_FAILURE(status)) {
30 dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
31 return;
32 }
33 METER = *unit;
34
35 KILOMETER = *LocalPointer<MeasureUnit>(MeasureUnit::createKilometer(status));
36 FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
37 KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
38 }
39
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)40 void NumberRangeFormatterTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
41 if (exec) {
42 logln("TestSuite NumberRangeFormatterTest: ");
43 }
44 TESTCASE_AUTO_BEGIN;
45 TESTCASE_AUTO(testSanity);
46 TESTCASE_AUTO(testBasic);
47 TESTCASE_AUTO(testCollapse);
48 TESTCASE_AUTO(testIdentity);
49 TESTCASE_AUTO(testDifferentFormatters);
50 TESTCASE_AUTO(testPlurals);
51 TESTCASE_AUTO(testCopyMove);
52 TESTCASE_AUTO_END;
53 }
54
testSanity()55 void NumberRangeFormatterTest::testSanity() {
56 IcuTestErrorCode status(*this, "testSanity");
57 LocalizedNumberRangeFormatter lnrf1 = NumberRangeFormatter::withLocale("en-us");
58 LocalizedNumberRangeFormatter lnrf2 = NumberRangeFormatter::with().locale("en-us");
59 assertEquals("Formatters should have same behavior 1",
60 lnrf1.formatFormattableRange(4, 6, status).toString(status),
61 lnrf2.formatFormattableRange(4, 6, status).toString(status));
62 }
63
testBasic()64 void NumberRangeFormatterTest::testBasic() {
65 assertFormatRange(
66 u"Basic",
67 NumberRangeFormatter::with(),
68 Locale("en-us"),
69 u"1–5",
70 u"~5",
71 u"~5",
72 u"0–3",
73 u"~0",
74 u"3–3,000",
75 u"3,000–5,000",
76 u"4,999–5,001",
77 u"~5,000",
78 u"5,000–5,000,000");
79
80 assertFormatRange(
81 u"Basic with units",
82 NumberRangeFormatter::with()
83 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
84 Locale("en-us"),
85 u"1–5 m",
86 u"~5 m",
87 u"~5 m",
88 u"0–3 m",
89 u"~0 m",
90 u"3–3,000 m",
91 u"3,000–5,000 m",
92 u"4,999–5,001 m",
93 u"~5,000 m",
94 u"5,000–5,000,000 m");
95
96 assertFormatRange(
97 u"Basic with different units",
98 NumberRangeFormatter::with()
99 .numberFormatterFirst(NumberFormatter::with().unit(METER))
100 .numberFormatterSecond(NumberFormatter::with().unit(KILOMETER)),
101 Locale("en-us"),
102 u"1 m – 5 km",
103 u"5 m – 5 km",
104 u"5 m – 5 km",
105 u"0 m – 3 km",
106 u"0 m – 0 km",
107 u"3 m – 3,000 km",
108 u"3,000 m – 5,000 km",
109 u"4,999 m – 5,001 km",
110 u"5,000 m – 5,000 km",
111 u"5,000 m – 5,000,000 km");
112
113 assertFormatRange(
114 u"Basic long unit",
115 NumberRangeFormatter::with()
116 .numberFormatterBoth(NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)),
117 Locale("en-us"),
118 u"1–5 meters",
119 u"~5 meters",
120 u"~5 meters",
121 u"0–3 meters",
122 u"~0 meters",
123 u"3–3,000 meters",
124 u"3,000–5,000 meters",
125 u"4,999–5,001 meters",
126 u"~5,000 meters",
127 u"5,000–5,000,000 meters");
128
129 assertFormatRange(
130 u"Non-English locale and unit",
131 NumberRangeFormatter::with()
132 .numberFormatterBoth(NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)),
133 Locale("fr-FR"),
134 u"1–5\u00A0degrés Fahrenheit",
135 u"≈5\u00A0degrés Fahrenheit",
136 u"≈5\u00A0degrés Fahrenheit",
137 u"0–3\u00A0degrés Fahrenheit",
138 u"≈0\u00A0degré Fahrenheit",
139 u"3–3\u202F000\u00A0degrés Fahrenheit",
140 u"3\u202F000–5\u202F000\u00A0degrés Fahrenheit",
141 u"4\u202F999–5\u202F001\u00A0degrés Fahrenheit",
142 u"≈5\u202F000\u00A0degrés Fahrenheit",
143 u"5\u202F000–5\u202F000\u202F000\u00A0degrés Fahrenheit");
144
145 assertFormatRange(
146 u"Locale with custom range separator",
147 NumberRangeFormatter::with(),
148 Locale("ja"),
149 u"1~5",
150 u"約 5",
151 u"約 5",
152 u"0~3",
153 u"約 0",
154 u"3~3,000",
155 u"3,000~5,000",
156 u"4,999~5,001",
157 u"約 5,000",
158 u"5,000~5,000,000");
159
160 assertFormatRange(
161 u"Locale that already has spaces around range separator",
162 NumberRangeFormatter::with()
163 .collapse(UNUM_RANGE_COLLAPSE_NONE)
164 .numberFormatterBoth(NumberFormatter::with().unit(KELVIN)),
165 Locale("hr"),
166 u"1 K – 5 K",
167 u"~5 K",
168 u"~5 K",
169 u"0 K – 3 K",
170 u"~0 K",
171 u"3 K – 3.000 K",
172 u"3.000 K – 5.000 K",
173 u"4.999 K – 5.001 K",
174 u"~5.000 K",
175 u"5.000 K – 5.000.000 K");
176
177 assertFormatRange(
178 u"Locale with custom numbering system and no plural ranges data",
179 NumberRangeFormatter::with(),
180 Locale("shn@numbers=beng"),
181 // 012459 = ০১৩৪৫৯
182 u"১–৫",
183 u"~৫",
184 u"~৫",
185 u"০–৩",
186 u"~০",
187 u"৩–৩,০০০",
188 u"৩,০০০–৫,০০০",
189 u"৪,৯৯৯–৫,০০১",
190 u"~৫,০০০",
191 u"৫,০০০–৫,০০০,০০০");
192
193 assertFormatRange(
194 u"Portuguese currency",
195 NumberRangeFormatter::with()
196 .numberFormatterBoth(NumberFormatter::with().unit(PTE)),
197 Locale("pt-PT"),
198 u"1$00 - 5$00 \u200B",
199 u"~5$00 \u200B",
200 u"~5$00 \u200B",
201 u"0$00 - 3$00 \u200B",
202 u"~0$00 \u200B",
203 u"3$00 - 3000$00 \u200B",
204 u"3000$00 - 5000$00 \u200B",
205 u"4999$00 - 5001$00 \u200B",
206 u"~5000$00 \u200B",
207 u"5000$00 - 5,000,000$00 \u200B");
208 }
209
testCollapse()210 void NumberRangeFormatterTest::testCollapse() {
211 assertFormatRange(
212 u"Default collapse on currency (default rounding)",
213 NumberRangeFormatter::with()
214 .numberFormatterBoth(NumberFormatter::with().unit(USD)),
215 Locale("en-us"),
216 u"$1.00 – $5.00",
217 u"~$5.00",
218 u"~$5.00",
219 u"$0.00 – $3.00",
220 u"~$0.00",
221 u"$3.00 – $3,000.00",
222 u"$3,000.00 – $5,000.00",
223 u"$4,999.00 – $5,001.00",
224 u"~$5,000.00",
225 u"$5,000.00 – $5,000,000.00");
226
227 assertFormatRange(
228 u"Default collapse on currency",
229 NumberRangeFormatter::with()
230 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
231 Locale("en-us"),
232 u"$1 – $5",
233 u"~$5",
234 u"~$5",
235 u"$0 – $3",
236 u"~$0",
237 u"$3 – $3,000",
238 u"$3,000 – $5,000",
239 u"$4,999 – $5,001",
240 u"~$5,000",
241 u"$5,000 – $5,000,000");
242
243 assertFormatRange(
244 u"No collapse on currency",
245 NumberRangeFormatter::with()
246 .collapse(UNUM_RANGE_COLLAPSE_NONE)
247 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
248 Locale("en-us"),
249 u"$1 – $5",
250 u"~$5",
251 u"~$5",
252 u"$0 – $3",
253 u"~$0",
254 u"$3 – $3,000",
255 u"$3,000 – $5,000",
256 u"$4,999 – $5,001",
257 u"~$5,000",
258 u"$5,000 – $5,000,000");
259
260 assertFormatRange(
261 u"Unit collapse on currency",
262 NumberRangeFormatter::with()
263 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
264 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
265 Locale("en-us"),
266 u"$1–5",
267 u"~$5",
268 u"~$5",
269 u"$0–3",
270 u"~$0",
271 u"$3–3,000",
272 u"$3,000–5,000",
273 u"$4,999–5,001",
274 u"~$5,000",
275 u"$5,000–5,000,000");
276
277 assertFormatRange(
278 u"All collapse on currency",
279 NumberRangeFormatter::with()
280 .collapse(UNUM_RANGE_COLLAPSE_ALL)
281 .numberFormatterBoth(NumberFormatter::with().unit(USD).precision(Precision::integer())),
282 Locale("en-us"),
283 u"$1–5",
284 u"~$5",
285 u"~$5",
286 u"$0–3",
287 u"~$0",
288 u"$3–3,000",
289 u"$3,000–5,000",
290 u"$4,999–5,001",
291 u"~$5,000",
292 u"$5,000–5,000,000");
293
294 assertFormatRange(
295 u"Default collapse on currency ISO code",
296 NumberRangeFormatter::with()
297 .numberFormatterBoth(NumberFormatter::with()
298 .unit(GBP)
299 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
300 .precision(Precision::integer())),
301 Locale("en-us"),
302 u"GBP 1–5",
303 u"~GBP 5", // TODO: Fix this at some point
304 u"~GBP 5",
305 u"GBP 0–3",
306 u"~GBP 0",
307 u"GBP 3–3,000",
308 u"GBP 3,000–5,000",
309 u"GBP 4,999–5,001",
310 u"~GBP 5,000",
311 u"GBP 5,000–5,000,000");
312
313 assertFormatRange(
314 u"No collapse on currency ISO code",
315 NumberRangeFormatter::with()
316 .collapse(UNUM_RANGE_COLLAPSE_NONE)
317 .numberFormatterBoth(NumberFormatter::with()
318 .unit(GBP)
319 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
320 .precision(Precision::integer())),
321 Locale("en-us"),
322 u"GBP 1 – GBP 5",
323 u"~GBP 5", // TODO: Fix this at some point
324 u"~GBP 5",
325 u"GBP 0 – GBP 3",
326 u"~GBP 0",
327 u"GBP 3 – GBP 3,000",
328 u"GBP 3,000 – GBP 5,000",
329 u"GBP 4,999 – GBP 5,001",
330 u"~GBP 5,000",
331 u"GBP 5,000 – GBP 5,000,000");
332
333 assertFormatRange(
334 u"Unit collapse on currency ISO code",
335 NumberRangeFormatter::with()
336 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
337 .numberFormatterBoth(NumberFormatter::with()
338 .unit(GBP)
339 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
340 .precision(Precision::integer())),
341 Locale("en-us"),
342 u"GBP 1–5",
343 u"~GBP 5", // TODO: Fix this at some point
344 u"~GBP 5",
345 u"GBP 0–3",
346 u"~GBP 0",
347 u"GBP 3–3,000",
348 u"GBP 3,000–5,000",
349 u"GBP 4,999–5,001",
350 u"~GBP 5,000",
351 u"GBP 5,000–5,000,000");
352
353 assertFormatRange(
354 u"All collapse on currency ISO code",
355 NumberRangeFormatter::with()
356 .collapse(UNUM_RANGE_COLLAPSE_ALL)
357 .numberFormatterBoth(NumberFormatter::with()
358 .unit(GBP)
359 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE)
360 .precision(Precision::integer())),
361 Locale("en-us"),
362 u"GBP 1–5",
363 u"~GBP 5", // TODO: Fix this at some point
364 u"~GBP 5",
365 u"GBP 0–3",
366 u"~GBP 0",
367 u"GBP 3–3,000",
368 u"GBP 3,000–5,000",
369 u"GBP 4,999–5,001",
370 u"~GBP 5,000",
371 u"GBP 5,000–5,000,000");
372
373 // Default collapse on measurement unit is in testBasic()
374
375 assertFormatRange(
376 u"No collapse on measurement unit",
377 NumberRangeFormatter::with()
378 .collapse(UNUM_RANGE_COLLAPSE_NONE)
379 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
380 Locale("en-us"),
381 u"1 m – 5 m",
382 u"~5 m",
383 u"~5 m",
384 u"0 m – 3 m",
385 u"~0 m",
386 u"3 m – 3,000 m",
387 u"3,000 m – 5,000 m",
388 u"4,999 m – 5,001 m",
389 u"~5,000 m",
390 u"5,000 m – 5,000,000 m");
391
392 assertFormatRange(
393 u"Unit collapse on measurement unit",
394 NumberRangeFormatter::with()
395 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
396 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
397 Locale("en-us"),
398 u"1–5 m",
399 u"~5 m",
400 u"~5 m",
401 u"0–3 m",
402 u"~0 m",
403 u"3–3,000 m",
404 u"3,000–5,000 m",
405 u"4,999–5,001 m",
406 u"~5,000 m",
407 u"5,000–5,000,000 m");
408
409 assertFormatRange(
410 u"All collapse on measurement unit",
411 NumberRangeFormatter::with()
412 .collapse(UNUM_RANGE_COLLAPSE_ALL)
413 .numberFormatterBoth(NumberFormatter::with().unit(METER)),
414 Locale("en-us"),
415 u"1–5 m",
416 u"~5 m",
417 u"~5 m",
418 u"0–3 m",
419 u"~0 m",
420 u"3–3,000 m",
421 u"3,000–5,000 m",
422 u"4,999–5,001 m",
423 u"~5,000 m",
424 u"5,000–5,000,000 m");
425
426 assertFormatRange(
427 u"Default collapse, long-form compact notation",
428 NumberRangeFormatter::with()
429 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactLong())),
430 Locale("de-CH"),
431 u"1–5",
432 u"≈5",
433 u"≈5",
434 u"0–3",
435 u"≈0",
436 u"3–3 Tausend",
437 u"3–5 Tausend",
438 u"≈5 Tausend",
439 u"≈5 Tausend",
440 u"5 Tausend – 5 Millionen");
441
442 assertFormatRange(
443 u"Unit collapse, long-form compact notation",
444 NumberRangeFormatter::with()
445 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
446 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactLong())),
447 Locale("de-CH"),
448 u"1–5",
449 u"≈5",
450 u"≈5",
451 u"0–3",
452 u"≈0",
453 u"3–3 Tausend",
454 u"3 Tausend – 5 Tausend",
455 u"≈5 Tausend",
456 u"≈5 Tausend",
457 u"5 Tausend – 5 Millionen");
458
459 assertFormatRange(
460 u"Default collapse on measurement unit with compact-short notation",
461 NumberRangeFormatter::with()
462 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
463 Locale("en-us"),
464 u"1–5 m",
465 u"~5 m",
466 u"~5 m",
467 u"0–3 m",
468 u"~0 m",
469 u"3–3K m",
470 u"3K – 5K m",
471 u"~5K m",
472 u"~5K m",
473 u"5K – 5M m");
474
475 assertFormatRange(
476 u"No collapse on measurement unit with compact-short notation",
477 NumberRangeFormatter::with()
478 .collapse(UNUM_RANGE_COLLAPSE_NONE)
479 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
480 Locale("en-us"),
481 u"1 m – 5 m",
482 u"~5 m",
483 u"~5 m",
484 u"0 m – 3 m",
485 u"~0 m",
486 u"3 m – 3K m",
487 u"3K m – 5K m",
488 u"~5K m",
489 u"~5K m",
490 u"5K m – 5M m");
491
492 assertFormatRange(
493 u"Unit collapse on measurement unit with compact-short notation",
494 NumberRangeFormatter::with()
495 .collapse(UNUM_RANGE_COLLAPSE_UNIT)
496 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
497 Locale("en-us"),
498 u"1–5 m",
499 u"~5 m",
500 u"~5 m",
501 u"0–3 m",
502 u"~0 m",
503 u"3–3K m",
504 u"3K – 5K m",
505 u"~5K m",
506 u"~5K m",
507 u"5K – 5M m");
508
509 assertFormatRange(
510 u"All collapse on measurement unit with compact-short notation",
511 NumberRangeFormatter::with()
512 .collapse(UNUM_RANGE_COLLAPSE_ALL)
513 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort()).unit(METER)),
514 Locale("en-us"),
515 u"1–5 m",
516 u"~5 m",
517 u"~5 m",
518 u"0–3 m",
519 u"~0 m",
520 u"3–3K m",
521 u"3–5K m", // this one is the key use case for ALL
522 u"~5K m",
523 u"~5K m",
524 u"5K – 5M m");
525
526 assertFormatRange(
527 u"No collapse on scientific notation",
528 NumberRangeFormatter::with()
529 .collapse(UNUM_RANGE_COLLAPSE_NONE)
530 .numberFormatterBoth(NumberFormatter::with().notation(Notation::scientific())),
531 Locale("en-us"),
532 u"1E0 – 5E0",
533 u"~5E0",
534 u"~5E0",
535 u"0E0 – 3E0",
536 u"~0E0",
537 u"3E0 – 3E3",
538 u"3E3 – 5E3",
539 u"4.999E3 – 5.001E3",
540 u"~5E3",
541 u"5E3 – 5E6");
542
543 assertFormatRange(
544 u"All collapse on scientific notation",
545 NumberRangeFormatter::with()
546 .collapse(UNUM_RANGE_COLLAPSE_ALL)
547 .numberFormatterBoth(NumberFormatter::with().notation(Notation::scientific())),
548 Locale("en-us"),
549 u"1–5E0",
550 u"~5E0",
551 u"~5E0",
552 u"0–3E0",
553 u"~0E0",
554 u"3E0 – 3E3",
555 u"3–5E3",
556 u"4.999–5.001E3",
557 u"~5E3",
558 u"5E3 – 5E6");
559
560 // TODO: Test compact currency?
561 // The code is not smart enough to differentiate the notation from the unit.
562 }
563
testIdentity()564 void NumberRangeFormatterTest::testIdentity() {
565 assertFormatRange(
566 u"Identity fallback Range",
567 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_RANGE),
568 Locale("en-us"),
569 u"1–5",
570 u"5–5",
571 u"5–5",
572 u"0–3",
573 u"0–0",
574 u"3–3,000",
575 u"3,000–5,000",
576 u"4,999–5,001",
577 u"5,000–5,000",
578 u"5,000–5,000,000");
579
580 assertFormatRange(
581 u"Identity fallback Approximately or Single Value",
582 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE),
583 Locale("en-us"),
584 u"1–5",
585 u"~5",
586 u"5",
587 u"0–3",
588 u"0",
589 u"3–3,000",
590 u"3,000–5,000",
591 u"4,999–5,001",
592 u"5,000",
593 u"5,000–5,000,000");
594
595 assertFormatRange(
596 u"Identity fallback Single Value",
597 NumberRangeFormatter::with().identityFallback(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE),
598 Locale("en-us"),
599 u"1–5",
600 u"5",
601 u"5",
602 u"0–3",
603 u"0",
604 u"3–3,000",
605 u"3,000–5,000",
606 u"4,999–5,001",
607 u"5,000",
608 u"5,000–5,000,000");
609
610 assertFormatRange(
611 u"Identity fallback Approximately or Single Value with compact notation",
612 NumberRangeFormatter::with()
613 .identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE)
614 .numberFormatterBoth(NumberFormatter::with().notation(Notation::compactShort())),
615 Locale("en-us"),
616 u"1–5",
617 u"~5",
618 u"5",
619 u"0–3",
620 u"0",
621 u"3–3K",
622 u"3K – 5K",
623 u"~5K",
624 u"5K",
625 u"5K – 5M");
626
627 assertFormatRange(
628 u"Approximately in middle of unit string",
629 NumberRangeFormatter::with().numberFormatterBoth(
630 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)),
631 Locale("zh-Hant"),
632 u"華氏 1-5 度",
633 u"華氏 ~5 度",
634 u"華氏 ~5 度",
635 u"華氏 0-3 度",
636 u"華氏 ~0 度",
637 u"華氏 3-3,000 度",
638 u"華氏 3,000-5,000 度",
639 u"華氏 4,999-5,001 度",
640 u"華氏 ~5,000 度",
641 u"華氏 5,000-5,000,000 度");
642 }
643
testDifferentFormatters()644 void NumberRangeFormatterTest::testDifferentFormatters() {
645 assertFormatRange(
646 u"Different rounding rules",
647 NumberRangeFormatter::with()
648 .numberFormatterFirst(NumberFormatter::with().precision(Precision::integer()))
649 .numberFormatterSecond(NumberFormatter::with().precision(Precision::fixedDigits(2))),
650 Locale("en-us"),
651 u"1–5.0",
652 u"5–5.0",
653 u"5–5.0",
654 u"0–3.0",
655 u"0–0.0",
656 u"3–3,000",
657 u"3,000–5,000",
658 u"4,999–5,000",
659 u"5,000–5,000", // TODO: Should this one be ~5,000?
660 u"5,000–5,000,000");
661 }
662
testPlurals()663 void NumberRangeFormatterTest::testPlurals() {
664 IcuTestErrorCode status(*this, "testPlurals");
665
666 // Locale sl has interesting plural forms:
667 // GBP{
668 // one{"britanski funt"}
669 // two{"britanska funta"}
670 // few{"britanski funti"}
671 // other{"britanskih funtov"}
672 // }
673 Locale locale("sl");
674
675 UnlocalizedNumberFormatter unf = NumberFormatter::with()
676 .unit(GBP)
677 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
678 .precision(Precision::integer());
679 LocalizedNumberFormatter lnf = unf.locale(locale);
680
681 // For comparison, run the non-range version of the formatter
682 assertEquals(Int64ToUnicodeString(1), u"1 britanski funt", lnf.formatDouble(1, status).toString(status));
683 assertEquals(Int64ToUnicodeString(2), u"2 britanska funta", lnf.formatDouble(2, status).toString(status));
684 assertEquals(Int64ToUnicodeString(3), u"3 britanski funti", lnf.formatDouble(3, status).toString(status));
685 assertEquals(Int64ToUnicodeString(5), u"5 britanskih funtov", lnf.formatDouble(5, status).toString(status));
686 if (status.errIfFailureAndReset()) { return; }
687
688 LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter::with()
689 .numberFormatterBoth(unf)
690 .identityFallback(UNUM_IDENTITY_FALLBACK_RANGE)
691 .locale(locale);
692
693 struct TestCase {
694 double first;
695 double second;
696 const char16_t* expected;
697 } cases[] = {
698 {1, 1, u"1–1 britanski funti"}, // one + one -> few
699 {1, 2, u"1–2 britanska funta"}, // one + two -> two
700 {1, 3, u"1–3 britanski funti"}, // one + few -> few
701 {1, 5, u"1–5 britanskih funtov"}, // one + other -> other
702 {2, 1, u"2–1 britanski funti"}, // two + one -> few
703 {2, 2, u"2–2 britanska funta"}, // two + two -> two
704 {2, 3, u"2–3 britanski funti"}, // two + few -> few
705 {2, 5, u"2–5 britanskih funtov"}, // two + other -> other
706 {3, 1, u"3–1 britanski funti"}, // few + one -> few
707 {3, 2, u"3–2 britanska funta"}, // few + two -> two
708 {3, 3, u"3–3 britanski funti"}, // few + few -> few
709 {3, 5, u"3–5 britanskih funtov"}, // few + other -> other
710 {5, 1, u"5–1 britanski funti"}, // other + one -> few
711 {5, 2, u"5–2 britanska funta"}, // other + two -> two
712 {5, 3, u"5–3 britanski funti"}, // other + few -> few
713 {5, 5, u"5–5 britanskih funtov"}, // other + other -> other
714 };
715 for (auto& cas : cases) {
716 UnicodeString message = Int64ToUnicodeString(cas.first);
717 message += u" ";
718 message += Int64ToUnicodeString(cas.second);
719 status.setScope(message);
720 UnicodeString actual = lnrf.formatFormattableRange(cas.first, cas.second, status).toString(status);
721 assertEquals(message, cas.expected, actual);
722 status.errIfFailureAndReset();
723 }
724 }
725
testCopyMove()726 void NumberRangeFormatterTest::testCopyMove() {
727 IcuTestErrorCode status(*this, "testCopyMove");
728
729 // Default constructors
730 LocalizedNumberRangeFormatter l1;
731 assertEquals("Initial behavior", u"1–5", l1.formatFormattableRange(1, 5, status).toString(status));
732 if (status.errDataIfFailureAndReset()) { return; }
733
734 // Setup
735 l1 = NumberRangeFormatter::withLocale("fr-FR")
736 .numberFormatterBoth(NumberFormatter::with().unit(USD));
737 assertEquals("Currency behavior", u"1,00–5,00 $US", l1.formatFormattableRange(1, 5, status).toString(status));
738
739 // Copy constructor
740 LocalizedNumberRangeFormatter l2 = l1;
741 assertEquals("Copy constructor", u"1,00–5,00 $US", l2.formatFormattableRange(1, 5, status).toString(status));
742
743 // Move constructor
744 LocalizedNumberRangeFormatter l3 = std::move(l1);
745 assertEquals("Move constructor", u"1,00–5,00 $US", l3.formatFormattableRange(1, 5, status).toString(status));
746
747 // Reset objects for assignment tests
748 l1 = NumberRangeFormatter::withLocale("en-us");
749 l2 = NumberRangeFormatter::withLocale("en-us");
750 assertEquals("Rest behavior, l1", u"1–5", l1.formatFormattableRange(1, 5, status).toString(status));
751 assertEquals("Rest behavior, l2", u"1–5", l2.formatFormattableRange(1, 5, status).toString(status));
752
753 // Copy assignment
754 l1 = l3;
755 assertEquals("Copy constructor", u"1,00–5,00 $US", l1.formatFormattableRange(1, 5, status).toString(status));
756
757 // Move assignment
758 l2 = std::move(l3);
759 assertEquals("Copy constructor", u"1,00–5,00 $US", l2.formatFormattableRange(1, 5, status).toString(status));
760
761 // FormattedNumberRange
762 FormattedNumberRange result = l1.formatFormattableRange(1, 5, status);
763 assertEquals("FormattedNumberRange move constructor", u"1,00–5,00 $US", result.toString(status));
764 result = l1.formatFormattableRange(3, 6, status);
765 assertEquals("FormattedNumberRange move assignment", u"3,00–6,00 $US", result.toString(status));
766 }
767
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)768 void NumberRangeFormatterTest::assertFormatRange(
769 const char16_t* message,
770 const UnlocalizedNumberRangeFormatter& f,
771 Locale locale,
772 const char16_t* expected_10_50,
773 const char16_t* expected_49_51,
774 const char16_t* expected_50_50,
775 const char16_t* expected_00_30,
776 const char16_t* expected_00_00,
777 const char16_t* expected_30_3K,
778 const char16_t* expected_30K_50K,
779 const char16_t* expected_49K_51K,
780 const char16_t* expected_50K_50K,
781 const char16_t* expected_50K_50M) {
782 LocalizedNumberRangeFormatter l = f.locale(locale);
783 assertFormattedRangeEquals(message, l, 1, 5, expected_10_50);
784 assertFormattedRangeEquals(message, l, 4.9999999, 5.0000001, expected_49_51);
785 assertFormattedRangeEquals(message, l, 5, 5, expected_50_50);
786 assertFormattedRangeEquals(message, l, 0, 3, expected_00_30);
787 assertFormattedRangeEquals(message, l, 0, 0, expected_00_00);
788 assertFormattedRangeEquals(message, l, 3, 3000, expected_30_3K);
789 assertFormattedRangeEquals(message, l, 3000, 5000, expected_30K_50K);
790 assertFormattedRangeEquals(message, l, 4999, 5001, expected_49K_51K);
791 assertFormattedRangeEquals(message, l, 5000, 5000, expected_50K_50K);
792 assertFormattedRangeEquals(message, l, 5e3, 5e6, expected_50K_50M);
793 }
794
assertFormattedRangeEquals(const char16_t * message,const LocalizedNumberRangeFormatter & l,double first,double second,const char16_t * expected)795 void NumberRangeFormatterTest::assertFormattedRangeEquals(
796 const char16_t* message,
797 const LocalizedNumberRangeFormatter& l,
798 double first,
799 double second,
800 const char16_t* expected) {
801 IcuTestErrorCode status(*this, "assertFormattedRangeEquals");
802 UnicodeString fullMessage = UnicodeString(message) + u": " + DoubleToUnicodeString(first) + u", " + DoubleToUnicodeString(second);
803 status.setScope(fullMessage);
804 UnicodeString actual = l.formatFormattableRange(first, second, status).toString(status);
805 assertEquals(fullMessage, expected, actual);
806 }
807
808
809 #endif
810