// 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.slimResponder;
import fitnesse.FitNesseContext;
import fitnesse.http.MockRequest;
import fitnesse.http.SimpleResponse;
import fitnesse.responders.run.TestSummary;
import fitnesse.responders.run.TestSystemListener;
import fitnesse.slim.SlimClient;
import fitnesse.slimTables.HtmlTableScanner;
import fitnesse.slimTables.Table;
import fitnesse.slimTables.TableScanner;
import fitnesse.testutil.FitNesseUtil;
import fitnesse.wiki.*;
import fitnesse.wikitext.Utils;
import fitnesse.wikitext.parser.Collapsible;
import fitnesse.wikitext.parser.Include;
import fitnesse.wikitext.parser.Symbol;
import fitnesse.wikitext.test.ParserTestHelper;
import org.junit.Before;
import org.junit.Test;
import java.net.ServerSocket;
import java.net.SocketException;
import static org.junit.Assert.*;
public class SlimTestSystemTest {
private WikiPage root;
private PageCrawler crawler;
private FitNesseContext context;
private MockRequest request;
protected SlimResponder responder;
private WikiPage testPage;
public String testResults;
protected SimpleResponse response;
private TestSystemListener dummyListener = new DummyListener();
private void assertTestResultsContain(String fragment) {
String unescapedResults = unescape(testResults);
assertTrue(unescapedResults, unescapedResults.indexOf(fragment) != -1);
}
private void assertTestResultsDoNotContain(String fragment) {
String unescapedResults = unescape(testResults);
assertTrue(unescapedResults, unescapedResults.indexOf(fragment) == -1);
}
private void getResultsForPageContents(String pageContents) throws Exception {
request.setResource("TestPage");
PageData data = testPage.getData();
data.setContent(data.getContent() + "\n" + pageContents);
testPage.commit(data);
response = (SimpleResponse) responder.makeResponse(context, request);
testResults = response.getContent();
}
@Before
public void setUp() throws Exception {
root = InMemoryPage.makeRoot("root");
crawler = root.getPageCrawler();
context = FitNesseUtil.makeTestContext(root);
request = new MockRequest();
responder = getSlimResponder();
responder.setFastTest(true);
// Enforce the test runner here, to make sure we're talking to the right system
testPage = crawler.addPage(root, PathParser.parse("TestPage"), "!define TEST_RUNNER {fitnesse.slim.SlimService}\n!path classes");
SlimTestSystem.clearSlimPortOffset();
}
protected SlimResponder getSlimResponder() {
return new HtmlSlimResponder();
}
@Test
public void portRotates() throws Exception {
SlimTestSystem sys = new HtmlSlimTestSystem(root, dummyListener);
for (int i = 1; i < 15; i++)
assertEquals(8085 + (i % 10), sys.getNextSlimSocket());
}
@Test
public void portStartsAtSlimPortVariable() throws Exception {
WikiPage pageWithSlimPortDefined = crawler.addPage(root, PathParser.parse("PageWithSlimPortDefined"), "!define SLIM_PORT {9000}\n");
SlimTestSystem sys = new HtmlSlimTestSystem(pageWithSlimPortDefined, dummyListener);
for (int i = 1; i < 15; i++)
assertEquals(9000 + (i % 10), sys.getNextSlimSocket());
}
@Test
public void badSlimPortVariableDefaults() throws Exception {
WikiPage pageWithBadSlimPortDefined = crawler.addPage(root, PathParser.parse("PageWithBadSlimPortDefined"), "!define SLIM_PORT {BOB}\n");
SlimTestSystem sys = new HtmlSlimTestSystem(pageWithBadSlimPortDefined, dummyListener);
for (int i = 1; i < 15; i++)
assertEquals(8085 + (i % 10), sys.getNextSlimSocket());
}
@Test
public void slimHostDefaultsTolocalhost() throws Exception {
WikiPage pageWithoutSlimHostVariable = crawler.addPage(root, PathParser.parse("PageWithoutSlimHostVariable"), "some gunk\n");
SlimTestSystem sys = new HtmlSlimTestSystem(pageWithoutSlimHostVariable, dummyListener);
assertEquals("localhost", sys.determineSlimHost());
}
@Test
public void slimHostVariableSetsTheHost() throws Exception {
WikiPage pageWithSlimHostVariable = crawler.addPage(root, PathParser.parse("PageWithSlimHostVariable"), "!define SLIM_HOST {somehost}\n");
SlimTestSystem sys = new HtmlSlimTestSystem(pageWithSlimHostVariable, dummyListener);
assertEquals("somehost", sys.determineSlimHost());
}
@Test
public void slimResponderStartsAndQuitsSlim() throws Exception {
responder.setFastTest(false);
request.setResource("TestPage");
responder.makeResponse(context, request);
assertTrue(!responder.slimOpen());
}
@Test
public void verboseOutputIfSlimFlagSet() throws Exception {
getResultsForPageContents("!define SLIM_FLAGS {-v}\n");
assertTrue(responder.getCommandLine().indexOf("fitnesse.slim.SlimService -v") != -1);
}
@Test
public void tableWithoutPrefixWillBeConstructed() throws Exception {
getResultsForPageContents("|XX|\n");
assertTestResultsContain("<td>XX <span class=\"error\">Could not invoke constructor for XX[0]</span></td>");
}
@Test
public void emptyQueryTable() throws Exception {
getResultsForPageContents("|Query:x|\n");
assertTestResultsContain("Query tables must have at least two rows.");
}
@Test
public void queryFixtureHasNoQueryFunction() throws Exception {
getResultsForPageContents(
"!|Query:fitnesse.slim.test.TestSlim|\n" +
"|x|y|\n"
);
assertTestResultsContain("Method query[0] not found in fitnesse.slim.test.TestSlim");
}
@Test
public void emptyOrderedQueryTable() throws Exception {
getResultsForPageContents("|ordered query:x|\n");
assertTestResultsContain("Query tables must have at least two rows.");
}
@Test
public void orderedQueryFixtureHasNoQueryFunction() throws Exception {
getResultsForPageContents(
"!|ordered query:fitnesse.slim.test.TestSlim|\n" +
"|x|y|\n"
);
assertTestResultsContain("Method query[0] not found in fitnesse.slim.test.TestSlim");
}
@Test
public void emptySubsetQueryTable() throws Exception {
getResultsForPageContents("|subset query:x|\n");
assertTestResultsContain("Query tables must have at least two rows.");
}
@Test
public void subsetQueryFixtureHasNoQueryFunction() throws Exception {
getResultsForPageContents(
"!|subset query:fitnesse.slim.test.TestSlim|\n" +
"|x|y|\n"
);
assertTestResultsContain("Method query[0] not found in fitnesse.slim.test.TestSlim");
}
@Test
public void scriptTableWithBadConstructor() throws Exception {
getResultsForPageContents("!|Script|NoSuchClass|\n");
assertTestResultsContain("<span class=\"error\">Could not invoke constructor for NoSuchClass");
}
@Test
public void emptyImportTable() throws Exception {
getResultsForPageContents("|Import|\n");
assertTestResultsContain("Import tables must have at least two rows.");
}
@Test
public void emptyTableTable() throws Exception {
getResultsForPageContents("!|Table:TableFixture|\n");
assertTestResultsContain("<span class=\"error\">Could not invoke constructor for TableFixture[0]</span>");
}
@Test
public void tableFixtureHasNoDoTableFunction() throws Exception {
getResultsForPageContents(
"!|Table:fitnesse.slim.test.TestSlim|\n" +
"|a|b|\n"
);
assertTestResultsContain("Table fixture has no valid doTable method");
}
@Test
public void simpleDecisionTable() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.TestSlim|\n" +
"|returnInt?|\n" +
"|7|\n"
);
assertTestResultsContain("<span class=\"pass\">7</span>");
}
@Test
public void decisionTableIgnoresMethodMissingForResetExecuteaAndTable() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.DummyDecisionTable|\n" +
"|x?|\n" +
"|1|\n"
);
assertEquals(0, responder.getTestSummary().getExceptions());
}
@Test
public void decisionTableWithNoResetDoesNotCountExceptionsForExecute() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.DummyDecisionTableWithExecuteButNoReset|\n" +
"|x?|\n" +
"|1|\n"
);
assertEquals(0, responder.getTestSummary().getExceptions());
}
@Test
public void queryTableWithoutTableFunctionIgnoresMissingMethodException() throws Exception {
getResultsForPageContents(
"!|query:fitnesse.slim.test.DummyQueryTableWithNoTableMethod|\n" +
"|x|\n" +
"|1|\n"
);
assertEquals(0, responder.getTestSummary().getExceptions());
}
@Test
public void decisionTableWithExecuteThatThrowsDoesShowsException() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.DecisionTableExecuteThrows|\n" +
"|x?|\n" +
"|1|\n"
);
assertEquals(1, responder.getTestSummary().getExceptions());
assertTestResultsContain("EXECUTE_THROWS");
}
@Test
public void tableWithException() throws Exception {
getResultsForPageContents(
"!|DT:NoSuchClass|\n" +
"|returnInt?|\n" +
"|7|\n"
);
assertTestResultsContain("<span class=\"error\">Could not invoke constructor for NoSuchClass");
}
@Test
public void tableWithBadConstructorHasException() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.TestSlim|badArgument|\n" +
"|returnConstructorArgument?|\n" +
"|3|\n"
);
TableScanner ts = new HtmlTableScanner(responder.getTestResults().getHtml());
ts.getTable(0);
assertTestResultsContain("Could not invoke constructor");
}
@Test
public void tableWithBadVariableHasException() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.TestSlim|\n" +
"|noSuchVar|\n" +
"|3|\n"
);
assertTestResultsContain("<span class=\"error\">Method setNoSuchVar[1] not found in fitnesse.slim.test.TestSlim");
}
@Test
public void tableWithStopTestMessageException() throws Exception {
getResultsForPageContents("!|DT:fitnesse.slim.test.TestSlim|\n" +
"|throwStopTestExceptionWithMessage?|\n" +
"| once |\n" +
"| twice |\n");
assertTestResultsContain("<td>once <span class=\"fail\">Stop Test</span></td>");
assertTestResultsContain("<td>twice <span class=\"ignore\">Test not run</span>");
}
@Test
public void tableWithMessageException() throws Exception {
getResultsForPageContents("!|DT:fitnesse.slim.test.TestSlim|\n" +
"|throwExceptionWithMessage?|\n" +
"| once |\n");
assertTestResultsContain("<td>once <span class=\"error\">Test message</span></td>");
}
@Test
public void tableWithStopTestExceptionThrown() throws Exception {
getResultsForPageContents("!|DT:fitnesse.slim.test.TestSlim|\n" +
"|throwNormal?| throwStopping? |\n" +
"| first | second |\n" +
"| should fail1| true |\n" +
"\n\n" +
"!|DT:fitnesse.slim.test.ThrowException|\n" +
"|throwNormal?|\n" +
"| should fail2|\n"
);
assertTestResultsContain("<td><span class=\"error\">Exception: <a href");
assertTestResultsContain("<td><span class=\"error\">Exception: <a href");
assertTestResultsContain("<td>should fail1 <span class=\"ignore\">Test not run</span></td>");
assertTestResultsContain("<td>should fail2 <span class=\"ignore\">Test not run</span></td>");
}
@Test
public void tableWithSymbolSubstitution() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.TestSlim|\n" +
"|string|getStringArg?|\n" +
"|Bob|$V=|\n" +
"|$V|$V|\n"
);
TableScanner ts = getScannedResults();
Table dt = ts.getTable(0);
assertEquals("$V<-[Bob]", unescape(dt.getCellContents(1, 2)));
assertEquals("$V->[Bob]", unescape(dt.getCellContents(0, 3)));
}
protected TableScanner getScannedResults() throws Exception {
return new HtmlTableScanner(testResults);
}
private String unescape(String x) {
return Utils.unescapeWiki(Utils.unescapeHTML(x));
}
@Test
public void substituteSymbolIntoExpression() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.TestSlim|\n" +
"|string|getStringArg?|\n" +
"|3|$A=|\n" +
"|2|<$A|\n" +
"|5|$B=|\n" +
"|4|$A<_<$B|\n"
);
TableScanner ts = getScannedResults();
Table dt = ts.getTable(0);
assertEquals("<span class=\"pass\">2<$A->[3]</span>", unescape(dt.getCellContents(1, 3)));
assertEquals("<span class=\"pass\">$A->[3]<4<$B->[5]</span>", unescape(dt.getCellContents(1, 5)));
}
@Test
public void tableWithExpression() throws Exception {
getResultsForPageContents(
"!|DT:fitnesse.slim.test.TestSlim|\n" +
"|string|getStringArg?|\n" +
"|${=3+4=}|7|\n"
);
TableScanner ts = getScannedResults();
Table dt = ts.getTable(0);
assertEquals("<span class=\"pass\">7</span>", dt.getCellContents(1, 2));
}
@Test
public void noSuchConverter() throws Exception {
getResultsForPageContents(
"|!-DT:fitnesse.slim.test.TestSlim-!|\n" +
"|noSuchConverter|noSuchConverter?|\n" +
"|x|x|\n"
);
TableScanner ts = getScannedResults();
Table dt = ts.getTable(0);
assertEquals("x <span class=\"error\">No converter for fitnesse.slim.test.TestSlim$NoSuchConverter.</span>", dt.getCellContents(0, 2));
}
@Test
public void translateExceptionMessage() throws Exception {
assertTranslatedException("Could not find constructor for SomeClass", "NO_CONSTRUCTOR SomeClass");
assertTranslatedException("Could not invoke constructor for SomeClass", "COULD_NOT_INVOKE_CONSTRUCTOR SomeClass");
assertTranslatedException("No converter for SomeClass", "NO_CONVERTER_FOR_ARGUMENT_NUMBER SomeClass");
assertTranslatedException("Method someMethod not found in SomeClass", "NO_METHOD_IN_CLASS someMethod SomeClass");
assertTranslatedException("The instance someInstance does not exist", "NO_INSTANCE someInstance");
assertTranslatedException("Could not find class SomeClass", "NO_CLASS SomeClass");
assertTranslatedException("The instruction [a, b, c] is malformed", "MALFORMED_INSTRUCTION [a, b, c]");
}
private void assertTranslatedException(String expected, String message) {
assertEquals(expected, SlimTestSystem.translateExceptionMessage(message));
}
@Test
public void returnedListsBecomeStrings() throws Exception {
getResultsForPageContents("!|script|\n" +
"|start|fitnesse.slim.test.TestSlim|\n" +
"|one list|1,2|\n" +
"|check|get list arg|[1, 2]|\n");
assertTestResultsContain("<td><span class=\"pass\">[1, 2]</span></td>");
}
@Test
public void nullStringReturned() throws Exception {
getResultsForPageContents("!|fitnesse.slim.test.TestSlim|\n" +
"|nullString?|\n" +
"|null|\n");
assertTestResultsContain("<td><span class=\"pass\">null</span></td>");
}
@Test
public void reportableExceptionsAreReported() throws Exception {
getResultsForPageContents(
"!|fitnesse.slim.test.ExecuteThrowsReportableException|\n" +
"|x|\n" +
"|1|\n");
assertTestResultsContain("A Reportable Exception");
}
@Test
public void versionMismatchIsNotReported() throws Exception {
getResultsForPageContents("");
assertTestResultsDoNotContain("Slim Protocol Version Error");
}
@Test
public void versionMismatchIsReported() throws Exception {
SlimClient.MINIMUM_REQUIRED_SLIM_VERSION = 1000.0; // I doubt will ever get here.
getResultsForPageContents("");
assertTestResultsContain("Slim Protocol Version Error");
}
@Test
public void checkTestClassPrecededByDefine() throws Exception {
getResultsForPageContents("!define PI {3.141592}\n" +
"!path classes\n" +
"!path fitnesse.jar\n" +
"|fitnesse.testutil.PassFixture|\n");
assertTestResultsContain("PassFixture");
}
@Test
public void emptyScenarioTable() throws Exception {
getResultsForPageContents("|Scenario|\n");
assertTestResultsContain("Scenario tables must have a name.");
}
@Test
public void scenarioTableIsRegistered() throws Exception {
getResultsForPageContents("|Scenario|myScenario|\n");
assertTrue("scenario should be registered", responder.testSystem.getScenarios().containsKey("myScenario"));
}
@Test(expected = SocketException.class)
public void createSlimServiceFailsFastWhenSlimPortIsNotAvailable() throws Exception {
final int slimServerPort = 10258;
ServerSocket slimSocket = new ServerSocket(slimServerPort);
try {
SlimTestSystem sys = new HtmlSlimTestSystem(root, dummyListener);
String slimArguments = String.format("%s %d", "", slimServerPort);
sys.createSlimService(slimArguments);
} finally {
slimSocket.close();
}
}
@Test
public void gettingPrecompiledScenarioWidgetsForChildLibraryPage() throws Exception {
WikiPage suitePage = crawler.addPage(root, PathParser.parse("MySuite"), "my suite content");
crawler.addPage(suitePage, PathParser.parse("ScenarioLibrary"), "child library");
SlimTestSystem sys = new HtmlSlimTestSystem(suitePage, dummyListener);
Symbol scenarios = sys.getPreparsedScenarioLibrary();
Symbol includeParent = getCollapsibleSymbol(scenarios);
assertNotNull(includeParent);
assertEquals("Precompiled Libraries", ParserTestHelper.serializeContent(includeParent.childAt(0)));
Symbol childLibraryInclude = getIncludeSymbol(includeParent.childAt(1));
assertTrue(ParserTestHelper.serializeContent(childLibraryInclude).contains("child library"));
}
@Test
public void gettingPrecompiledScenarioWidgetsForUncleLibraryPage() throws Exception {
WikiPage suitePage = crawler.addPage(root, PathParser.parse("ParentPage.MySuite"), "my suite content");
crawler.addPage(root, PathParser.parse("ScenarioLibrary"), "uncle library");
SlimTestSystem sys = new HtmlSlimTestSystem(suitePage, dummyListener);
Symbol scenarios = sys.getPreparsedScenarioLibrary();
Symbol includeParent = getCollapsibleSymbol(scenarios);
assertNotNull(includeParent);
assertEquals("Precompiled Libraries", ParserTestHelper.serializeContent(includeParent.childAt(0)));
Symbol uncleLibraryInclude = getIncludeSymbol(includeParent.childAt(1));
assertNotNull(uncleLibraryInclude);
assertTrue(ParserTestHelper.serializeContent(uncleLibraryInclude).contains("uncle library"));
}
@Test
public void precompiledScenarioWidgetsAreCreatedOnlyOnce() throws Exception {
WikiPage suitePage = crawler.addPage(root, PathParser.parse("MySuite"), "my suite content");
SlimTestSystem sys = new HtmlSlimTestSystem(suitePage, dummyListener);
assertSame(sys.getPreparsedScenarioLibrary(), sys.getPreparsedScenarioLibrary());
}
private Symbol getIncludeSymbol(Symbol collapsibleSymbol) {
for (Symbol symbol : collapsibleSymbol.getChildren())
if (symbol.getType() instanceof Include)
return symbol;
return null;
}
private Symbol getCollapsibleSymbol(Symbol syntaxTree) throws Exception {
for (Symbol symbol : syntaxTree.getChildren()) {
if (symbol.getType() instanceof Collapsible)
return symbol;
}
return null;
}
private static class DummyListener implements TestSystemListener {
public void acceptOutputFirst(String output) {
}
public void testComplete(TestSummary testSummary) {
}
public void exceptionOccurred(Throwable e) {
}
}
}