// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.testsystems.slim.tables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import fitnesse.slim.converters.BooleanConverter;
import fitnesse.slim.converters.VoidConverter;
import fitnesse.slim.instructions.Instruction;
import fitnesse.testsystems.TestExecutionException;
import fitnesse.testsystems.TestResult;
import fitnesse.testsystems.slim.SlimTestContext;
import fitnesse.testsystems.slim.Table;
import fitnesse.testsystems.slim.results.SlimTestResult;
public class ScriptTable extends SlimTable {
private static final String SEQUENTIAL_ARGUMENT_PROCESSING_SUFFIX = ";";
public ScriptTable(Table table, String tableId, SlimTestContext context) {
super(table, tableId, context);
}
/**
* Template method to provide the keyword that identifies the table type.
*
* @return table type
*/
@Override
protected String getTableType() {
return "scriptTable";
}
/**
* Template method to provide the keyword that identifies the table type.
*
* @return keyword for script table
*/
protected String getTableKeyword() {
return "script";
}
/**
* Template method to provide the keyword for the {@code start} action.
*
* @return keyword for {@code start} action
*/
protected String getStartKeyword() {
return "start";
}
/**
* Template method to provide the keyword for the {@code check} action.
*
* @return keyword for {@code check} action
*/
protected String getCheckKeyword() {
return "check";
}
/**
* Template method to provide the keyword for the {@code checkNot} action.
*
* @return keyword for {@code checkNot} action
*/
protected String getCheckNotKeyword() {
return "check not";
}
/**
* Template method to provide the keyword for the {@code reject} action.
*
* @return keyword for {@code reject} action
*/
protected String getRejectKeyword() {
return "reject";
}
/**
* Template method to provide the keyword for the {@code ensure} action.
*
* @return keyword for {@code ensure} action
*/
protected String getEnsureKeyword() {
return "ensure";
}
/**
* Template method to provide the keyword for the {@code show} action.
*
* @return keyword for {@code show} action
*/
protected String getShowKeyword() {
return "show";
}
/**
* Template method to provide the keyword for the {@code note} action.
*
* @return keyword for {@code note} action
*/
protected String getNoteKeyword() {
return "note";
}
@Override
public List<SlimAssertion> getAssertions() throws TestExecutionException {
List<SlimAssertion> assertions = new ArrayList<>();
ScenarioTable.setDefaultChildClass(getClass());
if (table.getCellContents(0, 0).toLowerCase().startsWith(getTableKeyword())) {
List<SlimAssertion> createAssertions = startActor();
if (createAssertions != null) {
assertions.addAll(createAssertions);
}
}
for (int row = 1; row < table.getRowCount(); row++)
assertions.addAll(instructionsForRow(row));
return assertions;
}
// returns a list of statements
protected List<SlimAssertion> instructionsForRow(int row) throws TestExecutionException {
String firstCell = table.getCellContents(0, row).trim();
List<SlimAssertion> assertions;
String match;
if (firstCell.equalsIgnoreCase(getStartKeyword()))
assertions = startActor(row);
else if (firstCell.equalsIgnoreCase(getCheckKeyword()))
assertions = checkAction(row);
else if (firstCell.equalsIgnoreCase(getCheckNotKeyword()))
assertions = checkNotAction(row);
else if (firstCell.equalsIgnoreCase(getRejectKeyword()))
assertions = reject(row);
else if (firstCell.equalsIgnoreCase(getEnsureKeyword()))
assertions = ensure(row);
else if (firstCell.equalsIgnoreCase(getShowKeyword()))
assertions = show(row);
else if (firstCell.equalsIgnoreCase(getNoteKeyword()))
assertions = note(row);
else if ((match = ifSymbolAssignment(0, row)) != null)
assertions = actionAndAssign(match, row);
else if (firstCell.isEmpty())
assertions = note(row);
else if (firstCell.trim().startsWith("#") || firstCell.trim().startsWith("*"))
assertions = note(row);
else {
// action() is for now the only function that returns a list of statements
assertions = action(row);
}
return assertions;
}
protected List<SlimAssertion> actionAndAssign(String symbolName, int row) {
List<SlimAssertion> assertions = new ArrayList<>();
int lastCol = table.getColumnCountInRow(row) - 1;
String actionName = getActionNameStartingAt(1, lastCol, row);
if (!actionName.equals("")) {
String[] args = getArgumentsStartingAt(1 + 1, lastCol, row, assertions);
assertions.add(makeAssertion(callAndAssign(symbolName, getTableType() + "Actor", actionName, (Object[]) args),
new SymbolAssignmentExpectation(symbolName, 0, row)));
}
return assertions;
}
protected List<SlimAssertion> action(int row) throws TestExecutionException {
List<SlimAssertion> assertions = assertionsFromScenario(row);
if (assertions.isEmpty()) {
// Invoke fixture:
int lastCol = table.getColumnCountInRow(row) - 1;
return invokeAction(0, lastCol, row, new ScriptActionExpectation(0, row));
}
return assertions;
}
protected List<SlimAssertion> assertionsFromScenario(int row) throws TestExecutionException {
int lastCol = table.getColumnCountInRow(row) - 1;
String scenarioName = getScenarioNameFromAlternatingCells(lastCol, row);
ScenarioTable scenario = getTestContext().getScenario(scenarioName);
String[] args = null;
List<SlimAssertion> assertions = new ArrayList<>();
if (scenario != null) {
args = getArgumentsStartingAt(1, lastCol, row, assertions);
} else if (lastCol == 0) {
String cellContents = table.getCellContents(0, row);
scenario = getTestContext().getScenarioByPattern(cellContents);
if (scenario != null) {
args = scenario.matchParameters(cellContents);
}
}
if (scenario != null) {
scenario.setCustomComparatorRegistry(customComparatorRegistry);
assertions.addAll(scenario.call(args, this, row));
}
return assertions;
}
protected String getScenarioNameFromAlternatingCells(int endingCol, int row) {
String actionName = getActionNameStartingAt(0, endingCol, row);
String simpleName = actionName.replace(SEQUENTIAL_ARGUMENT_PROCESSING_SUFFIX, "");
return Disgracer.disgraceClassName(simpleName);
}
protected List<SlimAssertion> note(int row) {
return Collections.emptyList();
}
protected List<SlimAssertion> show(int row) {
int lastCol = table.getColumnCountInRow(row) - 1;
return invokeAction(1, lastCol, row,
new ShowActionExpectation(0, row));
}
protected List<SlimAssertion> ensure(int row) {
int lastCol = table.getColumnCountInRow(row) - 1;
return invokeAction(1, lastCol, row,
new EnsureActionExpectation(0, row));
}
protected List<SlimAssertion> reject(int row) {
int lastCol = table.getColumnCountInRow(row) - 1;
return invokeAction(1, lastCol, row,
new RejectActionExpectation(0, row));
}
protected List<SlimAssertion> checkAction(int row) {
int lastColInAction = table.getColumnCountInRow(row) - 1;
table.getCellContents(lastColInAction, row);
return invokeAction(1, lastColInAction - 1, row,
new ReturnedValueExpectation(lastColInAction, row));
}
protected List<SlimAssertion> checkNotAction(int row) {
int lastColInAction = table.getColumnCountInRow(row) - 1;
table.getCellContents(lastColInAction, row);
return invokeAction(1, lastColInAction - 1, row,
new RejectedValueExpectation(lastColInAction, row));
}
protected List<SlimAssertion> invokeAction(int startingCol, int endingCol, int row, SlimExpectation expectation) {
String actionName = getActionNameStartingAt(startingCol, endingCol, row);
List<SlimAssertion> assertions = new ArrayList<>();
String[] args = getArgumentsStartingAt(startingCol + 1, endingCol, row, assertions);
assertions.add(makeAssertion(callFunction(getTableType() + "Actor", actionName, (Object[]) args),
expectation));
return assertions;
}
protected String getActionNameStartingAt(int startingCol, int endingCol, int row) {
StringBuilder actionName = new StringBuilder();
actionName.append(table.getCellContents(startingCol, row));
int actionNameCol = startingCol + 2;
while (actionNameCol <= endingCol &&
!invokesSequentialArgumentProcessing(actionName.toString())) {
actionName.append(" ").append(table.getCellContents(actionNameCol, row));
actionNameCol += 2;
}
return actionName.toString().trim();
}
// Adds extra assertions to the "assertions" list!
protected String[] getArgumentsStartingAt(int startingCol, int endingCol, int row, List<SlimAssertion> assertions) {
ArgumentExtractor extractor = new ArgumentExtractor(startingCol, endingCol, row);
while (extractor.hasMoreToExtract()) {
assertions.add(makeAssertion(Instruction.NOOP_INSTRUCTION,
new ArgumentExpectation(extractor.argumentColumn, row)));
extractor.extractNextArgument();
}
return extractor.getArguments();
}
protected boolean invokesSequentialArgumentProcessing(String cellContents) {
return cellContents.endsWith(SEQUENTIAL_ARGUMENT_PROCESSING_SUFFIX);
}
protected List<SlimAssertion> startActor() {
String firstCellContents = table.getCellContents(0, 0);
String keyworkd = getTableKeyword() + ":";
int pos = firstCellContents.toLowerCase().indexOf(keyworkd);
if (pos == 0) {
return startActor(0, firstCellContents.substring(keyworkd.length() ), 0);
} else if (table.getColumnCountInRow(0) > 1) {
return startActor(0);
}
return null;
}
protected List<SlimAssertion> startActor(int row) {
int classNameColumn = 1;
String cellContents = table.getCellContents(classNameColumn, row);
return startActor(row, cellContents, classNameColumn);
}
protected List<SlimAssertion> startActor(int row, String cellContents, int classNameColumn) {
List<SlimAssertion> assertions = new ArrayList<>();
String className = Disgracer.disgraceClassName(cellContents);
assertions.add(constructInstance(getTableType() + "Actor", className, classNameColumn, row));
getArgumentsStartingAt(classNameColumn + 1, table.getColumnCountInRow(row) - 1, row, assertions);
return assertions;
}
class ArgumentExtractor {
private int argumentColumn;
private int endingCol;
private int row;
private List<String> arguments = new ArrayList<>();
private int increment = 2;
private boolean sequentialArguments = false;
ArgumentExtractor(int startingCol, int endingCol, int row) {
this.argumentColumn = startingCol;
this.endingCol = endingCol;
this.row = row;
}
public boolean hasMoreToExtract() {
return argumentColumn <= endingCol;
}
public void extractNextArgument() {
arguments.add(table.getCellContents(argumentColumn, row));
String argumentKeyword = table.getCellContents(argumentColumn - 1, row);
boolean argumentIsSequential = invokesSequentialArgumentProcessing(argumentKeyword);
sequentialArguments = (sequentialArguments || argumentIsSequential);
increment = sequentialArguments ? 1 : 2;
argumentColumn += increment;
}
public String[] getArguments() {
return arguments.toArray(new String[arguments.size()]);
}
}
private class ScriptActionExpectation extends RowExpectation {
private ScriptActionExpectation(int col, int row) {
super(col, row);
}
@Override
protected SlimTestResult createEvaluationMessage(String actual, String expected) {
if (actual == null)
return SlimTestResult.fail("null", expected);
else if (actual.equals(VoidConverter.VOID_TAG) || actual.equals("null"))
return SlimTestResult.plain();
else if (actual.equals(BooleanConverter.FALSE))
return SlimTestResult.fail();
else if (actual.equals(BooleanConverter.TRUE))
return SlimTestResult.pass();
else
return SlimTestResult.plain();
}
}
private class EnsureActionExpectation extends RowExpectation {
public EnsureActionExpectation(int col, int row) {
super(col, row);
}
@Override
protected SlimTestResult createEvaluationMessage(String actual, String expected) {
return (actual != null && actual.equals(BooleanConverter.TRUE)) ?
SlimTestResult.pass() : SlimTestResult.fail();
}
}
private class RejectActionExpectation extends RowExpectation {
public RejectActionExpectation(int col, int row) {
super(col, row);
}
@Override
protected SlimTestResult createEvaluationMessage(String actual, String expected) {
if (actual == null)
return SlimTestResult.pass();
else
return actual.equals(BooleanConverter.FALSE) ? SlimTestResult.pass() : SlimTestResult.fail();
}
}
private class ShowActionExpectation extends RowExpectation {
public ShowActionExpectation(int col, int row) {
super(col, row);
}
@Override
protected SlimTestResult createEvaluationMessage(String actual, String expected) {
try {
table.addColumnToRow(getRow(), actual);
} catch (Exception e) {
return SlimTestResult.fail(actual, e.getMessage());
}
return SlimTestResult.plain();
}
}
private class ArgumentExpectation extends RowExpectation {
private ArgumentExpectation(int col, int row) {
super(col, row);
}
@Override
public TestResult evaluateExpectation(Object returnValue) {
table.substitute(getCol(), getRow(), replaceSymbolsWithFullExpansion(getExpected()));
return null;
}
@Override
protected SlimTestResult createEvaluationMessage(String actual, String expected) {
return null;
}
}
}