From 7451331ff70a2976193241b289aa6b59fea09770 Mon Sep 17 00:00:00 2001 From: Scokart Gilles Date: Tue, 27 Jan 2009 21:18:22 +0000 Subject: [PATCH] Finally extract AntUnitScriptRunner (I have also added assertion validating the proper sequence of method invocation) git-svn-id: https://svn.apache.org/repos/asf/ant/antlibs/antunit/trunk@738248 13f79535-47bb-0310-9956-ffa450edef68 --- src/main/org/apache/ant/antunit/AntUnit.java | 223 ------------ .../ant/antunit/AntUnitScriptRunner.java | 326 ++++++++++++++++++ 2 files changed, 326 insertions(+), 223 deletions(-) create mode 100644 src/main/org/apache/ant/antunit/AntUnitScriptRunner.java diff --git a/src/main/org/apache/ant/antunit/AntUnit.java b/src/main/org/apache/ant/antunit/AntUnit.java index 4adea8f..c48d5f1 100644 --- a/src/main/org/apache/ant/antunit/AntUnit.java +++ b/src/main/org/apache/ant/antunit/AntUnit.java @@ -24,10 +24,8 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Vector; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildException; @@ -224,227 +222,6 @@ public class AntUnit extends Task { } } - /** Manage the project reference in order to minimize the number of project creation - * while allowing every test to run in isolation. - */ - private static class AntUnitScriptRunner { - - /** ant script currently under testing */ - private File scriptFile; - - /** The project currently used. */ - private Project project = null; - - /** Indicates if a target has already be executed using this project. - * Value is undefined when project is null. - */ - private boolean projectIsDirty; - - /** - * The environment that creates the project and receive execution notification. - */ - private final AntUnitExecutionPlatform env; - - - public AntUnitScriptRunner(AntUnitExecutionPlatform env) { - if (env==null) { - throw new AssertionError(); - } - this.env = env; - } - - /** Set the ant script to use. */ - public void activate(File f) { - scriptFile = f; - project = null; - } - - /** Declare that the current ant script doesn't need to be used anymore. */ - 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("scriptFile==null"); - } - if (project == null) { - project = env.createProjectForFile(scriptFile); - projectIsDirty = false; - } - return project; - } - - /** - * 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("scriptFile==null"); - } - if (project == null || projectIsDirty) { - project = env.createProjectForFile(scriptFile); - } - //we already set isDirty to true in order to make sure we didn't reuse - //this project next time getRenewed is called. - projectIsDirty = true; - return project; - } - - /** - * 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"; - - /** - * name of the magic suiteSetUp target. - */ - private static final String SUITESETUP = "suiteSetUp"; - - /** - * name of the magic suiteTearDown target. - */ - private static final String SUITETEARDOWN = "suiteTearDown"; - - private boolean setUp; - private boolean tearDown; - private boolean suiteSetUp; - private boolean suiteTearDown; - - public List scanFile() { - Project newProject = getCurrentProject(); - Map targets = newProject.getTargets(); - setUp = targets.containsKey(SETUP); - tearDown = targets.containsKey(TEARDOWN); - suiteSetUp = targets.containsKey(SUITESETUP); - suiteTearDown = 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); - } - } - return testTargets; - } - - public boolean startSuite() { - getCurrentProject().fireBuildStarted(); - if (suiteSetUp) { - try { - Project newProject = getCleanProject(); - newProject.executeTarget(SUITESETUP); - } catch (AssertionFailedException e) { - env.fireStartTest(SUITESETUP); - env.fireFail(SUITESETUP, e); - return false; - } catch (BuildException e) { - env.fireStartTest(SUITESETUP); - fireFailOrError(SUITESETUP, e); - return false; - } - } - return true; - } - - public void runTarget(String name) { - Project newProject = getCleanProject(); - Vector v = new Vector(); - if (setUp) { - v.add(SETUP); - } - v.add(name); - // create and register a logcapturer on the newProject - LogCapturer lc = new LogCapturer(newProject); - try { - env.fireStartTest(name); - newProject.executeTargets(v); - } catch (AssertionFailedException e) { - env.fireFail(name, e); - } catch (BuildException e) { - fireFailOrError(name, e); - } 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); - // clean up - if (tearDown) { - try { - newProject.executeTarget(TEARDOWN); - } catch (final AssertionFailedException e) { - env.fireFail(name, e); - } catch (final BuildException e) { - fireFailOrError(name, e); - } - } - } - } - - public void endSuite(Throwable caught) { - if (suiteTearDown) { - try { - Project newProject = getCleanProject(); - newProject.executeTarget(SUITETEARDOWN); - } catch (AssertionFailedException e) { - env.fireStartTest(SUITETEARDOWN); - env.fireFail(SUITETEARDOWN, e); - } catch (BuildException e) { - env.fireStartTest(SUITETEARDOWN); - fireFailOrError(SUITETEARDOWN, e); - } - } - getCurrentProject().fireBuildFinished(caught); - } - - /** Report a failure or an exception for the test target name */ - private void fireFailOrError(String name, BuildException 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; - env.fireFail(name, (AssertionFailedException) t); - break; - } - t = ((BuildException) t).getCause(); - } - - if (!failed) { - env.fireError(name, e); - } - } - - } - /** * Processes a single build file. */ diff --git a/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java b/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java new file mode 100644 index 0000000..f5ea872 --- /dev/null +++ b/src/main/org/apache/ant/antunit/AntUnitScriptRunner.java @@ -0,0 +1,326 @@ +package org.apache.ant.antunit; + +import java.io.File; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; + +/** + * Run antunit tests. The lifecycle of this object is : + *
    + *
  1. activate(file) : Indicates the runner that the given file should be used.
  2. + *
  3. scanFile() : Provides you the list of targets.
  4. + *
  5. startSuite() : Start the suite
  6. + *
  7. runTarget(targetName) : Executed one or more time
  8. + *
  9. endSuite() : End the suite
  10. + *
  11. deactivate() Indicates to the runner that the test is finished and all + * resources can be freed
  12. + *
