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