// 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;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import fitnesse.slim.instructions.AssignInstruction;
import fitnesse.slim.instructions.Instruction;
import fitnesse.testsystems.*;
import fitnesse.testsystems.slim.results.SlimExceptionResult;
import fitnesse.testsystems.slim.tables.SlimAssertion;
import fitnesse.testsystems.slim.tables.SlimTable;
import static fitnesse.slim.SlimServer.*;
public abstract class SlimTestSystem implements TestSystem {
private final SlimClient slimClient;
private final CompositeTestSystemListener testSystemListener;
private final String testSystemName;
private SlimTestContextImpl testContext;
boolean stopTestCalled;
private boolean stopSuiteCalled;
private boolean testSystemIsStopped;
public SlimTestSystem(String testSystemName, SlimClient slimClient) {
this.testSystemName = testSystemName;
this.slimClient = slimClient;
this.testSystemListener = new CompositeTestSystemListener();
}
public SlimTestContext getTestContext() {
return testContext;
}
@Override
public String getName() {
return testSystemName;
}
@Override
public boolean isSuccessfullyStarted() {
return !testSystemIsStopped;
}
@Override
public void start() throws UnableToStartException {
try {
slimClient.start();
} catch (SlimVersionMismatch slimVersionMismatch) {
stopTestSystem(slimVersionMismatch);
return;
} catch (IOException e) {
stopTestSystem(e);
throw new UnableToStartException("Could not start test system", e);
}
testSystemListener.testSystemStarted(this);
}
@Override
public void kill() {
slimClient.kill();
}
@Override
public void bye() throws UnableToStopException {
if (testSystemIsStopped) return;
try {
slimClient.bye();
testSystemStopped(null);
} catch (IOException e) {
stopTestSystem(e);
throw new UnableToStopException("Could not stop test system", e);
}
}
@Override
public void runTests(TestPage pageToTest) throws TestExecutionException {
initializeTest(pageToTest);
testStarted(pageToTest);
try {
processAllTablesOnPage(pageToTest);
testComplete(pageToTest, testContext.getTestSummary());
} catch (Exception e) {
stopTestSystem(e);
throw new TestExecutionException(e);
}
}
@Override
public void addTestSystemListener(TestSystemListener listener) {
testSystemListener.addTestSystemListener(listener);
}
private void initializeTest(TestPage testPage) {
testContext = createTestContext(testPage);
stopTestCalled = false;
}
protected SlimTestContextImpl createTestContext(TestPage testPage) {
return new SlimTestContextImpl(testPage);
}
protected abstract void processAllTablesOnPage(TestPage testPage) throws TestExecutionException;
protected void processTable(SlimTable table, boolean isSuiteTearDownPage) throws TestExecutionException {
List<SlimAssertion> assertions = table.getAssertions();
final Map<String, Object> instructionResults;
if (stopTestCalled && !table.isTearDown()) {
instructionResults = Collections.emptyMap();
} else {
boolean tearDownOfAlreadyStartedTest = stopTestCalled && table.isTearDown();
if (stopSuiteCalled && !isSuiteTearDownPage && !tearDownOfAlreadyStartedTest) {
instructionResults = Collections.emptyMap();
} else {
instructionResults = slimClient.invokeAndGetResponse(SlimAssertion.getInstructions(assertions));
}
}
evaluateTables(assertions, instructionResults);
}
protected void evaluateTables(List<SlimAssertion> assertions, Map<String, Object> instructionResults) throws SlimCommunicationException {
for (SlimAssertion a : assertions) {
final String key = a.getInstruction().getId();
final Object returnValue = instructionResults.get(key);
//Exception management
if (returnValue != null && returnValue instanceof String && ((String) returnValue).startsWith(EXCEPTION_TAG)) {
SlimExceptionResult exceptionResult = new SlimExceptionResult(key, (String) returnValue);
if (exceptionResult.isStopTestException()) {
stopTestCalled = true;
}
if (exceptionResult.isStopSuiteException()) {
stopTestCalled = stopSuiteCalled = true;
}
exceptionResult = a.getExpectation().evaluateException(exceptionResult);
if (exceptionResult != null) {
testExceptionOccurred(a, exceptionResult);
}
} else {
//Normal results
TestResult testResult = a.getExpectation().evaluateExpectation(returnValue);
testAssertionVerified(a, testResult);
//Retrieve variables set during expectation step
if (testResult != null) {
Map<String, ?> variables = testResult.getVariablesToStore();
if (variables != null) {
List<Instruction> instructions = new ArrayList<>(variables.size());
int i = 0;
for (Entry<String, ?> variable : variables.entrySet()) {
instructions.add(new AssignInstruction("assign_" + i++, variable.getKey(), variable.getValue()));
}
//Store variables in context
if (i > 0) {
slimClient.invokeAndGetResponse(instructions);
}
}
}
}
}
}
protected void testOutputChunk(String output) {
testSystemListener.testOutputChunk(output);
}
protected void testStarted(TestPage testPage) {
testSystemListener.testStarted(testPage);
}
protected void testComplete(TestPage testPage, TestSummary testSummary) {
testSystemListener.testComplete(testPage, testSummary);
}
protected void stopTestSystem(Throwable e) {
slimClient.kill();
testSystemStopped(e);
}
protected void testAssertionVerified(Assertion assertion, TestResult testResult) {
testSystemListener.testAssertionVerified(assertion, testResult);
}
protected void testExceptionOccurred(Assertion assertion, ExceptionResult exceptionResult) {
testSystemListener.testExceptionOccurred(assertion, exceptionResult);
}
// Ensure testSystemStopped is called only once per test system. First call counts.
protected void testSystemStopped(Throwable e) {
if (testSystemIsStopped) return;
testSystemIsStopped = true;
testSystemListener.testSystemStopped(this, e);
}
}