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