OLD | NEW |
(Empty) | |
| 1 /******************************************************************** |
| 2 * COPYRIGHT: |
| 3 * Copyright (c) 2002-2010, International Business Machines Corporation and |
| 4 * others. All Rights Reserved. |
| 5 ********************************************************************/ |
| 6 |
| 7 // |
| 8 // dcfmtest.cpp |
| 9 // |
| 10 // Decimal Formatter tests, data driven. |
| 11 // |
| 12 |
| 13 #include "intltest.h" |
| 14 |
| 15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_REGULAR_EXPRESSIONS |
| 16 |
| 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" |
| 24 #include "cmemory.h" |
| 25 #include "dcfmtest.h" |
| 26 #include "util.h" |
| 27 #include "cstring.h" |
| 28 #include <stdlib.h> |
| 29 #include <string.h> |
| 30 #include <stdio.h> |
| 31 |
| 32 #include <string> |
| 33 #include <iostream> |
| 34 |
| 35 //--------------------------------------------------------------------------- |
| 36 // |
| 37 // Test class boilerplate |
| 38 // |
| 39 //--------------------------------------------------------------------------- |
| 40 DecimalFormatTest::DecimalFormatTest() |
| 41 { |
| 42 } |
| 43 |
| 44 |
| 45 DecimalFormatTest::~DecimalFormatTest() |
| 46 { |
| 47 } |
| 48 |
| 49 |
| 50 |
| 51 void DecimalFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &
name, char* /*par*/ ) |
| 52 { |
| 53 if (exec) logln("TestSuite DecimalFormatTest: "); |
| 54 switch (index) { |
| 55 |
| 56 #if !UCONFIG_NO_FILE_IO |
| 57 case 0: name = "DataDrivenTests"; |
| 58 if (exec) DataDrivenTests(); |
| 59 break; |
| 60 #else |
| 61 case 0: name = "skip"; |
| 62 break; |
| 63 #endif |
| 64 |
| 65 default: name = ""; |
| 66 break; //needed to end loop |
| 67 } |
| 68 } |
| 69 |
| 70 |
| 71 //--------------------------------------------------------------------------- |
| 72 // |
| 73 // Error Checking / Reporting macros used in all of the tests. |
| 74 // |
| 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;}} |
| 79 |
| 80 #define DF_ASSERT(expr) {if ((expr)==FALSE) {errln("DecimalFormatTest failure at
line %d.\n", __LINE__);};} |
| 81 |
| 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));};} |
| 85 |
| 86 #define DF_CHECK_STATUS_L(line) {if (U_FAILURE(status)) {errln( \ |
| 87 "DecimalFormatTest failure at line %d, from %d. status=%d\n",__LINE__, (lin
e), status); }} |
| 88 |
| 89 #define DF_ASSERT_L(expr, line) {if ((expr)==FALSE) { \ |
| 90 errln("DecimalFormatTest failure at line %d, from %d.", __LINE__, (line)); r
eturn;}} |
| 91 |
| 92 |
| 93 |
| 94 // |
| 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 StringP
iece |
| 98 // in general, but is true of InvariantStringPiece) |
| 99 // |
| 100 class InvariantStringPiece: public StringPiece { |
| 101 public: |
| 102 InvariantStringPiece(const UnicodeString &s); |
| 103 ~InvariantStringPiece() {}; |
| 104 private: |
| 105 MaybeStackArray<char, 20> buf; |
| 106 }; |
| 107 |
| 108 InvariantStringPiece::InvariantStringPiece(const UnicodeString &s) { |
| 109 int32_t len = s.length(); |
| 110 if (len+1 > buf.getCapacity()) { |
| 111 buf.resize(len+1); |
| 112 } |
| 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, len); |
| 116 } |
| 117 |
| 118 |
| 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 UnicodeStringP
iece) |
| 123 // |
| 124 class UnicodeStringPiece: public StringPiece { |
| 125 public: |
| 126 UnicodeStringPiece(const UnicodeString &s); |
| 127 ~UnicodeStringPiece() {}; |
| 128 private: |
| 129 MaybeStackArray<char, 20> buf; |
| 130 }; |
| 131 |
| 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); |
| 140 } |
| 141 this->set(buf, requiredCapacity - 1); |
| 142 } |
| 143 |
| 144 |
| 145 |
| 146 //--------------------------------------------------------------------------- |
| 147 // |
| 148 // DataDrivenTests |
| 149 // The test cases are in a separate data file, |
| 150 // |
| 151 //--------------------------------------------------------------------------- |
| 152 |
| 153 // Translate a Formattable::type enum value to a string, for error message forma
tting. |
| 154 static const char *formattableType(Formattable::Type typ) { |
| 155 static const char *types[] = {"kDate", |
| 156 "kDouble", |
| 157 "kLong", |
| 158 "kString", |
| 159 "kArray", |
| 160 "kInt64", |
| 161 "kObject" |
| 162 }; |
| 163 if (typ<0 || typ>Formattable::kObject) { |
| 164 return "Unknown"; |
| 165 } |
| 166 return types[typ]; |
| 167 } |
| 168 |
| 169 const char * |
| 170 DecimalFormatTest::getPath(char *buffer, const char *filename) { |
| 171 UErrorCode status=U_ZERO_ERROR; |
| 172 const char *testDataDirectory = IntlTest::getSourceTestData(status); |
| 173 DF_CHECK_STATUS; |
| 174 |
| 175 strcpy(buffer, testDataDirectory); |
| 176 strcat(buffer, filename); |
| 177 return buffer; |
| 178 } |
| 179 |
| 180 void DecimalFormatTest::DataDrivenTests() { |
| 181 char tdd[2048]; |
| 182 const char *srcPath; |
| 183 UErrorCode status = U_ZERO_ERROR; |
| 184 int32_t lineNum = 0; |
| 185 |
| 186 // |
| 187 // Open and read the test data file. |
| 188 // |
| 189 srcPath=getPath(tdd, "dcfmtest.txt"); |
| 190 if(srcPath==NULL) { |
| 191 return; /* something went wrong, error already output */ |
| 192 } |
| 193 |
| 194 int32_t len; |
| 195 UChar *testData = ReadAndConvertFile(srcPath, len, status); |
| 196 if (U_FAILURE(status)) { |
| 197 return; /* something went wrong, error already output */ |
| 198 } |
| 199 |
| 200 // |
| 201 // Put the test data into a UnicodeString |
| 202 // |
| 203 UnicodeString testString(FALSE, testData, len); |
| 204 |
| 205 RegexMatcher parseLineMat(UnicodeString( |
| 206 "(?i)\\s*parse\\s+" |
| 207 "\"([^\"]*)\"\\s+" // Capture group 1: input text |
| 208 "([ild])\\s+" // Capture group 2: expected parsed typ
e |
| 209 "\"([^\"]*)\"\\s+" // Capture group 3: expected parsed dec
imal |
| 210 "\\s*(?:#.*)?"), // Trailing comment |
| 211 0, status); |
| 212 |
| 213 RegexMatcher formatLineMat(UnicodeString( |
| 214 "(?i)\\s*format\\s+" |
| 215 "(\\S+)\\s+" // Capture group 1: pattern |
| 216 "(ceiling|floor|down|up|halfeven|halfdown|halfup|default)\\s+" // C
apture group 2: Rounding Mode |
| 217 "\"([^\"]*)\"\\s+" // Capture group 3: input |
| 218 "\"([^\"]*)\"" // Capture group 4: expected output |
| 219 "\\s*(?:#.*)?"), // Trailing comment |
| 220 0, status); |
| 221 |
| 222 RegexMatcher commentMat (UNICODE_STRING_SIMPLE("\\s*(#.*)?$"), 0, stat
us); |
| 223 RegexMatcher lineMat(UNICODE_STRING_SIMPLE("(?m)^(.*?)$"), testString, 0,
status); |
| 224 |
| 225 if (U_FAILURE(status)){ |
| 226 dataerrln("Construct RegexMatcher() error."); |
| 227 delete [] testData; |
| 228 return; |
| 229 } |
| 230 |
| 231 // |
| 232 // Loop over the test data file, once per line. |
| 233 // |
| 234 while (lineMat.find()) { |
| 235 lineNum++; |
| 236 if (U_FAILURE(status)) { |
| 237 errln("File dcfmtest.txt, line %d: ICU Error \"%s\"", lineNum, u_err
orName(status)); |
| 238 } |
| 239 |
| 240 status = U_ZERO_ERROR; |
| 241 UnicodeString testLine = lineMat.group(1, status); |
| 242 // printf("%s\n", UnicodeStringPiece(testLine).data()); |
| 243 if (testLine.length() == 0) { |
| 244 continue; |
| 245 } |
| 246 |
| 247 // |
| 248 // Parse the test line. Skip blank and comment only lines. |
| 249 // Separate out the three main fields - pattern, flags, target. |
| 250 // |
| 251 |
| 252 commentMat.reset(testLine); |
| 253 if (commentMat.lookingAt(status)) { |
| 254 // This line is a comment, or blank. |
| 255 continue; |
| 256 } |
| 257 |
| 258 |
| 259 // |
| 260 // Handle "parse" test case line from file |
| 261 // |
| 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 |
| 268 status |
| 269 ); |
| 270 continue; |
| 271 } |
| 272 |
| 273 // |
| 274 // Handle "format" test case line |
| 275 // |
| 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 n
umber |
| 282 formatLineMat.group(4, status), // expected format
ted result |
| 283 status); |
| 284 continue; |
| 285 } |
| 286 |
| 287 // |
| 288 // Line is not a recognizable test case. |
| 289 // |
| 290 errln("Badly formed test case at line %d.\n%s\n", |
| 291 lineNum, UnicodeStringPiece(testLine).data()); |
| 292 |
| 293 } |
| 294 |
| 295 delete [] testData; |
| 296 } |
| 297 |
| 298 |
| 299 |
| 300 void DecimalFormatTest::execParseTest(int32_t lineNum, |
| 301 const UnicodeString &inputText, |
| 302 const UnicodeString &expectedType, |
| 303 const UnicodeString &expectedDecimal, |
| 304 UErrorCode &status) { |
| 305 |
| 306 if (U_FAILURE(status)) { |
| 307 return; |
| 308 } |
| 309 |
| 310 DecimalFormatSymbols symbols(Locale::getUS(), status); |
| 311 UnicodeString pattern = UNICODE_STRING_SIMPLE("####"); |
| 312 DecimalFormat format(pattern, symbols, status); |
| 313 Formattable result; |
| 314 if (U_FAILURE(status)) { |
| 315 errln("file dcfmtest.txt, line %d: %s error creating the formatter.", |
| 316 lineNum, u_errorName(status)); |
| 317 return; |
| 318 } |
| 319 |
| 320 ParsePosition pos; |
| 321 int32_t expectedParseEndPosition = inputText.length(); |
| 322 |
| 323 format.parse(inputText, result, pos); |
| 324 |
| 325 if (expectedParseEndPosition != pos.getIndex()) { |
| 326 errln("file dcfmtest.txt, line %d: Expected parse position afeter parsin
g: %d. " |
| 327 "Actual parse position: %d", expectedParseEndPosition, pos.getInde
x()); |
| 328 return; |
| 329 } |
| 330 |
| 331 char expectedTypeC[2]; |
| 332 expectedType.extract(0, 1, expectedTypeC, 2, US_INV); |
| 333 Formattable::Type expectType = Formattable::kDate; |
| 334 switch (expectedTypeC[0]) { |
| 335 case 'd': expectType = Formattable::kDouble; break; |
| 336 case 'i': expectType = Formattable::kLong; break; |
| 337 case 'l': expectType = Formattable::kInt64; break; |
| 338 default: |
| 339 errln("file dcfmtest.tx, line %d: unrecongized expected type \"%s\"", |
| 340 lineNum, InvariantStringPiece(expectedType).data()); |
| 341 return; |
| 342 } |
| 343 if (result.getType() != expectType) { |
| 344 errln("file dcfmtest.txt, line %d: expectedParseType(%s) != actual parse
Type(%s)", |
| 345 lineNum, formattableType(expectType), formattableType(result.getTyp
e())); |
| 346 return; |
| 347 } |
| 348 |
| 349 StringPiece decimalResult = result.getDecimalNumber(status); |
| 350 if (U_FAILURE(status)) { |
| 351 errln("File %s, line %d: error %s. Line in file dcfmtest.txt: %d:", |
| 352 __FILE__, __LINE__, u_errorName(status), lineNum); |
| 353 return; |
| 354 } |
| 355 |
| 356 InvariantStringPiece expectedResults(expectedDecimal); |
| 357 if (decimalResult != expectedResults) { |
| 358 errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"", |
| 359 lineNum, expectedResults.data(), decimalResult.data()); |
| 360 } |
| 361 |
| 362 return; |
| 363 } |
| 364 |
| 365 |
| 366 void DecimalFormatTest::execFormatTest(int32_t lineNum, |
| 367 const UnicodeString &pattern, // Pattern |
| 368 const UnicodeString &round, // rounding mode |
| 369 const UnicodeString &input, // input decimal nu
mber |
| 370 const UnicodeString &expected, // expected formatt
ed result |
| 371 UErrorCode &status) { |
| 372 if (U_FAILURE(status)) { |
| 373 return; |
| 374 } |
| 375 |
| 376 DecimalFormatSymbols symbols(Locale::getUS(), status); |
| 377 // printf("Pattern = %s\n", UnicodeStringPiece(pattern).data()); |
| 378 DecimalFormat fmtr(pattern, symbols, status); |
| 379 if (U_FAILURE(status)) { |
| 380 errln("file dcfmtest.txt, line %d: %s error creating the formatter.", |
| 381 lineNum, u_errorName(status)); |
| 382 return; |
| 383 } |
| 384 if (round=="ceiling") { |
| 385 fmtr.setRoundingMode(DecimalFormat::kRoundCeiling); |
| 386 } else if (round=="floor") { |
| 387 fmtr.setRoundingMode(DecimalFormat::kRoundFloor); |
| 388 } else if (round=="down") { |
| 389 fmtr.setRoundingMode(DecimalFormat::kRoundDown); |
| 390 } else if (round=="up") { |
| 391 fmtr.setRoundingMode(DecimalFormat::kRoundUp); |
| 392 } else if (round=="halfeven") { |
| 393 fmtr.setRoundingMode(DecimalFormat::kRoundHalfEven); |
| 394 } else if (round=="halfdown") { |
| 395 fmtr.setRoundingMode(DecimalFormat::kRoundHalfDown); |
| 396 } else if (round=="halfup") { |
| 397 fmtr.setRoundingMode(DecimalFormat::kRoundHalfUp); |
| 398 } else if (round=="default") { |
| 399 // don't set any value. |
| 400 } else { |
| 401 fmtr.setRoundingMode(DecimalFormat::kRoundFloor); |
| 402 errln("file dcfmtest.txt, line %d: Bad rounding mode \"%s\"", |
| 403 lineNum, UnicodeStringPiece(round).data()); |
| 404 } |
| 405 |
| 406 UnicodeString result; |
| 407 UnicodeStringPiece spInput(input); |
| 408 //fmtr.format(spInput, result, NULL, status); |
| 409 |
| 410 Formattable fmtbl; |
| 411 fmtbl.setDecimalNumber(spInput, status); |
| 412 //NumberFormat &nfmtr = fmtr; |
| 413 fmtr.format(fmtbl, result, NULL, status); |
| 414 |
| 415 if (U_FAILURE(status)) { |
| 416 errln("file dcfmtest.txt, line %d: format() returned %s.", |
| 417 lineNum, u_errorName(status)); |
| 418 return; |
| 419 } |
| 420 |
| 421 if (result != expected) { |
| 422 errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"", |
| 423 lineNum, UnicodeStringPiece(expected).data(), UnicodeStringPiece(res
ult).data()); |
| 424 } |
| 425 } |
| 426 |
| 427 |
| 428 //------------------------------------------------------------------------------
- |
| 429 // |
| 430 // Read a text data file, convert it from UTF-8 to UChars, and return the data |
| 431 // in one big UChar * buffer, which the caller must delete. |
| 432 // |
| 433 // (Lightly modified version of a similar function in regextst.cpp) |
| 434 // |
| 435 //------------------------------------------------------------------------------
-- |
| 436 UChar *DecimalFormatTest::ReadAndConvertFile(const char *fileName, int32_t &ulen
, |
| 437 UErrorCode &status) { |
| 438 UChar *retPtr = NULL; |
| 439 char *fileBuf = NULL; |
| 440 const char *fileBufNoBOM = NULL; |
| 441 FILE *f = NULL; |
| 442 |
| 443 ulen = 0; |
| 444 if (U_FAILURE(status)) { |
| 445 return retPtr; |
| 446 } |
| 447 |
| 448 // |
| 449 // Open the file. |
| 450 // |
| 451 f = fopen(fileName, "rb"); |
| 452 if (f == 0) { |
| 453 dataerrln("Error opening test data file %s\n", fileName); |
| 454 status = U_FILE_ACCESS_ERROR; |
| 455 return NULL; |
| 456 } |
| 457 // |
| 458 // Read it in |
| 459 // |
| 460 int32_t fileSize; |
| 461 int32_t amtRead; |
| 462 int32_t amtReadNoBOM; |
| 463 |
| 464 fseek( f, 0, SEEK_END); |
| 465 fileSize = ftell(f); |
| 466 fileBuf = new char[fileSize]; |
| 467 fseek(f, 0, SEEK_SET); |
| 468 amtRead = fread(fileBuf, 1, fileSize, f); |
| 469 if (amtRead != fileSize || fileSize <= 0) { |
| 470 errln("Error reading test data file."); |
| 471 goto cleanUpAndReturn; |
| 472 } |
| 473 |
| 474 // |
| 475 // Look for a UTF-8 BOM on the data just read. |
| 476 // The test data file is UTF-8. |
| 477 // The BOM needs to be there in the source file to keep the Windows & |
| 478 // EBCDIC machines happy, so force an error if it goes missing. |
| 479 // Many Linux editors will silently strip it. |
| 480 // |
| 481 fileBufNoBOM = fileBuf + 3; |
| 482 amtReadNoBOM = amtRead - 3; |
| 483 if (fileSize<3 || uprv_strncmp(fileBuf, "\xEF\xBB\xBF", 3) != 0) { |
| 484 // TODO: restore this check. |
| 485 errln("Test data file %s is missing its BOM", fileName); |
| 486 fileBufNoBOM = fileBuf; |
| 487 amtReadNoBOM = amtRead; |
| 488 } |
| 489 |
| 490 // |
| 491 // Find the length of the input in UTF-16 UChars |
| 492 // (by preflighting the conversion) |
| 493 // |
| 494 u_strFromUTF8(NULL, 0, &ulen, fileBufNoBOM, amtReadNoBOM, &status); |
| 495 |
| 496 // |
| 497 // Convert file contents from UTF-8 to UTF-16 |
| 498 // |
| 499 if (status == U_BUFFER_OVERFLOW_ERROR) { |
| 500 // Buffer Overflow is expected from the preflight operation. |
| 501 status = U_ZERO_ERROR; |
| 502 retPtr = new UChar[ulen+1]; |
| 503 u_strFromUTF8(retPtr, ulen+1, NULL, fileBufNoBOM, amtReadNoBOM, &status)
; |
| 504 } |
| 505 |
| 506 cleanUpAndReturn: |
| 507 fclose(f); |
| 508 delete[] fileBuf; |
| 509 if (U_FAILURE(status)) { |
| 510 errln("ICU Error \"%s\"\n", u_errorName(status)); |
| 511 delete retPtr; |
| 512 retPtr = NULL; |
| 513 }; |
| 514 return retPtr; |
| 515 } |
| 516 |
| 517 #endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */ |
| 518 |
OLD | NEW |