001: package com.meterware.servletunit;
002:
003: /********************************************************************************************************************
004: * $Id: JUnitServlet.java,v 1.7 2003/07/22 01:49:08 russgold Exp $
005: *
006: * Copyright (c) 2001-2003, Russell Gold
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
009: * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
010: * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
011: * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included in all copies or substantial portions
014: * of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
017: * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
019: * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020: * DEALINGS IN THE SOFTWARE.
021: *
022: *******************************************************************************************************************/
023: import java.io.IOException;
024: import java.io.PrintWriter;
025: import java.util.Enumeration;
026:
027: import javax.servlet.http.HttpServlet;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030: import javax.servlet.ServletException;
031:
032: import junit.runner.BaseTestRunner;
033: import junit.runner.TestSuiteLoader;
034: import junit.runner.StandardTestSuiteLoader;
035: import junit.framework.Test;
036: import junit.framework.AssertionFailedError;
037: import junit.framework.TestResult;
038: import junit.framework.TestFailure;
039:
040: /**
041: * A servlet which can run unit tests inside a servlet context. It may be extended to provide InvocationContext-access
042: * to such tests if a container-specific implementation of InvocationContextFactory is provided.
043: * Combined with ServletTestCase, this would permit
044: * in-container tests of servlets in a fashion similar to that supported by ServletUnit.
045: *
046: * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
047: **/
048: public class JUnitServlet extends HttpServlet {
049:
050: public JUnitServlet() {
051: }
052:
053: protected JUnitServlet(InvocationContextFactory factory) {
054: _factory = factory;
055: }
056:
057: protected void doGet(HttpServletRequest request,
058: HttpServletResponse response) throws ServletException,
059: IOException {
060: ResultsFormatter formatter = getResultsFormatter(request
061: .getParameter("format"));
062: response.setContentType(formatter.getContentType());
063: final String testName = request.getParameter("test");
064: if (testName == null || testName.length() == 0) {
065: reportCannotRunTest(response.getWriter(),
066: "No test class specified");
067: } else {
068: ServletTestRunner runner = new ServletTestRunner(response
069: .getWriter(), formatter);
070: runner.runTestSuite(testName);
071: }
072: response.getWriter().close();
073: }
074:
075: private ResultsFormatter getResultsFormatter(String formatterName) {
076: if ("text".equalsIgnoreCase(formatterName)) {
077: return new TextResultsFormatter();
078: } else if ("xml".equalsIgnoreCase(formatterName)) {
079: return new XMLResultsFormatter();
080: } else {
081: return new HTMLResultsFormatter();
082: }
083: }
084:
085: private InvocationContextFactory _factory;
086:
087: private void reportCannotRunTest(PrintWriter writer,
088: final String errorMessage) {
089: writer
090: .print("<html><head><title>Cannot run test</title></head><body>"
091: + errorMessage + "</body></html>");
092: }
093:
094: class ServletTestRunner extends BaseTestRunner {
095: private PrintWriter _writer;
096: private ResultsFormatter _formatter;
097:
098: public ServletTestRunner(PrintWriter writer,
099: ResultsFormatter formatter) {
100: ServletTestCase.setInvocationContextFactory(_factory);
101: _writer = writer;
102: _formatter = formatter;
103: }
104:
105: void runTestSuite(String testClassName) {
106: Test suite = getTest(testClassName);
107:
108: if (suite != null) {
109: TestResult testResult = new TestResult();
110: testResult.addListener(this );
111: long startTime = System.currentTimeMillis();
112: suite.run(testResult);
113: long endTime = System.currentTimeMillis();
114: _formatter.displayResults(_writer, testClassName,
115: elapsedTimeAsString(endTime - startTime),
116: testResult);
117: }
118: }
119:
120: public void addError(Test test, Throwable throwable) {
121: }
122:
123: public void addFailure(Test test, AssertionFailedError error) {
124: }
125:
126: public void endTest(Test test) {
127: }
128:
129: protected void runFailed(String s) {
130: reportCannotRunTest(_writer, s);
131: }
132:
133: public void startTest(Test test) {
134: }
135:
136: public void testStarted(String s) {
137: }
138:
139: public void testEnded(String s) {
140: }
141:
142: public void testFailed(int i, Test test, Throwable throwable) {
143: }
144:
145: /**
146: * Always use the StandardTestSuiteLoader. Overridden from
147: * BaseTestRunner.
148: */
149: public TestSuiteLoader getLoader() {
150: return new StandardTestSuiteLoader();
151: }
152:
153: }
154:
155: static abstract class ResultsFormatter {
156:
157: private static final char LF = 10;
158: private static final char CR = 13;
159:
160: abstract String getContentType();
161:
162: void displayResults(PrintWriter writer, String testClassName,
163: String elapsedTimeString, TestResult testResult) {
164: displayHeader(writer, testClassName, testResult,
165: elapsedTimeString);
166: displayResults(writer, testResult);
167: displayFooter(writer);
168: }
169:
170: protected abstract void displayHeader(PrintWriter writer,
171: String testClassName, TestResult testResult,
172: String elapsedTimeString);
173:
174: protected abstract void displayResults(PrintWriter writer,
175: TestResult testResult);
176:
177: protected abstract void displayFooter(PrintWriter writer);
178:
179: protected String sgmlEscape(String s) {
180: if (s == null)
181: return "NULL";
182: StringBuffer result = new StringBuffer(s.length());
183: char[] chars = s.toCharArray();
184: for (int i = 0; i < chars.length; i++) {
185: switch (chars[i]) {
186: case '&':
187: result.append("&");
188: break;
189: case '<':
190: result.append("<");
191: break;
192: case '>':
193: result.append(">");
194: break;
195: case LF:
196: if (i > 0 && chars[i - 1] == CR) {
197: result.append(chars[i]);
198: break;
199: }
200: case CR:
201: result.append(getLineBreak());
202: default:
203: result.append(chars[i]);
204: }
205: }
206: return result.toString();
207: }
208:
209: protected String getLineBreak() {
210: return "<br>";
211: }
212: }
213:
214: abstract static class DisplayedResultsFormatter extends
215: ResultsFormatter {
216:
217: protected void displayHeader(PrintWriter writer,
218: String testClassName, TestResult testResult,
219: String elapsedTimeString) {
220: displayHeader(writer, testClassName, getFormatted(
221: testResult.runCount(), "test"), elapsedTimeString,
222: testResult.wasSuccessful() ? "OK"
223: : "Problems Occurred");
224: }
225:
226: protected void displayResults(PrintWriter writer,
227: TestResult testResult) {
228: if (!testResult.wasSuccessful()) {
229: displayProblems(writer, "failure", testResult
230: .failureCount(), testResult.failures());
231: displayProblems(writer, "error", testResult
232: .errorCount(), testResult.errors());
233: }
234: }
235:
236: protected abstract void displayHeader(PrintWriter writer,
237: String testClassName, String testCountText,
238: String elapsedTimeString, String resultString);
239:
240: protected abstract void displayProblemTitle(PrintWriter writer,
241: String title);
242:
243: protected abstract void displayProblemDetailHeader(
244: PrintWriter writer, int i, String testName);
245:
246: protected abstract void displayProblemDetailFooter(
247: PrintWriter writer);
248:
249: protected abstract void displayProblemDetail(
250: PrintWriter writer, String message);
251:
252: private void displayProblems(PrintWriter writer, String kind,
253: int count, Enumeration enumeration) {
254: if (count != 0) {
255: displayProblemTitle(writer, getFormatted(count, kind));
256: Enumeration e = enumeration;
257: for (int i = 1; e.hasMoreElements(); i++) {
258: TestFailure failure = (TestFailure) e.nextElement();
259: displayProblemDetailHeader(writer, i, failure
260: .failedTest().toString());
261: if (failure.thrownException() instanceof AssertionFailedError) {
262: displayProblemDetail(writer, failure
263: .thrownException().getMessage());
264: } else {
265: displayProblemDetail(writer, BaseTestRunner
266: .getFilteredTrace(failure
267: .thrownException()));
268: }
269: displayProblemDetailFooter(writer);
270: }
271: }
272: }
273:
274: private String getFormatted(int count, String name) {
275: return count + " " + name + (count == 1 ? "" : "s");
276: }
277:
278: }
279:
280: static class TextResultsFormatter extends DisplayedResultsFormatter {
281:
282: String getContentType() {
283: return "text/plain";
284: }
285:
286: protected void displayHeader(PrintWriter writer,
287: String testClassName, String testCountText,
288: String elapsedTimeString, String resultString) {
289: writer.println(testClassName + " (" + testCountText + "): "
290: + resultString);
291: }
292:
293: protected void displayFooter(PrintWriter writer) {
294: }
295:
296: protected void displayProblemTitle(PrintWriter writer,
297: String title) {
298: writer.println();
299: writer.println(title + ':');
300: }
301:
302: protected void displayProblemDetailHeader(PrintWriter writer,
303: int i, String testName) {
304: writer.println(i + ". " + testName + ":");
305: }
306:
307: protected void displayProblemDetailFooter(PrintWriter writer) {
308: writer.println();
309: }
310:
311: protected void displayProblemDetail(PrintWriter writer,
312: String message) {
313: writer.println(message);
314: }
315: }
316:
317: static class HTMLResultsFormatter extends DisplayedResultsFormatter {
318:
319: String getContentType() {
320: return "text/html";
321: }
322:
323: protected void displayHeader(PrintWriter writer,
324: String testClassName, String testCountText,
325: String elapsedTimeString, String resultString) {
326: writer.println("<html><head><title>Test Suite: "
327: + testClassName + "</title>");
328: writer.println("<style type='text/css'>");
329: writer.println("<!--");
330: writer
331: .println(" td.detail { font-size:smaller; vertical-align: top }");
332: writer.println(" -->");
333: writer.println("</style></head><body>");
334: writer.println("<table id='results' border='1'><tr>");
335: writer.println("<td>" + testCountText + "</td>");
336: writer.println("<td>Time: " + elapsedTimeString + "</td>");
337: writer.println("<td>" + resultString + "</td></tr>");
338: }
339:
340: protected void displayFooter(PrintWriter writer) {
341: writer.println("</table>");
342: writer.println("</body></html>");
343: }
344:
345: protected void displayProblemTitle(PrintWriter writer,
346: String title) {
347: writer.println("<tr><td colspan=3>" + title + "</td></tr>");
348: }
349:
350: protected void displayProblemDetailHeader(PrintWriter writer,
351: int i, String testName) {
352: writer.println("<tr><td class='detail' align='right'>" + i
353: + "</td>");
354: writer.println("<td class='detail'>" + testName
355: + "</td><td class='detail'>");
356: }
357:
358: protected void displayProblemDetailFooter(PrintWriter writer) {
359: writer.println("</td></tr>");
360: }
361:
362: protected void displayProblemDetail(PrintWriter writer,
363: String message) {
364: writer.println(sgmlEscape(message));
365: }
366:
367: }
368:
369: static class XMLResultsFormatter extends ResultsFormatter {
370:
371: String getContentType() {
372: return "text/xml;charset=UTF-8";
373: }
374:
375: protected void displayHeader(PrintWriter writer,
376: String testClassName, TestResult testResult,
377: String elapsedTimeString) {
378: writer.println("<?xml version='1.0' encoding='UTF-8' ?>\n"
379: + "<testsuite name=" + asAttribute(testClassName)
380: + " tests=" + asAttribute(testResult.runCount())
381: + " failures="
382: + asAttribute(testResult.failureCount())
383: + " errors=" + asAttribute(testResult.errorCount())
384: + " time=" + asAttribute(elapsedTimeString) + ">");
385: }
386:
387: private String asAttribute(int value) {
388: return '"' + Integer.toString(value) + '"';
389: }
390:
391: private String asAttribute(String value) {
392: return '"' + sgmlEscape(value) + '"';
393: }
394:
395: protected void displayFooter(PrintWriter writer) {
396: writer.println("</testsuite>");
397: }
398:
399: protected void displayResults(PrintWriter writer,
400: TestResult testResult) {
401: displayResults(writer, "failure", testResult.failures());
402: displayResults(writer, "error", testResult.errors());
403: }
404:
405: private void displayResults(PrintWriter writer,
406: String failureNodeName, Enumeration resultsEnumeration) {
407: for (Enumeration e = resultsEnumeration; e
408: .hasMoreElements();) {
409: TestFailure failure = (TestFailure) e.nextElement();
410: writer.println(" <testcase name="
411: + asAttribute(failure.failedTest().toString())
412: + ">");
413: writer.print(" <"
414: + failureNodeName
415: + " type="
416: + asAttribute(failure.thrownException()
417: .getClass().getName()) + " message="
418: + asAttribute(failure.exceptionMessage()));
419: if (!displayException(failure)) {
420: writer.println("/>");
421: } else {
422: writer.println(">");
423: writer
424: .print(sgmlEscape(BaseTestRunner
425: .getFilteredTrace(failure
426: .thrownException())));
427: writer.println(" </" + failureNodeName + ">");
428: }
429: writer.println(" </testcase>");
430: }
431: }
432:
433: private boolean displayException(TestFailure failure) {
434: return true;
435: }
436:
437: protected String getLineBreak() {
438: return "";
439: }
440: }
441:
442: }
|