Index: icu46/source/test/intltest/dcfmtest.cpp |
=================================================================== |
--- icu46/source/test/intltest/dcfmtest.cpp (revision 0) |
+++ icu46/source/test/intltest/dcfmtest.cpp (revision 0) |
@@ -0,0 +1,518 @@ |
+/******************************************************************** |
+ * COPYRIGHT: |
+ * Copyright (c) 2002-2010, International Business Machines Corporation and |
+ * others. All Rights Reserved. |
+ ********************************************************************/ |
+ |
+// |
+// dcfmtest.cpp |
+// |
+// Decimal Formatter tests, data driven. |
+// |
+ |
+#include "intltest.h" |
+ |
+#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_REGULAR_EXPRESSIONS |
+ |
+#include "unicode/regex.h" |
+#include "unicode/uchar.h" |
+#include "unicode/ustring.h" |
+#include "unicode/unistr.h" |
+#include "unicode/dcfmtsym.h" |
+#include "unicode/decimfmt.h" |
+#include "unicode/locid.h" |
+#include "cmemory.h" |
+#include "dcfmtest.h" |
+#include "util.h" |
+#include "cstring.h" |
+#include <stdlib.h> |
+#include <string.h> |
+#include <stdio.h> |
+ |
+#include <string> |
+#include <iostream> |
+ |
+//--------------------------------------------------------------------------- |
+// |
+// Test class boilerplate |
+// |
+//--------------------------------------------------------------------------- |
+DecimalFormatTest::DecimalFormatTest() |
+{ |
+} |
+ |
+ |
+DecimalFormatTest::~DecimalFormatTest() |
+{ |
+} |
+ |
+ |
+ |
+void DecimalFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) |
+{ |
+ if (exec) logln("TestSuite DecimalFormatTest: "); |
+ switch (index) { |
+ |
+#if !UCONFIG_NO_FILE_IO |
+ case 0: name = "DataDrivenTests"; |
+ if (exec) DataDrivenTests(); |
+ break; |
+#else |
+ case 0: name = "skip"; |
+ break; |
+#endif |
+ |
+ default: name = ""; |
+ break; //needed to end loop |
+ } |
+} |
+ |
+ |
+//--------------------------------------------------------------------------- |
+// |
+// Error Checking / Reporting macros used in all of the tests. |
+// |
+//--------------------------------------------------------------------------- |
+#define DF_CHECK_STATUS {if (U_FAILURE(status)) \ |
+ {dataerrln("DecimalFormatTest failure at line %d. status=%s", \ |
+ __LINE__, u_errorName(status)); return 0;}} |
+ |
+#define DF_ASSERT(expr) {if ((expr)==FALSE) {errln("DecimalFormatTest failure at line %d.\n", __LINE__);};} |
+ |
+#define DF_ASSERT_FAIL(expr, errcode) {UErrorCode status=U_ZERO_ERROR; (expr);\ |
+if (status!=errcode) {dataerrln("DecimalFormatTest failure at line %d. Expected status=%s, got %s", \ |
+ __LINE__, u_errorName(errcode), u_errorName(status));};} |
+ |
+#define DF_CHECK_STATUS_L(line) {if (U_FAILURE(status)) {errln( \ |
+ "DecimalFormatTest failure at line %d, from %d. status=%d\n",__LINE__, (line), status); }} |
+ |
+#define DF_ASSERT_L(expr, line) {if ((expr)==FALSE) { \ |
+ errln("DecimalFormatTest failure at line %d, from %d.", __LINE__, (line)); return;}} |
+ |
+ |
+ |
+// |
+// InvariantStringPiece |
+// Wrap a StringPiece around the extracted invariant data of a UnicodeString. |
+// The data is guaranteed to be nul terminated. (This is not true of StringPiece |
+// in general, but is true of InvariantStringPiece) |
+// |
+class InvariantStringPiece: public StringPiece { |
+ public: |
+ InvariantStringPiece(const UnicodeString &s); |
+ ~InvariantStringPiece() {}; |
+ private: |
+ MaybeStackArray<char, 20> buf; |
+}; |
+ |
+InvariantStringPiece::InvariantStringPiece(const UnicodeString &s) { |
+ int32_t len = s.length(); |
+ if (len+1 > buf.getCapacity()) { |
+ buf.resize(len+1); |
+ } |
+ // Buffer size is len+1 so that s.extract() will nul-terminate the string. |
+ s.extract(0, len, buf.getAlias(), len+1, US_INV); |
+ this->set(buf, len); |
+} |
+ |
+ |
+// UnicodeStringPiece |
+// Wrap a StringPiece around the extracted (to the default charset) data of |
+// a UnicodeString. The extracted data is guaranteed to be nul terminated. |
+// (This is not true of StringPiece in general, but is true of UnicodeStringPiece) |
+// |
+class UnicodeStringPiece: public StringPiece { |
+ public: |
+ UnicodeStringPiece(const UnicodeString &s); |
+ ~UnicodeStringPiece() {}; |
+ private: |
+ MaybeStackArray<char, 20> buf; |
+}; |
+ |
+UnicodeStringPiece::UnicodeStringPiece(const UnicodeString &s) { |
+ int32_t len = s.length(); |
+ int32_t capacity = buf.getCapacity(); |
+ int32_t requiredCapacity = s.extract(0, len, buf.getAlias(), capacity) + 1; |
+ if (capacity < requiredCapacity) { |
+ buf.resize(requiredCapacity); |
+ capacity = requiredCapacity; |
+ s.extract(0, len, buf.getAlias(), capacity); |
+ } |
+ this->set(buf, requiredCapacity - 1); |
+} |
+ |
+ |
+ |
+//--------------------------------------------------------------------------- |
+// |
+// DataDrivenTests |
+// The test cases are in a separate data file, |
+// |
+//--------------------------------------------------------------------------- |
+ |
+// Translate a Formattable::type enum value to a string, for error message formatting. |
+static const char *formattableType(Formattable::Type typ) { |
+ static const char *types[] = {"kDate", |
+ "kDouble", |
+ "kLong", |
+ "kString", |
+ "kArray", |
+ "kInt64", |
+ "kObject" |
+ }; |
+ if (typ<0 || typ>Formattable::kObject) { |
+ return "Unknown"; |
+ } |
+ return types[typ]; |
+} |
+ |
+const char * |
+DecimalFormatTest::getPath(char *buffer, const char *filename) { |
+ UErrorCode status=U_ZERO_ERROR; |
+ const char *testDataDirectory = IntlTest::getSourceTestData(status); |
+ DF_CHECK_STATUS; |
+ |
+ strcpy(buffer, testDataDirectory); |
+ strcat(buffer, filename); |
+ return buffer; |
+} |
+ |
+void DecimalFormatTest::DataDrivenTests() { |
+ char tdd[2048]; |
+ const char *srcPath; |
+ UErrorCode status = U_ZERO_ERROR; |
+ int32_t lineNum = 0; |
+ |
+ // |
+ // Open and read the test data file. |
+ // |
+ srcPath=getPath(tdd, "dcfmtest.txt"); |
+ if(srcPath==NULL) { |
+ return; /* something went wrong, error already output */ |
+ } |
+ |
+ int32_t len; |
+ UChar *testData = ReadAndConvertFile(srcPath, len, status); |
+ if (U_FAILURE(status)) { |
+ return; /* something went wrong, error already output */ |
+ } |
+ |
+ // |
+ // Put the test data into a UnicodeString |
+ // |
+ UnicodeString testString(FALSE, testData, len); |
+ |
+ RegexMatcher parseLineMat(UnicodeString( |
+ "(?i)\\s*parse\\s+" |
+ "\"([^\"]*)\"\\s+" // Capture group 1: input text |
+ "([ild])\\s+" // Capture group 2: expected parsed type |
+ "\"([^\"]*)\"\\s+" // Capture group 3: expected parsed decimal |
+ "\\s*(?:#.*)?"), // Trailing comment |
+ 0, status); |
+ |
+ RegexMatcher formatLineMat(UnicodeString( |
+ "(?i)\\s*format\\s+" |
+ "(\\S+)\\s+" // Capture group 1: pattern |
+ "(ceiling|floor|down|up|halfeven|halfdown|halfup|default)\\s+" // Capture group 2: Rounding Mode |
+ "\"([^\"]*)\"\\s+" // Capture group 3: input |
+ "\"([^\"]*)\"" // Capture group 4: expected output |
+ "\\s*(?:#.*)?"), // Trailing comment |
+ 0, status); |
+ |
+ RegexMatcher commentMat (UNICODE_STRING_SIMPLE("\\s*(#.*)?$"), 0, status); |
+ RegexMatcher lineMat(UNICODE_STRING_SIMPLE("(?m)^(.*?)$"), testString, 0, status); |
+ |
+ if (U_FAILURE(status)){ |
+ dataerrln("Construct RegexMatcher() error."); |
+ delete [] testData; |
+ return; |
+ } |
+ |
+ // |
+ // Loop over the test data file, once per line. |
+ // |
+ while (lineMat.find()) { |
+ lineNum++; |
+ if (U_FAILURE(status)) { |
+ errln("File dcfmtest.txt, line %d: ICU Error \"%s\"", lineNum, u_errorName(status)); |
+ } |
+ |
+ status = U_ZERO_ERROR; |
+ UnicodeString testLine = lineMat.group(1, status); |
+ // printf("%s\n", UnicodeStringPiece(testLine).data()); |
+ if (testLine.length() == 0) { |
+ continue; |
+ } |
+ |
+ // |
+ // Parse the test line. Skip blank and comment only lines. |
+ // Separate out the three main fields - pattern, flags, target. |
+ // |
+ |
+ commentMat.reset(testLine); |
+ if (commentMat.lookingAt(status)) { |
+ // This line is a comment, or blank. |
+ continue; |
+ } |
+ |
+ |
+ // |
+ // Handle "parse" test case line from file |
+ // |
+ parseLineMat.reset(testLine); |
+ if (parseLineMat.lookingAt(status)) { |
+ execParseTest(lineNum, |
+ parseLineMat.group(1, status), // input |
+ parseLineMat.group(2, status), // Expected Type |
+ parseLineMat.group(3, status), // Expected Decimal String |
+ status |
+ ); |
+ continue; |
+ } |
+ |
+ // |
+ // Handle "format" test case line |
+ // |
+ formatLineMat.reset(testLine); |
+ if (formatLineMat.lookingAt(status)) { |
+ execFormatTest(lineNum, |
+ formatLineMat.group(1, status), // Pattern |
+ formatLineMat.group(2, status), // rounding mode |
+ formatLineMat.group(3, status), // input decimal number |
+ formatLineMat.group(4, status), // expected formatted result |
+ status); |
+ continue; |
+ } |
+ |
+ // |
+ // Line is not a recognizable test case. |
+ // |
+ errln("Badly formed test case at line %d.\n%s\n", |
+ lineNum, UnicodeStringPiece(testLine).data()); |
+ |
+ } |
+ |
+ delete [] testData; |
+} |
+ |
+ |
+ |
+void DecimalFormatTest::execParseTest(int32_t lineNum, |
+ const UnicodeString &inputText, |
+ const UnicodeString &expectedType, |
+ const UnicodeString &expectedDecimal, |
+ UErrorCode &status) { |
+ |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ |
+ DecimalFormatSymbols symbols(Locale::getUS(), status); |
+ UnicodeString pattern = UNICODE_STRING_SIMPLE("####"); |
+ DecimalFormat format(pattern, symbols, status); |
+ Formattable result; |
+ if (U_FAILURE(status)) { |
+ errln("file dcfmtest.txt, line %d: %s error creating the formatter.", |
+ lineNum, u_errorName(status)); |
+ return; |
+ } |
+ |
+ ParsePosition pos; |
+ int32_t expectedParseEndPosition = inputText.length(); |
+ |
+ format.parse(inputText, result, pos); |
+ |
+ if (expectedParseEndPosition != pos.getIndex()) { |
+ errln("file dcfmtest.txt, line %d: Expected parse position afeter parsing: %d. " |
+ "Actual parse position: %d", expectedParseEndPosition, pos.getIndex()); |
+ return; |
+ } |
+ |
+ char expectedTypeC[2]; |
+ expectedType.extract(0, 1, expectedTypeC, 2, US_INV); |
+ Formattable::Type expectType = Formattable::kDate; |
+ switch (expectedTypeC[0]) { |
+ case 'd': expectType = Formattable::kDouble; break; |
+ case 'i': expectType = Formattable::kLong; break; |
+ case 'l': expectType = Formattable::kInt64; break; |
+ default: |
+ errln("file dcfmtest.tx, line %d: unrecongized expected type \"%s\"", |
+ lineNum, InvariantStringPiece(expectedType).data()); |
+ return; |
+ } |
+ if (result.getType() != expectType) { |
+ errln("file dcfmtest.txt, line %d: expectedParseType(%s) != actual parseType(%s)", |
+ lineNum, formattableType(expectType), formattableType(result.getType())); |
+ return; |
+ } |
+ |
+ StringPiece decimalResult = result.getDecimalNumber(status); |
+ if (U_FAILURE(status)) { |
+ errln("File %s, line %d: error %s. Line in file dcfmtest.txt: %d:", |
+ __FILE__, __LINE__, u_errorName(status), lineNum); |
+ return; |
+ } |
+ |
+ InvariantStringPiece expectedResults(expectedDecimal); |
+ if (decimalResult != expectedResults) { |
+ errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"", |
+ lineNum, expectedResults.data(), decimalResult.data()); |
+ } |
+ |
+ return; |
+} |
+ |
+ |
+void DecimalFormatTest::execFormatTest(int32_t lineNum, |
+ const UnicodeString &pattern, // Pattern |
+ const UnicodeString &round, // rounding mode |
+ const UnicodeString &input, // input decimal number |
+ const UnicodeString &expected, // expected formatted result |
+ UErrorCode &status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ |
+ DecimalFormatSymbols symbols(Locale::getUS(), status); |
+ // printf("Pattern = %s\n", UnicodeStringPiece(pattern).data()); |
+ DecimalFormat fmtr(pattern, symbols, status); |
+ if (U_FAILURE(status)) { |
+ errln("file dcfmtest.txt, line %d: %s error creating the formatter.", |
+ lineNum, u_errorName(status)); |
+ return; |
+ } |
+ if (round=="ceiling") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundCeiling); |
+ } else if (round=="floor") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundFloor); |
+ } else if (round=="down") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundDown); |
+ } else if (round=="up") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundUp); |
+ } else if (round=="halfeven") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundHalfEven); |
+ } else if (round=="halfdown") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundHalfDown); |
+ } else if (round=="halfup") { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundHalfUp); |
+ } else if (round=="default") { |
+ // don't set any value. |
+ } else { |
+ fmtr.setRoundingMode(DecimalFormat::kRoundFloor); |
+ errln("file dcfmtest.txt, line %d: Bad rounding mode \"%s\"", |
+ lineNum, UnicodeStringPiece(round).data()); |
+ } |
+ |
+ UnicodeString result; |
+ UnicodeStringPiece spInput(input); |
+ //fmtr.format(spInput, result, NULL, status); |
+ |
+ Formattable fmtbl; |
+ fmtbl.setDecimalNumber(spInput, status); |
+ //NumberFormat &nfmtr = fmtr; |
+ fmtr.format(fmtbl, result, NULL, status); |
+ |
+ if (U_FAILURE(status)) { |
+ errln("file dcfmtest.txt, line %d: format() returned %s.", |
+ lineNum, u_errorName(status)); |
+ return; |
+ } |
+ |
+ if (result != expected) { |
+ errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"", |
+ lineNum, UnicodeStringPiece(expected).data(), UnicodeStringPiece(result).data()); |
+ } |
+} |
+ |
+ |
+//------------------------------------------------------------------------------- |
+// |
+// Read a text data file, convert it from UTF-8 to UChars, and return the data |
+// in one big UChar * buffer, which the caller must delete. |
+// |
+// (Lightly modified version of a similar function in regextst.cpp) |
+// |
+//-------------------------------------------------------------------------------- |
+UChar *DecimalFormatTest::ReadAndConvertFile(const char *fileName, int32_t &ulen, |
+ UErrorCode &status) { |
+ UChar *retPtr = NULL; |
+ char *fileBuf = NULL; |
+ const char *fileBufNoBOM = NULL; |
+ FILE *f = NULL; |
+ |
+ ulen = 0; |
+ if (U_FAILURE(status)) { |
+ return retPtr; |
+ } |
+ |
+ // |
+ // Open the file. |
+ // |
+ f = fopen(fileName, "rb"); |
+ if (f == 0) { |
+ dataerrln("Error opening test data file %s\n", fileName); |
+ status = U_FILE_ACCESS_ERROR; |
+ return NULL; |
+ } |
+ // |
+ // Read it in |
+ // |
+ int32_t fileSize; |
+ int32_t amtRead; |
+ int32_t amtReadNoBOM; |
+ |
+ fseek( f, 0, SEEK_END); |
+ fileSize = ftell(f); |
+ fileBuf = new char[fileSize]; |
+ fseek(f, 0, SEEK_SET); |
+ amtRead = fread(fileBuf, 1, fileSize, f); |
+ if (amtRead != fileSize || fileSize <= 0) { |
+ errln("Error reading test data file."); |
+ goto cleanUpAndReturn; |
+ } |
+ |
+ // |
+ // Look for a UTF-8 BOM on the data just read. |
+ // The test data file is UTF-8. |
+ // The BOM needs to be there in the source file to keep the Windows & |
+ // EBCDIC machines happy, so force an error if it goes missing. |
+ // Many Linux editors will silently strip it. |
+ // |
+ fileBufNoBOM = fileBuf + 3; |
+ amtReadNoBOM = amtRead - 3; |
+ if (fileSize<3 || uprv_strncmp(fileBuf, "\xEF\xBB\xBF", 3) != 0) { |
+ // TODO: restore this check. |
+ errln("Test data file %s is missing its BOM", fileName); |
+ fileBufNoBOM = fileBuf; |
+ amtReadNoBOM = amtRead; |
+ } |
+ |
+ // |
+ // Find the length of the input in UTF-16 UChars |
+ // (by preflighting the conversion) |
+ // |
+ u_strFromUTF8(NULL, 0, &ulen, fileBufNoBOM, amtReadNoBOM, &status); |
+ |
+ // |
+ // Convert file contents from UTF-8 to UTF-16 |
+ // |
+ if (status == U_BUFFER_OVERFLOW_ERROR) { |
+ // Buffer Overflow is expected from the preflight operation. |
+ status = U_ZERO_ERROR; |
+ retPtr = new UChar[ulen+1]; |
+ u_strFromUTF8(retPtr, ulen+1, NULL, fileBufNoBOM, amtReadNoBOM, &status); |
+ } |
+ |
+cleanUpAndReturn: |
+ fclose(f); |
+ delete[] fileBuf; |
+ if (U_FAILURE(status)) { |
+ errln("ICU Error \"%s\"\n", u_errorName(status)); |
+ delete retPtr; |
+ retPtr = NULL; |
+ }; |
+ return retPtr; |
+} |
+ |
+#endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */ |
+ |
Property changes on: icu46/source/test/intltest/dcfmtest.cpp |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |