| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| package org.apache.ant.antunit; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.tools.ant.BuildEvent; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.BuildListener; |
| import org.apache.tools.ant.MagicNames; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.ProjectHelper; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.types.PropertySet; |
| import org.apache.tools.ant.types.ResourceCollection; |
| import org.apache.tools.ant.types.resources.FileResource; |
| import org.apache.tools.ant.types.resources.Union; |
| |
| /** |
| * Run every target whose name starts with "test" in a set of build files. |
| * |
| * <p>Run the "setUp" target before each of them if present, same for |
| * "tearDown" after each "test*" target (targets named just "test" are |
| * ignored). If a target throws an AssertionFailedException, the test |
| * has failed; any other exception is considered an error (although |
| * BuildException will be scanned recursively for nested |
| * AssertionFailedExceptions).</p> |
| */ |
| public class AntUnit extends Task { |
| |
| /** |
| * Message to print if an error or failure occured. |
| */ |
| public static final String ERROR_TESTS_FAILED = "Tests failed with "; |
| |
| /** |
| * Message if no tests have been specified. |
| */ |
| public static final String ERROR_NO_TESTS = |
| "You must specify build files to test."; |
| |
| /** |
| * Message if non-File resources have been specified. |
| */ |
| public static final String ERROR_NON_FILES = |
| "Only file system resources are supported."; |
| |
| /** |
| * The build files to process. |
| */ |
| private Union buildFiles; |
| |
| |
| private AntUnitExecutionPlatform myExecutionPlatform = new AntUnitExecutionPlatform() { |
| |
| public Project createProjectForFile(File f) { |
| return AntUnit.this.createProjectForFile(f); |
| } |
| |
| public void fireEndTest(String targetName) { |
| AntUnit.this.fireEndTest(targetName); |
| } |
| |
| public void fireError(String targetName, Throwable t) { |
| AntUnit.this.fireError(targetName, t); |
| } |
| |
| public void fireFail(String targetName, AssertionFailedException ae) { |
| AntUnit.this.fireFail(targetName, ae); |
| } |
| |
| public void fireStartTest(String targetName) { |
| AntUnit.this.fireStartTest(targetName); |
| } |
| }; |
| |
| /** |
| * The object responsible for the execution of the unit test. |
| * scriptRunner is invoked to executes the targets and keep the |
| * reference to the project. |
| */ |
| private AntUnitScriptRunner scriptRunner = new AntUnitScriptRunner(myExecutionPlatform); |
| |
| /** |
| * listeners. |
| */ |
| private ArrayList listeners = new ArrayList(); |
| |
| /** |
| * propertysets. |
| */ |
| private ArrayList propertySets = new ArrayList(); |
| |
| /** |
| * has a failure occured? |
| */ |
| private int failures = 0; |
| |
| /** |
| * has an error occured? |
| */ |
| private int errors = 0; |
| |
| /** |
| * stop testing if an error or failure occurs? |
| */ |
| private boolean failOnError = true; |
| |
| /** |
| * Name of a property to set in case of an error. |
| */ |
| private String errorProperty = null; |
| |
| /** |
| * Add build files to run as tests. |
| * @param rc the ResourceCollection to add. |
| */ |
| public void add(ResourceCollection rc) { |
| if (buildFiles == null) { |
| buildFiles = new Union(); |
| } |
| buildFiles.add(rc); |
| } |
| |
| /** |
| * Add a test listener. |
| * @param al the AntUnitListener to add. |
| */ |
| public void add(AntUnitListener al) { |
| listeners.add(al); |
| al.setParentTask(this); |
| } |
| |
| /** |
| * Add a PropertySet. |
| * @param ps the PropertySet to add. |
| */ |
| public void addPropertySet(PropertySet ps) { |
| propertySets.add(ps); |
| } |
| |
| /** |
| * Set the name of a property to set if an error or failure occurs. |
| * @param s the name of the error property. |
| */ |
| public void setErrorProperty(String s) { |
| errorProperty = s; |
| } |
| |
| /** |
| * Set whether to stop testing if an error or failure occurs? |
| * @param failOnError default <code>true</code> |
| */ |
| public void setFailOnError(boolean failOnError) { |
| this.failOnError = failOnError; |
| } |
| |
| /** |
| * Execute the tests. |
| */ |
| public void execute() { |
| if (buildFiles == null) { |
| throw new BuildException(ERROR_NO_TESTS); |
| } |
| doResourceCollection(buildFiles); |
| if (failures > 0 || errors > 0) { |
| if (errorProperty != null) { |
| getProject().setNewProperty(errorProperty, "true"); |
| } |
| if (failOnError) { |
| throw new BuildException(ERROR_TESTS_FAILED |
| + failures + " failure" |
| + (failures != 1 ? "s" : "") |
| + " and " |
| + errors + " error" |
| + (errors != 1 ? "s" : "")); |
| } |
| } |
| } |
| |
| /** |
| * Processes a ResourceCollection. |
| */ |
| private void doResourceCollection(ResourceCollection rc) { |
| //should relax this restriction if/when Ant core allows non-files |
| if (!rc.isFilesystemOnly()) { |
| throw new BuildException(ERROR_NON_FILES); |
| } |
| |
| Iterator i = rc.iterator(); |
| while (i.hasNext()) { |
| FileResource r = (FileResource) i.next(); |
| if (r.isExists()) { |
| doFile(r.getFile()); |
| } else { |
| log("Skipping " + r + " since it doesn't exist", |
| Project.MSG_VERBOSE); |
| } |
| } |
| } |
| |
| /** |
| * Processes a single build file. |
| */ |
| private void doFile(File f) { |
| log("Running tests in build file " + f, Project.MSG_DEBUG); |
| scriptRunner.activate(f); |
| List testTargets = scriptRunner.scanFile(); |
| |
| // start test |
| Throwable caught = null; |
| try { |
| if (!scriptRunner.startSuite()) { |
| return; |
| } |
| Iterator iter = testTargets.iterator(); |
| while (iter.hasNext()) { |
| String name = (String) iter.next(); |
| scriptRunner.runTarget(name); |
| } |
| } catch (Throwable e) { |
| caught = e; |
| } finally { |
| scriptRunner.endSuite(caught); |
| scriptRunner.deactivate(); |
| } |
| } |
| |
| |
| /** |
| * Redirect output to new project instance. |
| * @param outputToHandle the output to handle. |
| */ |
| public void handleOutput(String outputToHandle) { |
| if (scriptRunner.isActive()) { |
| scriptRunner.getCurrentProject().demuxOutput(outputToHandle, false); |
| } else { |
| super.handleOutput(outputToHandle); |
| } |
| } |
| |
| /** |
| * Redirect input to new project instance. |
| * @param buffer the buffer containing the input. |
| * @param offset the offset into <code>buffer</code>. |
| * @param length the length of the data. |
| */ |
| public int handleInput(byte[] buffer, int offset, int length) |
| throws IOException { |
| if (scriptRunner.isActive()) { |
| return scriptRunner.getCurrentProject().demuxInput(buffer, offset, length); |
| } |
| return super.handleInput(buffer, offset, length); |
| } |
| |
| /** |
| * Redirect flush to new project instance. |
| * @param toFlush the output String to flush. |
| */ |
| public void handleFlush(String toFlush) { |
| if (scriptRunner.isActive()) { |
| scriptRunner.getCurrentProject().demuxFlush(toFlush, false); |
| } else { |
| super.handleFlush(toFlush); |
| } |
| } |
| |
| /** |
| * Redirect error output to new project instance. |
| * @param errorOutputToHandle the error output to handle. |
| */ |
| public void handleErrorOutput(String errorOutputToHandle) { |
| if (scriptRunner.isActive()) { |
| scriptRunner.getCurrentProject().demuxOutput(errorOutputToHandle, true); |
| } else { |
| super.handleErrorOutput(errorOutputToHandle); |
| } |
| } |
| |
| /** |
| * Redirect error flush to new project instance. |
| * @param errorOutputToFlush the error output to flush. |
| */ |
| public void handleErrorFlush(String errorOutputToFlush) { |
| if (scriptRunner.isActive()) { |
| scriptRunner.getCurrentProject().demuxFlush(errorOutputToFlush, true); |
| } else { |
| super.handleErrorFlush(errorOutputToFlush); |
| } |
| } |
| |
| /** |
| * Creates a new project instance and configures it. |
| * @param f the File for which to create a Project. |
| */ |
| private Project createProjectForFile(File f) { |
| Project p = new Project(); |
| p.setDefaultInputStream(getProject().getDefaultInputStream()); |
| p.initProperties(); |
| p.setInputHandler(getProject().getInputHandler()); |
| getProject().initSubProject(p); |
| for (Iterator outer = propertySets.iterator(); outer.hasNext(); ) { |
| PropertySet set = (PropertySet) outer.next(); |
| Map props = set.getProperties(); |
| for (Iterator keys = props.keySet().iterator(); |
| keys.hasNext(); ) { |
| String key = keys.next().toString(); |
| if (MagicNames.PROJECT_BASEDIR.equals(key) |
| || MagicNames.ANT_FILE.equals(key)) { |
| continue; |
| } |
| Object value = props.get(key); |
| if (value != null && value instanceof String |
| && p.getProperty(key) == null) { |
| p.setNewProperty(key, (String) value); |
| } |
| } |
| } |
| p.setUserProperty(MagicNames.ANT_FILE, f.getAbsolutePath()); |
| attachListeners(f, p); |
| |
| // read build file |
| ProjectHelper.configureProject(p, f); |
| |
| return p; |
| } |
| |
| /** |
| * Wraps all registered test listeners in BuildListeners and |
| * attaches them to the new project instance. |
| * @param buildFile a build file. |
| * @param p the Project to attach to. |
| */ |
| private void attachListeners(File buildFile, Project p) { |
| Iterator it = listeners.iterator(); |
| while (it.hasNext()) { |
| AntUnitListener al = (AntUnitListener) it.next(); |
| p.addBuildListener(new BuildToAntUnitListener(buildFile |
| .getAbsolutePath(), |
| al)); |
| al.setCurrentTestProject(p); |
| } |
| } |
| |
| /** |
| * invokes start on all registered test listeners. |
| * @param targetName the name of the target. |
| */ |
| private void fireStartTest(String targetName) { |
| Iterator it = listeners.iterator(); |
| while (it.hasNext()) { |
| AntUnitListener al = (AntUnitListener) it.next(); |
| al.startTest(targetName); |
| } |
| } |
| |
| /** |
| * invokes addFailure on all registered test listeners. |
| * @param targetName the name of the failed target. |
| * @param ae the associated AssertionFailedException. |
| */ |
| private void fireFail(String targetName, AssertionFailedException ae) { |
| failures++; |
| Iterator it = listeners.iterator(); |
| while (it.hasNext()) { |
| AntUnitListener al = (AntUnitListener) it.next(); |
| al.addFailure(targetName, ae); |
| } |
| } |
| |
| /** |
| * invokes addError on all registered test listeners. |
| * @param targetName the name of the failed target. |
| * @param t the associated Throwable. |
| */ |
| private void fireError(String targetName, Throwable t) { |
| errors++; |
| Iterator it = listeners.iterator(); |
| while (it.hasNext()) { |
| AntUnitListener al = (AntUnitListener) it.next(); |
| al.addError(targetName, t); |
| } |
| } |
| |
| /** |
| * invokes endTest on all registered test listeners. |
| * @param targetName the name of the current target. |
| */ |
| private void fireEndTest(String targetName) { |
| Iterator it = listeners.iterator(); |
| while (it.hasNext()) { |
| AntUnitListener al = (AntUnitListener) it.next(); |
| al.endTest(targetName); |
| } |
| } |
| |
| /** |
| * Adapts AntUnitListener to BuildListener. |
| */ |
| private class BuildToAntUnitListener implements BuildListener { |
| private String buildFile; |
| private AntUnitListener a; |
| |
| BuildToAntUnitListener(String buildFile, AntUnitListener a) { |
| this.buildFile = buildFile; |
| this.a = a; |
| } |
| |
| public void buildStarted(BuildEvent event) { |
| a.startTestSuite(event.getProject(), buildFile); |
| } |
| public void buildFinished(BuildEvent event) { |
| a.endTestSuite(event.getProject(), buildFile); |
| } |
| public void targetStarted(BuildEvent event) { |
| } |
| public void targetFinished(BuildEvent event) { |
| } |
| public void taskStarted(BuildEvent event) {} |
| public void taskFinished(BuildEvent event) {} |
| public void messageLogged(BuildEvent event) {} |
| } |
| |
| } |