| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /******************************************************************** |
| * COPYRIGHT: |
| * Copyright (c) 2015, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ********************************************************************/ |
| |
| #include "datadrivennumberformattestsuite.h" |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| #include "charstr.h" |
| #include "ucbuf.h" |
| #include "unicode/localpointer.h" |
| #include "ustrfmt.h" |
| |
| static UBool isCROrLF(UChar c) { return c == 0xa || c == 0xd; } |
| static UBool isSpace(UChar c) { return c == 9 || c == 0x20 || c == 0x3000; } |
| |
| void DataDrivenNumberFormatTestSuite::run(const char *fileName, UBool runAllTests) { |
| fFileLineNumber = 0; |
| fFormatTestNumber = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) { |
| delete fPreviousFormatters[i]; |
| fPreviousFormatters[i] = newFormatter(status); |
| } |
| if (!assertSuccess("Can't create previous formatters", status)) { |
| return; |
| } |
| CharString path(getSourceTestData(status), status); |
| path.appendPathPart(fileName, status); |
| const char *codePage = "UTF-8"; |
| LocalUCHARBUFPointer f(ucbuf_open(path.data(), &codePage, TRUE, FALSE, &status)); |
| if (!assertSuccess("Can't open data file", status)) { |
| return; |
| } |
| UnicodeString columnValues[kNumberFormatTestTupleFieldCount]; |
| ENumberFormatTestTupleField columnTypes[kNumberFormatTestTupleFieldCount]; |
| int32_t columnCount = 0; |
| int32_t state = 0; |
| while(U_SUCCESS(status)) { |
| // Read a new line if necessary. |
| if(fFileLine.isEmpty()) { |
| if(!readLine(f.getAlias(), status)) { break; } |
| if (fFileLine.isEmpty() && state == 2) { |
| state = 0; |
| } |
| continue; |
| } |
| if (fFileLine.startsWith("//")) { |
| fFileLine.remove(); |
| continue; |
| } |
| // Initial setup of test. |
| if (state == 0) { |
| if (fFileLine.startsWith(UNICODE_STRING("test ", 5))) { |
| fFileTestName = fFileLine; |
| fTuple.clear(); |
| } else if(fFileLine.startsWith(UNICODE_STRING("set ", 4))) { |
| setTupleField(status); |
| } else if(fFileLine.startsWith(UNICODE_STRING("begin", 5))) { |
| state = 1; |
| } else { |
| showError("Unrecognized verb."); |
| return; |
| } |
| // column specification |
| } else if (state == 1) { |
| columnCount = splitBy(columnValues, UPRV_LENGTHOF(columnValues), 0x9); |
| for (int32_t i = 0; i < columnCount; ++i) { |
| columnTypes[i] = NumberFormatTestTuple::getFieldByName( |
| columnValues[i]); |
| if (columnTypes[i] == kNumberFormatTestTupleFieldCount) { |
| showError("Unrecognized field name."); |
| return; |
| } |
| } |
| state = 2; |
| // run the tests |
| } else { |
| int32_t columnsInThisRow = splitBy(columnValues, columnCount, 0x9); |
| for (int32_t i = 0; i < columnsInThisRow; ++i) { |
| fTuple.setField( |
| columnTypes[i], columnValues[i].unescape(), status); |
| } |
| for (int32_t i = columnsInThisRow; i < columnCount; ++i) { |
| fTuple.clearField(columnTypes[i], status); |
| } |
| if (U_FAILURE(status)) { |
| showError("Invalid column values"); |
| return; |
| } |
| if (runAllTests || !breaksC()) { |
| UnicodeString errorMessage; |
| UBool shouldFail = (NFTT_GET_FIELD(fTuple, output, "") == "fail") |
| ? !breaksC() |
| : breaksC(); |
| UBool actualSuccess = isPass(fTuple, errorMessage, status); |
| if (shouldFail && actualSuccess) { |
| showFailure("Expected failure, but passed: " + errorMessage); |
| break; |
| } else if (!shouldFail && !actualSuccess) { |
| showFailure(errorMessage); |
| break; |
| } |
| status = U_ZERO_ERROR; |
| } |
| } |
| fFileLine.remove(); |
| } |
| } |
| |
| DataDrivenNumberFormatTestSuite::~DataDrivenNumberFormatTestSuite() { |
| for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) { |
| delete fPreviousFormatters[i]; |
| } |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::breaksC() { |
| return (NFTT_GET_FIELD(fTuple, breaks, "").toUpper().indexOf((UChar)0x43) != -1); |
| } |
| |
| void DataDrivenNumberFormatTestSuite::setTupleField(UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return; |
| } |
| UnicodeString parts[3]; |
| int32_t partCount = splitBy(parts, UPRV_LENGTHOF(parts), 0x20); |
| if (partCount < 3) { |
| showError("Set expects 2 parameters"); |
| status = U_PARSE_ERROR; |
| return; |
| } |
| if (!fTuple.setField( |
| NumberFormatTestTuple::getFieldByName(parts[1]), |
| parts[2].unescape(), |
| status)) { |
| showError("Invalid field value"); |
| } |
| } |
| |
| |
| int32_t |
| DataDrivenNumberFormatTestSuite::splitBy( |
| UnicodeString *columnValues, |
| int32_t columnValuesCount, |
| UChar delimiter) { |
| int32_t colIdx = 0; |
| int32_t colStart = 0; |
| int32_t len = fFileLine.length(); |
| for (int32_t idx = 0; colIdx < columnValuesCount - 1 && idx < len; ++idx) { |
| UChar ch = fFileLine.charAt(idx); |
| if (ch == delimiter) { |
| columnValues[colIdx++] = |
| fFileLine.tempSubString(colStart, idx - colStart); |
| colStart = idx + 1; |
| } |
| } |
| columnValues[colIdx++] = |
| fFileLine.tempSubString(colStart, len - colStart); |
| return colIdx; |
| } |
| |
| void DataDrivenNumberFormatTestSuite::showLineInfo() { |
| UnicodeString indent(" "); |
| infoln(indent + fFileTestName); |
| infoln(indent + fFileLine); |
| } |
| |
| void DataDrivenNumberFormatTestSuite::showError(const char *message) { |
| errln("line %d: %s", (int) fFileLineNumber, message); |
| showLineInfo(); |
| } |
| |
| void DataDrivenNumberFormatTestSuite::showFailure(const UnicodeString &message) { |
| UChar lineStr[20]; |
| uprv_itou( |
| lineStr, UPRV_LENGTHOF(lineStr), (uint32_t) fFileLineNumber, 10, 1); |
| UnicodeString fullMessage("line "); |
| dataerrln(fullMessage.append(lineStr).append(": ") |
| .append(prettify(message))); |
| showLineInfo(); |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::readLine( |
| UCHARBUF *f, UErrorCode &status) { |
| int32_t lineLength; |
| const UChar *line = ucbuf_readline(f, &lineLength, &status); |
| if(line == NULL || U_FAILURE(status)) { |
| if (U_FAILURE(status)) { |
| errln("Error reading line from file."); |
| } |
| fFileLine.remove(); |
| return FALSE; |
| } |
| ++fFileLineNumber; |
| // Strip trailing CR/LF, comments, and spaces. |
| while(lineLength > 0 && isCROrLF(line[lineLength - 1])) { --lineLength; } |
| fFileLine.setTo(FALSE, line, lineLength); |
| while(lineLength > 0 && isSpace(line[lineLength - 1])) { --lineLength; } |
| if (lineLength == 0) { |
| fFileLine.remove(); |
| } |
| return TRUE; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isPass( |
| const NumberFormatTestTuple &tuple, |
| UnicodeString &appendErrorMessage, |
| UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return FALSE; |
| } |
| UBool result = FALSE; |
| if (tuple.formatFlag && tuple.outputFlag) { |
| ++fFormatTestNumber; |
| result = isFormatPass( |
| tuple, |
| fPreviousFormatters[ |
| fFormatTestNumber % UPRV_LENGTHOF(fPreviousFormatters)], |
| appendErrorMessage, |
| status); |
| } |
| else if (tuple.toPatternFlag || tuple.toLocalizedPatternFlag) { |
| result = isToPatternPass(tuple, appendErrorMessage, status); |
| } else if (tuple.parseFlag && tuple.outputFlag && tuple.outputCurrencyFlag) { |
| result = isParseCurrencyPass(tuple, appendErrorMessage, status); |
| |
| } else if (tuple.parseFlag && tuple.outputFlag) { |
| result = isParsePass(tuple, appendErrorMessage, status); |
| } else if (tuple.pluralFlag) { |
| result = isSelectPass(tuple, appendErrorMessage, status); |
| } else { |
| appendErrorMessage.append("Unrecognized test type."); |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| } |
| if (!result) { |
| if (appendErrorMessage.length() > 0) { |
| appendErrorMessage.append(": "); |
| } |
| if (U_FAILURE(status)) { |
| appendErrorMessage.append(u_errorName(status)); |
| appendErrorMessage.append(": "); |
| } |
| tuple.toString(appendErrorMessage); |
| } |
| return result; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isFormatPass( |
| const NumberFormatTestTuple & /* tuple */, |
| UnicodeString & /*appendErrorMessage*/, |
| UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isFormatPass( |
| const NumberFormatTestTuple &tuple, |
| UObject * /* somePreviousFormatter */, |
| UnicodeString &appendErrorMessage, |
| UErrorCode &status) { |
| return isFormatPass(tuple, appendErrorMessage, status); |
| } |
| |
| UObject *DataDrivenNumberFormatTestSuite::newFormatter( |
| UErrorCode & /*status*/) { |
| return NULL; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isToPatternPass( |
| const NumberFormatTestTuple & /* tuple */, |
| UnicodeString & /*appendErrorMessage*/, |
| UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isParsePass( |
| const NumberFormatTestTuple & /* tuple */, |
| UnicodeString & /*appendErrorMessage*/, |
| UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isParseCurrencyPass( |
| const NumberFormatTestTuple & /* tuple */, |
| UnicodeString & /*appendErrorMessage*/, |
| UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UBool DataDrivenNumberFormatTestSuite::isSelectPass( |
| const NumberFormatTestTuple & /* tuple */, |
| UnicodeString & /*appendErrorMessage*/, |
| UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| #endif /* !UCONFIG_NO_FORMATTING */ |