// © 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 (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 */