1 /********************************************************************
3 * Copyright (c) 2002-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
10 // Decimal Formatter tests, data driven.
15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_REGULAR_EXPRESSIONS
17 #include "unicode/regex.h"
18 #include "unicode/uchar.h"
19 #include "unicode/ustring.h"
20 #include "unicode/unistr.h"
21 #include "unicode/dcfmtsym.h"
22 #include "unicode/decimfmt.h"
23 #include "unicode/locid.h"
35 //---------------------------------------------------------------------------
37 // Test class boilerplate
39 //---------------------------------------------------------------------------
40 DecimalFormatTest::DecimalFormatTest()
45 DecimalFormatTest::~DecimalFormatTest()
51 void DecimalFormatTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
53 if (exec
) logln("TestSuite DecimalFormatTest: ");
56 #if !UCONFIG_NO_FILE_IO
57 case 0: name
= "DataDrivenTests";
58 if (exec
) DataDrivenTests();
61 case 0: name
= "skip";
66 break; //needed to end loop
71 //---------------------------------------------------------------------------
73 // Error Checking / Reporting macros used in all of the tests.
75 //---------------------------------------------------------------------------
76 #define DF_CHECK_STATUS {if (U_FAILURE(status)) \
77 {dataerrln("DecimalFormatTest failure at line %d. status=%s", \
78 __LINE__, u_errorName(status)); return 0;}}
80 #define DF_ASSERT(expr) {if ((expr)==FALSE) {errln("DecimalFormatTest failure at line %d.\n", __LINE__);};}
82 #define DF_ASSERT_FAIL(expr, errcode) {UErrorCode status=U_ZERO_ERROR; (expr);\
83 if (status!=errcode) {dataerrln("DecimalFormatTest failure at line %d. Expected status=%s, got %s", \
84 __LINE__, u_errorName(errcode), u_errorName(status));};}
86 #define DF_CHECK_STATUS_L(line) {if (U_FAILURE(status)) {errln( \
87 "DecimalFormatTest failure at line %d, from %d. status=%d\n",__LINE__, (line), status); }}
89 #define DF_ASSERT_L(expr, line) {if ((expr)==FALSE) { \
90 errln("DecimalFormatTest failure at line %d, from %d.", __LINE__, (line)); return;}}
95 // InvariantStringPiece
96 // Wrap a StringPiece around the extracted invariant data of a UnicodeString.
97 // The data is guaranteed to be nul terminated. (This is not true of StringPiece
98 // in general, but is true of InvariantStringPiece)
100 class InvariantStringPiece
: public StringPiece
{
102 InvariantStringPiece(const UnicodeString
&s
);
103 ~InvariantStringPiece() {};
105 MaybeStackArray
<char, 20> buf
;
108 InvariantStringPiece::InvariantStringPiece(const UnicodeString
&s
) {
109 int32_t len
= s
.length();
110 if (len
+1 > buf
.getCapacity()) {
113 // Buffer size is len+1 so that s.extract() will nul-terminate the string.
114 s
.extract(0, len
, buf
.getAlias(), len
+1, US_INV
);
115 this->set(buf
.getAlias(), len
);
119 // UnicodeStringPiece
120 // Wrap a StringPiece around the extracted (to the default charset) data of
121 // a UnicodeString. The extracted data is guaranteed to be nul terminated.
122 // (This is not true of StringPiece in general, but is true of UnicodeStringPiece)
124 class UnicodeStringPiece
: public StringPiece
{
126 UnicodeStringPiece(const UnicodeString
&s
);
127 ~UnicodeStringPiece() {};
129 MaybeStackArray
<char, 20> buf
;
132 UnicodeStringPiece::UnicodeStringPiece(const UnicodeString
&s
) {
133 int32_t len
= s
.length();
134 int32_t capacity
= buf
.getCapacity();
135 int32_t requiredCapacity
= s
.extract(0, len
, buf
.getAlias(), capacity
) + 1;
136 if (capacity
< requiredCapacity
) {
137 buf
.resize(requiredCapacity
);
138 capacity
= requiredCapacity
;
139 s
.extract(0, len
, buf
.getAlias(), capacity
);
141 this->set(buf
.getAlias(), requiredCapacity
- 1);
146 //---------------------------------------------------------------------------
149 // The test cases are in a separate data file,
151 //---------------------------------------------------------------------------
153 // Translate a Formattable::type enum value to a string, for error message formatting.
154 static const char *formattableType(Formattable::Type typ
) {
155 static const char *types
[] = {"kDate",
163 if (typ
<0 || typ
>Formattable::kObject
) {
170 DecimalFormatTest::getPath(char *buffer
, const char *filename
) {
171 UErrorCode status
=U_ZERO_ERROR
;
172 const char *testDataDirectory
= IntlTest::getSourceTestData(status
);
175 strcpy(buffer
, testDataDirectory
);
176 strcat(buffer
, filename
);
180 void DecimalFormatTest::DataDrivenTests() {
183 UErrorCode status
= U_ZERO_ERROR
;
187 // Open and read the test data file.
189 srcPath
=getPath(tdd
, "dcfmtest.txt");
191 return; /* something went wrong, error already output */
195 UChar
*testData
= ReadAndConvertFile(srcPath
, len
, status
);
196 if (U_FAILURE(status
)) {
197 return; /* something went wrong, error already output */
201 // Put the test data into a UnicodeString
203 UnicodeString
testString(FALSE
, testData
, len
);
205 RegexMatcher
parseLineMat(UnicodeString(
207 "\"([^\"]*)\"\\s+" // Capture group 1: input text
208 "([ild])\\s+" // Capture group 2: expected parsed type
209 "\"([^\"]*)\"\\s+" // Capture group 3: expected parsed decimal
210 "\\s*(?:#.*)?"), // Trailing comment
213 RegexMatcher
formatLineMat(UnicodeString(
215 "(\\S+)\\s+" // Capture group 1: pattern
216 "(ceiling|floor|down|up|halfeven|halfdown|halfup|default|unnecessary)\\s+" // Capture group 2: Rounding Mode
217 "\"([^\"]*)\"\\s+" // Capture group 3: input
218 "\"([^\"]*)\"" // Capture group 4: expected output
219 "\\s*(?:#.*)?"), // Trailing comment
222 RegexMatcher
commentMat (UNICODE_STRING_SIMPLE("\\s*(#.*)?$"), 0, status
);
223 RegexMatcher
lineMat(UNICODE_STRING_SIMPLE("(?m)^(.*?)$"), testString
, 0, status
);
225 if (U_FAILURE(status
)){
226 dataerrln("Construct RegexMatcher() error.");
232 // Loop over the test data file, once per line.
234 while (lineMat
.find()) {
236 if (U_FAILURE(status
)) {
237 dataerrln("File dcfmtest.txt, line %d: ICU Error \"%s\"", lineNum
, u_errorName(status
));
240 status
= U_ZERO_ERROR
;
241 UnicodeString testLine
= lineMat
.group(1, status
);
242 // printf("%s\n", UnicodeStringPiece(testLine).data());
243 if (testLine
.length() == 0) {
248 // Parse the test line. Skip blank and comment only lines.
249 // Separate out the three main fields - pattern, flags, target.
252 commentMat
.reset(testLine
);
253 if (commentMat
.lookingAt(status
)) {
254 // This line is a comment, or blank.
260 // Handle "parse" test case line from file
262 parseLineMat
.reset(testLine
);
263 if (parseLineMat
.lookingAt(status
)) {
264 execParseTest(lineNum
,
265 parseLineMat
.group(1, status
), // input
266 parseLineMat
.group(2, status
), // Expected Type
267 parseLineMat
.group(3, status
), // Expected Decimal String
274 // Handle "format" test case line
276 formatLineMat
.reset(testLine
);
277 if (formatLineMat
.lookingAt(status
)) {
278 execFormatTest(lineNum
,
279 formatLineMat
.group(1, status
), // Pattern
280 formatLineMat
.group(2, status
), // rounding mode
281 formatLineMat
.group(3, status
), // input decimal number
282 formatLineMat
.group(4, status
), // expected formatted result
286 execFormatTest(lineNum
,
287 formatLineMat
.group(1, status
), // Pattern
288 formatLineMat
.group(2, status
), // rounding mode
289 formatLineMat
.group(3, status
), // input decimal number
290 formatLineMat
.group(4, status
), // expected formatted result
297 // Line is not a recognizable test case.
299 errln("Badly formed test case at line %d.\n%s\n",
300 lineNum
, UnicodeStringPiece(testLine
).data());
309 void DecimalFormatTest::execParseTest(int32_t lineNum
,
310 const UnicodeString
&inputText
,
311 const UnicodeString
&expectedType
,
312 const UnicodeString
&expectedDecimal
,
313 UErrorCode
&status
) {
315 if (U_FAILURE(status
)) {
319 DecimalFormatSymbols
symbols(Locale::getUS(), status
);
320 UnicodeString pattern
= UNICODE_STRING_SIMPLE("####");
321 DecimalFormat
format(pattern
, symbols
, status
);
323 if (U_FAILURE(status
)) {
324 dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.",
325 lineNum
, u_errorName(status
));
330 int32_t expectedParseEndPosition
= inputText
.length();
332 format
.parse(inputText
, result
, pos
);
334 if (expectedParseEndPosition
!= pos
.getIndex()) {
335 errln("file dcfmtest.txt, line %d: Expected parse position afeter parsing: %d. "
336 "Actual parse position: %d", expectedParseEndPosition
, pos
.getIndex());
340 char expectedTypeC
[2];
341 expectedType
.extract(0, 1, expectedTypeC
, 2, US_INV
);
342 Formattable::Type expectType
= Formattable::kDate
;
343 switch (expectedTypeC
[0]) {
344 case 'd': expectType
= Formattable::kDouble
; break;
345 case 'i': expectType
= Formattable::kLong
; break;
346 case 'l': expectType
= Formattable::kInt64
; break;
348 errln("file dcfmtest.tx, line %d: unrecongized expected type \"%s\"",
349 lineNum
, InvariantStringPiece(expectedType
).data());
352 if (result
.getType() != expectType
) {
353 errln("file dcfmtest.txt, line %d: expectedParseType(%s) != actual parseType(%s)",
354 lineNum
, formattableType(expectType
), formattableType(result
.getType()));
358 StringPiece decimalResult
= result
.getDecimalNumber(status
);
359 if (U_FAILURE(status
)) {
360 errln("File %s, line %d: error %s. Line in file dcfmtest.txt: %d:",
361 __FILE__
, __LINE__
, u_errorName(status
), lineNum
);
365 InvariantStringPiece
expectedResults(expectedDecimal
);
366 if (decimalResult
!= expectedResults
) {
367 errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"",
368 lineNum
, expectedResults
.data(), decimalResult
.data());
375 void DecimalFormatTest::execFormatTest(int32_t lineNum
,
376 const UnicodeString
&pattern
, // Pattern
377 const UnicodeString
&round
, // rounding mode
378 const UnicodeString
&input
, // input decimal number
379 const UnicodeString
&expected
, // expected formatted result
380 EFormatInputType inType
, // input number type
381 UErrorCode
&status
) {
382 if (U_FAILURE(status
)) {
386 DecimalFormatSymbols
symbols(Locale::getUS(), status
);
387 // printf("Pattern = %s\n", UnicodeStringPiece(pattern).data());
388 DecimalFormat
fmtr(pattern
, symbols
, status
);
389 if (U_FAILURE(status
)) {
390 dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.",
391 lineNum
, u_errorName(status
));
394 if (round
=="ceiling") {
395 fmtr
.setRoundingMode(DecimalFormat::kRoundCeiling
);
396 } else if (round
=="floor") {
397 fmtr
.setRoundingMode(DecimalFormat::kRoundFloor
);
398 } else if (round
=="down") {
399 fmtr
.setRoundingMode(DecimalFormat::kRoundDown
);
400 } else if (round
=="up") {
401 fmtr
.setRoundingMode(DecimalFormat::kRoundUp
);
402 } else if (round
=="halfeven") {
403 fmtr
.setRoundingMode(DecimalFormat::kRoundHalfEven
);
404 } else if (round
=="halfdown") {
405 fmtr
.setRoundingMode(DecimalFormat::kRoundHalfDown
);
406 } else if (round
=="halfup") {
407 fmtr
.setRoundingMode(DecimalFormat::kRoundHalfUp
);
408 } else if (round
=="default") {
409 // don't set any value.
410 } else if (round
=="unnecessary") {
411 fmtr
.setRoundingMode(DecimalFormat::kRoundUnnecessary
);
413 fmtr
.setRoundingMode(DecimalFormat::kRoundFloor
);
414 errln("file dcfmtest.txt, line %d: Bad rounding mode \"%s\"",
415 lineNum
, UnicodeStringPiece(round
).data());
419 UnicodeString result
;
420 UnicodeStringPiece
spInput(input
);
425 typeStr
= "Formattable";
427 fmtbl
.setDecimalNumber(spInput
, status
);
428 fmtr
.format(fmtbl
, result
, NULL
, status
);
432 typeStr
= "StringPiece";
433 fmtr
.format(spInput
, result
, NULL
, status
);
437 if ((status
== U_FORMAT_INEXACT_ERROR
) && (result
== "") && (expected
== "Inexact")) {
439 status
= U_ZERO_ERROR
;
443 if (U_FAILURE(status
)) {
444 errln("[%s] file dcfmtest.txt, line %d: format() returned %s.",
445 typeStr
, lineNum
, u_errorName(status
));
446 status
= U_ZERO_ERROR
;
450 if (result
!= expected
) {
451 errln("[%s] file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"",
452 typeStr
, lineNum
, UnicodeStringPiece(expected
).data(), UnicodeStringPiece(result
).data());
457 //-------------------------------------------------------------------------------
459 // Read a text data file, convert it from UTF-8 to UChars, and return the data
460 // in one big UChar * buffer, which the caller must delete.
462 // (Lightly modified version of a similar function in regextst.cpp)
464 //--------------------------------------------------------------------------------
465 UChar
*DecimalFormatTest::ReadAndConvertFile(const char *fileName
, int32_t &ulen
,
466 UErrorCode
&status
) {
467 UChar
*retPtr
= NULL
;
468 char *fileBuf
= NULL
;
469 const char *fileBufNoBOM
= NULL
;
473 if (U_FAILURE(status
)) {
480 f
= fopen(fileName
, "rb");
482 dataerrln("Error opening test data file %s\n", fileName
);
483 status
= U_FILE_ACCESS_ERROR
;
491 int32_t amtReadNoBOM
;
493 fseek( f
, 0, SEEK_END
);
495 fileBuf
= new char[fileSize
];
496 fseek(f
, 0, SEEK_SET
);
497 amtRead
= fread(fileBuf
, 1, fileSize
, f
);
498 if (amtRead
!= fileSize
|| fileSize
<= 0) {
499 errln("Error reading test data file.");
500 goto cleanUpAndReturn
;
504 // Look for a UTF-8 BOM on the data just read.
505 // The test data file is UTF-8.
506 // The BOM needs to be there in the source file to keep the Windows &
507 // EBCDIC machines happy, so force an error if it goes missing.
508 // Many Linux editors will silently strip it.
510 fileBufNoBOM
= fileBuf
+ 3;
511 amtReadNoBOM
= amtRead
- 3;
512 if (fileSize
<3 || uprv_strncmp(fileBuf
, "\xEF\xBB\xBF", 3) != 0) {
513 // TODO: restore this check.
514 errln("Test data file %s is missing its BOM", fileName
);
515 fileBufNoBOM
= fileBuf
;
516 amtReadNoBOM
= amtRead
;
520 // Find the length of the input in UTF-16 UChars
521 // (by preflighting the conversion)
523 u_strFromUTF8(NULL
, 0, &ulen
, fileBufNoBOM
, amtReadNoBOM
, &status
);
526 // Convert file contents from UTF-8 to UTF-16
528 if (status
== U_BUFFER_OVERFLOW_ERROR
) {
529 // Buffer Overflow is expected from the preflight operation.
530 status
= U_ZERO_ERROR
;
531 retPtr
= new UChar
[ulen
+1];
532 u_strFromUTF8(retPtr
, ulen
+1, NULL
, fileBufNoBOM
, amtReadNoBOM
, &status
);
538 if (U_FAILURE(status
)) {
539 errln("ICU Error \"%s\"\n", u_errorName(status
));
546 #endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */