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