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