blob: 22777b91abb9cc95cf3e23d9de60e162c93a3fd7 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.dev.test.format;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.impl.Utility;
/**
* A collection of methods to run the data driven number format test suite.
*/
public class DataDrivenNumberFormatTestSuite extends TestFmwk {
/**
* Base class for code under test.
*/
public static abstract class CodeUnderTest {
/**
* Returns the ID of the code under test. This ID is used to identify
* tests that are known to fail for this particular code under test.
* This implementation returns null which means that by default all
* tests should work with this code under test.
* @return 'J' means ICU4J, 'K' means JDK
*/
public Character Id() {
return null;
}
/**
* Runs a single formatting test. On success, returns null.
* On failure, returns the error. This implementation just returns null.
* Subclasses should override.
* @param tuple contains the parameters of the format test.
*/
public String format(NumberFormatTestTuple tuple) {
return null;
}
/**
* Runs a single toPattern test. On success, returns null.
* On failure, returns the error. This implementation just returns null.
* Subclasses should override.
* @param tuple contains the parameters of the format test.
*/
public String toPattern(NumberFormatTestTuple tuple) {
return null;
}
/**
* Runs a single parse test. On success, returns null.
* On failure, returns the error. This implementation just returns null.
* Subclasses should override.
* @param tuple contains the parameters of the format test.
*/
public String parse(NumberFormatTestTuple tuple) {
return null;
}
/**
* Runs a single parse currency test. On success, returns null.
* On failure, returns the error. This implementation just returns null.
* Subclasses should override.
* @param tuple contains the parameters of the format test.
*/
public String parseCurrency(NumberFormatTestTuple tuple) {
return null;
}
/**
* Runs a single select test. On success, returns null.
* On failure, returns the error. This implementation just returns null.
* Subclasses should override.
* @param tuple contains the parameters of the format test.
*/
public String select(NumberFormatTestTuple tuple) {
return null;
}
}
private static enum RunMode {
SKIP_KNOWN_FAILURES,
INCLUDE_KNOWN_FAILURES
}
private final CodeUnderTest codeUnderTest;
private String fileLine = null;
private int fileLineNumber = 0;
private String fileTestName = "";
private NumberFormatTestTuple tuple = new NumberFormatTestTuple();
/**
* Runs all the tests in the data driven test suite against codeUnderTest.
* @param params this.params from TestFmwk.
* @param fileName The name of the test file. A relative file name under
* com/ibm/icu/dev/data such as "data.txt"
* @param codeUnderTest the code under test
*/
static void runSuite(
TestParams params, String fileName, CodeUnderTest codeUnderTest) {
new DataDrivenNumberFormatTestSuite(params, codeUnderTest)
.run(fileName, RunMode.SKIP_KNOWN_FAILURES);
}
/**
* Runs every format test in data driven test suite including those
* that are known to fail.
*
* @param params this.params from TestFmwk
* @param fileName The name of the test file. A relative file name under
* com/ibm/icu/dev/data such as "data.txt"
* @param codeUnderTest the code under test
*/
static void runFormatSuiteIncludingKnownFailures(
TestParams params, String fileName, CodeUnderTest codeUnderTest) {
new DataDrivenNumberFormatTestSuite(params, codeUnderTest)
.run(fileName, RunMode.INCLUDE_KNOWN_FAILURES);
}
private DataDrivenNumberFormatTestSuite(
TestParams params, CodeUnderTest codeUnderTest) {
this.params = params;
this.codeUnderTest = codeUnderTest;
}
private void run(String fileName, RunMode runMode) {
Character codeUnderTestIdObj = codeUnderTest.Id();
char codeUnderTestId =
codeUnderTestIdObj == null ? 0 : Character.toUpperCase(codeUnderTestIdObj.charValue());
BufferedReader in = null;
try {
in = TestUtil.getDataReader("numberformattestspecification.txt", "UTF-8");
// read first line and remove BOM if present
readLine(in);
if (fileLine != null && fileLine.charAt(0) == '\uFEFF') {
fileLine = fileLine.substring(1);
}
int state = 0;
List<String> columnValues;
List<String> columnNames = null;
while (true) {
if (fileLine == null || fileLine.length() == 0) {
if (!readLine(in)) {
break;
}
if (fileLine.length() == 0 && state == 2) {
state = 0;
}
continue;
}
if (fileLine.startsWith("//")) {
fileLine = null;
continue;
}
// Initial setup of test.
if (state == 0) {
if (fileLine.startsWith("test ")) {
fileTestName = fileLine;
tuple = new NumberFormatTestTuple();
} else if (fileLine.startsWith("set ")) {
if (!setTupleField()) {
return;
}
} else if(fileLine.startsWith("begin")) {
state = 1;
} else {
showError("Unrecognized verb.");
return;
}
// column specification
} else if (state == 1) {
columnNames = splitBy((char) 0x09);
state = 2;
// run the tests
} else {
int columnNamesSize = columnNames.size();
columnValues = splitBy(columnNamesSize, (char) 0x09);
int columnValuesSize = columnValues.size();
for (int i = 0; i < columnValuesSize; ++i) {
if (!setField(columnNames.get(i), columnValues.get(i))) {
return;
}
}
for (int i = columnValuesSize; i < columnNamesSize; ++i) {
if (!clearField(columnNames.get(i))) {
return;
}
}
if (runMode == RunMode.INCLUDE_KNOWN_FAILURES
|| !breaks(codeUnderTestId)) {
String errorMessage = isPass(tuple);
if (errorMessage != null) {
showError(errorMessage);
}
}
}
fileLine = null;
}
} catch (Exception e) {
showError(e.toString());
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private boolean breaks(char code) {
String breaks = tuple.breaks == null ? "" : tuple.breaks;
return (breaks.toUpperCase().indexOf(code) != -1);
}
private static boolean isSpace(char c) {
return (c == 0x09 || c == 0x20 || c == 0x3000);
}
private boolean setTupleField() {
List<String> parts = splitBy(3, (char) 0x20);
if (parts.size() < 3) {
showError("Set expects 2 parameters");
return false;
}
return setField(parts.get(1), parts.get(2));
}
private boolean setField(String name, String value) {
try {
tuple.setField(name, Utility.unescape(value));
return true;
} catch (Exception e) {
showError("No such field: " + name + ", or bad value: " + value);
return false;
}
}
private boolean clearField(String name) {
try {
tuple.clearField(name);
return true;
} catch (Exception e) {
showError("Field cannot be clared: " + name);
return false;
}
}
private void showError(String message) {
errln(String.format("line %d: %s\n%s\n%s", fileLineNumber, Utility.escape(message), fileTestName,fileLine));
}
private List<String> splitBy(char delimiter) {
return splitBy(Integer.MAX_VALUE, delimiter);
}
private List<String> splitBy(int max, char delimiter) {
ArrayList<String> result = new ArrayList<String>();
int colIdx = 0;
int colStart = 0;
int len = fileLine.length();
for (int idx = 0; colIdx < max - 1 && idx < len; ++idx) {
char ch = fileLine.charAt(idx);
if (ch == delimiter) {
result.add(
fileLine.substring(colStart, idx));
++colIdx;
colStart = idx + 1;
}
}
result.add(fileLine.substring(colStart, len));
return result;
}
private boolean readLine(BufferedReader in) throws IOException {
String line = in.readLine();
if (line == null) {
fileLine = null;
return false;
}
++fileLineNumber;
// Strip trailing comments and spaces
int idx = line.length();
for (; idx > 0; idx--) {
if (!isSpace(line.charAt(idx -1))) {
break;
}
}
fileLine = idx == 0 ? "" : line;
return true;
}
private String isPass(NumberFormatTestTuple tuple) {
StringBuilder result = new StringBuilder();
if (tuple.format != null && tuple.output != null) {
String errorMessage = codeUnderTest.format(tuple);
if (errorMessage != null) {
result.append(errorMessage);
}
} else if (tuple.toPattern != null || tuple.toLocalizedPattern != null) {
String errorMessage = codeUnderTest.toPattern(tuple);
if (errorMessage != null) {
result.append(errorMessage);
}
} else if (tuple.parse != null && tuple.output != null && tuple.outputCurrency != null) {
String errorMessage = codeUnderTest.parseCurrency(tuple);
if (errorMessage != null) {
result.append(errorMessage);
}
} else if (tuple.parse != null && tuple.output != null) {
String errorMessage = codeUnderTest.parse(tuple);
if (errorMessage != null) {
result.append(errorMessage);
}
} else if (tuple.plural != null) {
String errorMessage = codeUnderTest.select(tuple);
if (errorMessage != null) {
result.append(errorMessage);
}
} else {
result.append("Unrecognized test type.");
}
if (result.length() > 0) {
result.append(": ");
result.append(tuple);
return result.toString();
}
return null;
}
}