Home | History | Annotate | Download | only in intltest
      1 /********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 2002-2012, 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__, (line), status); }}
     88 
     89 #define DF_ASSERT_L(expr, line) {if ((expr)==FALSE) { \
     90     errln("DecimalFormatTest failure at line %d, from %d.", __LINE__, (line)); return;}}
     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 StringPiece
     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.getAlias(), 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 UnicodeStringPiece)
    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.getAlias(), 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 formatting.
    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 type
    209             "\"([^\"]*)\"\\s+"           // Capture group 3: expected parsed decimal
    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|unnecessary)\\s+"  // Capture 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, status);
    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             dataerrln("File dcfmtest.txt, line %d: ICU Error \"%s\"", lineNum, u_errorName(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 number
    282                            formatLineMat.group(4, status),    // expected formatted result
    283                            kFormattable,
    284                            status);
    285 
    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
    291                            kStringPiece,
    292                            status);
    293             continue;
    294         }
    295 
    296         //
    297         //  Line is not a recognizable test case.
    298         //
    299         errln("Badly formed test case at line %d.\n%s\n",
    300              lineNum, UnicodeStringPiece(testLine).data());
    301 
    302     }
    303 
    304     delete [] testData;
    305 }
    306 
    307 
    308 
    309 void DecimalFormatTest::execParseTest(int32_t lineNum,
    310                                      const UnicodeString &inputText,
    311                                      const UnicodeString &expectedType,
    312                                      const UnicodeString &expectedDecimal,
    313                                      UErrorCode &status) {
    314 
    315     if (U_FAILURE(status)) {
    316         return;
    317     }
    318 
    319     DecimalFormatSymbols symbols(Locale::getUS(), status);
    320     UnicodeString pattern = UNICODE_STRING_SIMPLE("####");
    321     DecimalFormat format(pattern, symbols, status);
    322     Formattable   result;
    323     if (U_FAILURE(status)) {
    324         dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.",
    325             lineNum, u_errorName(status));
    326         return;
    327     }
    328 
    329     ParsePosition pos;
    330     int32_t expectedParseEndPosition = inputText.length();
    331 
    332     format.parse(inputText, result, pos);
    333 
    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());
    337         return;
    338     }
    339 
    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;
    347       default:
    348           errln("file dcfmtest.tx, line %d: unrecongized expected type \"%s\"",
    349               lineNum, InvariantStringPiece(expectedType).data());
    350           return;
    351     }
    352     if (result.getType() != expectType) {
    353         errln("file dcfmtest.txt, line %d: expectedParseType(%s) != actual parseType(%s)",
    354              lineNum, formattableType(expectType), formattableType(result.getType()));
    355         return;
    356     }
    357 
    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);
    362         return;
    363     }
    364 
    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());
    369     }
    370 
    371     return;
    372 }
    373 
    374 
    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)) {
    383         return;
    384     }
    385 
    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));
    392         return;
    393     }
    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);
    412     } else {
    413         fmtr.setRoundingMode(DecimalFormat::kRoundFloor);
    414         errln("file dcfmtest.txt, line %d: Bad rounding mode \"%s\"",
    415                 lineNum, UnicodeStringPiece(round).data());
    416     }
    417 
    418     const char *typeStr;
    419     UnicodeString result;
    420     UnicodeStringPiece spInput(input);
    421 
    422     switch (inType) {
    423     case kFormattable:
    424         {
    425             typeStr = "Formattable";
    426             Formattable fmtbl;
    427             fmtbl.setDecimalNumber(spInput, status);
    428             fmtr.format(fmtbl, result, NULL, status);
    429         }
    430         break;
    431     case kStringPiece:
    432         typeStr = "StringPiece";
    433         fmtr.format(spInput, result, NULL, status);
    434         break;
    435     }
    436 
    437     if ((status == U_FORMAT_INEXACT_ERROR) && (result == "") && (expected == "Inexact")) {
    438         // Test succeeded.
    439         status = U_ZERO_ERROR;
    440         return;
    441     }
    442 
    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;
    447         return;
    448     }
    449 
    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());
    453     }
    454 }
    455 
    456 
    457 //-------------------------------------------------------------------------------
    458 //
    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.
    461 //
    462 //    (Lightly modified version of a similar function in regextst.cpp)
    463 //
    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;
    470     FILE        *f       = NULL;
    471 
    472     ulen = 0;
    473     if (U_FAILURE(status)) {
    474         return retPtr;
    475     }
    476 
    477     //
    478     //  Open the file.
    479     //
    480     f = fopen(fileName, "rb");
    481     if (f == 0) {
    482         dataerrln("Error opening test data file %s\n", fileName);
    483         status = U_FILE_ACCESS_ERROR;
    484         return NULL;
    485     }
    486     //
    487     //  Read it in
    488     //
    489     int32_t            fileSize;
    490     int32_t            amtRead;
    491     int32_t            amtReadNoBOM;
    492 
    493     fseek( f, 0, SEEK_END);
    494     fileSize = ftell(f);
    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;
    501     }
    502 
    503     //
    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.
    509     //
    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;
    517     }
    518 
    519     //
    520     // Find the length of the input in UTF-16 UChars
    521     //  (by preflighting the conversion)
    522     //
    523     u_strFromUTF8(NULL, 0, &ulen, fileBufNoBOM, amtReadNoBOM, &status);
    524 
    525     //
    526     // Convert file contents from UTF-8 to UTF-16
    527     //
    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);
    533     }
    534 
    535 cleanUpAndReturn:
    536     fclose(f);
    537     delete[] fileBuf;
    538     if (U_FAILURE(status)) {
    539         errln("ICU Error \"%s\"\n", u_errorName(status));
    540         delete retPtr;
    541         retPtr = NULL;
    542     };
    543     return retPtr;
    544 }
    545 
    546 #endif  /* !UCONFIG_NO_REGULAR_EXPRESSIONS  */
    547 
    548