// Copyright (C) 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; 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 (!breaksC() || runAllTests) { UnicodeString errorMessage; if (!isPass(fTuple, errorMessage, status)) { showFailure(errorMessage); } } } 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 */