]> git.saurik.com Git - apple/icu.git/blame_incremental - icuSources/test/intltest/numfmtdatadriventest.cpp
ICU-62123.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / numfmtdatadriventest.cpp
... / ...
CommitLineData
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 "numfmtst.h"
9#include "digitlst.h"
10#include "number_decimalquantity.h"
11#include "putilimp.h"
12#include "charstr.h"
13#include <cmath>
14
15void NumberFormatDataDrivenTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
16 if (exec) {
17 logln("TestSuite NumberFormatDataDrivenTest: ");
18 }
19 TESTCASE_AUTO_BEGIN;
20 TESTCASE_AUTO(TestNumberFormatTestTuple);
21 TESTCASE_AUTO(TestDataDrivenICU4C);
22 TESTCASE_AUTO_END;
23}
24
25static DigitList&
26strToDigitList(const UnicodeString& str, DigitList& digitList, UErrorCode& status) {
27 if (U_FAILURE(status)) {
28 return digitList;
29 }
30 if (str == "NaN") {
31 digitList.set(uprv_getNaN());
32 return digitList;
33 }
34 if (str == "-Inf") {
35 digitList.set(-1 * uprv_getInfinity());
36 return digitList;
37 }
38 if (str == "Inf") {
39 digitList.set(uprv_getInfinity());
40 return digitList;
41 }
42 CharString formatValue;
43 formatValue.appendInvariantChars(str, status);
44 digitList.set({formatValue.data(), formatValue.length()}, status);
45 return digitList;
46}
47
48static UnicodeString&
49format(const DecimalFormat& fmt, const DigitList& digitList, UnicodeString& appendTo,
50 UErrorCode& status) {
51 if (U_FAILURE(status)) {
52 return appendTo;
53 }
54 FieldPosition fpos(FieldPosition::DONT_CARE);
55 return fmt.format(digitList, appendTo, fpos, status);
56}
57
58template<class T>
59static UnicodeString&
60format(const DecimalFormat& fmt, T value, UnicodeString& appendTo, UErrorCode& status) {
61 if (U_FAILURE(status)) {
62 return appendTo;
63 }
64 FieldPosition fpos(FieldPosition::DONT_CARE);
65 return fmt.format(value, appendTo, fpos, status);
66}
67
68static void adjustDecimalFormat(const NumberFormatTestTuple& tuple, DecimalFormat& fmt,
69 UnicodeString& appendErrorMessage) {
70 if (tuple.minIntegerDigitsFlag) {
71 fmt.setMinimumIntegerDigits(tuple.minIntegerDigits);
72 }
73 if (tuple.maxIntegerDigitsFlag) {
74 fmt.setMaximumIntegerDigits(tuple.maxIntegerDigits);
75 }
76 if (tuple.minFractionDigitsFlag) {
77 fmt.setMinimumFractionDigits(tuple.minFractionDigits);
78 }
79 if (tuple.maxFractionDigitsFlag) {
80 fmt.setMaximumFractionDigits(tuple.maxFractionDigits);
81 }
82 if (tuple.currencyFlag) {
83 UErrorCode status = U_ZERO_ERROR;
84 UnicodeString currency(tuple.currency);
85 const UChar* terminatedCurrency = currency.getTerminatedBuffer();
86 fmt.setCurrency(terminatedCurrency, status);
87 if (U_FAILURE(status)) {
88 appendErrorMessage.append("Error setting currency.");
89 }
90 }
91 if (tuple.minGroupingDigitsFlag) {
92 fmt.setMinimumGroupingDigits(tuple.minGroupingDigits);
93 }
94 if (tuple.useSigDigitsFlag) {
95 fmt.setSignificantDigitsUsed(tuple.useSigDigits != 0);
96 }
97 if (tuple.minSigDigitsFlag) {
98 fmt.setMinimumSignificantDigits(tuple.minSigDigits);
99 }
100 if (tuple.maxSigDigitsFlag) {
101 fmt.setMaximumSignificantDigits(tuple.maxSigDigits);
102 }
103 if (tuple.useGroupingFlag) {
104 fmt.setGroupingUsed(tuple.useGrouping != 0);
105 }
106 if (tuple.multiplierFlag) {
107 fmt.setMultiplier(tuple.multiplier);
108 }
109 if (tuple.roundingIncrementFlag) {
110 fmt.setRoundingIncrement(tuple.roundingIncrement);
111 }
112 if (tuple.formatWidthFlag) {
113 fmt.setFormatWidth(tuple.formatWidth);
114 }
115 if (tuple.padCharacterFlag) {
116 fmt.setPadCharacter(tuple.padCharacter);
117 }
118 if (tuple.useScientificFlag) {
119 fmt.setScientificNotation(tuple.useScientific != 0);
120 }
121 if (tuple.groupingFlag) {
122 fmt.setGroupingSize(tuple.grouping);
123 }
124 if (tuple.grouping2Flag) {
125 fmt.setSecondaryGroupingSize(tuple.grouping2);
126 }
127 if (tuple.roundingModeFlag) {
128 fmt.setRoundingMode(tuple.roundingMode);
129 }
130 if (tuple.currencyUsageFlag) {
131 UErrorCode status = U_ZERO_ERROR;
132 fmt.setCurrencyUsage(tuple.currencyUsage, &status);
133 if (U_FAILURE(status)) {
134 appendErrorMessage.append("CurrencyUsage: error setting.");
135 }
136 }
137 if (tuple.minimumExponentDigitsFlag) {
138 fmt.setMinimumExponentDigits(tuple.minimumExponentDigits);
139 }
140 if (tuple.exponentSignAlwaysShownFlag) {
141 fmt.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0);
142 }
143 if (tuple.decimalSeparatorAlwaysShownFlag) {
144 fmt.setDecimalSeparatorAlwaysShown(
145 tuple.decimalSeparatorAlwaysShown != 0);
146 }
147 if (tuple.padPositionFlag) {
148 fmt.setPadPosition(tuple.padPosition);
149 }
150 if (tuple.positivePrefixFlag) {
151 fmt.setPositivePrefix(tuple.positivePrefix);
152 }
153 if (tuple.positiveSuffixFlag) {
154 fmt.setPositiveSuffix(tuple.positiveSuffix);
155 }
156 if (tuple.negativePrefixFlag) {
157 fmt.setNegativePrefix(tuple.negativePrefix);
158 }
159 if (tuple.negativeSuffixFlag) {
160 fmt.setNegativeSuffix(tuple.negativeSuffix);
161 }
162 if (tuple.signAlwaysShownFlag) { // ICU 61 DecimalFormat does not have this, skip tests that need this
163 // fmt.setSignAlwaysShown(tuple.signAlwaysShown != 0);
164 }
165 if (tuple.localizedPatternFlag) {
166 UErrorCode status = U_ZERO_ERROR;
167 fmt.applyLocalizedPattern(tuple.localizedPattern, status);
168 if (U_FAILURE(status)) {
169 appendErrorMessage.append("Error setting localized pattern.");
170 }
171 }
172 fmt.setLenient(NFTT_GET_FIELD(tuple, lenient, 1) != 0);
173 if (tuple.parseIntegerOnlyFlag) {
174 fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
175 }
176 if (tuple.decimalPatternMatchRequiredFlag) {
177 fmt.setDecimalPatternMatchRequired(
178 tuple.decimalPatternMatchRequired != 0);
179 }
180 if (tuple.parseNoExponentFlag) {
181 UErrorCode status = U_ZERO_ERROR;
182 fmt.setAttribute(
183 UNUM_PARSE_NO_EXPONENT, tuple.parseNoExponent, status);
184 if (U_FAILURE(status)) {
185 appendErrorMessage.append("Error setting parse no exponent flag.");
186 }
187 }
188 if (tuple.parseCaseSensitiveFlag) { // ICU 61 DecimalFormat does not have this, skip tests that need this
189 // fmt.setParseCaseSensitive(tuple.parseCaseSensitive != 0);
190 }
191}
192
193static DecimalFormat*
194newDecimalFormat(const Locale& locale, const UnicodeString& pattern, UErrorCode& status) {
195 if (U_FAILURE(status)) {
196 return NULL;
197 }
198 LocalPointer<DecimalFormatSymbols> symbols(
199 new DecimalFormatSymbols(locale, status), status);
200 if (U_FAILURE(status)) {
201 return NULL;
202 }
203 UParseError perror;
204 LocalPointer<DecimalFormat> result(
205 new DecimalFormat(
206 pattern, symbols.getAlias(), perror, status), status);
207 if (!result.isNull()) {
208 symbols.orphan();
209 }
210 if (U_FAILURE(status)) {
211 return NULL;
212 }
213 return result.orphan();
214}
215
216static DecimalFormat* newDecimalFormat(const NumberFormatTestTuple& tuple, UErrorCode& status) {
217 if (U_FAILURE(status)) {
218 return NULL;
219 }
220 Locale en("en");
221 return newDecimalFormat(NFTT_GET_FIELD(tuple, locale, en),
222 NFTT_GET_FIELD(tuple, pattern, "0"),
223 status);
224}
225
226UBool NumberFormatDataDrivenTest::isFormatPass(const NumberFormatTestTuple& tuple,
227 UnicodeString& appendErrorMessage, UErrorCode& status) {
228 if (U_FAILURE(status)) {
229 return FALSE;
230 }
231 LocalPointer<DecimalFormat> fmtPtr(newDecimalFormat(tuple, status));
232 if (U_FAILURE(status)) {
233 appendErrorMessage.append("Error creating DecimalFormat.");
234 return FALSE;
235 }
236 adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage);
237 if (appendErrorMessage.length() > 0) {
238 return FALSE;
239 }
240 DigitList digitList;
241 strToDigitList(tuple.format, digitList, status);
242 {
243 UnicodeString appendTo;
244 format(*fmtPtr, digitList, appendTo, status);
245 if (U_FAILURE(status)) {
246 appendErrorMessage.append("Error formatting.");
247 return FALSE;
248 }
249 if (appendTo != tuple.output) {
250 appendErrorMessage.append(
251 UnicodeString("Expected: ") + tuple.output + ", got: " + appendTo);
252 return FALSE;
253 }
254 }
255 double doubleVal = digitList.getDouble();
256 DigitList doubleCheck;
257 doubleCheck.set(doubleVal);
258 if (digitList == doubleCheck) { // skip cases where the double does not round-trip
259 UnicodeString appendTo;
260 format(*fmtPtr, doubleVal, appendTo, status);
261 if (U_FAILURE(status)) {
262 appendErrorMessage.append("Error formatting.");
263 return FALSE;
264 }
265 if (appendTo != tuple.output) {
266 appendErrorMessage.append(
267 UnicodeString("double Expected: ") + tuple.output + ", got: " + appendTo);
268 return FALSE;
269 }
270 }
271 if (!uprv_isNaN(doubleVal) && !uprv_isInfinite(doubleVal) && digitList.fitsIntoInt64(FALSE)) {
272 int64_t intVal = digitList.getInt64();
273 {
274 UnicodeString appendTo;
275 format(*fmtPtr, intVal, appendTo, status);
276 if (U_FAILURE(status)) {
277 appendErrorMessage.append("Error formatting.");
278 return FALSE;
279 }
280 if (appendTo != tuple.output) {
281 appendErrorMessage.append(
282 UnicodeString("int64 Expected: ") + tuple.output + ", got: " + appendTo);
283 return FALSE;
284 }
285 }
286 }
287 return TRUE;
288}
289
290UBool NumberFormatDataDrivenTest::isToPatternPass(const NumberFormatTestTuple& tuple,
291 UnicodeString& appendErrorMessage, UErrorCode& status) {
292 if (U_FAILURE(status)) {
293 return FALSE;
294 }
295 LocalPointer<DecimalFormat> fmtPtr(newDecimalFormat(tuple, status));
296 if (U_FAILURE(status)) {
297 appendErrorMessage.append("Error creating DecimalFormat.");
298 return FALSE;
299 }
300 adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage);
301 if (appendErrorMessage.length() > 0) {
302 return FALSE;
303 }
304 if (tuple.toPatternFlag) {
305 UnicodeString actual;
306 fmtPtr->toPattern(actual);
307 if (actual != tuple.toPattern) {
308 appendErrorMessage.append(
309 UnicodeString("Expected: ") + tuple.toPattern + ", got: " + actual + ". ");
310 }
311 }
312 if (tuple.toLocalizedPatternFlag) {
313 UnicodeString actual;
314 fmtPtr->toLocalizedPattern(actual);
315 if (actual != tuple.toLocalizedPattern) {
316 appendErrorMessage.append(
317 UnicodeString("Expected: ") + tuple.toLocalizedPattern + ", got: " + actual + ". ");
318 }
319 }
320 return appendErrorMessage.length() == 0;
321}
322
323UBool NumberFormatDataDrivenTest::isParsePass(const NumberFormatTestTuple& tuple,
324 UnicodeString& appendErrorMessage, UErrorCode& status) {
325 if (U_FAILURE(status)) {
326 return FALSE;
327 }
328 LocalPointer<DecimalFormat> fmtPtr(newDecimalFormat(tuple, status));
329 if (U_FAILURE(status)) {
330 appendErrorMessage.append("Error creating DecimalFormat.");
331 return FALSE;
332 }
333 adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage);
334 if (appendErrorMessage.length() > 0) {
335 return FALSE;
336 }
337 Formattable result;
338 ParsePosition ppos;
339 fmtPtr->parse(tuple.parse, result, ppos);
340 if (ppos.getIndex() == 0) {
341 appendErrorMessage.append("Parse failed; got error index ");
342 appendErrorMessage = appendErrorMessage + ppos.getErrorIndex();
343 return FALSE;
344 }
345 if (tuple.output == "fail") {
346 appendErrorMessage.append(
347 UnicodeString("Parse succeeded: ") + result.getDouble() + ", but was expected to fail.");
348 return TRUE; // TRUE because failure handling is in the test suite
349 }
350 if (tuple.output == "NaN") {
351 if (!uprv_isNaN(result.getDouble())) {
352 appendErrorMessage.append(UnicodeString("Expected NaN, but got: ") + result.getDouble());
353 return FALSE;
354 }
355 return TRUE;
356 } else if (tuple.output == "Inf") {
357 if (!uprv_isInfinite(result.getDouble()) || result.getDouble() < 0) {
358 appendErrorMessage.append(UnicodeString("Expected Inf, but got: ") + result.getDouble());
359 return FALSE;
360 }
361 return TRUE;
362 } else if (tuple.output == "-Inf") {
363 if (!uprv_isInfinite(result.getDouble()) || result.getDouble() > 0) {
364 appendErrorMessage.append(UnicodeString("Expected -Inf, but got: ") + result.getDouble());
365 return FALSE;
366 }
367 return TRUE;
368 } else if (tuple.output == "-0.0") {
369 if (!std::signbit(result.getDouble()) || result.getDouble() != 0) {
370 appendErrorMessage.append(UnicodeString("Expected -0.0, but got: ") + result.getDouble());
371 return FALSE;
372 }
373 return TRUE;
374 }
375 // All other cases parse to a DigitList, not a double.
376
377 DigitList expected;
378 strToDigitList(tuple.output, expected, status);
379 if (U_FAILURE(status)) {
380 appendErrorMessage.append("[Error parsing decnumber] ");
381 return FALSE;
382 }
383 if (expected != *result.getDigitList()) {
384 UnicodeString resultStr(UnicodeString::fromUTF8(result.getDecimalNumber(status)));
385 appendErrorMessage.append(UnicodeString("Expected: ") + tuple.output + ", but got: " + resultStr + " (" + ppos.getIndex() + ":" + ppos.getErrorIndex() + ")");
386 return FALSE;
387 }
388
389 return TRUE;
390}
391
392UBool NumberFormatDataDrivenTest::isParseCurrencyPass(const NumberFormatTestTuple& tuple,
393 UnicodeString& appendErrorMessage,
394 UErrorCode& status) {
395 if (U_FAILURE(status)) {
396 return FALSE;
397 }
398 LocalPointer<DecimalFormat> fmtPtr(newDecimalFormat(tuple, status));
399 if (U_FAILURE(status)) {
400 appendErrorMessage.append("Error creating DecimalFormat.");
401 return FALSE;
402 }
403 adjustDecimalFormat(tuple, *fmtPtr, appendErrorMessage);
404 if (appendErrorMessage.length() > 0) {
405 return FALSE;
406 }
407 ParsePosition ppos;
408 LocalPointer<CurrencyAmount> currAmt(
409 fmtPtr->parseCurrency(tuple.parse, ppos));
410 if (ppos.getIndex() == 0) {
411 appendErrorMessage.append("Parse failed; got error index ");
412 appendErrorMessage = appendErrorMessage + ppos.getErrorIndex();
413 return FALSE;
414 }
415 UnicodeString currStr(currAmt->getISOCurrency());
416 Formattable resultFormattable(currAmt->getNumber());
417 UnicodeString resultStr(UnicodeString::fromUTF8(resultFormattable.getDecimalNumber(status)));
418 if (tuple.output == "fail") {
419 appendErrorMessage.append(UnicodeString("Parse succeeded: ") + resultStr + ", but was expected to fail.");
420 return TRUE; // TRUE because failure handling is in the test suite
421 }
422
423 DigitList expected;
424 strToDigitList(tuple.output, expected, status);
425 if (U_FAILURE(status)) {
426 appendErrorMessage.append("Error parsing decnumber");
427 return FALSE;
428 }
429 if (expected != *currAmt->getNumber().getDigitList()) {
430 appendErrorMessage.append(UnicodeString("Expected: ") + tuple.output + ", but got: " + resultStr + " (" + ppos.getIndex() + ":" + ppos.getErrorIndex() + ")");
431 return FALSE;
432 }
433
434 if (currStr != tuple.outputCurrency) {
435 appendErrorMessage.append(UnicodeString(
436 "Expected currency: ") + tuple.outputCurrency + ", got: " + currStr + ". ");
437 return FALSE;
438 }
439 return TRUE;
440}
441
442void NumberFormatDataDrivenTest::TestNumberFormatTestTuple() {
443 NumberFormatTestTuple tuple;
444 UErrorCode status = U_ZERO_ERROR;
445
446 tuple.setField(
447 NumberFormatTestTuple::getFieldByName("locale"), "en", status);
448 tuple.setField(
449 NumberFormatTestTuple::getFieldByName("pattern"), "#,##0.00", status);
450 tuple.setField(
451 NumberFormatTestTuple::getFieldByName("minIntegerDigits"), "-10", status);
452 if (!assertSuccess("", status)) {
453 return;
454 }
455
456 // only what we set should be set.
457 assertEquals("", "en", tuple.locale.getName());
458 assertEquals("", "#,##0.00", tuple.pattern);
459 assertEquals("", -10, tuple.minIntegerDigits);
460 assertTrue("", tuple.localeFlag);
461 assertTrue("", tuple.patternFlag);
462 assertTrue("", tuple.minIntegerDigitsFlag);
463 assertFalse("", tuple.formatFlag);
464
465 UnicodeString appendTo;
466 assertEquals(
467 "", "{locale: en, pattern: #,##0.00, minIntegerDigits: -10}", tuple.toString(appendTo));
468
469 tuple.clear();
470 appendTo.remove();
471 assertEquals(
472 "", "{}", tuple.toString(appendTo));
473 tuple.setField(
474 NumberFormatTestTuple::getFieldByName("aBadFieldName"), "someValue", status);
475 if (status != U_ILLEGAL_ARGUMENT_ERROR) {
476 errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
477 }
478 status = U_ZERO_ERROR;
479 tuple.setField(
480 NumberFormatTestTuple::getFieldByName("minIntegerDigits"), "someBadValue", status);
481 if (status != U_ILLEGAL_ARGUMENT_ERROR) {
482 errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
483 }
484}
485
486void NumberFormatDataDrivenTest::TestDataDrivenICU4C() {
487 run("numberformattestspecification.txt", TRUE);
488}
489
490#endif // !UCONFIG_NO_FORMATTING