]>
Commit | Line | Data |
---|---|---|
3d1f044b A |
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 | ||
18 | NumberRangeFormatterTest::NumberRangeFormatterTest() | |
19 | : NumberRangeFormatterTest(globalNumberRangeFormatterTestStatus) { | |
20 | } | |
21 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 |