0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.tools.ant.taskdefs.optional.junit;
0020:
0021: import java.io.BufferedReader;
0022: import java.io.BufferedWriter;
0023: import java.io.File;
0024: import java.io.FileOutputStream;
0025: import java.io.FileReader;
0026: import java.io.FileWriter;
0027: import java.io.IOException;
0028: import java.io.OutputStream;
0029: import java.io.PrintWriter;
0030: import java.lang.reflect.Constructor;
0031: import java.util.ArrayList;
0032: import java.util.Collection;
0033: import java.util.Enumeration;
0034: import java.util.HashMap;
0035: import java.util.Hashtable;
0036: import java.util.Iterator;
0037: import java.util.List;
0038: import java.util.Map;
0039: import java.util.Properties;
0040: import java.util.Vector;
0041:
0042: import org.apache.tools.ant.AntClassLoader;
0043: import org.apache.tools.ant.BuildException;
0044: import org.apache.tools.ant.Project;
0045: import org.apache.tools.ant.Task;
0046: import org.apache.tools.ant.taskdefs.Execute;
0047: import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
0048: import org.apache.tools.ant.taskdefs.LogOutputStream;
0049: import org.apache.tools.ant.taskdefs.PumpStreamHandler;
0050: import org.apache.tools.ant.types.Assertions;
0051: import org.apache.tools.ant.types.Commandline;
0052: import org.apache.tools.ant.types.CommandlineJava;
0053: import org.apache.tools.ant.types.EnumeratedAttribute;
0054: import org.apache.tools.ant.types.Environment;
0055: import org.apache.tools.ant.types.Path;
0056: import org.apache.tools.ant.types.Permissions;
0057: import org.apache.tools.ant.types.PropertySet;
0058: import org.apache.tools.ant.util.FileUtils;
0059: import org.apache.tools.ant.util.LoaderUtils;
0060:
0061: /**
0062: * Runs JUnit tests.
0063: *
0064: * <p> JUnit is a framework to create unit tests. It has been initially
0065: * created by Erich Gamma and Kent Beck. JUnit can be found at <a
0066: * href="http://www.junit.org">http://www.junit.org</a>.
0067: *
0068: * <p> <code>JUnitTask</code> can run a single specific
0069: * <code>JUnitTest</code> using the <code>test</code> element.</p>
0070: * For example, the following target <code><pre>
0071: * <target name="test-int-chars" depends="jar-test">
0072: * <echo message="testing international characters"/>
0073: * <junit printsummary="no" haltonfailure="yes" fork="false">
0074: * <classpath refid="classpath"/>
0075: * <formatter type="plain" usefile="false" />
0076: * <test name="org.apache.ecs.InternationalCharTest" />
0077: * </junit>
0078: * </target>
0079: * </pre></code>
0080: * <p>runs a single junit test
0081: * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
0082: * VM using the path with id <code>classpath</code> as classpath and
0083: * presents the results formatted using the standard
0084: * <code>plain</code> formatter on the command line.</p>
0085: *
0086: * <p> This task can also run batches of tests. The
0087: * <code>batchtest</code> element creates a <code>BatchTest</code>
0088: * based on a fileset. This allows, for example, all classes found in
0089: * directory to be run as testcases.</p>
0090: *
0091: * <p>For example,</p><code><pre>
0092: * <target name="run-tests" depends="dump-info,compile-tests" if="junit.present">
0093: * <junit printsummary="no" haltonfailure="yes" fork="${junit.fork}">
0094: * <jvmarg value="-classic"/>
0095: * <classpath refid="tests-classpath"/>
0096: * <sysproperty key="build.tests" value="${build.tests}"/>
0097: * <formatter type="brief" usefile="false" />
0098: * <batchtest>
0099: * <fileset dir="${tests.dir}">
0100: * <include name="**/*Test*" />
0101: * </fileset>
0102: * </batchtest>
0103: * </junit>
0104: * </target>
0105: * </pre></code>
0106: * <p>this target finds any classes with a <code>test</code> directory
0107: * anywhere in their path (under the top <code>${tests.dir}</code>, of
0108: * course) and creates <code>JUnitTest</code>'s for each one.</p>
0109: *
0110: * <p> Of course, <code><junit></code> and
0111: * <code><batch></code> elements can be combined for more
0112: * complex tests. For an example, see the ant <code>build.xml</code>
0113: * target <code>run-tests</code> (the second example is an edited
0114: * version).</p>
0115: *
0116: * <p> To spawn a new Java VM to prevent interferences between
0117: * different testcases, you need to enable <code>fork</code>. A
0118: * number of attributes and elements allow you to set up how this JVM
0119: * runs.
0120: *
0121: *
0122: * @since Ant 1.2
0123: *
0124: * @see JUnitTest
0125: * @see BatchTest
0126: */
0127: public class JUnitTask extends Task {
0128:
0129: private static final String CLASSPATH = "CLASSPATH=";
0130: private CommandlineJava commandline;
0131: private Vector tests = new Vector();
0132: private Vector batchTests = new Vector();
0133: private Vector formatters = new Vector();
0134: private File dir = null;
0135:
0136: private Integer timeout = null;
0137: private boolean summary = false;
0138: private boolean reloading = true;
0139: private String summaryValue = "";
0140: private JUnitTaskMirror.JUnitTestRunnerMirror runner = null;
0141:
0142: private boolean newEnvironment = false;
0143: private Environment env = new Environment();
0144:
0145: private boolean includeAntRuntime = true;
0146: private Path antRuntimeClasses = null;
0147:
0148: // Do we send output to System.out/.err in addition to the formatters?
0149: private boolean showOutput = false;
0150:
0151: // Do we send output to the formatters ?
0152: private boolean outputToFormatters = true;
0153:
0154: private File tmpDir;
0155: private AntClassLoader classLoader = null;
0156: private Permissions perm = null;
0157: private ForkMode forkMode = new ForkMode("perTest");
0158:
0159: private boolean splitJunit = false;
0160: private JUnitTaskMirror delegate;
0161:
0162: // Attributes for basetest
0163: private boolean haltOnError = false;
0164: private boolean haltOnFail = false;
0165: private boolean filterTrace = true;
0166: private boolean fork = false;
0167: private String failureProperty;
0168: private String errorProperty;
0169:
0170: private static final int STRING_BUFFER_SIZE = 128;
0171: /**
0172: * @since Ant 1.7
0173: */
0174: public static final String TESTLISTENER_PREFIX = "junit.framework.TestListener: ";
0175:
0176: private static final FileUtils FILE_UTILS = FileUtils
0177: .getFileUtils();
0178:
0179: /**
0180: * If true, force ant to re-classload all classes for each JUnit TestCase
0181: *
0182: * @param value force class reloading for each test case
0183: */
0184: public void setReloading(boolean value) {
0185: reloading = value;
0186: }
0187:
0188: /**
0189: * If true, smartly filter the stack frames of
0190: * JUnit errors and failures before reporting them.
0191: *
0192: * <p>This property is applied on all BatchTest (batchtest) and
0193: * JUnitTest (test) however it can possibly be overridden by their
0194: * own properties.</p>
0195: * @param value <tt>false</tt> if it should not filter, otherwise
0196: * <tt>true<tt>
0197: *
0198: * @since Ant 1.5
0199: */
0200: public void setFiltertrace(boolean value) {
0201: this .filterTrace = value;
0202: }
0203:
0204: /**
0205: * If true, stop the build process when there is an error in a test.
0206: * This property is applied on all BatchTest (batchtest) and JUnitTest
0207: * (test) however it can possibly be overridden by their own
0208: * properties.
0209: * @param value <tt>true</tt> if it should halt, otherwise
0210: * <tt>false</tt>
0211: *
0212: * @since Ant 1.2
0213: */
0214: public void setHaltonerror(boolean value) {
0215: this .haltOnError = value;
0216: }
0217:
0218: /**
0219: * Property to set to "true" if there is a error in a test.
0220: *
0221: * <p>This property is applied on all BatchTest (batchtest) and
0222: * JUnitTest (test), however, it can possibly be overriden by
0223: * their own properties.</p>
0224: * @param propertyName the name of the property to set in the
0225: * event of an error.
0226: *
0227: * @since Ant 1.4
0228: */
0229: public void setErrorProperty(String propertyName) {
0230: this .errorProperty = propertyName;
0231: }
0232:
0233: /**
0234: * If true, stop the build process if a test fails
0235: * (errors are considered failures as well).
0236: * This property is applied on all BatchTest (batchtest) and
0237: * JUnitTest (test) however it can possibly be overridden by their
0238: * own properties.
0239: * @param value <tt>true</tt> if it should halt, otherwise
0240: * <tt>false</tt>
0241: *
0242: * @since Ant 1.2
0243: */
0244: public void setHaltonfailure(boolean value) {
0245: this .haltOnFail = value;
0246: }
0247:
0248: /**
0249: * Property to set to "true" if there is a failure in a test.
0250: *
0251: * <p>This property is applied on all BatchTest (batchtest) and
0252: * JUnitTest (test), however, it can possibly be overriden by
0253: * their own properties.</p>
0254: * @param propertyName the name of the property to set in the
0255: * event of an failure.
0256: *
0257: * @since Ant 1.4
0258: */
0259: public void setFailureProperty(String propertyName) {
0260: this .failureProperty = propertyName;
0261: }
0262:
0263: /**
0264: * If true, JVM should be forked for each test.
0265: *
0266: * <p>It avoids interference between testcases and possibly avoids
0267: * hanging the build. this property is applied on all BatchTest
0268: * (batchtest) and JUnitTest (test) however it can possibly be
0269: * overridden by their own properties.</p>
0270: * @param value <tt>true</tt> if a JVM should be forked, otherwise
0271: * <tt>false</tt>
0272: * @see #setTimeout
0273: *
0274: * @since Ant 1.2
0275: */
0276: public void setFork(boolean value) {
0277: this .fork = value;
0278: }
0279:
0280: /**
0281: * Set the behavior when {@link #setFork fork} fork has been enabled.
0282: *
0283: * <p>Possible values are "once", "perTest" and "perBatch". If
0284: * set to "once", only a single Java VM will be forked for all
0285: * tests, with "perTest" (the default) each test will run in a
0286: * fresh Java VM and "perBatch" will run all tests from the same
0287: * <batchtest> in the same Java VM.</p>
0288: *
0289: * <p>This attribute will be ignored if tests run in the same VM
0290: * as Ant.</p>
0291: *
0292: * <p>Only tests with the same configuration of haltonerror,
0293: * haltonfailure, errorproperty, failureproperty and filtertrace
0294: * can share a forked Java VM, so even if you set the value to
0295: * "once", Ant may need to fork mutliple VMs.</p>
0296: * @param mode the mode to use.
0297: * @since Ant 1.6.2
0298: */
0299: public void setForkMode(ForkMode mode) {
0300: this .forkMode = mode;
0301: }
0302:
0303: /**
0304: * If true, print one-line statistics for each test, or "withOutAndErr"
0305: * to also show standard output and error.
0306: *
0307: * Can take the values on, off, and withOutAndErr.
0308: * @param value <tt>true</tt> to print a summary,
0309: * <tt>withOutAndErr</tt> to include the test's output as
0310: * well, <tt>false</tt> otherwise.
0311: * @see SummaryJUnitResultFormatter
0312: *
0313: * @since Ant 1.2
0314: */
0315: public void setPrintsummary(SummaryAttribute value) {
0316: summaryValue = value.getValue();
0317: summary = value.asBoolean();
0318: }
0319:
0320: /**
0321: * Print summary enumeration values.
0322: */
0323: public static class SummaryAttribute extends EnumeratedAttribute {
0324: /**
0325: * list the possible values
0326: * @return array of allowed values
0327: */
0328: public String[] getValues() {
0329: return new String[] { "true", "yes", "false", "no", "on",
0330: "off", "withOutAndErr" };
0331: }
0332:
0333: /**
0334: * gives the boolean equivalent of the authorized values
0335: * @return boolean equivalent of the value
0336: */
0337: public boolean asBoolean() {
0338: String v = getValue();
0339: return "true".equals(v) || "on".equals(v)
0340: || "yes".equals(v) || "withOutAndErr".equals(v);
0341: }
0342: }
0343:
0344: /**
0345: * Set the timeout value (in milliseconds).
0346: *
0347: * <p>If the test is running for more than this value, the test
0348: * will be canceled. (works only when in 'fork' mode).</p>
0349: * @param value the maximum time (in milliseconds) allowed before
0350: * declaring the test as 'timed-out'
0351: * @see #setFork(boolean)
0352: *
0353: * @since Ant 1.2
0354: */
0355: public void setTimeout(Integer value) {
0356: timeout = value;
0357: }
0358:
0359: /**
0360: * Set the maximum memory to be used by all forked JVMs.
0361: * @param max the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
0362: * in the java command line options.
0363: *
0364: * @since Ant 1.2
0365: */
0366: public void setMaxmemory(String max) {
0367: getCommandline().setMaxmemory(max);
0368: }
0369:
0370: /**
0371: * The command used to invoke the Java Virtual Machine,
0372: * default is 'java'. The command is resolved by
0373: * java.lang.Runtime.exec(). Ignored if fork is disabled.
0374: *
0375: * @param value the new VM to use instead of <tt>java</tt>
0376: * @see #setFork(boolean)
0377: *
0378: * @since Ant 1.2
0379: */
0380: public void setJvm(String value) {
0381: getCommandline().setVm(value);
0382: }
0383:
0384: /**
0385: * Adds a JVM argument; ignored if not forking.
0386: *
0387: * @return create a new JVM argument so that any argument can be
0388: * passed to the JVM.
0389: * @see #setFork(boolean)
0390: *
0391: * @since Ant 1.2
0392: */
0393: public Commandline.Argument createJvmarg() {
0394: return getCommandline().createVmArgument();
0395: }
0396:
0397: /**
0398: * The directory to invoke the VM in. Ignored if no JVM is forked.
0399: * @param dir the directory to invoke the JVM from.
0400: * @see #setFork(boolean)
0401: *
0402: * @since Ant 1.2
0403: */
0404: public void setDir(File dir) {
0405: this .dir = dir;
0406: }
0407:
0408: /**
0409: * Adds a system property that tests can access.
0410: * This might be useful to tranfer Ant properties to the
0411: * testcases when JVM forking is not enabled.
0412: *
0413: * @since Ant 1.3
0414: * @deprecated since ant 1.6
0415: * @param sysp environment variable to add
0416: */
0417: public void addSysproperty(Environment.Variable sysp) {
0418:
0419: getCommandline().addSysproperty(sysp);
0420: }
0421:
0422: /**
0423: * Adds a system property that tests can access.
0424: * This might be useful to tranfer Ant properties to the
0425: * testcases when JVM forking is not enabled.
0426: * @param sysp new environment variable to add
0427: * @since Ant 1.6
0428: */
0429: public void addConfiguredSysproperty(Environment.Variable sysp) {
0430: // get a build exception if there is a missing key or value
0431: // see bugzilla report 21684
0432: String testString = sysp.getContent();
0433: getProject().log("sysproperty added : " + testString,
0434: Project.MSG_DEBUG);
0435: getCommandline().addSysproperty(sysp);
0436: }
0437:
0438: /**
0439: * Adds a set of properties that will be used as system properties
0440: * that tests can access.
0441: *
0442: * This might be useful to tranfer Ant properties to the
0443: * testcases when JVM forking is not enabled.
0444: *
0445: * @param sysp set of properties to be added
0446: * @since Ant 1.6
0447: */
0448: public void addSyspropertyset(PropertySet sysp) {
0449: getCommandline().addSyspropertyset(sysp);
0450: }
0451:
0452: /**
0453: * Adds path to classpath used for tests.
0454: *
0455: * @return reference to the classpath in the embedded java command line
0456: * @since Ant 1.2
0457: */
0458: public Path createClasspath() {
0459: return getCommandline().createClasspath(getProject())
0460: .createPath();
0461: }
0462:
0463: /**
0464: * Adds a path to the bootclasspath.
0465: * @return reference to the bootclasspath in the embedded java command line
0466: * @since Ant 1.6
0467: */
0468: public Path createBootclasspath() {
0469: return getCommandline().createBootclasspath(getProject())
0470: .createPath();
0471: }
0472:
0473: /**
0474: * Adds an environment variable; used when forking.
0475: *
0476: * <p>Will be ignored if we are not forking a new VM.</p>
0477: * @param var environment variable to be added
0478: * @since Ant 1.5
0479: */
0480: public void addEnv(Environment.Variable var) {
0481: env.addVariable(var);
0482: }
0483:
0484: /**
0485: * If true, use a new environment when forked.
0486: *
0487: * <p>Will be ignored if we are not forking a new VM.</p>
0488: *
0489: * @param newenv boolean indicating if setting a new environment is wished
0490: * @since Ant 1.5
0491: */
0492: public void setNewenvironment(boolean newenv) {
0493: newEnvironment = newenv;
0494: }
0495:
0496: /**
0497: * Preset the attributes of the test
0498: * before configuration in the build
0499: * script.
0500: * This allows attributes in the <junit> task
0501: * be be defaults for the tests, but allows
0502: * individual tests to override the defaults.
0503: */
0504: private void preConfigure(BaseTest test) {
0505: test.setFiltertrace(filterTrace);
0506: test.setHaltonerror(haltOnError);
0507: if (errorProperty != null) {
0508: test.setErrorProperty(errorProperty);
0509: }
0510: test.setHaltonfailure(haltOnFail);
0511: if (failureProperty != null) {
0512: test.setFailureProperty(failureProperty);
0513: }
0514: test.setFork(fork);
0515: }
0516:
0517: /**
0518: * Add a new single testcase.
0519: * @param test a new single testcase
0520: * @see JUnitTest
0521: *
0522: * @since Ant 1.2
0523: */
0524: public void addTest(JUnitTest test) {
0525: tests.addElement(test);
0526: preConfigure(test);
0527: }
0528:
0529: /**
0530: * Adds a set of tests based on pattern matching.
0531: *
0532: * @return a new instance of a batch test.
0533: * @see BatchTest
0534: *
0535: * @since Ant 1.2
0536: */
0537: public BatchTest createBatchTest() {
0538: BatchTest test = new BatchTest(getProject());
0539: batchTests.addElement(test);
0540: preConfigure(test);
0541: return test;
0542: }
0543:
0544: /**
0545: * Add a new formatter to all tests of this task.
0546: *
0547: * @param fe formatter element
0548: * @since Ant 1.2
0549: */
0550: public void addFormatter(FormatterElement fe) {
0551: formatters.addElement(fe);
0552: }
0553:
0554: /**
0555: * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
0556: *
0557: * @param b include ant run time yes or no
0558: * @since Ant 1.5
0559: */
0560: public void setIncludeantruntime(boolean b) {
0561: includeAntRuntime = b;
0562: }
0563:
0564: /**
0565: * If true, send any output generated by tests to Ant's logging system
0566: * as well as to the formatters.
0567: * By default only the formatters receive the output.
0568: *
0569: * <p>Output will always be passed to the formatters and not by
0570: * shown by default. This option should for example be set for
0571: * tests that are interactive and prompt the user to do
0572: * something.</p>
0573: *
0574: * @param showOutput if true, send output to Ant's logging system too
0575: * @since Ant 1.5
0576: */
0577: public void setShowOutput(boolean showOutput) {
0578: this .showOutput = showOutput;
0579: }
0580:
0581: /**
0582: * If true, send any output generated by tests to the formatters.
0583: *
0584: * @param outputToFormatters if true, send output to formatters (Default
0585: * is true).
0586: * @since Ant 1.7.0
0587: */
0588: public void setOutputToFormatters(boolean outputToFormatters) {
0589: this .outputToFormatters = outputToFormatters;
0590: }
0591:
0592: /**
0593: * Assertions to enable in this program (if fork=true)
0594: * @since Ant 1.6
0595: * @param asserts assertion set
0596: */
0597: public void addAssertions(Assertions asserts) {
0598: if (getCommandline().getAssertions() != null) {
0599: throw new BuildException(
0600: "Only one assertion declaration is allowed");
0601: }
0602: getCommandline().setAssertions(asserts);
0603: }
0604:
0605: /**
0606: * Sets the permissions for the application run inside the same JVM.
0607: * @since Ant 1.6
0608: * @return .
0609: */
0610: public Permissions createPermissions() {
0611: if (perm == null) {
0612: perm = new Permissions();
0613: }
0614: return perm;
0615: }
0616:
0617: /**
0618: * If set, system properties will be copied to the cloned VM - as
0619: * well as the bootclasspath unless you have explicitly specified
0620: * a bootclaspath.
0621: *
0622: * <p>Doesn't have any effect unless fork is true.</p>
0623: * @param cloneVm a <code>boolean</code> value.
0624: * @since Ant 1.7
0625: */
0626: public void setCloneVm(boolean cloneVm) {
0627: getCommandline().setCloneVm(cloneVm);
0628: }
0629:
0630: /**
0631: * Creates a new JUnitRunner and enables fork of a new Java VM.
0632: *
0633: * @throws Exception under ??? circumstances
0634: * @since Ant 1.2
0635: */
0636: public JUnitTask() throws Exception {
0637: getCommandline()
0638: .setClassname(
0639: "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
0640: }
0641:
0642: /**
0643: * Where Ant should place temporary files.
0644: *
0645: * @param tmpDir location where temporary files should go to
0646: * @since Ant 1.6
0647: */
0648: public void setTempdir(File tmpDir) {
0649: if (tmpDir != null) {
0650: if (!tmpDir.exists() || !tmpDir.isDirectory()) {
0651: throw new BuildException(tmpDir.toString()
0652: + " is not a valid temp directory");
0653: }
0654: }
0655: this .tmpDir = tmpDir;
0656: }
0657:
0658: /**
0659: * Adds the jars or directories containing Ant, this task and
0660: * JUnit to the classpath - this should make the forked JVM work
0661: * without having to specify them directly.
0662: *
0663: * @since Ant 1.4
0664: */
0665: public void init() {
0666: antRuntimeClasses = new Path(getProject());
0667: splitJunit = !addClasspathEntry("/junit/framework/TestCase.class");
0668: addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class");
0669: addClasspathEntry("/org/apache/tools/ant/Task.class");
0670: addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
0671: }
0672:
0673: private static JUnitTaskMirror createMirror(JUnitTask task,
0674: ClassLoader loader) {
0675: try {
0676: loader.loadClass("junit.framework.Test"); // sanity check
0677: } catch (ClassNotFoundException e) {
0678: throw new BuildException(
0679: "The <classpath> for <junit> must include junit.jar "
0680: + "if not in Ant's own classpath", e, task
0681: .getLocation());
0682: }
0683: try {
0684: Class c = loader.loadClass(JUnitTaskMirror.class.getName()
0685: + "Impl");
0686: if (c.getClassLoader() != loader) {
0687: throw new BuildException("Overdelegating loader", task
0688: .getLocation());
0689: }
0690: Constructor cons = c
0691: .getConstructor(new Class[] { JUnitTask.class });
0692: return (JUnitTaskMirror) cons
0693: .newInstance(new Object[] { task });
0694: } catch (Exception e) {
0695: throw new BuildException(e, task.getLocation());
0696: }
0697: }
0698:
0699: private final class SplitLoader extends AntClassLoader {
0700:
0701: public SplitLoader(ClassLoader parent, Path path) {
0702: super (parent, getProject(), path, true);
0703: }
0704:
0705: // forceLoadClass is not convenient here since it would not
0706: // properly deal with inner classes of these classes.
0707: protected synchronized Class loadClass(String classname,
0708: boolean resolve) throws ClassNotFoundException {
0709: Class theClass = findLoadedClass(classname);
0710: if (theClass != null) {
0711: return theClass;
0712: }
0713: if (isSplit(classname)) {
0714: theClass = findClass(classname);
0715: if (resolve) {
0716: resolveClass(theClass);
0717: }
0718: return theClass;
0719: } else {
0720: return super .loadClass(classname, resolve);
0721: }
0722: }
0723:
0724: private final String[] splitClasses = {
0725: "BriefJUnitResultFormatter", "JUnitResultFormatter",
0726: "JUnitTaskMirrorImpl", "JUnitTestRunner",
0727: "JUnitVersionHelper",
0728: "OutErrSummaryJUnitResultFormatter",
0729: "PlainJUnitResultFormatter",
0730: "SummaryJUnitResultFormatter",
0731: "XMLJUnitResultFormatter", };
0732:
0733: private boolean isSplit(String classname) {
0734: String simplename = classname.substring(classname
0735: .lastIndexOf('.') + 1);
0736: for (int i = 0; i < splitClasses.length; i++) {
0737: if (simplename.equals(splitClasses[i])
0738: || simplename.startsWith(splitClasses[i] + '$')) {
0739: return true;
0740: }
0741: }
0742: return false;
0743: }
0744:
0745: }
0746:
0747: /**
0748: * Runs the testcase.
0749: *
0750: * @throws BuildException in case of test failures or errors
0751: * @since Ant 1.2
0752: */
0753: public void execute() throws BuildException {
0754: ClassLoader myLoader = JUnitTask.class.getClassLoader();
0755: ClassLoader mirrorLoader;
0756: if (splitJunit) {
0757: Path path = new Path(getProject());
0758: path.add(antRuntimeClasses);
0759: path.add(getCommandline().getClasspath());
0760: mirrorLoader = new SplitLoader(myLoader, path);
0761: } else {
0762: mirrorLoader = myLoader;
0763: }
0764: delegate = createMirror(this , mirrorLoader);
0765:
0766: List testLists = new ArrayList();
0767:
0768: boolean forkPerTest = forkMode.getValue().equals(
0769: ForkMode.PER_TEST);
0770: if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) {
0771: testLists.addAll(executeOrQueue(getIndividualTests(),
0772: forkPerTest));
0773: } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */
0774: final int count = batchTests.size();
0775: for (int i = 0; i < count; i++) {
0776: BatchTest batchtest = (BatchTest) batchTests
0777: .elementAt(i);
0778: testLists.addAll(executeOrQueue(batchtest.elements(),
0779: false));
0780: }
0781: testLists.addAll(executeOrQueue(tests.elements(),
0782: forkPerTest));
0783: }
0784:
0785: try {
0786: Iterator iter = testLists.iterator();
0787: while (iter.hasNext()) {
0788: List l = (List) iter.next();
0789: if (l.size() == 1) {
0790: execute((JUnitTest) l.get(0));
0791: } else {
0792: execute(l);
0793: }
0794: }
0795: } finally {
0796: deleteClassLoader();
0797: if (mirrorLoader instanceof SplitLoader) {
0798: ((SplitLoader) mirrorLoader).cleanup();
0799: }
0800: delegate = null;
0801: }
0802: }
0803:
0804: /**
0805: * Run the tests.
0806: * @param arg one JunitTest
0807: * @throws BuildException in case of test failures or errors
0808: */
0809: protected void execute(JUnitTest arg) throws BuildException {
0810: JUnitTest test = (JUnitTest) arg.clone();
0811: // set the default values if not specified
0812: //@todo should be moved to the test class instead.
0813: if (test.getTodir() == null) {
0814: test.setTodir(getProject().resolveFile("."));
0815: }
0816:
0817: if (test.getOutfile() == null) {
0818: test.setOutfile("TEST-" + test.getName());
0819: }
0820:
0821: // execute the test and get the return code
0822: TestResultHolder result = null;
0823: if (!test.getFork()) {
0824: result = executeInVM(test);
0825: } else {
0826: ExecuteWatchdog watchdog = createWatchdog();
0827: result = executeAsForked(test, watchdog, null);
0828: // null watchdog means no timeout, you'd better not check with null
0829: }
0830: actOnTestResult(result, test, "Test " + test.getName());
0831: }
0832:
0833: /**
0834: * Execute a list of tests in a single forked Java VM.
0835: * @param tests the list of tests to execute.
0836: * @throws BuildException on error.
0837: */
0838: protected void execute(List tests) throws BuildException {
0839: JUnitTest test = null;
0840: // Create a temporary file to pass the test cases to run to
0841: // the runner (one test case per line)
0842: File casesFile = createTempPropertiesFile("junittestcases");
0843: PrintWriter writer = null;
0844: try {
0845: writer = new PrintWriter(new BufferedWriter(new FileWriter(
0846: casesFile)));
0847: Iterator iter = tests.iterator();
0848: while (iter.hasNext()) {
0849: test = (JUnitTest) iter.next();
0850: writer.print(test.getName());
0851: if (test.getTodir() == null) {
0852: writer.print("," + getProject().resolveFile("."));
0853: } else {
0854: writer.print("," + test.getTodir());
0855: }
0856:
0857: if (test.getOutfile() == null) {
0858: writer.println("," + "TEST-" + test.getName());
0859: } else {
0860: writer.println("," + test.getOutfile());
0861: }
0862: }
0863: writer.flush();
0864: writer.close();
0865: writer = null;
0866:
0867: // execute the test and get the return code
0868: ExecuteWatchdog watchdog = createWatchdog();
0869: TestResultHolder result = executeAsForked(test, watchdog,
0870: casesFile);
0871: actOnTestResult(result, test, "Tests");
0872: } catch (IOException e) {
0873: log(e.toString(), Project.MSG_ERR);
0874: throw new BuildException(e);
0875: } finally {
0876: if (writer != null) {
0877: writer.close();
0878: }
0879:
0880: try {
0881: casesFile.delete();
0882: } catch (Exception e) {
0883: log(e.toString(), Project.MSG_ERR);
0884: }
0885: }
0886: }
0887:
0888: /**
0889: * Execute a testcase by forking a new JVM. The command will block
0890: * until it finishes. To know if the process was destroyed or not
0891: * or whether the forked Java VM exited abnormally, use the
0892: * attributes of the returned holder object.
0893: * @param test the testcase to execute.
0894: * @param watchdog the watchdog in charge of cancelling the test if it
0895: * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
0896: * the test could probably hang forever.
0897: * @param casesFile list of test cases to execute. Can be <tt>null</tt>,
0898: * in this case only one test is executed.
0899: * @throws BuildException in case of error creating a temporary property file,
0900: * or if the junit process can not be forked
0901: */
0902: private TestResultHolder executeAsForked(JUnitTest test,
0903: ExecuteWatchdog watchdog, File casesFile)
0904: throws BuildException {
0905:
0906: if (perm != null) {
0907: log("Permissions ignored when running in forked mode!",
0908: Project.MSG_WARN);
0909: }
0910:
0911: CommandlineJava cmd = null;
0912: try {
0913: cmd = (CommandlineJava) (getCommandline().clone());
0914: } catch (CloneNotSupportedException e) {
0915: throw new BuildException("This shouldn't happen", e,
0916: getLocation());
0917: }
0918: cmd
0919: .setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
0920: if (casesFile == null) {
0921: cmd.createArgument().setValue(test.getName());
0922: } else {
0923: log("Running multiple tests in the same VM",
0924: Project.MSG_VERBOSE);
0925: cmd.createArgument().setValue(
0926: Constants.TESTSFILE + casesFile);
0927: }
0928:
0929: cmd.createArgument().setValue(
0930: Constants.FILTERTRACE + test.getFiltertrace());
0931: cmd.createArgument().setValue(
0932: Constants.HALT_ON_ERROR + test.getHaltonerror());
0933: cmd.createArgument().setValue(
0934: Constants.HALT_ON_FAILURE + test.getHaltonfailure());
0935: if (includeAntRuntime) {
0936: Vector v = Execute.getProcEnvironment();
0937: Enumeration e = v.elements();
0938: while (e.hasMoreElements()) {
0939: String s = (String) e.nextElement();
0940: if (s.startsWith(CLASSPATH)) {
0941: cmd.createClasspath(getProject()).createPath()
0942: .append(
0943: new Path(getProject(), s
0944: .substring(CLASSPATH
0945: .length())));
0946: }
0947: }
0948: log("Implicitly adding " + antRuntimeClasses
0949: + " to CLASSPATH", Project.MSG_VERBOSE);
0950: cmd.createClasspath(getProject()).createPath().append(
0951: antRuntimeClasses);
0952: }
0953:
0954: if (summary) {
0955: String prefix = "";
0956: if ("withoutanderr".equalsIgnoreCase(summaryValue)) {
0957: prefix = "OutErr";
0958: }
0959: cmd
0960: .createArgument()
0961: .setValue(
0962: Constants.FORMATTER
0963: + "org.apache.tools.ant.taskdefs.optional.junit."
0964: + prefix
0965: + "SummaryJUnitResultFormatter");
0966: }
0967:
0968: cmd.createArgument().setValue(
0969: Constants.SHOWOUTPUT + String.valueOf(showOutput));
0970: cmd.createArgument().setValue(
0971: Constants.OUTPUT_TO_FORMATTERS
0972: + String.valueOf(outputToFormatters));
0973:
0974: cmd.createArgument().setValue(
0975: Constants.LOGTESTLISTENEREVENTS + "true"); // #31885
0976:
0977: StringBuffer formatterArg = new StringBuffer(STRING_BUFFER_SIZE);
0978: final FormatterElement[] feArray = mergeFormatters(test);
0979: for (int i = 0; i < feArray.length; i++) {
0980: FormatterElement fe = feArray[i];
0981: if (fe.shouldUse(this )) {
0982: formatterArg.append(Constants.FORMATTER);
0983: formatterArg.append(fe.getClassname());
0984: File outFile = getOutput(fe, test);
0985: if (outFile != null) {
0986: formatterArg.append(",");
0987: formatterArg.append(outFile);
0988: }
0989: cmd.createArgument().setValue(formatterArg.toString());
0990: formatterArg = new StringBuffer();
0991: }
0992: }
0993:
0994: File vmWatcher = createTempPropertiesFile("junitvmwatcher");
0995: cmd.createArgument().setValue(
0996: Constants.CRASHFILE + vmWatcher.getAbsolutePath());
0997: File propsFile = createTempPropertiesFile("junit");
0998: cmd.createArgument().setValue(
0999: Constants.PROPSFILE + propsFile.getAbsolutePath());
1000: Hashtable p = getProject().getProperties();
1001: Properties props = new Properties();
1002: for (Enumeration e = p.keys(); e.hasMoreElements();) {
1003: Object key = e.nextElement();
1004: props.put(key, p.get(key));
1005: }
1006: try {
1007: FileOutputStream outstream = new FileOutputStream(propsFile);
1008: props.store(outstream,
1009: "Ant JUnitTask generated properties file");
1010: outstream.close();
1011: } catch (java.io.IOException e) {
1012: propsFile.delete();
1013: throw new BuildException(
1014: "Error creating temporary properties " + "file.",
1015: e, getLocation());
1016: }
1017:
1018: Execute execute = new Execute(new JUnitLogStreamHandler(this ,
1019: Project.MSG_INFO, Project.MSG_WARN), watchdog);
1020: execute.setCommandline(cmd.getCommandline());
1021: execute.setAntRun(getProject());
1022: if (dir != null) {
1023: execute.setWorkingDirectory(dir);
1024: }
1025:
1026: String[] environment = env.getVariables();
1027: if (environment != null) {
1028: for (int i = 0; i < environment.length; i++) {
1029: log("Setting environment variable: " + environment[i],
1030: Project.MSG_VERBOSE);
1031: }
1032: }
1033: execute.setNewenvironment(newEnvironment);
1034: execute.setEnvironment(environment);
1035:
1036: log(cmd.describeCommand(), Project.MSG_VERBOSE);
1037: TestResultHolder result = new TestResultHolder();
1038: try {
1039: result.exitCode = execute.execute();
1040: } catch (IOException e) {
1041: throw new BuildException("Process fork failed.", e,
1042: getLocation());
1043: } finally {
1044: String vmCrashString = "unknown";
1045: BufferedReader br = null;
1046: try {
1047: br = new BufferedReader(new FileReader(vmWatcher));
1048: vmCrashString = br.readLine();
1049: } catch (Exception e) {
1050: e.printStackTrace();
1051: // ignored.
1052: } finally {
1053: FileUtils.close(br);
1054: }
1055: if (watchdog != null && watchdog.killedProcess()) {
1056: result.timedOut = true;
1057: logTimeout(feArray, test, vmCrashString);
1058: } else if (!Constants.TERMINATED_SUCCESSFULLY
1059: .equals(vmCrashString)) {
1060: result.crashed = true;
1061: logVmCrash(feArray, test, vmCrashString);
1062: }
1063: vmWatcher.delete();
1064:
1065: if (!propsFile.delete()) {
1066: throw new BuildException("Could not delete temporary "
1067: + "properties file.");
1068: }
1069: }
1070:
1071: return result;
1072: }
1073:
1074: /**
1075: * Create a temporary file to pass the properties to a new process.
1076: * Will auto-delete on (graceful) exit.
1077: * The file will be in the project basedir unless tmpDir declares
1078: * something else.
1079: * @param prefix
1080: * @return created file
1081: */
1082: private File createTempPropertiesFile(String prefix) {
1083: File propsFile = FILE_UTILS.createTempFile(prefix,
1084: ".properties", tmpDir != null ? tmpDir : getProject()
1085: .getBaseDir(), true);
1086: return propsFile;
1087: }
1088:
1089: /**
1090: * Pass output sent to System.out to the TestRunner so it can
1091: * collect ot for the formatters.
1092: *
1093: * @param output output coming from System.out
1094: * @since Ant 1.5
1095: */
1096: protected void handleOutput(String output) {
1097: if (output.startsWith(TESTLISTENER_PREFIX)) {
1098: log(output, Project.MSG_VERBOSE);
1099: } else if (runner != null) {
1100: if (outputToFormatters) {
1101: runner.handleOutput(output);
1102: }
1103: if (showOutput) {
1104: super .handleOutput(output);
1105: }
1106: } else {
1107: super .handleOutput(output);
1108: }
1109: }
1110:
1111: /**
1112: * Handle an input request by this task.
1113: * @see Task#handleInput(byte[], int, int)
1114: * This implementation delegates to a runner if it
1115: * present.
1116: * @param buffer the buffer into which data is to be read.
1117: * @param offset the offset into the buffer at which data is stored.
1118: * @param length the amount of data to read.
1119: *
1120: * @return the number of bytes read.
1121: * @exception IOException if the data cannot be read.
1122: *
1123: * @since Ant 1.6
1124: */
1125: protected int handleInput(byte[] buffer, int offset, int length)
1126: throws IOException {
1127: if (runner != null) {
1128: return runner.handleInput(buffer, offset, length);
1129: } else {
1130: return super .handleInput(buffer, offset, length);
1131: }
1132: }
1133:
1134: /**
1135: * Pass output sent to System.out to the TestRunner so it can
1136: * collect ot for the formatters.
1137: *
1138: * @param output output coming from System.out
1139: * @since Ant 1.5.2
1140: */
1141: protected void handleFlush(String output) {
1142: if (runner != null) {
1143: runner.handleFlush(output);
1144: if (showOutput) {
1145: super .handleFlush(output);
1146: }
1147: } else {
1148: super .handleFlush(output);
1149: }
1150: }
1151:
1152: /**
1153: * Pass output sent to System.err to the TestRunner so it can
1154: * collect it for the formatters.
1155: *
1156: * @param output output coming from System.err
1157: * @since Ant 1.5
1158: */
1159: public void handleErrorOutput(String output) {
1160: if (runner != null) {
1161: runner.handleErrorOutput(output);
1162: if (showOutput) {
1163: super .handleErrorOutput(output);
1164: }
1165: } else {
1166: super .handleErrorOutput(output);
1167: }
1168: }
1169:
1170: /**
1171: * Pass output sent to System.err to the TestRunner so it can
1172: * collect it for the formatters.
1173: *
1174: * @param output coming from System.err
1175: * @since Ant 1.5.2
1176: */
1177: public void handleErrorFlush(String output) {
1178: if (runner != null) {
1179: runner.handleErrorFlush(output);
1180: if (showOutput) {
1181: super .handleErrorFlush(output);
1182: }
1183: } else {
1184: super .handleErrorFlush(output);
1185: }
1186: }
1187:
1188: // in VM is not very nice since it could probably hang the
1189: // whole build. IMHO this method should be avoided and it would be best
1190: // to remove it in future versions. TBD. (SBa)
1191:
1192: /**
1193: * Execute inside VM.
1194: * @param arg one JUnitTest
1195: * @throws BuildException under unspecified circumstances
1196: */
1197: private TestResultHolder executeInVM(JUnitTest arg)
1198: throws BuildException {
1199: JUnitTest test = (JUnitTest) arg.clone();
1200: test.setProperties(getProject().getProperties());
1201: if (dir != null) {
1202: log("dir attribute ignored if running in the same VM",
1203: Project.MSG_WARN);
1204: }
1205:
1206: if (newEnvironment || null != env.getVariables()) {
1207: log(
1208: "Changes to environment variables are ignored if running in "
1209: + "the same VM.", Project.MSG_WARN);
1210: }
1211:
1212: if (getCommandline().getBootclasspath() != null) {
1213: log("bootclasspath is ignored if running in the same VM.",
1214: Project.MSG_WARN);
1215: }
1216:
1217: CommandlineJava.SysProperties sysProperties = getCommandline()
1218: .getSystemProperties();
1219: if (sysProperties != null) {
1220: sysProperties.setSystem();
1221: }
1222:
1223: try {
1224: log("Using System properties " + System.getProperties(),
1225: Project.MSG_VERBOSE);
1226: if (splitJunit) {
1227: classLoader = (AntClassLoader) delegate.getClass()
1228: .getClassLoader();
1229: } else {
1230: createClassLoader();
1231: }
1232: if (classLoader != null) {
1233: classLoader.setThreadContextLoader();
1234: }
1235: runner = delegate.newJUnitTestRunner(test, test
1236: .getHaltonerror(), test.getFiltertrace(), test
1237: .getHaltonfailure(), false, true, classLoader);
1238: if (summary) {
1239:
1240: JUnitTaskMirror.SummaryJUnitResultFormatterMirror f = delegate
1241: .newSummaryJUnitResultFormatter();
1242: f.setWithOutAndErr("withoutanderr"
1243: .equalsIgnoreCase(summaryValue));
1244: f.setOutput(getDefaultOutput());
1245: runner.addFormatter(f);
1246: }
1247:
1248: runner.setPermissions(perm);
1249:
1250: final FormatterElement[] feArray = mergeFormatters(test);
1251: for (int i = 0; i < feArray.length; i++) {
1252: FormatterElement fe = feArray[i];
1253: if (fe.shouldUse(this )) {
1254: File outFile = getOutput(fe, test);
1255: if (outFile != null) {
1256: fe.setOutfile(outFile);
1257: } else {
1258: fe.setOutput(getDefaultOutput());
1259: }
1260: runner
1261: .addFormatter(fe
1262: .createFormatter(classLoader));
1263: }
1264: }
1265:
1266: runner.run();
1267: TestResultHolder result = new TestResultHolder();
1268: result.exitCode = runner.getRetCode();
1269: return result;
1270: } finally {
1271: if (sysProperties != null) {
1272: sysProperties.restoreSystem();
1273: }
1274: if (classLoader != null) {
1275: classLoader.resetThreadContextLoader();
1276: }
1277: }
1278: }
1279:
1280: /**
1281: * @return <tt>null</tt> if there is a timeout value, otherwise the
1282: * watchdog instance.
1283: *
1284: * @throws BuildException under unspecified circumstances
1285: * @since Ant 1.2
1286: */
1287: protected ExecuteWatchdog createWatchdog() throws BuildException {
1288: if (timeout == null) {
1289: return null;
1290: }
1291: return new ExecuteWatchdog((long) timeout.intValue());
1292: }
1293:
1294: /**
1295: * Get the default output for a formatter.
1296: *
1297: * @return default output stream for a formatter
1298: * @since Ant 1.3
1299: */
1300: protected OutputStream getDefaultOutput() {
1301: return new LogOutputStream(this , Project.MSG_INFO);
1302: }
1303:
1304: /**
1305: * Merge all individual tests from the batchtest with all individual tests
1306: * and return an enumeration over all <tt>JUnitTest</tt>.
1307: *
1308: * @return enumeration over individual tests
1309: * @since Ant 1.3
1310: */
1311: protected Enumeration getIndividualTests() {
1312: final int count = batchTests.size();
1313: final Enumeration[] enums = new Enumeration[count + 1];
1314: for (int i = 0; i < count; i++) {
1315: BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
1316: enums[i] = batchtest.elements();
1317: }
1318: enums[enums.length - 1] = tests.elements();
1319: return Enumerations.fromCompound(enums);
1320: }
1321:
1322: /**
1323: * return an enumeration listing each test, then each batchtest
1324: * @return enumeration
1325: * @since Ant 1.3
1326: */
1327: protected Enumeration allTests() {
1328: Enumeration[] enums = { tests.elements(), batchTests.elements() };
1329: return Enumerations.fromCompound(enums);
1330: }
1331:
1332: /**
1333: * @param test junit test
1334: * @return array of FormatterElement
1335: * @since Ant 1.3
1336: */
1337: private FormatterElement[] mergeFormatters(JUnitTest test) {
1338: Vector feVector = (Vector) formatters.clone();
1339: test.addFormattersTo(feVector);
1340: FormatterElement[] feArray = new FormatterElement[feVector
1341: .size()];
1342: feVector.copyInto(feArray);
1343: return feArray;
1344: }
1345:
1346: /**
1347: * If the formatter sends output to a file, return that file.
1348: * null otherwise.
1349: * @param fe formatter element
1350: * @param test one JUnit test
1351: * @return file reference
1352: * @since Ant 1.3
1353: */
1354: protected File getOutput(FormatterElement fe, JUnitTest test) {
1355: if (fe.getUseFile()) {
1356: String base = test.getOutfile();
1357: if (base == null) {
1358: base = JUnitTaskMirror.JUnitTestRunnerMirror.IGNORED_FILE_NAME;
1359: }
1360: String filename = base + fe.getExtension();
1361: File destFile = new File(test.getTodir(), filename);
1362: String absFilename = destFile.getAbsolutePath();
1363: return getProject().resolveFile(absFilename);
1364: }
1365: return null;
1366: }
1367:
1368: /**
1369: * Search for the given resource and add the directory or archive
1370: * that contains it to the classpath.
1371: *
1372: * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
1373: * getResource doesn't contain the name of the archive.</p>
1374: *
1375: * @param resource resource that one wants to lookup
1376: * @return true if something was in fact added
1377: * @since Ant 1.4
1378: */
1379: protected boolean addClasspathEntry(String resource) {
1380: /*
1381: * pre Ant 1.6 this method used to call getClass().getResource
1382: * while Ant 1.6 will call ClassLoader.getResource().
1383: *
1384: * The difference is that Class.getResource expects a leading
1385: * slash for "absolute" resources and will strip it before
1386: * delegating to ClassLoader.getResource - so we now have to
1387: * emulate Class's behavior.
1388: */
1389: if (resource.startsWith("/")) {
1390: resource = resource.substring(1);
1391: } else {
1392: resource = "org/apache/tools/ant/taskdefs/optional/junit/"
1393: + resource;
1394: }
1395:
1396: File f = LoaderUtils.getResourceSource(getClass()
1397: .getClassLoader(), resource);
1398: if (f != null) {
1399: log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
1400: antRuntimeClasses.createPath().setLocation(f);
1401: return true;
1402: } else {
1403: log("Couldn\'t find " + resource, Project.MSG_DEBUG);
1404: return false;
1405: }
1406: }
1407:
1408: /**
1409: * Take care that some output is produced in report files if the
1410: * watchdog kills the test.
1411: *
1412: * @since Ant 1.5.2
1413: */
1414:
1415: private void logTimeout(FormatterElement[] feArray, JUnitTest test,
1416: String testCase) {
1417: logVmExit(feArray, test,
1418: "Timeout occurred. Please note the time in the report does"
1419: + " not reflect the time until the timeout.",
1420: testCase);
1421: }
1422:
1423: /**
1424: * Take care that some output is produced in report files if the
1425: * forked machine exited before the test suite finished but the
1426: * reason is not a timeout.
1427: *
1428: * @since Ant 1.7
1429: */
1430: private void logVmCrash(FormatterElement[] feArray, JUnitTest test,
1431: String testCase) {
1432: logVmExit(
1433: feArray,
1434: test,
1435: "Forked Java VM exited abnormally. Please note the time in the report"
1436: + " does not reflect the time until the VM exit.",
1437: testCase);
1438: }
1439:
1440: /**
1441: * Take care that some output is produced in report files if the
1442: * forked machine terminated before the test suite finished
1443: *
1444: * @since Ant 1.7
1445: */
1446: private void logVmExit(FormatterElement[] feArray, JUnitTest test,
1447: String message, String testCase) {
1448: try {
1449: log("Using System properties " + System.getProperties(),
1450: Project.MSG_VERBOSE);
1451: if (splitJunit) {
1452: classLoader = (AntClassLoader) delegate.getClass()
1453: .getClassLoader();
1454: } else {
1455: createClassLoader();
1456: }
1457: if (classLoader != null) {
1458: classLoader.setThreadContextLoader();
1459: }
1460:
1461: test.setCounts(1, 0, 1);
1462: test.setProperties(getProject().getProperties());
1463: for (int i = 0; i < feArray.length; i++) {
1464: FormatterElement fe = feArray[i];
1465: File outFile = getOutput(fe, test);
1466: JUnitTaskMirror.JUnitResultFormatterMirror formatter = fe
1467: .createFormatter(classLoader);
1468: if (outFile != null && formatter != null) {
1469: try {
1470: OutputStream out = new FileOutputStream(outFile);
1471: delegate.addVmExit(test, formatter, out,
1472: message, testCase);
1473: } catch (IOException e) {
1474: // ignore
1475: }
1476: }
1477: }
1478: if (summary) {
1479: JUnitTaskMirror.SummaryJUnitResultFormatterMirror f = delegate
1480: .newSummaryJUnitResultFormatter();
1481: f.setWithOutAndErr("withoutanderr"
1482: .equalsIgnoreCase(summaryValue));
1483: delegate.addVmExit(test, f, getDefaultOutput(),
1484: message, testCase);
1485: }
1486: } finally {
1487: if (classLoader != null) {
1488: classLoader.resetThreadContextLoader();
1489: }
1490: }
1491: }
1492:
1493: /**
1494: * Creates and configures an AntClassLoader instance from the
1495: * nested classpath element.
1496: *
1497: * @since Ant 1.6
1498: */
1499: private void createClassLoader() {
1500: Path userClasspath = getCommandline().getClasspath();
1501: if (userClasspath != null) {
1502: if (reloading || classLoader == null) {
1503: deleteClassLoader();
1504: Path classpath = (Path) userClasspath.clone();
1505: if (includeAntRuntime) {
1506: log("Implicitly adding " + antRuntimeClasses
1507: + " to CLASSPATH", Project.MSG_VERBOSE);
1508: classpath.append(antRuntimeClasses);
1509: }
1510: classLoader = getProject().createClassLoader(classpath);
1511: if (getClass().getClassLoader() != null
1512: && getClass().getClassLoader() != Project.class
1513: .getClassLoader()) {
1514: classLoader.setParent(getClass().getClassLoader());
1515: }
1516: classLoader.setParentFirst(false);
1517: classLoader.addJavaLibraries();
1518: log("Using CLASSPATH " + classLoader.getClasspath(),
1519: Project.MSG_VERBOSE);
1520: // make sure the test will be accepted as a TestCase
1521: classLoader.addSystemPackageRoot("junit");
1522: // make sure the test annotation are accepted
1523: classLoader.addSystemPackageRoot("org.junit");
1524: // will cause trouble in JDK 1.1 if omitted
1525: classLoader
1526: .addSystemPackageRoot("org.apache.tools.ant");
1527: }
1528: }
1529: }
1530:
1531: /**
1532: * Removes a classloader if needed.
1533: * @since Ant 1.7
1534: */
1535: private void deleteClassLoader() {
1536: if (classLoader != null) {
1537: classLoader.cleanup();
1538: classLoader = null;
1539: }
1540: }
1541:
1542: /**
1543: * Get the command line used to run the tests.
1544: * @return the command line.
1545: * @since Ant 1.6.2
1546: */
1547: protected CommandlineJava getCommandline() {
1548: if (commandline == null) {
1549: commandline = new CommandlineJava();
1550: }
1551: return commandline;
1552: }
1553:
1554: /**
1555: * Forked test support
1556: * @since Ant 1.6.2
1557: */
1558: private static final class ForkedTestConfiguration {
1559: private boolean filterTrace;
1560: private boolean haltOnError;
1561: private boolean haltOnFailure;
1562: private String errorProperty;
1563: private String failureProperty;
1564:
1565: /**
1566: * constructor for forked test configuration
1567: * @param filterTrace
1568: * @param haltOnError
1569: * @param haltOnFailure
1570: * @param errorProperty
1571: * @param failureProperty
1572: */
1573: ForkedTestConfiguration(boolean filterTrace,
1574: boolean haltOnError, boolean haltOnFailure,
1575: String errorProperty, String failureProperty) {
1576: this .filterTrace = filterTrace;
1577: this .haltOnError = haltOnError;
1578: this .haltOnFailure = haltOnFailure;
1579: this .errorProperty = errorProperty;
1580: this .failureProperty = failureProperty;
1581: }
1582:
1583: /**
1584: * configure from a test; sets member variables to attributes of the test
1585: * @param test
1586: */
1587: ForkedTestConfiguration(JUnitTest test) {
1588: this (test.getFiltertrace(), test.getHaltonerror(), test
1589: .getHaltonfailure(), test.getErrorProperty(), test
1590: .getFailureProperty());
1591: }
1592:
1593: /**
1594: * equality test checks all the member variables
1595: * @param other
1596: * @return true if everything is equal
1597: */
1598: public boolean equals(Object other) {
1599: if (other == null
1600: || other.getClass() != ForkedTestConfiguration.class) {
1601: return false;
1602: }
1603: ForkedTestConfiguration o = (ForkedTestConfiguration) other;
1604: return filterTrace == o.filterTrace
1605: && haltOnError == o.haltOnError
1606: && haltOnFailure == o.haltOnFailure
1607: && ((errorProperty == null && o.errorProperty == null) || (errorProperty != null && errorProperty
1608: .equals(o.errorProperty)))
1609: && ((failureProperty == null && o.failureProperty == null) || (failureProperty != null && failureProperty
1610: .equals(o.failureProperty)));
1611: }
1612:
1613: /**
1614: * hashcode is based only on the boolean members, and returns a value
1615: * in the range 0-7.
1616: * @return hash code value
1617: */
1618: public int hashCode() {
1619: return (filterTrace ? 1 : 0) + (haltOnError ? 2 : 0)
1620: + (haltOnFailure ? 4 : 0);
1621: }
1622: }
1623:
1624: /**
1625: * These are the different forking options
1626: * @since 1.6.2
1627: */
1628: public static final class ForkMode extends EnumeratedAttribute {
1629:
1630: /**
1631: * fork once only
1632: */
1633: public static final String ONCE = "once";
1634: /**
1635: * fork once per test class
1636: */
1637: public static final String PER_TEST = "perTest";
1638: /**
1639: * fork once per batch of tests
1640: */
1641: public static final String PER_BATCH = "perBatch";
1642:
1643: /** No arg constructor. */
1644: public ForkMode() {
1645: super ();
1646: }
1647:
1648: /**
1649: * Constructor using a value.
1650: * @param value the value to use - once, perTest or perBatch.
1651: */
1652: public ForkMode(String value) {
1653: super ();
1654: setValue(value);
1655: }
1656:
1657: /** {@inheritDoc}. */
1658: public String[] getValues() {
1659: return new String[] { ONCE, PER_TEST, PER_BATCH };
1660: }
1661: }
1662:
1663: /**
1664: * Executes all tests that don't need to be forked (or all tests
1665: * if the runIndividual argument is true. Returns a collection of
1666: * lists of tests that share the same VM configuration and haven't
1667: * been executed yet.
1668: * @param testList the list of tests to be executed or queued.
1669: * @param runIndividual if true execute each test individually.
1670: * @return a list of tasks to be executed.
1671: * @since 1.6.2
1672: */
1673: protected Collection executeOrQueue(Enumeration testList,
1674: boolean runIndividual) {
1675: Map testConfigurations = new HashMap();
1676: while (testList.hasMoreElements()) {
1677: JUnitTest test = (JUnitTest) testList.nextElement();
1678: if (test.shouldRun(getProject())) {
1679: if (runIndividual || !test.getFork()) {
1680: execute(test);
1681: } else {
1682: ForkedTestConfiguration c = new ForkedTestConfiguration(
1683: test);
1684: List l = (List) testConfigurations.get(c);
1685: if (l == null) {
1686: l = new ArrayList();
1687: testConfigurations.put(c, l);
1688: }
1689: l.add(test);
1690: }
1691: }
1692: }
1693: return testConfigurations.values();
1694: }
1695:
1696: /**
1697: * Logs information about failed tests, potentially stops
1698: * processing (by throwing a BuildException) if a failure/error
1699: * occurred or sets a property.
1700: * @param exitValue the exitValue of the test.
1701: * @param wasKilled if true, the test had been killed.
1702: * @param test the test in question.
1703: * @param name the name of the test.
1704: * @since Ant 1.6.2
1705: */
1706: protected void actOnTestResult(int exitValue, boolean wasKilled,
1707: JUnitTest test, String name) {
1708: TestResultHolder t = new TestResultHolder();
1709: t.exitCode = exitValue;
1710: t.timedOut = wasKilled;
1711: actOnTestResult(t, test, name);
1712: }
1713:
1714: /**
1715: * Logs information about failed tests, potentially stops
1716: * processing (by throwing a BuildException) if a failure/error
1717: * occurred or sets a property.
1718: * @param result the result of the test.
1719: * @param test the test in question.
1720: * @param name the name of the test.
1721: * @since Ant 1.7
1722: */
1723: protected void actOnTestResult(TestResultHolder result,
1724: JUnitTest test, String name) {
1725: // if there is an error/failure and that it should halt, stop
1726: // everything otherwise just log a statement
1727: boolean fatal = result.timedOut || result.crashed;
1728: boolean errorOccurredHere = result.exitCode == JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS
1729: || fatal;
1730: boolean failureOccurredHere = result.exitCode != JUnitTaskMirror.JUnitTestRunnerMirror.SUCCESS
1731: || fatal;
1732: if (errorOccurredHere || failureOccurredHere) {
1733: if ((errorOccurredHere && test.getHaltonerror())
1734: || (failureOccurredHere && test.getHaltonfailure())) {
1735: throw new BuildException(name + " failed"
1736: + (result.timedOut ? " (timeout)" : "")
1737: + (result.crashed ? " (crashed)" : ""),
1738: getLocation());
1739: } else {
1740: log(name + " FAILED"
1741: + (result.timedOut ? " (timeout)" : "")
1742: + (result.crashed ? " (crashed)" : ""),
1743: Project.MSG_ERR);
1744: if (errorOccurredHere
1745: && test.getErrorProperty() != null) {
1746: getProject().setNewProperty(
1747: test.getErrorProperty(), "true");
1748: }
1749: if (failureOccurredHere
1750: && test.getFailureProperty() != null) {
1751: getProject().setNewProperty(
1752: test.getFailureProperty(), "true");
1753: }
1754: }
1755: }
1756: }
1757:
1758: /**
1759: * A value class that contains thee result of a test.
1760: */
1761: protected class TestResultHolder {
1762: // CheckStyle:VisibilityModifier OFF - bc
1763: /** the exit code of the test. */
1764: public int exitCode = JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS;
1765: /** true if the test timed out */
1766: public boolean timedOut = false;
1767: /** true if the test crashed */
1768: public boolean crashed = false;
1769: // CheckStyle:VisibilityModifier ON
1770: }
1771:
1772: /**
1773: * A stream handler for handling the junit task.
1774: * @since Ant 1.7
1775: */
1776: protected static class JUnitLogOutputStream extends LogOutputStream {
1777: private Task task; // local copy since LogOutputStream.task is private
1778:
1779: /**
1780: * Constructor.
1781: * @param task the task being logged.
1782: * @param level the log level used to log data written to this stream.
1783: */
1784: public JUnitLogOutputStream(Task task, int level) {
1785: super (task, level);
1786: this .task = task;
1787: }
1788:
1789: /**
1790: * Logs a line.
1791: * If the line starts with junit.framework.TestListener: set the level
1792: * to MSG_VERBOSE.
1793: * @param line the line to log.
1794: * @param level the logging level to use.
1795: */
1796: protected void processLine(String line, int level) {
1797: if (line.startsWith(TESTLISTENER_PREFIX)) {
1798: task.log(line, Project.MSG_VERBOSE);
1799: } else {
1800: super .processLine(line, level);
1801: }
1802: }
1803: }
1804:
1805: /**
1806: * A log stream handler for junit.
1807: * @since Ant 1.7
1808: */
1809: protected static class JUnitLogStreamHandler extends
1810: PumpStreamHandler {
1811: /**
1812: * Constructor.
1813: * @param task the task to log.
1814: * @param outlevel the level to use for standard output.
1815: * @param errlevel the level to use for error output.
1816: */
1817: public JUnitLogStreamHandler(Task task, int outlevel,
1818: int errlevel) {
1819: super (new JUnitLogOutputStream(task, outlevel),
1820: new LogOutputStream(task, errlevel));
1821: }
1822: }
1823: }
|