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