1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
10 #include "number_decimalquantity.h"
15 void NumberFormatDataDrivenTest::runIndexedTest(int32_t index
, UBool exec
, const char*& name
, char*) {
17 logln("TestSuite NumberFormatDataDrivenTest: ");
20 TESTCASE_AUTO(TestNumberFormatTestTuple
);
21 TESTCASE_AUTO(TestDataDrivenICU4C
);
26 strToDigitList(const UnicodeString
& str
, DigitList
& digitList
, UErrorCode
& status
) {
27 if (U_FAILURE(status
)) {
31 digitList
.set(uprv_getNaN());
35 digitList
.set(-1 * uprv_getInfinity());
39 digitList
.set(uprv_getInfinity());
42 CharString formatValue
;
43 formatValue
.appendInvariantChars(str
, status
);
44 digitList
.set({formatValue
.data(), formatValue
.length()}, status
);
49 format(const DecimalFormat
& fmt
, const DigitList
& digitList
, UnicodeString
& appendTo
,
51 if (U_FAILURE(status
)) {
54 FieldPosition
fpos(FieldPosition::DONT_CARE
);
55 return fmt
.format(digitList
, appendTo
, fpos
, status
);
60 format(const DecimalFormat
& fmt
, T value
, UnicodeString
& appendTo
, UErrorCode
& status
) {
61 if (U_FAILURE(status
)) {
64 FieldPosition
fpos(FieldPosition::DONT_CARE
);
65 return fmt
.format(value
, appendTo
, fpos
, status
);
68 static void adjustDecimalFormat(const NumberFormatTestTuple
& tuple
, DecimalFormat
& fmt
,
69 UnicodeString
& appendErrorMessage
) {
70 if (tuple
.minIntegerDigitsFlag
) {
71 fmt
.setMinimumIntegerDigits(tuple
.minIntegerDigits
);
73 if (tuple
.maxIntegerDigitsFlag
) {
74 fmt
.setMaximumIntegerDigits(tuple
.maxIntegerDigits
);
76 if (tuple
.minFractionDigitsFlag
) {
77 fmt
.setMinimumFractionDigits(tuple
.minFractionDigits
);
79 if (tuple
.maxFractionDigitsFlag
) {
80 fmt
.setMaximumFractionDigits(tuple
.maxFractionDigits
);
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.");
91 if (tuple
.minGroupingDigitsFlag
) {
92 fmt
.setMinimumGroupingDigits(tuple
.minGroupingDigits
);
94 if (tuple
.useSigDigitsFlag
) {
95 fmt
.setSignificantDigitsUsed(tuple
.useSigDigits
!= 0);
97 if (tuple
.minSigDigitsFlag
) {
98 fmt
.setMinimumSignificantDigits(tuple
.minSigDigits
);
100 if (tuple
.maxSigDigitsFlag
) {
101 fmt
.setMaximumSignificantDigits(tuple
.maxSigDigits
);
103 if (tuple
.useGroupingFlag
) {
104 fmt
.setGroupingUsed(tuple
.useGrouping
!= 0);
106 if (tuple
.multiplierFlag
) {
107 fmt
.setMultiplier(tuple
.multiplier
);
109 if (tuple
.roundingIncrementFlag
) {
110 fmt
.setRoundingIncrement(tuple
.roundingIncrement
);
112 if (tuple
.formatWidthFlag
) {
113 fmt
.setFormatWidth(tuple
.formatWidth
);
115 if (tuple
.padCharacterFlag
) {
116 fmt
.setPadCharacter(tuple
.padCharacter
);
118 if (tuple
.useScientificFlag
) {
119 fmt
.setScientificNotation(tuple
.useScientific
!= 0);
121 if (tuple
.groupingFlag
) {
122 fmt
.setGroupingSize(tuple
.grouping
);
124 if (tuple
.grouping2Flag
) {
125 fmt
.setSecondaryGroupingSize(tuple
.grouping2
);
127 if (tuple
.roundingModeFlag
) {
128 fmt
.setRoundingMode(tuple
.roundingMode
);
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.");
137 if (tuple
.minimumExponentDigitsFlag
) {
138 fmt
.setMinimumExponentDigits(tuple
.minimumExponentDigits
);
140 if (tuple
.exponentSignAlwaysShownFlag
) {
141 fmt
.setExponentSignAlwaysShown(tuple
.exponentSignAlwaysShown
!= 0);
143 if (tuple
.decimalSeparatorAlwaysShownFlag
) {
144 fmt
.setDecimalSeparatorAlwaysShown(
145 tuple
.decimalSeparatorAlwaysShown
!= 0);
147 if (tuple
.padPositionFlag
) {
148 fmt
.setPadPosition(tuple
.padPosition
);
150 if (tuple
.positivePrefixFlag
) {
151 fmt
.setPositivePrefix(tuple
.positivePrefix
);
153 if (tuple
.positiveSuffixFlag
) {
154 fmt
.setPositiveSuffix(tuple
.positiveSuffix
);
156 if (tuple
.negativePrefixFlag
) {
157 fmt
.setNegativePrefix(tuple
.negativePrefix
);
159 if (tuple
.negativeSuffixFlag
) {
160 fmt
.setNegativeSuffix(tuple
.negativeSuffix
);
162 if (tuple
.signAlwaysShownFlag
) { // ICU 61 DecimalFormat does not have this, skip tests that need this
163 // fmt.setSignAlwaysShown(tuple.signAlwaysShown != 0);
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.");
172 fmt
.setLenient(NFTT_GET_FIELD(tuple
, lenient
, 1) != 0);
173 if (tuple
.parseIntegerOnlyFlag
) {
174 fmt
.setParseIntegerOnly(tuple
.parseIntegerOnly
!= 0);
176 if (tuple
.decimalPatternMatchRequiredFlag
) {
177 fmt
.setDecimalPatternMatchRequired(
178 tuple
.decimalPatternMatchRequired
!= 0);
180 if (tuple
.parseNoExponentFlag
) {
181 UErrorCode status
= U_ZERO_ERROR
;
183 UNUM_PARSE_NO_EXPONENT
, tuple
.parseNoExponent
, status
);
184 if (U_FAILURE(status
)) {
185 appendErrorMessage
.append("Error setting parse no exponent flag.");
188 if (tuple
.parseCaseSensitiveFlag
) { // ICU 61 DecimalFormat does not have this, skip tests that need this
189 // fmt.setParseCaseSensitive(tuple.parseCaseSensitive != 0);
193 static DecimalFormat
*
194 newDecimalFormat(const Locale
& locale
, const UnicodeString
& pattern
, UErrorCode
& status
) {
195 if (U_FAILURE(status
)) {
198 LocalPointer
<DecimalFormatSymbols
> symbols(
199 new DecimalFormatSymbols(locale
, status
), status
);
200 if (U_FAILURE(status
)) {
204 LocalPointer
<DecimalFormat
> result(
206 pattern
, symbols
.getAlias(), perror
, status
), status
);
207 if (!result
.isNull()) {
210 if (U_FAILURE(status
)) {
213 return result
.orphan();
216 static DecimalFormat
* newDecimalFormat(const NumberFormatTestTuple
& tuple
, UErrorCode
& status
) {
217 if (U_FAILURE(status
)) {
221 return newDecimalFormat(NFTT_GET_FIELD(tuple
, locale
, en
),
222 NFTT_GET_FIELD(tuple
, pattern
, "0"),
226 UBool
NumberFormatDataDrivenTest::isFormatPass(const NumberFormatTestTuple
& tuple
,
227 UnicodeString
& appendErrorMessage
, UErrorCode
& status
) {
228 if (U_FAILURE(status
)) {
231 LocalPointer
<DecimalFormat
> fmtPtr(newDecimalFormat(tuple
, status
));
232 if (U_FAILURE(status
)) {
233 appendErrorMessage
.append("Error creating DecimalFormat.");
236 adjustDecimalFormat(tuple
, *fmtPtr
, appendErrorMessage
);
237 if (appendErrorMessage
.length() > 0) {
241 strToDigitList(tuple
.format
, digitList
, status
);
243 UnicodeString appendTo
;
244 format(*fmtPtr
, digitList
, appendTo
, status
);
245 if (U_FAILURE(status
)) {
246 appendErrorMessage
.append("Error formatting.");
249 if (appendTo
!= tuple
.output
) {
250 appendErrorMessage
.append(
251 UnicodeString("Expected: ") + tuple
.output
+ ", got: " + appendTo
);
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.");
265 if (appendTo
!= tuple
.output
) {
266 appendErrorMessage
.append(
267 UnicodeString("double Expected: ") + tuple
.output
+ ", got: " + appendTo
);
271 if (!uprv_isNaN(doubleVal
) && !uprv_isInfinite(doubleVal
) && digitList
.fitsIntoInt64(FALSE
)) {
272 int64_t intVal
= digitList
.getInt64();
274 UnicodeString appendTo
;
275 format(*fmtPtr
, intVal
, appendTo
, status
);
276 if (U_FAILURE(status
)) {
277 appendErrorMessage
.append("Error formatting.");
280 if (appendTo
!= tuple
.output
) {
281 appendErrorMessage
.append(
282 UnicodeString("int64 Expected: ") + tuple
.output
+ ", got: " + appendTo
);
290 UBool
NumberFormatDataDrivenTest::isToPatternPass(const NumberFormatTestTuple
& tuple
,
291 UnicodeString
& appendErrorMessage
, UErrorCode
& status
) {
292 if (U_FAILURE(status
)) {
295 LocalPointer
<DecimalFormat
> fmtPtr(newDecimalFormat(tuple
, status
));
296 if (U_FAILURE(status
)) {
297 appendErrorMessage
.append("Error creating DecimalFormat.");
300 adjustDecimalFormat(tuple
, *fmtPtr
, appendErrorMessage
);
301 if (appendErrorMessage
.length() > 0) {
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
+ ". ");
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
+ ". ");
320 return appendErrorMessage
.length() == 0;
323 UBool
NumberFormatDataDrivenTest::isParsePass(const NumberFormatTestTuple
& tuple
,
324 UnicodeString
& appendErrorMessage
, UErrorCode
& status
) {
325 if (U_FAILURE(status
)) {
328 LocalPointer
<DecimalFormat
> fmtPtr(newDecimalFormat(tuple
, status
));
329 if (U_FAILURE(status
)) {
330 appendErrorMessage
.append("Error creating DecimalFormat.");
333 adjustDecimalFormat(tuple
, *fmtPtr
, appendErrorMessage
);
334 if (appendErrorMessage
.length() > 0) {
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();
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
350 if (tuple
.output
== "NaN") {
351 if (!uprv_isNaN(result
.getDouble())) {
352 appendErrorMessage
.append(UnicodeString("Expected NaN, but got: ") + result
.getDouble());
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());
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());
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());
375 // All other cases parse to a DigitList, not a double.
378 strToDigitList(tuple
.output
, expected
, status
);
379 if (U_FAILURE(status
)) {
380 appendErrorMessage
.append("[Error parsing decnumber] ");
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() + ")");
392 UBool
NumberFormatDataDrivenTest::isParseCurrencyPass(const NumberFormatTestTuple
& tuple
,
393 UnicodeString
& appendErrorMessage
,
394 UErrorCode
& status
) {
395 if (U_FAILURE(status
)) {
398 LocalPointer
<DecimalFormat
> fmtPtr(newDecimalFormat(tuple
, status
));
399 if (U_FAILURE(status
)) {
400 appendErrorMessage
.append("Error creating DecimalFormat.");
403 adjustDecimalFormat(tuple
, *fmtPtr
, appendErrorMessage
);
404 if (appendErrorMessage
.length() > 0) {
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();
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
424 strToDigitList(tuple
.output
, expected
, status
);
425 if (U_FAILURE(status
)) {
426 appendErrorMessage
.append("Error parsing decnumber");
429 if (expected
!= *currAmt
->getNumber().getDigitList()) {
430 appendErrorMessage
.append(UnicodeString("Expected: ") + tuple
.output
+ ", but got: " + resultStr
+ " (" + ppos
.getIndex() + ":" + ppos
.getErrorIndex() + ")");
434 if (currStr
!= tuple
.outputCurrency
) {
435 appendErrorMessage
.append(UnicodeString(
436 "Expected currency: ") + tuple
.outputCurrency
+ ", got: " + currStr
+ ". ");
442 void NumberFormatDataDrivenTest::TestNumberFormatTestTuple() {
443 NumberFormatTestTuple tuple
;
444 UErrorCode status
= U_ZERO_ERROR
;
447 NumberFormatTestTuple::getFieldByName("locale"), "en", status
);
449 NumberFormatTestTuple::getFieldByName("pattern"), "#,##0.00", status
);
451 NumberFormatTestTuple::getFieldByName("minIntegerDigits"), "-10", status
);
452 if (!assertSuccess("", status
)) {
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
);
465 UnicodeString appendTo
;
467 "", "{locale: en, pattern: #,##0.00, minIntegerDigits: -10}", tuple
.toString(appendTo
));
472 "", "{}", tuple
.toString(appendTo
));
474 NumberFormatTestTuple::getFieldByName("aBadFieldName"), "someValue", status
);
475 if (status
!= U_ILLEGAL_ARGUMENT_ERROR
) {
476 errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
478 status
= U_ZERO_ERROR
;
480 NumberFormatTestTuple::getFieldByName("minIntegerDigits"), "someBadValue", status
);
481 if (status
!= U_ILLEGAL_ARGUMENT_ERROR
) {
482 errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
486 void NumberFormatDataDrivenTest::TestDataDrivenICU4C() {
487 run("numberformattestspecification.txt", TRUE
);
490 #endif // !UCONFIG_NO_FORMATTING