+ * Every step is mandatory. + */ +public class AntUnitScriptRunner { + + /** + * 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"; + + /** + * name of the magic suiteSetUp target. + */ + private static final String SUITESETUP = "suiteSetUp"; + + /** + * name of the magic suiteTearDown target. + */ + private static final String SUITETEARDOWN = "suiteTearDown"; + + /** + * Object used to interact with the environment (for example an ant task or a junit runner) + */ + 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.
+ * It is only defined when the project isActive() + */ + private File scriptFile; + + /** + * 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 + */ + private boolean isSuiteStarted; + + /** + * Does that script have a setUp target (defined when scanning the script) + */ + private boolean hasSetUp; + + /** + * Does that script have a tearDown target (defined when scanning the script) + */ + private boolean hasTearDown; + + /** + * Does that script has a suiteSetUp target. + */ + private boolean hasSuiteSetUp; + + /** + * Does that script has a suite tearDown target that should be executed. + */ + private boolean hasSuiteTearDown; + + /** + * The project currently used. + */ + private Project project = null; + + /** + * Indicates if a target has already be executed using this project. + * Value is undefined when project is null. + */ + private boolean projectIsDirty; + + + /** + * Create a new AntScriptRunner on the given environment. + * @param env The environment used to create project and where the test progress will be + * notified. + */ + public AntUnitScriptRunner(AntUnitExecutionPlatform env) { + if (env == null) { + throw new AssertionError(); + } + 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(); + } + if (project == null) { + project = env.createProjectForFile(scriptFile); + projectIsDirty = false; + } + return project; + } + + /** + * 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); + } + //we already set isDirty to true in order to make sure we didn't reuse + //this project next time getCleanProject is called. + projectIsDirty = true; + return project; + } + + /** + * Provide the list of test targets of the active antunit script. + * @pre isActive() + * @return List List of test target names + */ + 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; + return testTargets; + } + + /** + * Executes the suiteSetUp target if presents and report any execution error. + * 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(); + } + getCurrentProject().fireBuildStarted(); + if (hasSuiteSetUp) { + try { + Project newProject = getCleanProject(); + newProject.executeTarget(SUITESETUP); + } catch (AssertionFailedException e) { + env.fireStartTest(SUITESETUP); + env.fireFail(SUITESETUP, e); + return false; + } catch (BuildException e) { + env.fireStartTest(SUITESETUP); + fireFailOrError(SUITESETUP, e); + return false; + } + } + isSuiteStarted = true; //set to true only if suiteSetUp executed properly. + return true; + } + + /** + * Run the specific test target, possibly between the setUp and tearDown targets if + * it exists. Exception or failures are reported to the execution environment. + * @param name name of the test target to execute. + */ + public void runTarget(String name) { + if (!isSuiteStarted) { + throw new AssertionError(); + } + Project newProject = getCleanProject(); + Vector v = new Vector(); + if (hasSetUp) { + v.add(SETUP); + } + v.add(name); + // create and register a logcapturer on the newProject + LogCapturer lc = new LogCapturer(newProject); + try { + env.fireStartTest(name); + newProject.executeTargets(v); + } catch (AssertionFailedException e) { + env.fireFail(name, e); + } catch (BuildException e) { + fireFailOrError(name, e); + } 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); + // clean up + if (hasTearDown) { + try { + newProject.executeTarget(TEARDOWN); + } catch (final AssertionFailedException e) { + env.fireFail(name, e); + } catch (final BuildException e) { + fireFailOrError(name, e); + } + } + } + } + + /** + * 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. + */ + public void endSuite(Throwable caught) { + if (!isScanned) { + throw new AssertionError(); + } + if (hasSuiteTearDown) { + try { + Project newProject = getCleanProject(); + newProject.executeTarget(SUITETEARDOWN); + } catch (AssertionFailedException e) { + env.fireStartTest(SUITETEARDOWN); + env.fireFail(SUITETEARDOWN, e); + } catch (BuildException e) { + env.fireStartTest(SUITETEARDOWN); + fireFailOrError(SUITETEARDOWN, e); + } + } + getCurrentProject().fireBuildFinished(caught); + isSuiteStarted = false; + } + + /** Report a failure or an exception for the test target name */ + private void fireFailOrError(String name, BuildException 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; + env.fireFail(name, (AssertionFailedException) t); + break; + } + t = ((BuildException) t).getCause(); + } + + if (!failed) { + env.fireError(name, e); + } + } + +} \ No newline at end of file