Refactoring : - Move active/inactive state managment from AntUnitScriptRunner to AntUnit - Split AntUnitExecutionPlatform into a ProjectFactory (required to parse the script) and AntUnitExceutionNotifier (required to run the script) - Move the invocation of the sequence suiteSetUp, test targets suiteTearDown from AntUnit to AntUnitScriptRunner
git-svn-id: https://svn.apache.org/repos/asf/ant/antlibs/antunit/trunk@740452 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/org/apache/ant/antunit/AntUnit.java b/src/main/org/apache/ant/antunit/AntUnit.java
index c48d5f1..5bcadda 100644
--- a/src/main/org/apache/ant/antunit/AntUnit.java
+++ b/src/main/org/apache/ant/antunit/AntUnit.java
@@ -74,11 +74,7 @@
private Union buildFiles;
- private AntUnitExecutionPlatform myExecutionPlatform = new AntUnitExecutionPlatform() {
-
- public Project createProjectForFile(File f) {
- return AntUnit.this.createProjectForFile(f);
- }
+ private AntUnitExecutionNotifier notifier = new AntUnitExecutionNotifier() {
public void fireEndTest(String targetName) {
AntUnit.this.fireEndTest(targetName);
@@ -100,9 +96,10 @@
/**
* The object responsible for the execution of the unit test.
* scriptRunner is invoked to executes the targets and keep the
- * reference to the project.
+ * reference to the project. scriptRunner is defined only when the
+ * antunit script is running.
*/
- private AntUnitScriptRunner scriptRunner = new AntUnitScriptRunner(myExecutionPlatform);
+ private AntUnitScriptRunner scriptRunner;
/**
* listeners.
@@ -225,27 +222,19 @@
/**
* Processes a single build file.
*/
- private void doFile(File f) {
+ private void doFile(final File f) {
log("Running tests in build file " + f, Project.MSG_DEBUG);
- scriptRunner.activate(f);
- List testTargets = scriptRunner.scanFile();
-
- // start test
- Throwable caught = null;
+ ProjectFactory prjFactory = new ProjectFactory() {
+ public Project createProject() {
+ return createProjectForFile(f);
+ }
+ };
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;
+ scriptRunner = new AntUnitScriptRunner(prjFactory);
+ List testTargets = scriptRunner.getTestTartgets();
+ scriptRunner.runSuite(testTargets, notifier);
} finally {
- scriptRunner.endSuite(caught);
- scriptRunner.deactivate();
+ scriptRunner=null;
}
}
@@ -255,7 +244,7 @@
* @param outputToHandle the output to handle.
*/
public void handleOutput(String outputToHandle) {
- if (scriptRunner.isActive()) {
+ if (scriptRunner!=null) {
scriptRunner.getCurrentProject().demuxOutput(outputToHandle, false);
} else {
super.handleOutput(outputToHandle);
@@ -270,7 +259,7 @@
*/
public int handleInput(byte[] buffer, int offset, int length)
throws IOException {
- if (scriptRunner.isActive()) {
+ if (scriptRunner!=null) {
return scriptRunner.getCurrentProject().demuxInput(buffer, offset, length);
}
return super.handleInput(buffer, offset, length);
@@ -281,7 +270,7 @@
* @param toFlush the output String to flush.
*/
public void handleFlush(String toFlush) {
- if (scriptRunner.isActive()) {
+ if (scriptRunner!=null) {
scriptRunner.getCurrentProject().demuxFlush(toFlush, false);
} else {
super.handleFlush(toFlush);
@@ -293,7 +282,7 @@
* @param errorOutputToHandle the error output to handle.
*/
public void handleErrorOutput(String errorOutputToHandle) {
- if (scriptRunner.isActive()) {
+ if (scriptRunner!=null) {
scriptRunner.getCurrentProject().demuxOutput(errorOutputToHandle, true);
} else {
super.handleErrorOutput(errorOutputToHandle);
@@ -305,7 +294,7 @@
* @param errorOutputToFlush the error output to flush.
*/
public void handleErrorFlush(String errorOutputToFlush) {
- if (scriptRunner.isActive()) {
+ if (scriptRunner!=null) {
scriptRunner.getCurrentProject().demuxFlush(errorOutputToFlush, true);
} else {
super.handleErrorFlush(errorOutputToFlush);
diff --git a/src/main/org/apache/ant/antunit/AntUnitExecutionPlatform.java b/src/main/org/apache/ant/antunit/AntUnitExecutionNotifier.java
similarity index 83%
rename from src/main/org/apache/ant/antunit/AntUnitExecutionPlatform.java
rename to src/main/org/apache/ant/antunit/AntUnitExecutionNotifier.java
index 15f9270..a531766 100644
--- a/src/main/org/apache/ant/antunit/AntUnitExecutionPlatform.java
+++ b/src/main/org/apache/ant/antunit/AntUnitExecutionNotifier.java
@@ -25,16 +25,10 @@
import org.apache.tools.ant.Project;
/**
- * Provides methods that allow the AntUnitScriptRunner to interact
- * with the environment in which it executes.
+ * Provides methods that allow the AntUnitScriptRunner to notify the test progress.
+ * @since 1.2
*/
-public interface AntUnitExecutionPlatform {
-
- /**
- * Creates a new project instance and configures it.
- * @param f the File for which to create a Project.
- */
- public Project createProjectForFile(File f);
+public interface AntUnitExecutionNotifier {
/**
* invokes start on all registered test listeners.
diff --git a/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java b/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java
index cf207a3..3b9fe80 100644
--- a/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java
+++ b/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java
@@ -1,6 +1,5 @@
package org.apache.ant.antunit;
-import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -11,17 +10,13 @@
import org.apache.tools.ant.Project;
/**
- * Run antunit tests. The lifecycle of this object is :
- * <ol>
- * <li> activate(file) : Indicates the runner that the given file should be used.</li>
- * <li> scanFile() : Provides you the list of targets.</li>
- * <li> startSuite() : Start the suite</li>
- * <li> runTarget(targetName) : Executed one or more time</li>
- * <li> endSuite() : End the suite</li>
- * <li> deactivate() Indicates to the runner that the test is finished and all
- * resources can be freed </li>
- * </ol>
- * Every step is mandatory.
+ * Run antunit tests suites. This AntUnitScriptRunner is responsible for the
+ * management of the ant project and the correct invocation the target (taking
+ * into account properly the [case]setUp and [case]tearDown targets).
+ * The user can however provide the order of the test targets and or can filter
+ * the list of test targets to execute.
+ * The user must also provide its ProjectFactory and an AntUnitExecutionNotifier.
+ * @since 1.2
*/
public class AntUnitScriptRunner {
@@ -51,24 +46,11 @@
private static final String SUITETEARDOWN = "suiteTearDown";
/**
- * Object used to interact with the environment (for example an ant task or a junit runner)
+ * Object used to create projects in order to support test isolation.
*/
- private final AntUnitExecutionPlatform env;
-
- /**
- * Ant script file currently under testing. The file is set at activation, and used
- * during all execution every time we want have to create a new project.</br>
- * It is only defined when the project isActive()
- */
- private File scriptFile;
+ private final ProjectFactory prjFactory;
/**
- * Indicates if the active project is already scanned and if the value the fields
- * hasSuiteSetUp, hasSetUp, hasTearDown, hasSuiteTearDown are defined.
- */
- private boolean isScanned;
-
- /**
* Indicates if the startSuite method has been invoked. Use to fail fast if the
* the caller forget to call the startSuite method
*/
@@ -77,22 +59,27 @@
/**
* Does that script have a setUp target (defined when scanning the script)
*/
- private boolean hasSetUp;
+ private final boolean hasSetUp;
/**
* Does that script have a tearDown target (defined when scanning the script)
*/
- private boolean hasTearDown;
+ private final boolean hasTearDown;
/**
* Does that script has a suiteSetUp target.
*/
- private boolean hasSuiteSetUp;
+ private final boolean hasSuiteSetUp;
/**
* Does that script has a suite tearDown target that should be executed.
*/
- private boolean hasSuiteTearDown;
+ private final boolean hasSuiteTearDown;
+
+ /**
+ * List of target names
+ */
+ private final List testTargets;
/**
* The project currently used.
@@ -108,54 +95,35 @@
/**
* Create a new AntScriptRunner on the given environment.
- * @param env The environment used to create project and where the test progress will be
- * notified.
+ * @param prjFactory A factory for the ant project that will contains the antunit test to execute.
+ * The factory might be invoked multiple time in order to provide test isolation.
*/
- public AntUnitScriptRunner(AntUnitExecutionPlatform env) {
- if (env == null) {
- throw new AssertionError();
+ public AntUnitScriptRunner(ProjectFactory prjFactory) {
+ this.prjFactory = prjFactory;
+ Project newProject = getCurrentProject();
+ Map targets = newProject.getTargets();
+ hasSetUp = targets.containsKey(SETUP);
+ hasTearDown = targets.containsKey(TEARDOWN);
+ hasSuiteSetUp = targets.containsKey(SUITESETUP);
+ hasSuiteTearDown = targets.containsKey(SUITETEARDOWN);
+ testTargets = new LinkedList();
+ Iterator it = targets.keySet().iterator();
+ while (it.hasNext()) {
+ String name = (String) it.next();
+ if (name.startsWith(TEST) && !name.equals(TEST)) {
+ testTargets.add(name);
+ }
}
- this.env = env;
- }
-
- /**
- * Set the ant script to use.
- * @post isActive()
- */
- public void activate(File f) {
- scriptFile = f;
- project = null;
- isScanned = false;
- isSuiteStarted = false;
- }
-
- /**
- * Declare that the current ant script doesn't need to be used anymore.
- * @post !isActive()
- */
- public void deactivate() {
- scriptFile = null;
- project = null;
- }
-
- /**
- * Indicates if there is a project currently under test.
- */
- public boolean isActive() {
- return scriptFile != null;
}
/**
* Get the project currently in use. The caller is not allowed to invoke a target or
* do anything that would break the isolation of the test targets.
- * @pre isActif()
*/
- public Project getCurrentProject() {
- if (!isActive()) {
- throw new AssertionError();
- }
+ public final Project getCurrentProject() {
+ //Method is final because it is called from the constructor
if (project == null) {
- project = env.createProjectForFile(scriptFile);
+ project = prjFactory.createProject();
projectIsDirty = false;
}
return project;
@@ -163,14 +131,10 @@
/**
* Get a project that has not yet been used in order to execute a target on it.
- * @pre isActive()
*/
private Project getCleanProject() {
- if (!isActive()) {
- throw new AssertionError();
- }
if (project == null || projectIsDirty) {
- project = env.createProjectForFile(scriptFile);
+ project = prjFactory.createProject();
}
//we already set isDirty to true in order to make sure we didn't reuse
//this project next time getCleanProject is called.
@@ -179,60 +143,34 @@
}
/**
- * Provides the list of test targets of the active antunit script.
- * @pre isActive()
- * @return List<String> List of test target names
+ * @return List<String> List of test targets of the script file
*/
- public List scanFile() {
- if (!isActive()) {
- throw new AssertionError();
- }
- Project newProject = getCurrentProject();
- Map targets = newProject.getTargets();
- hasSetUp = targets.containsKey(SETUP);
- hasTearDown = targets.containsKey(TEARDOWN);
- hasSuiteSetUp = targets.containsKey(SUITESETUP);
- hasSuiteTearDown = targets.containsKey(SUITETEARDOWN);
- List testTargets = new LinkedList();
- Iterator it = targets.keySet().iterator();
- while (it.hasNext()) {
- String name = (String) it.next();
- if (name.startsWith(TEST) && !name.equals(TEST)) {
- testTargets.add(name);
- }
- }
- isScanned = true;
+ public List getTestTartgets() {
return testTargets;
}
/**
* Provides the name of the active script.
- * @pre isAvtive()
*/
public String getName() {
- if (!isActive()) {
- throw new AssertionError();
- }
return getCurrentProject().getName();
}
/**
* Executes the suiteSetUp target if presents and report any execution error.
+ * A failure is reported to the notifier and by returning false.
* Note that if the method return false, you are not allowed to run targets.
* @return false in case of execution failure. true in case of success.
*/
- public boolean startSuite() {
- if (!isScanned) {
- throw new AssertionError();
- }
+ private boolean startSuite(AntUnitExecutionNotifier notifier) {
getCurrentProject().fireBuildStarted();
if (hasSuiteSetUp) {
try {
Project newProject = getCleanProject();
newProject.executeTarget(SUITESETUP);
} catch (BuildException e) {
- env.fireStartTest(SUITESETUP);
- fireFailOrError(SUITESETUP, e);
+ notifier.fireStartTest(SUITESETUP);
+ fireFailOrError(SUITESETUP, e, notifier);
return false;
}
}
@@ -242,10 +180,12 @@
/**
* Run the specific test target, possibly between the setUp and tearDown targets if
- * it exists. Exception or failures are reported to the execution environment.
+ * it exists. Exception or failures are reported to the notifier.
* @param name name of the test target to execute.
+ * @param notifier will receive execution notifications.
+ * @pre startSuite has been invoked successfully
*/
- public void runTarget(String name) {
+ private void runTarget(String name, AntUnitExecutionNotifier notifier) {
if (!isSuiteStarted) {
throw new AssertionError();
}
@@ -258,23 +198,23 @@
// create and register a logcapturer on the newProject
LogCapturer lc = new LogCapturer(newProject);
try {
- env.fireStartTest(name);
+ notifier.fireStartTest(name);
newProject.executeTargets(v);
} catch (BuildException e) {
- fireFailOrError(name, e);
+ fireFailOrError(name, e, notifier);
} finally {
// fire endTest here instead of the endTarget
// event, otherwise an error would be
// registered after the endTest event -
// endTarget is called before this method's catch block
// is reached.
- env.fireEndTest(name);
+ notifier.fireEndTest(name);
// clean up
if (hasTearDown) {
try {
newProject.executeTarget(TEARDOWN);
} catch (final BuildException e) {
- fireFailOrError(name, e);
+ fireFailOrError(name, e, notifier);
}
}
}
@@ -282,20 +222,18 @@
/**
* Executes the suiteTearDown target if presents and report any execution error.
- * @param caught Any internal exception triggered (and catched) by the caller indicating that
- * the this runner could not be invoked as expected.
+ * @param caught Any internal exception triggered (and caught) by the caller indicating that
+ * the execution could not be invoked as expected.
+ * @param notifier will receive execution notifications.
*/
- public void endSuite(Throwable caught) {
- if (!isScanned) {
- throw new AssertionError();
- }
+ private void endSuite(Throwable caught, AntUnitExecutionNotifier notifier) {
if (hasSuiteTearDown) {
try {
Project newProject = getCleanProject();
newProject.executeTarget(SUITETEARDOWN);
} catch (BuildException e) {
- env.fireStartTest(SUITETEARDOWN);
- fireFailOrError(SUITETEARDOWN, e);
+ notifier.fireStartTest(SUITETEARDOWN);
+ fireFailOrError(SUITETEARDOWN, e, notifier);
}
}
getCurrentProject().fireBuildFinished(caught);
@@ -307,20 +245,45 @@
* or is caused by an AssertionFailedException. If so, fire a failure for
* given targetName. Otherwise fire an error.
*/
- private void fireFailOrError(String targetName, BuildException e) {
+ private void fireFailOrError(String targetName, BuildException e,
+ AntUnitExecutionNotifier notifier) {
boolean failed = false;
Throwable t = e;
while (t != null && t instanceof BuildException) {
if (t instanceof AssertionFailedException) {
failed = true;
- env.fireFail(targetName, (AssertionFailedException) t);
+ notifier.fireFail(targetName, (AssertionFailedException) t);
break;
}
t = ((BuildException) t).getCause();
}
if (!failed) {
- env.fireError(targetName, e);
+ notifier.fireError(targetName, e);
+ }
+ }
+
+
+ /**
+ * Executes the suite.
+ * @param suiteTargets An ordered list of test targets. It must be a sublist of getTestTargets
+ * @param notifier
+ */
+ public void runSuite(List suiteTargets, AntUnitExecutionNotifier notifier) {
+ Throwable caught = null;
+ try {
+ if (!startSuite(notifier)) {
+ return;
+ }
+ Iterator iter = suiteTargets.iterator();
+ while (iter.hasNext()) {
+ String name = (String) iter.next();
+ runTarget(name, notifier);
+ }
+ } catch (Throwable e) {
+ caught = e;
+ } finally {
+ endSuite(caught, notifier);
}
}
diff --git a/src/main/org/apache/ant/antunit/ProjectFactory.java b/src/main/org/apache/ant/antunit/ProjectFactory.java
new file mode 100644
index 0000000..a056c76
--- /dev/null
+++ b/src/main/org/apache/ant/antunit/ProjectFactory.java
@@ -0,0 +1,20 @@
+package org.apache.ant.antunit;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * Provides project instances for AntUnit execution.<br/>
+ * The aproach to creates a project depends on the context. When invoked from an
+ * ant project, some elements might be intialized from the parent project. When
+ * executed in a junit runner, a brand new project must be initialized.<br/>
+ * The AntScriptRunner will usually creates multiple project in order to provide test isolation.
+ * @since 1.2
+ */
+public interface ProjectFactory {
+
+ /**
+ * Creates a new project instance and configures it according to the execution context.
+ */
+ public Project createProject();
+
+}