Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 613   Methods: 26
NCLOC: 330   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
JUnitTestRunner.java 38.9% 48.7% 65.4% 47.6%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.taskdefs.optional.junit;
 56   
 
 57   
 import java.io.BufferedReader;
 58   
 import java.io.ByteArrayOutputStream;
 59   
 import java.io.File;
 60   
 import java.io.FileInputStream;
 61   
 import java.io.IOException;
 62   
 import java.io.OutputStream;
 63   
 import java.io.PrintStream;
 64   
 import java.io.PrintWriter;
 65   
 import java.io.StringReader;
 66   
 import java.io.StringWriter;
 67   
 import java.lang.reflect.Method;
 68   
 import java.util.Enumeration;
 69   
 import java.util.Hashtable;
 70   
 import java.util.Properties;
 71   
 import java.util.Vector;
 72   
 import junit.framework.AssertionFailedError;
 73   
 import junit.framework.Test;
 74   
 import junit.framework.TestListener;
 75   
 import junit.framework.TestResult;
 76   
 import junit.framework.TestSuite;
 77   
 import org.apache.tools.ant.AntClassLoader;
 78   
 import org.apache.tools.ant.BuildException;
 79   
 import org.apache.tools.ant.Project;
 80   
 import org.apache.tools.ant.util.StringUtils;
 81   
 import org.apache.tools.ant.util.TeeOutputStream;
 82   
 
 83   
 /**
 84   
  * Simple Testrunner for JUnit that runs all tests of a testsuite.
 85   
  *
 86   
  * <p>This TestRunner expects a name of a TestCase class as its
 87   
  * argument. If this class provides a static suite() method it will be
 88   
  * called and the resulting Test will be run. So, the signature should be 
 89   
  * <pre><code>
 90   
  *     public static junit.framework.Test suite()
 91   
  * </code></pre>
 92   
  *
 93   
  * <p> If no such method exists, all public methods starting with
 94   
  * "test" and taking no argument will be run.
 95   
  *
 96   
  * <p> Summary output is generated at the end. 
 97   
  *
 98   
  * @author Stefan Bodewig
 99   
  * @author <a href="mailto:ehatcher@apache.org">Erik Hatcher</a>
 100   
  *
 101   
  * @since Ant 1.2
 102   
  */
 103   
 
 104   
 public class JUnitTestRunner implements TestListener {
 105   
 
 106   
     /**
 107   
      * No problems with this test.
 108   
      */
 109   
     public static final int SUCCESS = 0;
 110   
 
 111   
     /**
 112   
      * Some tests failed.
 113   
      */
 114   
     public static final int FAILURES = 1;
 115   
 
 116   
     /**
 117   
      * An error occurred.
 118   
      */
 119   
     public static final int ERRORS = 2;
 120   
 
 121   
     /**
 122   
      * Holds the registered formatters.
 123   
      */
 124   
     private Vector formatters = new Vector();
 125   
 
 126   
     /**
 127   
      * Collects TestResults.
 128   
      */
 129   
     private TestResult res;
 130   
 
 131   
     /**
 132   
      * Do we filter junit.*.* stack frames out of failure and error exceptions.
 133   
      */
 134   
     private static boolean filtertrace = true;
 135   
     
 136   
     /**
 137   
      * Do we send output to System.out/.err in addition to the formatters?
 138   
      */
 139   
     private boolean showOutput = false;
 140   
 
 141   
     private static final String[] DEFAULT_TRACE_FILTERS = new String[] {
 142   
                 "junit.framework.TestCase",
 143   
                 "junit.framework.TestResult",
 144   
                 "junit.framework.TestSuite",
 145   
                 "junit.framework.Assert.", // don't filter AssertionFailure
 146   
                 "junit.swingui.TestRunner",
 147   
                 "junit.awtui.TestRunner",
 148   
                 "junit.textui.TestRunner",
 149   
                 "java.lang.reflect.Method.invoke(",
 150   
                 "org.apache.tools.ant."
 151   
         };
 152   
 
 153   
     
 154   
     /**
 155   
      * Do we stop on errors.
 156   
      */
 157   
     private boolean haltOnError = false;
 158   
 
 159   
     /**
 160   
      * Do we stop on test failures.
 161   
      */
 162   
     private boolean haltOnFailure = false;
 163   
 
 164   
     /**
 165   
      * The corresponding testsuite.
 166   
      */
 167   
     private Test suite = null;
 168   
 
 169   
     /**
 170   
      * Exception caught in constructor.
 171   
      */
 172   
     private Exception exception;
 173   
 
 174   
     /**
 175   
      * Returncode
 176   
      */
 177   
     private int retCode = SUCCESS;
 178   
 
 179   
     /**
 180   
      * The TestSuite we are currently running.
 181   
      */
 182   
     private JUnitTest junitTest;
 183   
 
 184   
     /** output written during the test */
 185   
     private PrintStream systemError;
 186   
     
 187   
     /** Error output during the test */
 188   
     private PrintStream systemOut;    
 189   
     
 190   
     /** is this runner running in forked mode? */
 191   
     private boolean forked = false;
 192   
 
 193   
     /**
 194   
      * Constructor for fork=true or when the user hasn't specified a
 195   
      * classpath.  
 196   
      */
 197  0
     public JUnitTestRunner(JUnitTest test, boolean haltOnError, 
 198   
                            boolean filtertrace, boolean haltOnFailure) {
 199  0
         this(test, haltOnError, filtertrace, haltOnFailure, false);
 200   
     }
 201   
 
 202   
     /**
 203   
      * Constructor for fork=true or when the user hasn't specified a
 204   
      * classpath.  
 205   
      */
 206  0
     public JUnitTestRunner(JUnitTest test, boolean haltOnError, 
 207   
                            boolean filtertrace, boolean haltOnFailure,
 208   
                            boolean showOutput) {
 209  0
         this(test, haltOnError, filtertrace, haltOnFailure, showOutput, null);
 210   
     }
 211   
 
 212   
     /**
 213   
      * Constructor to use when the user has specified a classpath.
 214   
      */
 215  141
     public JUnitTestRunner(JUnitTest test, boolean haltOnError, 
 216   
                            boolean filtertrace, boolean haltOnFailure, 
 217   
                            ClassLoader loader) {
 218  141
         this(test, haltOnError, filtertrace, haltOnFailure, false, loader);
 219   
     }
 220   
 
 221   
     /**
 222   
      * Constructor to use when the user has specified a classpath.
 223   
      */
 224  141
     public JUnitTestRunner(JUnitTest test, boolean haltOnError, 
 225   
                            boolean filtertrace, boolean haltOnFailure, 
 226   
                            boolean showOutput, ClassLoader loader) {
 227  141
         this.filtertrace = filtertrace;
 228  141
         this.junitTest = test;
 229  141
         this.haltOnError = haltOnError;
 230  141
         this.haltOnFailure = haltOnFailure;
 231  141
         this.showOutput = showOutput;
 232   
 
 233  141
         try {
 234  141
             Class testClass = null;
 235  141
             if (loader == null) {
 236  0
                 testClass = Class.forName(test.getName());
 237   
             } else {
 238  141
                 testClass = loader.loadClass(test.getName());
 239  141
                 AntClassLoader.initializeClass(testClass);
 240   
             }
 241   
             
 242  141
             Method suiteMethod = null;
 243  141
             try {
 244   
                 // check if there is a suite method
 245  141
                 suiteMethod = testClass.getMethod("suite", new Class[0]);
 246   
             } catch (Exception e) {
 247   
                 // no appropriate suite method found. We don't report any
 248   
                 // error here since it might be perfectly normal. We don't
 249   
                 // know exactly what is the cause, but we're doing exactly
 250   
                 // the same as JUnit TestRunner do. We swallow the exceptions.
 251   
             }
 252  141
             if (suiteMethod != null){
 253   
                 // if there is a suite method available, then try
 254   
                 // to extract the suite from it. If there is an error
 255   
                 // here it will be caught below and reported.
 256  2
                 suite = (Test) suiteMethod.invoke(null, new Class[0]);
 257   
             } else {
 258   
                 // try to extract a test suite automatically
 259   
                 // this will generate warnings if the class is no suitable Test
 260  139
                 suite = new TestSuite(testClass);
 261   
             }
 262   
             
 263   
         } catch (Exception e) {
 264  1
             retCode = ERRORS;
 265  1
             exception = e;
 266   
         }
 267   
     }
 268   
 
 269  141
     public void run() {
 270  141
         res = new TestResult();
 271  141
         res.addListener(this);
 272  141
         for (int i = 0; i < formatters.size(); i++) {
 273  141
             res.addListener((TestListener) formatters.elementAt(i));
 274   
         }
 275   
 
 276  141
         long start = System.currentTimeMillis();
 277   
 
 278  141
         fireStartTestSuite();
 279  141
         if (exception != null) { // had an exception in the constructor
 280  1
             for (int i = 0; i < formatters.size(); i++) {
 281  1
                 ((TestListener) formatters.elementAt(i)).addError(null, 
 282   
                                                                  exception);
 283   
             }
 284  1
             junitTest.setCounts(1, 0, 1);
 285  1
             junitTest.setRunTime(0);
 286   
         } else {
 287   
 
 288   
 
 289  140
             ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
 290  140
             systemError = new PrintStream(errStrm);
 291   
             
 292  140
             ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
 293  140
             systemOut = new PrintStream(outStrm);
 294   
 
 295  140
             PrintStream savedOut = null;
 296  140
             PrintStream savedErr = null;
 297   
 
 298  140
             if (forked) {
 299  0
                 savedOut = System.out;
 300  0
                 savedErr = System.err;
 301  0
                 if (!showOutput) {
 302  0
                     System.setOut(systemOut);
 303  0
                     System.setErr(systemError);
 304   
                 } else {
 305  0
                     System.setOut(new PrintStream(
 306   
                                       new TeeOutputStream(savedOut, systemOut)
 307   
                                       )
 308   
                                   );
 309  0
                     System.setErr(new PrintStream(
 310   
                                       new TeeOutputStream(savedErr, 
 311   
                                                           systemError)
 312   
                                       )
 313   
                                   );
 314   
                 }
 315   
             }
 316   
             
 317   
 
 318  140
             try {
 319  140
                 suite.run(res);
 320   
             } finally {
 321  140
                 if (savedOut != null) {
 322  0
                     System.setOut(savedOut);
 323   
                 }
 324  140
                 if (savedErr != null) {
 325  0
                     System.setErr(savedErr);
 326   
                 }
 327   
                 
 328  140
                 systemError.close();
 329  140
                 systemError = null;
 330  140
                 systemOut.close();
 331  140
                 systemOut = null;
 332  140
                 sendOutAndErr(new String(outStrm.toByteArray()),
 333   
                               new String(errStrm.toByteArray()));
 334   
 
 335  140
                 junitTest.setCounts(res.runCount(), res.failureCount(), 
 336   
                                     res.errorCount());
 337  140
                 junitTest.setRunTime(System.currentTimeMillis() - start);
 338   
             }
 339   
         }
 340  141
         fireEndTestSuite();
 341   
 
 342  141
         if (retCode != SUCCESS || res.errorCount() != 0) {
 343  3
             retCode = ERRORS;
 344  138
         } else if (res.failureCount() != 0) {
 345  2
             retCode = FAILURES;
 346   
         }
 347   
     }
 348   
 
 349   
     /**
 350   
      * Returns what System.exit() would return in the standalone version.
 351   
      *
 352   
      * @return 2 if errors occurred, 1 if tests failed else 0.
 353   
      */
 354  141
     public int getRetCode() {
 355  141
         return retCode;
 356   
     }
 357   
 
 358   
     /**
 359   
      * Interface TestListener.
 360   
      *
 361   
      * <p>A new Test is started.
 362   
      */
 363  760
     public void startTest(Test t) {}
 364   
 
 365   
     /**
 366   
      * Interface TestListener.
 367   
      *
 368   
      * <p>A Test is finished.
 369   
      */
 370  760
     public void endTest(Test test) {}
 371   
 
 372   
     /**
 373   
      * Interface TestListener for JUnit &lt;= 3.4.
 374   
      *
 375   
      * <p>A Test failed.
 376   
      */
 377  3
     public void addFailure(Test test, Throwable t) {
 378  3
         if (haltOnFailure) {
 379  2
             res.stop();
 380   
         }
 381   
     }
 382   
 
 383   
     /**
 384   
      * Interface TestListener for JUnit &gt; 3.4.
 385   
      *
 386   
      * <p>A Test failed.
 387   
      */
 388  3
     public void addFailure(Test test, AssertionFailedError t) {
 389  3
         addFailure(test, (Throwable) t);
 390   
     }
 391   
 
 392   
     /**
 393   
      * Interface TestListener.
 394   
      *
 395   
      * <p>An error occurred while running the test.
 396   
      */
 397  7
     public void addError(Test test, Throwable t) {
 398  7
         if (haltOnError) {
 399  0
             res.stop();
 400   
         }
 401   
     }
 402   
 
 403  191
     protected void handleOutput(String line) {
 404  191
         if (systemOut != null) {
 405  191
             systemOut.println(line);
 406   
         }
 407   
     }
 408   
     
 409   
     /**
 410   
      * @see Task#handleInput(byte[], int, int)
 411   
      * 
 412   
      * @since Ant 1.6
 413   
      */
 414  0
     protected int handleInput(byte[] buffer, int offset, int length) 
 415   
         throws IOException {
 416  0
         return -1;
 417   
     }
 418   
     
 419  28
     protected void handleErrorOutput(String line) {
 420  28
         if (systemError != null) {
 421  28
             systemError.println(line);
 422   
         }
 423   
     }
 424   
     
 425  0
     protected void handleFlush(String line) {
 426  0
         if (systemOut != null) {
 427  0
             systemOut.print(line);
 428   
         }
 429   
     }
 430   
     
 431  0
     protected void handleErrorFlush(String line) {
 432  0
         if (systemError != null) {
 433  0
             systemError.print(line);
 434   
         }
 435   
     }
 436   
     
 437  140
     private void sendOutAndErr(String out, String err) {
 438  140
         for (int i = 0; i < formatters.size(); i++) {
 439  140
             JUnitResultFormatter formatter = 
 440   
                 ((JUnitResultFormatter) formatters.elementAt(i));
 441   
             
 442  140
             formatter.setSystemOutput(out);
 443  140
             formatter.setSystemError(err);
 444   
         }
 445   
     }
 446   
 
 447  141
     private void fireStartTestSuite() {
 448  141
         for (int i = 0; i < formatters.size(); i++) {
 449  141
             ((JUnitResultFormatter) formatters.elementAt(i))
 450   
                 .startTestSuite(junitTest);
 451   
         }
 452   
     }
 453   
 
 454  141
     private void fireEndTestSuite() {
 455  141
         for (int i = 0; i < formatters.size(); i++) {
 456  141
             ((JUnitResultFormatter) formatters.elementAt(i))
 457   
                 .endTestSuite(junitTest);
 458   
         }
 459   
     }
 460   
 
 461  141
     public void addFormatter(JUnitResultFormatter f) {
 462  141
         formatters.addElement(f);
 463   
     }
 464   
 
 465   
     /**
 466   
      * Entry point for standalone (forked) mode.
 467   
      *
 468   
      * Parameters: testcaseclassname plus parameters in the format
 469   
      * key=value, none of which is required.
 470   
      *
 471   
      * <table cols="4" border="1">
 472   
      * <tr><th>key</th><th>description</th><th>default value</th></tr>
 473   
      *
 474   
      * <tr><td>haltOnError</td><td>halt test on
 475   
      * errors?</td><td>false</td></tr>
 476   
      *
 477   
      * <tr><td>haltOnFailure</td><td>halt test on
 478   
      * failures?</td><td>false</td></tr>
 479   
      *
 480   
      * <tr><td>formatter</td><td>A JUnitResultFormatter given as
 481   
      * classname,filename. If filename is ommitted, System.out is
 482   
      * assumed.</td><td>none</td></tr>
 483   
      *
 484   
      * <tr><td>showoutput</td><td>send output to System.err/.out as
 485   
      * well as to the formatters?</td><td>false</td></tr>
 486   
      *
 487   
      * </table> 
 488   
      */
 489  0
     public static void main(String[] args) throws IOException {
 490  0
         boolean haltError = false;
 491  0
         boolean haltFail = false;
 492  0
         boolean stackfilter = true;
 493  0
         Properties props = new Properties();
 494  0
         boolean showOut = false;
 495   
 
 496  0
         if (args.length == 0) {
 497  0
             System.err.println("required argument TestClassName missing");
 498  0
             System.exit(ERRORS);
 499   
         }
 500   
 
 501  0
         for (int i = 1; i < args.length; i++) {
 502  0
             if (args[i].startsWith("haltOnError=")) {
 503  0
                 haltError = Project.toBoolean(args[i].substring(12));
 504  0
             } else if (args[i].startsWith("haltOnFailure=")) {
 505  0
                 haltFail = Project.toBoolean(args[i].substring(14));
 506  0
             } else if (args[i].startsWith("filtertrace=")) {
 507  0
                 stackfilter = Project.toBoolean(args[i].substring(12));
 508  0
             } else if (args[i].startsWith("formatter=")) {
 509  0
                 try {
 510  0
                     createAndStoreFormatter(args[i].substring(10));
 511   
                 } catch (BuildException be) {
 512  0
                     System.err.println(be.getMessage());
 513  0
                     System.exit(ERRORS);
 514   
                 }
 515  0
             } else if (args[i].startsWith("propsfile=")) {
 516  0
                 FileInputStream in = new FileInputStream(args[i]
 517   
                                                          .substring(10));
 518  0
                 props.load(in);
 519  0
                 in.close();
 520  0
             } else if (args[i].startsWith("showoutput=")) {
 521  0
                 showOut = Project.toBoolean(args[i].substring(11));
 522   
             }
 523   
         }
 524   
         
 525  0
         JUnitTest t = new JUnitTest(args[0]);
 526   
         
 527   
         // Add/overlay system properties on the properties from the Ant project
 528  0
         Hashtable p = System.getProperties();
 529  0
         for (Enumeration enum = p.keys(); enum.hasMoreElements();) {
 530  0
             Object key = enum.nextElement();
 531  0
             props.put(key, p.get(key));
 532   
         }
 533  0
         t.setProperties(props);
 534   
 
 535  0
         JUnitTestRunner runner = new JUnitTestRunner(t, haltError, stackfilter,
 536   
                                                      haltFail, showOut);
 537  0
         runner.forked = true;
 538  0
         transferFormatters(runner);
 539  0
         runner.run();
 540  0
         System.exit(runner.getRetCode());
 541   
     }
 542   
 
 543   
     private static Vector fromCmdLine = new Vector();
 544   
 
 545  0
     private static void transferFormatters(JUnitTestRunner runner) {
 546  0
         for (int i = 0; i < fromCmdLine.size(); i++) {
 547  0
             runner.addFormatter((JUnitResultFormatter) fromCmdLine
 548   
                                 .elementAt(i));
 549   
         }
 550   
     }
 551   
 
 552   
     /**
 553   
      * Line format is: formatter=<classname>(,<pathname>)?
 554   
      */
 555  0
     private static void createAndStoreFormatter(String line)
 556   
         throws BuildException {
 557  0
         FormatterElement fe = new FormatterElement();
 558  0
         int pos = line.indexOf(',');
 559  0
         if (pos == -1) {
 560  0
             fe.setClassname(line);
 561   
         } else {
 562  0
             fe.setClassname(line.substring(0, pos));
 563  0
             fe.setOutfile(new File(line.substring(pos + 1)));
 564   
         }
 565  0
         fromCmdLine.addElement(fe.createFormatter());
 566   
     }
 567   
     
 568   
     /**
 569   
      * Returns a filtered stack trace.
 570   
      * This is ripped out of junit.runner.BaseTestRunner.
 571   
      * Scott M. Stirling.
 572   
      */
 573  8
     public static String getFilteredTrace(Throwable t) {
 574  8
         String trace = StringUtils.getStackTrace(t);
 575  8
         return JUnitTestRunner.filterStack(trace);
 576   
     }
 577   
 
 578   
     /**
 579   
      * Filters stack frames from internal JUnit and Ant classes
 580   
      */
 581  8
     public static String filterStack(String stack) {
 582  8
         if (!filtertrace) {
 583  8
             return stack;
 584   
         }
 585  0
         StringWriter sw = new StringWriter();
 586  0
         PrintWriter pw = new PrintWriter(sw);
 587  0
         StringReader sr = new StringReader(stack);
 588  0
         BufferedReader br = new BufferedReader(sr);
 589   
 
 590  0
         String line;
 591  0
         try {
 592  0
             while ((line = br.readLine()) != null) {
 593  0
                 if (!filterLine(line)) {
 594  0
                     pw.println(line);
 595   
                 }
 596   
             }
 597   
         } catch (Exception IOException) {
 598  0
             return stack; // return the stack unfiltered
 599   
         }
 600  0
         return sw.toString();
 601   
     }
 602   
 
 603  0
     private static boolean filterLine(String line) {
 604  0
         for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) {
 605  0
             if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) > 0) {
 606  0
                 return true;
 607   
             }
 608   
         }
 609  0
         return false;
 610   
     }
 611   
     
 612   
 } // JUnitTestRunner
 613