001: // Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
002: // Released under the terms of the GNU General Public License version 2 or later.
003: package fitnesse.responders.run;
004:
005: import fitnesse.html.*;
006: import fitnesse.responders.*;
007: import fitnesse.components.*;
008: import fitnesse.wiki.*;
009: import fitnesse.http.*;
010: import fitnesse.authentication.*;
011: import fit.Counts;
012: import java.util.*;
013:
014: public class TestResponder extends ChunkingResponder implements
015: FitClientListener, SecureResponder {
016: protected static final String emptyPageContent = "OH NO! This page is empty!";
017: public static final String DEFAULT_COMMAND_PATTERN = "java -cp %p %m";
018: protected static final int htmlDepth = 2;
019:
020: private static LinkedList<TestEventListener> eventListeners = new LinkedList<TestEventListener>();
021:
022: protected HtmlPage html;
023: protected CommandRunningFitClient client;
024: protected String command;
025: protected ExecutionLog log;
026: protected PageData data;
027: private boolean closed = false;
028: private Counts assertionCounts = new Counts();
029: protected TestHtmlFormatter formatter;
030: protected String classPath;
031: private String testableHtml;
032:
033: protected void doSending() throws Exception {
034: data = page.getData();
035: startHtml();
036:
037: sendPreTestNotification();
038:
039: prepareForExecution();
040:
041: startFitClient(classPath);
042:
043: if (client.isSuccessfullyStarted())
044: performExecution();
045:
046: finishSending();
047: }
048:
049: private void sendPreTestNotification() throws Exception {
050: for (Iterator iterator = eventListeners.iterator(); iterator
051: .hasNext();) {
052: TestEventListener testEventListener = (TestEventListener) iterator
053: .next();
054: testEventListener.notifyPreTest(this , data);
055: }
056: }
057:
058: protected void finishSending() throws Exception {
059: completeResponse();
060: }
061:
062: protected void performExecution() throws Exception {
063: client.send(testableHtml);
064: client.done();
065: client.join();
066: }
067:
068: protected void prepareForExecution() throws Exception {
069: addToResponse(HtmlUtil.getHtmlOfInheritedPage("PageHeader",
070: page));
071:
072: testableHtml = HtmlUtil.testableHtml(data, true);
073: if (testableHtml.length() == 0)
074: testableHtml = handleBlankHtml();
075: classPath = new ClassPathBuilder().getClasspath(page);
076: }
077:
078: protected void startHtml() throws Exception {
079: buildHtml();
080: addToResponse(formatter.head());
081: }
082:
083: protected void startFitClient(String classPath) throws Exception {
084: command = buildCommand(data, getClassName(data, request),
085: classPath);
086: client = new CommandRunningFitClient(this , command,
087: context.port, context.socketDealer);
088: log = new ExecutionLog(page, client.commandRunner);
089:
090: client.start();
091: }
092:
093: protected PageCrawler getPageCrawler() {
094: PageCrawler crawler = root.getPageCrawler();
095: crawler.setDeadEndStrategy(new VirtualEnabledPageCrawler());
096: return crawler;
097: }
098:
099: public void acceptResults(Counts counts) throws Exception {
100: assertionCounts.tally(counts);
101: }
102:
103: public synchronized void exceptionOccurred(Exception e) {
104: try {
105: log.addException(e);
106: log
107: .addReason("Test execution aborted abnormally with error code "
108: + client.commandRunner.getExitCode());
109:
110: completeResponse();
111: client.kill();
112: } catch (Exception e1) {
113: e1.printStackTrace();
114: }
115: }
116:
117: protected synchronized void completeResponse() throws Exception {
118: if (!closed) {
119: closed = true;
120: log.publish();
121: addLogAndClose();
122: }
123: }
124:
125: protected final void addLogAndClose() throws Exception {
126: addLog();
127: close();
128: }
129:
130: protected void close() throws Exception {
131: response.add(HtmlUtil
132: .getHtmlOfInheritedPage("PageFooter", page));
133: response.add(formatter.tail());
134: response.closeChunks();
135: response.addTrailingHeader("Exit-Code", String
136: .valueOf(client.commandRunner.getExitCode()));
137: response.closeTrailer();
138: response.close();
139: }
140:
141: protected void addLog() throws Exception {
142: response.add(formatter.testSummary(assertionCounts));
143: response.add(formatter.executionStatus(log));
144: }
145:
146: public void addToResponse(String output) throws Exception {
147: if (!closed)
148: response.add(output);
149: }
150:
151: public void acceptOutput(String output) throws Exception {
152: response.add(output);
153: }
154:
155: private String handleBlankHtml() throws Exception {
156: response.add(formatter.messageForBlankHtml());
157: return emptyPageContent;
158: }
159:
160: protected void buildHtml() throws Exception {
161: PageCrawler pageCrawler = page.getPageCrawler();
162: WikiPagePath fullPath = pageCrawler.getFullPath(page);
163: String fullPathName = PathParser.render(fullPath);
164: html = context.htmlPageFactory.newPage();
165: html.title.use(pageType() + ": " + fullPathName);
166: html.header.use(HtmlUtil.makeBreadCrumbsWithPageType(
167: fullPathName, pageType()));
168: html.actions.use(HtmlUtil.makeActions(data));
169: WikiImportProperty.handleImportProperties(html, page, data);
170:
171: makeFormatter();
172: }
173:
174: protected void makeFormatter() throws Exception {
175: formatter = new TestHtmlFormatter(html);
176: }
177:
178: protected String pageType() {
179: return "Test Results";
180: }
181:
182: protected String title() throws Exception {
183: WikiPagePath fullPath = getPageCrawler().getFullPath(page);
184: TagGroup group = new TagGroup();
185: group.add(HtmlUtil.makeLink(PathParser.render(fullPath), page
186: .getName()));
187: group.add(HtmlUtil.makeItalic(pageType()));
188: return group.html();
189: }
190:
191: public String getClassName(PageData data, Request request)
192: throws Exception {
193: String program = (String) request.getInput("className");
194: if (program == null)
195: program = data.getVariable("TEST_RUNNER");
196: if (program == null)
197: program = "fit.FitServer";
198: return program;
199: }
200:
201: protected String buildCommand(PageData data, String program,
202: String classPath) throws Exception {
203: String testRunner = data.getVariable("COMMAND_PATTERN");
204: if (testRunner == null)
205: testRunner = DEFAULT_COMMAND_PATTERN;
206: String command = replace(testRunner, "%p", classPath);
207: command = replace(command, "%m", program);
208: return command;
209: }
210:
211: // String.replaceAll(...) is not trustworthy because it seems to remove all '\' characters.
212: protected String replace(String value, String mark,
213: String replacement) {
214: int index = value.indexOf(mark);
215: if (index == -1)
216: return value;
217:
218: return value.substring(0, index) + replacement
219: + value.substring(index + mark.length());
220: }
221:
222: public SecureOperation getSecureOperation() {
223: return new SecureTestOperation();
224: }
225:
226: protected String cssClassFor(Counts count) {
227: if (count.wrong > 0)
228: return "fail";
229: else if (count.exceptions > 0
230: || count.right + count.ignores == 0)
231: return "error";
232: else if (count.ignores > 0 && count.right == 0)
233: return "ignore";
234: else
235: return "pass";
236: }
237:
238: protected int exitCode() {
239: return assertionCounts.wrong + assertionCounts.exceptions;
240: }
241:
242: public static void registerListener(TestEventListener listener) {
243: eventListeners.add(listener);
244: }
245: }
|