1 /* BaseTestRunner.java */
2
3 package org.quilt.runner;
4
5 import java.io.ByteArrayOutputStream;
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.IOException;
9 import java.io.OutputStream;
10 import java.io.PrintStream;
11
12 import java.lang.reflect.Method;
13 import java.util.Enumeration;
14 import java.util.Properties;
15 import java.util.Vector;
16
17 import org.apache.tools.ant.AntClassLoader;
18 import org.apache.tools.ant.BuildException;
19 import org.apache.tools.ant.Project;
20
21 import junit.framework.*;
22
23 import org.quilt.cl.*;
24 import org.quilt.framework.QuiltTest;
25 import org.quilt.reports.*;
26
27 /***
28 * Stand-along Quilt test runner, fully compatible with Ant's JUnit
29 * options. Accepts options from Ant via QuiltTask; can also be run
30 * from the command line using TestRunner.
31 *
32 * @see QuiltTask
33 * @see QuiltTest
34 * @see TestRunner
35 */
36
37 public class BaseTestRunner extends Runner {
38
39 /*** The QuiltTest we are currently running. */
40 private QuiltTest qt;
41
42 /*** The JUnit test suite for this QuiltTest. Built by the constructor.*/
43 private Test suite = null;
44
45 /*** Exception caught in constructor. */
46 private Exception exception = null;
47
48 /*** Status code returned by main() */
49 private int retCode = SUCCESS;
50
51 /*** JUnit test result - which has the run() method. */
52 private TestResult res;
53
54 // RUN PARAMETERS ///////////////////////////////////////////////
55
56 /*** Formatters for this particular test. */
57 private Vector formatters = new Vector();
58
59 /*** Do we stop on errors? */
60 // private boolean haltOnError = false;
61
62 /*** Do we stop on test failures? */
63 // private boolean haltOnFailure = false;
64
65 /***
66 * Do we send output to System.out/.err as well as to the formatters.
67 * XXX BUG or inconsistency in documentation.
68 */
69 // private boolean showOutput = false;
70
71 /*** Error output during the test */
72 private PrintStream systemError;
73
74 /*** Output written during the test */
75 private PrintStream systemOut;
76
77 // CONSTRUCTORS /////////////////////////////////////////////////
78
79 /***
80 * Constructor used by command line test runner. And so also
81 * used when Ant forks the test runner. XXX No longer true.
82 *
83 * @param test Data structure holding parameters for a single
84 * test suite.
85 */
86 public BaseTestRunner (QuiltTest test) {
87 this (test, null);
88 }
89 /***
90 * Constructor used when not using Quilt class loader. XXX Not true.
91 * Uses BaseTestRunner.retCode to signal whether construction
92 * successful or not. If the operation fails, the exception
93 * involved is passed back in BaseTestRunner.exception.
94 *
95 * @param test Data structure holding parameters for a single
96 * test suite.
97 * @param loader Class loader passed from parent.
98 */
99 public BaseTestRunner(QuiltTest test, ClassLoader loader) {
100 qt = test;
101
102 try {
103 Class testClass = null;
104 if (loader == null) {
105 testClass = Class.forName(qt.getName());
106 } else {
107 testClass = loader.loadClass(qt.getName());
108 if (!(loader instanceof QuiltClassLoader)) {
109 // trick JVM into initializing class statics
110 AntClassLoader.initializeClass(testClass);
111 }
112 }
113
114 Method suiteMethod = null;
115 try {
116 // check if there is a no-arg "suite" method in the class
117 suiteMethod = testClass.getMethod("suite", new Class[0]);
118 } catch (Exception e) {
119 // not found
120 }
121 if (suiteMethod != null){
122 // we somehow have a suiteMethod; try to use it to
123 // extract the suite
124 suite = (Test) suiteMethod.invoke(null, new Class[0]);
125 } else {
126 // use the JUnit TestSuite constructor to extract a
127 // test suite
128 suite = new TestSuite(testClass);
129 }
130
131 } catch (Exception e) {
132 retCode = ERRORS;
133 exception = e;
134 }
135 }
136 // /***
137 // * Constructor using Quilt class loader. Uses
138 // * BaseTestRunner.retCode to signal whether construction
139 // * successful or not. If the operation fails, the exception
140 // * involved is passed back in BaseTestRunner.exception.
141 // *
142 // * @param test Data structure holding parameters for a single
143 // * test suite.
144 // * @param loader QuiltClassLoader passed from parent.
145 // */
146 // public BaseTestRunner(QuiltTest test, QuiltClassLoader loader) {
147 // this.qt = test;
148
149 // try {
150 // Class testClass = null;
151 // if (loader == null) {
152 // testClass = Class.forName(test.getName());
153 // } else {
154 // testClass = loader.loadClass(test.getName());
155 // // trick JVM into initializing class statics
156 // AntClassLoader.initializeClass(testClass);
157 // }
158 //
159 // Method suiteMethod = null;
160 // try {
161 // // check if there is a no-arg "suite" method in the class
162 // suiteMethod = testClass.getMethod("suite", new Class[0]);
163 // } catch (Exception e) {
164 // // not found
165 // }
166 // if (suiteMethod != null){
167 // // if there is a suite method available, then try
168 // // to extract the suite from it. If there is an error
169 // // here it will be caught below and reported.
170 // suite = (Test) suiteMethod.invoke(null, new Class[0]);
171 // } else {
172 // // use the JUnit TestSuite constructor to extract a
173 // // test suite
174 // suite = new TestSuite(testClass);
175 // }
176 //
177 // } catch (Exception e) {
178 // retCode = ERRORS;
179 // exception = e;
180 // }
181 // } // GEEP
182
183 public void run() {
184 res = new TestResult();
185 res.addListener(this);
186 for (int i = 0; i < formatters.size(); i++) {
187 res.addListener((TestListener) formatters.elementAt(i));
188 }
189
190 long start = System.currentTimeMillis();
191
192 fireStartTestSuite();
193 if (exception != null) { // had an exception in the constructor
194 for (int i = 0; i < formatters.size(); i++) {
195 ((TestListener) formatters.elementAt(i)).addError(null,
196 exception);
197 }
198 qt.setCounts(1, 0, 1);
199 qt.setRunTime(0);
200 } else {
201
202
203 ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
204 systemError = new PrintStream(errStrm);
205
206 ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
207 systemOut = new PrintStream(outStrm);
208
209 PrintStream savedOut = null;
210 PrintStream savedErr = null;
211
212 if ( qt.getFork() ) {
213 savedOut = System.out;
214 savedErr = System.err;
215 if (!qt.getShowOutput()) {
216 System.setOut(systemOut);
217 System.setErr(systemError);
218 } else {
219 System.setOut(new PrintStream(
220 new TeeOutputStream(
221 new OutputStream[] {savedOut,
222 systemOut}
223 )
224 )
225 );
226 System.setErr(new PrintStream(
227 new TeeOutputStream(
228 new OutputStream[] {savedErr,
229 systemError}
230 )
231 )
232 );
233 }
234 }
235
236
237 try {
238 suite.run(res);
239 } finally {
240 if (savedOut != null) {
241 System.setOut(savedOut);
242 }
243 if (savedErr != null) {
244 System.setErr(savedErr);
245 }
246
247 systemError.close();
248 systemError = null;
249 systemOut.close();
250 systemOut = null;
251 sendOutAndErr(new String(outStrm.toByteArray()),
252 new String(errStrm.toByteArray()));
253
254 qt.setCounts(res.runCount(), res.failureCount(),
255 res.errorCount());
256 qt.setRunTime(System.currentTimeMillis() - start);
257 }
258 }
259 fireEndTestSuite();
260
261 if (retCode != SUCCESS || res.errorCount() != 0) {
262 retCode = ERRORS;
263 } else if (res.failureCount() != 0) {
264 retCode = FAILURES;
265 }
266 }
267
268 /***
269 * Get status code from run.
270 *
271 * @return Status codes from RunnerConst
272 */
273 public int getRetCode() {
274 return retCode;
275 }
276
277 /////////////////////////////////////////////////////////////////
278 // INTERFACE TESTLISTENER
279 //
280 // NEEDS TO BE CHECKED CAREFULLY
281 /////////////////////////////////////////////////////////////////
282
283 /*** Called at start of test run. */
284 public void startTest(Test t) {}
285
286 /*** Called at end of test suite. */
287 public void endTest(Test test) {}
288
289 /*** A test failure (or error) has occurred. */
290 public void addFailure(Test test, Throwable t) {
291 if (qt.getHaltOnFailure()) {
292 res.stop();
293 }
294 }
295
296 /*** A test failure (or error) has occurred. */
297 public void addFailure(Test test, AssertionFailedError t) {
298 addFailure(test, (Throwable) t);
299 }
300
301 /*** An unexpected error occurred. */
302 public void addError(Test test, Throwable t) {
303 if (qt.getHaltOnError()) {
304 res.stop();
305 }
306 }
307
308 /*** Handle a block of output. */
309 public void handleOutput(String line) {
310 if (systemOut != null) {
311 systemOut.println(line);
312 }
313 }
314
315 /*** Process an error message. */
316 public void handleErrorOutput(String line) {
317 if (systemError != null) {
318 systemError.println(line);
319 }
320 }
321 /*** Flush standard output. */
322 public void handleFlush(String line) {
323 if (systemOut != null) {
324 systemOut.print(line);
325 }
326 }
327 /*** Flush error output. */
328 public void handleErrorFlush(String line) {
329 if (systemError != null) {
330 systemError.print(line);
331 }
332 }
333 /***
334 * Whether to duplicate test output to standard output and error
335 * output streams.
336 */
337 private void sendOutAndErr(String out, String err) {
338 for (int i = 0; i < formatters.size(); i++) {
339 Formatter formatter =
340 ((Formatter) formatters.elementAt(i));
341
342 formatter.setSystemOutput(out);
343 formatter.setSystemError(err);
344 }
345 }
346
347 /*** Notifies each formatter at start of processing test suite. */
348 private void fireStartTestSuite() {
349 for (int i = 0; i < formatters.size(); i++) {
350 ((Formatter) formatters.elementAt(i))
351 .startTestSuite(qt);
352 // actually has nothing to do with fireStart but it's
353 // convenient to drop it in here
354 ((Formatter) formatters.elementAt(i))
355 .setFiltertrace(qt.getFiltertrace());
356 }
357 }
358 /*** Called at end of test suite, notifies each formatter. */
359 private void fireEndTestSuite() {
360 for (int i = 0; i < formatters.size(); i++) {
361 ((Formatter) formatters.elementAt(i))
362 .endTestSuite(qt);
363 }
364 }
365
366 /*** Add a result formatter */
367 public void addFormatter(Formatter f) {
368 formatters.addElement(f);
369 }
370
371 // OTHER METHODS ////////////////////////////////////////////////
372 private class TeeOutputStream extends OutputStream {
373
374 private OutputStream[] outs;
375
376 private TeeOutputStream(OutputStream[] outs) {
377 this.outs = outs;
378 }
379
380 public void write(int b) throws IOException {
381 for (int i = 0; i < outs.length; i++) {
382 outs[i].write(b);
383 }
384 }
385 }
386 }
This page was automatically generated by Maven