355 lines
11 KiB
Java
355 lines
11 KiB
Java
/*
|
|
* Copyright 2005-2006 The Apache Software Foundation
|
|
*
|
|
* Licensed 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.Map;
|
|
import java.util.Vector;
|
|
|
|
import org.apache.tools.ant.BuildEvent;
|
|
import org.apache.tools.ant.BuildException;
|
|
import org.apache.tools.ant.BuildListener;
|
|
import org.apache.tools.ant.DirectoryScanner;
|
|
import org.apache.tools.ant.Project;
|
|
import org.apache.tools.ant.ProjectHelper;
|
|
import org.apache.tools.ant.Target;
|
|
import org.apache.tools.ant.Task;
|
|
import org.apache.tools.ant.taskdefs.LogOutputStream;
|
|
import org.apache.tools.ant.types.FileSet;
|
|
|
|
/**
|
|
* Run all targets in a given build file who's name starts with "test".
|
|
*
|
|
* <p>Run the "setUp" target before each of them if present, same for
|
|
* "tearDown" after each "test" target. If a target throws an
|
|
* AssertionFailedException, the test has failed, any other exception
|
|
* is counted as an error (although BuildException will be scanned
|
|
* recursively for nested AssertionFailedExceptions).</p>
|
|
*/
|
|
public class AntUnit extends Task {
|
|
|
|
/**
|
|
* name of the magic setUp target.
|
|
*/
|
|
private static final String SETUP = "setUp";
|
|
/**
|
|
* prefix that identifies test targets.
|
|
*/
|
|
private static final String TEST = "test";
|
|
/**
|
|
* name of the magic tearDown target.
|
|
*/
|
|
private static final String TEARDOWN = "tearDown";
|
|
|
|
/**
|
|
* The build files to process.
|
|
*/
|
|
private ArrayList filesets = new ArrayList();
|
|
|
|
/**
|
|
* project instance for the build file currently under test.
|
|
*/
|
|
private Project newProject;
|
|
|
|
/**
|
|
* listeners.
|
|
*/
|
|
private ArrayList listeners = 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;
|
|
|
|
/**
|
|
* 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_FILESET =
|
|
"You must specify at least one nested fileset.";
|
|
|
|
/**
|
|
* adds build files to run as tests.
|
|
*/
|
|
public void add(FileSet fs) {
|
|
filesets.add(fs);
|
|
}
|
|
|
|
/**
|
|
* Adds a test listener.
|
|
*/
|
|
public void add(AntUnitListener al) {
|
|
listeners.add(al);
|
|
}
|
|
|
|
/**
|
|
* stop testing if an error or failure occurs?
|
|
*/
|
|
public void setFailOnError(boolean failOnError) {
|
|
this.failOnError = failOnError;
|
|
}
|
|
|
|
public void execute() {
|
|
if (filesets.size() == 0) {
|
|
throw new BuildException(ERROR_NO_FILESET);
|
|
}
|
|
Iterator iter = filesets.iterator();
|
|
while (iter.hasNext()) {
|
|
doFileSet((FileSet) iter.next());
|
|
}
|
|
if (failOnError && (failures > 0 || errors > 0)) {
|
|
throw new BuildException(ERROR_TESTS_FAILED
|
|
+ failures + " failure" + (failures != 1 ? "s" : "")
|
|
+ " and "
|
|
+ errors + " error" + (errors != 1 ? "s" : ""));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes a fileset.
|
|
*/
|
|
private void doFileSet(FileSet fs) {
|
|
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
|
File fromDir = fs.getDir(getProject());
|
|
String[] files = ds.getIncludedFiles();
|
|
for (int i = 0; i < files.length; i++) {
|
|
doFile(new File(fromDir, files[i]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes a single build file.
|
|
*/
|
|
private void doFile(File f) {
|
|
// setup project instance
|
|
newProject = new Project();
|
|
newProject.setDefaultInputStream(getProject().getDefaultInputStream());
|
|
newProject.setJavaVersionProperty();
|
|
newProject.setInputHandler(getProject().getInputHandler());
|
|
getProject().initSubProject(newProject);
|
|
newProject.setUserProperty("ant.file" , f.getAbsolutePath());
|
|
attachListeners(f, newProject);
|
|
|
|
// read build file
|
|
ProjectHelper.configureProject(newProject, f);
|
|
|
|
// find targets
|
|
Map targets = newProject.getTargets();
|
|
Target setUp = (Target) targets.get(SETUP);
|
|
Target tearDown = (Target) targets.get(TEARDOWN);
|
|
|
|
// start test
|
|
newProject.fireBuildStarted();
|
|
Throwable caught = null;
|
|
try {
|
|
Iterator iter = targets.keySet().iterator();
|
|
while (iter.hasNext()) {
|
|
String name = (String) iter.next();
|
|
if (name.startsWith(TEST)) {
|
|
Vector v = new Vector();
|
|
if (setUp != null) {
|
|
v.add(SETUP);
|
|
}
|
|
v.add(name);
|
|
LogCapturer lc = new LogCapturer(newProject);
|
|
try {
|
|
newProject.executeTargets(v);
|
|
} catch (AssertionFailedException e) {
|
|
fireFail(name, e);
|
|
} catch (BuildException e) {
|
|
BuildException orig = e;
|
|
boolean failed = false;
|
|
|
|
// try to see whether the BuildException masks
|
|
// an AssertionFailedException. if so, treat
|
|
// it as failure instead of error.
|
|
Throwable t = e.getCause();
|
|
while (t != null && t instanceof BuildException) {
|
|
if (t instanceof AssertionFailedException) {
|
|
failed = true;
|
|
fireFail(name, (AssertionFailedException) t);
|
|
break;
|
|
}
|
|
t = ((BuildException) t).getCause();
|
|
}
|
|
|
|
if (!failed) {
|
|
fireError(name, e);
|
|
}
|
|
} finally {
|
|
// clean up
|
|
if (tearDown != null) {
|
|
newProject.executeTarget(TEARDOWN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (Throwable e) {
|
|
caught = e;
|
|
} finally {
|
|
newProject.fireBuildFinished(caught);
|
|
newProject = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redirect output to new project instance.
|
|
*/
|
|
public void handleOutput(String outputToHandle) {
|
|
if (newProject != null) {
|
|
newProject.demuxOutput(outputToHandle, false);
|
|
} else {
|
|
super.handleOutput(outputToHandle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redirect input to new project instance.
|
|
*/
|
|
public int handleInput(byte[] buffer, int offset, int length)
|
|
throws IOException {
|
|
if (newProject != null) {
|
|
return newProject.demuxInput(buffer, offset, length);
|
|
}
|
|
return super.handleInput(buffer, offset, length);
|
|
}
|
|
|
|
/**
|
|
* Redirect flush to new project instance.
|
|
*/
|
|
public void handleFlush(String toFlush) {
|
|
if (newProject != null) {
|
|
newProject.demuxFlush(toFlush, false);
|
|
} else {
|
|
super.handleFlush(toFlush);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redirect error output to new project instance.
|
|
*/
|
|
public void handleErrorOutput(String errorOutputToHandle) {
|
|
if (newProject != null) {
|
|
newProject.demuxOutput(errorOutputToHandle, true);
|
|
} else {
|
|
super.handleErrorOutput(errorOutputToHandle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redirect error flush to new project instance.
|
|
*/
|
|
public void handleErrorFlush(String errorOutputToFlush) {
|
|
if (newProject != null) {
|
|
newProject.demuxFlush(errorOutputToFlush, true);
|
|
} else {
|
|
super.handleErrorFlush(errorOutputToFlush);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wraps all registered test listeners in BuildListeners and
|
|
* attaches them to the new project instance.
|
|
*/
|
|
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));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* invokes addFailure on all registered test listeners.
|
|
*/
|
|
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.
|
|
*/
|
|
private void fireError(String targetName, Throwable t) {
|
|
errors++;
|
|
Iterator it = listeners.iterator();
|
|
while (it.hasNext()) {
|
|
AntUnitListener al = (AntUnitListener) it.next();
|
|
al.addError(targetName, t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
a.setOutput(new LogOutputStream(AntUnit.this, Project.MSG_INFO));
|
|
}
|
|
|
|
public void buildStarted(BuildEvent event) {
|
|
a.startTestSuite(buildFile);
|
|
}
|
|
public void buildFinished(BuildEvent event) {
|
|
a.endTestSuite(buildFile);
|
|
}
|
|
public void targetStarted(BuildEvent event) {
|
|
String tName = event.getTarget().getName();
|
|
if (tName.startsWith(TEST)) {
|
|
a.startTest(tName);
|
|
}
|
|
}
|
|
public void targetFinished(BuildEvent event) {
|
|
String tName = event.getTarget().getName();
|
|
if (tName.startsWith(TEST)) {
|
|
a.endTest(tName);
|
|
}
|
|
}
|
|
public void taskStarted(BuildEvent event) {}
|
|
public void taskFinished(BuildEvent event) {}
|
|
public void messageLogged(BuildEvent event) {}
|
|
}
|
|
|
|
}
|