Package org.apache.ant.antunit

Source Code of org.apache.ant.antunit.AntUnitScriptRunner

/*
* 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.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 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 {

    /**
     * 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 create projects in order to support test isolation.
     */
    private final ProjectFactory prjFactory;
   
    /**
     * 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 final boolean hasSetUp;

    /**
     * Does that script have a tearDown target (defined when scanning the script)
     */
    private final boolean hasTearDown;

    /**
     * Does that script has a suiteSetUp target.
     */
    private final boolean hasSuiteSetUp;

    /**
     * Does that script has a suite tearDown target that should be executed.
     */
    private final boolean hasSuiteTearDown;

    /**
     * List of target names
     */
    private final List testTargets;

    /**
     * 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 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.
     * @throws BuildException The project can not be parsed
     */
    public AntUnitScriptRunner(ProjectFactory prjFactory) throws BuildException {
        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);
            }
        }
    }

    /**
     * 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.
     * @throws BuildException The project can not be parsed
     */
    public final Project getCurrentProject() throws BuildException {
      //Method is final because it is called from the constructor
        if (project == null) {
            project = prjFactory.createProject();
            projectIsDirty = false;
        }
        return project;
    }

    /**
     * Get a project that has not yet been used in order to execute a target on it.
     */
    private Project getCleanProject() {
        if (project == null || projectIsDirty) {
            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. 
        projectIsDirty = true;
        return project;
    }

    /**
     * @return List<String> List of test targets of the script file
     */
    public List getTestTartgets() {
        return testTargets;
    }

    /**
     * Provides the name of the active script.
     */
    public String getName() {
        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.
     */
    private boolean startSuite(AntUnitExecutionNotifier notifier) {
        getCurrentProject().fireBuildStarted();
        if (hasSuiteSetUp) {
            try {
                Project newProject = getCleanProject();
                newProject.executeTarget(SUITESETUP);
            } catch (BuildException e) {
                notifier.fireStartTest(SUITESETUP);
                fireFailOrError(SUITESETUP, e, notifier);
                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 notifier.
     * @param name name of the test target to execute.
     * @param notifier will receive execution notifications.
     * @pre startSuite has been invoked successfully
     */
    private void runTarget(String name, AntUnitExecutionNotifier notifier) {
        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 {
            notifier.fireStartTest(name);
            newProject.executeTargets(v);
        } catch (BuildException 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.
            notifier.fireEndTest(name);
            // clean up
            if (hasTearDown) {
                try {
                    newProject.executeTarget(TEARDOWN);
                } catch (final BuildException e) {
                    fireFailOrError(name, e, notifier);
                }
            }
        }
    }

    /**
     * Executes the suiteTearDown target if presents and report any execution error.
     * @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.
     */
    private void endSuite(Throwable caught, AntUnitExecutionNotifier notifier) {
        if (hasSuiteTearDown) {
            try {
                Project newProject = getCleanProject();
                newProject.executeTarget(SUITETEARDOWN);
            } catch (BuildException e) {
                notifier.fireStartTest(SUITETEARDOWN);
                fireFailOrError(SUITETEARDOWN, e, notifier);
            }
        }
        getCurrentProject().fireBuildFinished(caught);
        isSuiteStarted = false;
    }

    /**
     * Try to see whether the BuildException e is an AssertionFailedException
     * 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,
                                 AntUnitExecutionNotifier notifier) {
        boolean failed = false;
        Throwable t = e;
        while (t != null && t instanceof BuildException) {
            if (t instanceof AssertionFailedException) {
                failed = true;
                notifier.fireFail(targetName, (AssertionFailedException) t);
                break;
            }
            t = ((BuildException) t).getCause();
        }

        if (!failed) {
            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);
        }
    }

}
TOP

Related Classes of org.apache.ant.antunit.AntUnitScriptRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.