// 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.responders.run;
import fitnesse.responders.PageFactory;
import fitnesse.wiki.PageData;
import fitnesse.wiki.ReadOnlyPageData;
import fitnesse.wiki.WikiPage;
import java.io.IOException;
import java.net.SocketException;
import java.util.Collections;
import java.util.Map;
public abstract class TestSystem implements TestSystemListener {
public static final String DEFAULT_COMMAND_PATTERN =
"java -cp fitnesse.jar" +
System.getProperties().get("path.separator") +
"%p %m";
public static final String DEFAULT_JAVA_DEBUG_COMMAND = "java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -cp %p %m";
public static final String DEFAULT_CSHARP_DEBUG_RUNNER_FIND = "runner.exe";
public static final String DEFAULT_CSHARP_DEBUG_RUNNER_REPLACE = "runnerw.exe";
protected WikiPage page;
protected boolean fastTest;
protected boolean manualStart;
protected static final String emptyPageContent = "OH NO! This page is empty!";
protected TestSystemListener testSystemListener;
protected ExecutionLog log;
public TestSystem(WikiPage page, TestSystemListener testSystemListener) {
this.page = page;
this.testSystemListener = testSystemListener;
}
public ExecutionLog getExecutionLog(String classPath, TestSystem.Descriptor descriptor) throws SocketException {
log = createExecutionLog(classPath, descriptor);
return log;
}
protected abstract ExecutionLog createExecutionLog(String classPath, Descriptor descriptor) throws SocketException;
protected String buildCommand(TestSystem.Descriptor descriptor, String classPath) {
String commandPattern = descriptor.commandPattern;
String command = replace(commandPattern, "%p", classPath);
command = replace(command, "%m", descriptor.testRunner);
return command;
}
private static String getRemoteDebugCommandPattern(ReadOnlyPageData pageData) {
String testRunner = pageData.getVariable("REMOTE_DEBUG_COMMAND");
if (testRunner == null) {
testRunner = pageData.getVariable(PageData.COMMAND_PATTERN);
if (testRunner == null || testRunner.toLowerCase().contains("java")) {
testRunner = DEFAULT_JAVA_DEBUG_COMMAND;
}
}
return testRunner;
}
private static String getNormalCommandPattern(ReadOnlyPageData pageData) {
String testRunner = pageData.getVariable(PageData.COMMAND_PATTERN);
if (testRunner == null)
testRunner = DEFAULT_COMMAND_PATTERN;
return testRunner;
}
private static String getCommandPattern(ReadOnlyPageData pageData, boolean isRemoteDebug) {
if (isRemoteDebug)
return getRemoteDebugCommandPattern(pageData);
else
return getNormalCommandPattern(pageData);
}
// String.replaceAll(...) is not trustworthy because it seems to remove all '\' characters.
protected static String replace(String value, String mark, String replacement) {
int index = value.indexOf(mark);
if (index == -1)
return value;
return value.substring(0, index) + replacement + value.substring(index + mark.length());
}
public void setFastTest(boolean fastTest) {
this.fastTest = fastTest;
}
public void setManualStart(boolean manualStart) {
this.manualStart = manualStart;
}
public static String getTestSystemName(PageData data) {
String testSystemName = getTestSystem(data);
String testRunner = getTestRunnerNormal(data);
return String.format("%s:%s", testSystemName, testRunner);
}
private static String getTestSystem(ReadOnlyPageData data) {
String testSystemName = data.getVariable("TEST_SYSTEM");
if (testSystemName == null)
return "fit";
return testSystemName;
}
public static String getPathSeparator(ReadOnlyPageData pageData) {
String separator = pageData.getVariable(PageData.PATH_SEPARATOR);
if (separator == null)
separator = (String) System.getProperties().get("path.separator");
return separator;
}
public static String getTestSystemType(String testSystemName) {
String parts[] = testSystemName.split(":");
return parts[0];
}
public void acceptOutputFirst(String output) throws IOException {
testSystemListener.acceptOutputFirst(output);
}
public void testComplete(TestSummary testSummary) throws IOException {
testSystemListener.testComplete(testSummary);
}
public void exceptionOccurred(Throwable e) {
log.addException(e);
log.addReason("Test execution aborted abnormally with error code " + log.getExitCode());
testSystemListener.exceptionOccurred(e);
}
public abstract void start() throws IOException;
private static String getTestRunner(ReadOnlyPageData pageData, boolean isRemoteDebug) {
if (isRemoteDebug)
return getTestRunnerDebug(pageData);
else
return getTestRunnerNormal(pageData);
}
private static String getTestRunnerDebug(ReadOnlyPageData data) {
String program = data.getVariable("REMOTE_DEBUG_RUNNER");
if (program == null) {
program = getTestRunnerNormal(data);
if (program.toLowerCase().contains(DEFAULT_CSHARP_DEBUG_RUNNER_FIND))
program = program.toLowerCase().replace(DEFAULT_CSHARP_DEBUG_RUNNER_FIND,
DEFAULT_CSHARP_DEBUG_RUNNER_REPLACE);
}
return program;
}
public static String getTestRunnerNormal(ReadOnlyPageData data) {
String program = data.getVariable(PageData.TEST_RUNNER);
if (program == null)
program = defaultTestRunner(data);
return program;
}
static String defaultTestRunner(ReadOnlyPageData data) {
String testSystemType = getTestSystemType(getTestSystem(data));
if ("slim".equalsIgnoreCase(testSystemType))
return "fitnesse.slim.SlimService";
else
return "fit.FitServer";
}
public abstract void bye() throws IOException, InterruptedException;
public abstract boolean isSuccessfullyStarted();
public abstract void kill() throws IOException;
public abstract String runTestsAndGenerateHtml(ReadOnlyPageData pageData) throws IOException, InterruptedException;
public static Descriptor getDescriptor(ReadOnlyPageData data, PageFactory pageFactory, boolean isRemoteDebug) {
String testSystemName = getTestSystem(data);
String testRunner = getTestRunner(data, isRemoteDebug);
String commandPattern = getCommandPattern(data, isRemoteDebug);
String pathSeparator = getPathSeparator(data);
return new Descriptor(testSystemName, testRunner, commandPattern, pathSeparator, pageFactory);
}
protected Map<String, String> createClasspathEnvironment(String classPath) {
String classpathProperty = page.readOnlyData().getVariable("CLASSPATH_PROPERTY");
Map<String, String> environmentVariables = null;
if (classpathProperty != null) {
environmentVariables = Collections.singletonMap(classpathProperty, classPath);
}
return environmentVariables;
}
public static class Descriptor {
public final String testSystemName;
public final String testRunner;
public final String commandPattern;
public final String pathSeparator;
public final PageFactory pageFactory;
Descriptor(String testSystemName, String testRunner, String commandPattern, String pathSeparator, PageFactory pageFactory) {
this.testSystemName = testSystemName;
this.testRunner = testRunner;
this.commandPattern = commandPattern;
this.pathSeparator = pathSeparator;
this.pageFactory = pageFactory;
}
public int hashCode() {
return testSystemName.hashCode() ^ testRunner.hashCode() ^ commandPattern.hashCode() ^ pathSeparator.hashCode();
}
public boolean equals(Object obj) {
Descriptor d = (Descriptor) obj;
return d.testSystemName.equals(testSystemName) &&
d.testRunner.equals(testRunner) &&
d.commandPattern.equals(commandPattern) &&
d.pathSeparator.equals(pathSeparator);
}
}
}