Code Search for Developers
 
 
  

TestRunnerImpl.java from SmartFrog at Krugle


Show TestRunnerImpl.java syntax highlighted

/** (C) Copyright 1998-2004 Hewlett-Packard Development Company, LP

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 For more information: www.smartfrog.org

 */
package org.smartfrog.services.xunit.base;

import org.smartfrog.sfcore.common.SmartFrogException;
import org.smartfrog.sfcore.common.SmartFrogInitException;
import org.smartfrog.sfcore.common.SmartFrogLivenessException;
import org.smartfrog.sfcore.common.SmartFrogRuntimeException;
import org.smartfrog.sfcore.compound.CompoundImpl;
import org.smartfrog.sfcore.logging.Log;
import org.smartfrog.sfcore.prim.Prim;
import org.smartfrog.sfcore.prim.TerminationRecord;
import org.smartfrog.sfcore.reference.Reference;
import org.smartfrog.sfcore.utils.ComponentHelper;
import org.smartfrog.sfcore.utils.ShouldDetachOrTerminate;
import org.smartfrog.services.xunit.serial.Statistics;
import org.smartfrog.services.xunit.serial.ThrowableTraceInfo;
import org.smartfrog.services.xunit.log.TestListenerLog;
import org.smartfrog.services.assertions.TestBlock;

import java.rmi.RemoteException;
import java.util.Enumeration;

/**
 * This is the test runner. It runs multiple test suites; It keeps all its
 * public state in a configuration object that can be got/cloned and serialized
 * to suites created.
 *
 * This class implements (incompletely) the {@link TestBlock} interface
 * This lets the class be hosted inside junit test running code that
 * has been written for TestBlock instances. 
 */

public class TestRunnerImpl extends CompoundImpl implements TestRunner,
        Runnable, TestBlock {

    private Log log;
    private ComponentHelper helper;
    private Reference name;

    /**
     * a cached exception that is thrown on a liveness failure
     */
    private Throwable cachedException = null;
    /**
     * flag set when the tests are finished
     */
    private boolean finished = false;

    /**
     * should we fail messily if a test failed
     */
    private boolean failOnError = true;

    /**
     * thread priority
     */
    private int threadPriority = Thread.NORM_PRIORITY;

    /**
     * Should we terminate after running our tests?
     * {@link ShouldDetachOrTerminate#ATTR_SHOULD_TERMINATE}
     */
    private boolean shouldTerminate = true;

    /**
     * if terminating, should we detach?
     * Should we terminate after running our tests?
     * {@link ShouldDetachOrTerminate#ATTR_SHOULD_DETACH}
     */
    private boolean shouldDetach = false;

    /**
     * String to set to the name of a single test component to run
     */
    private String singleTest=null;

    /**
     * thread to run the tests
     */
    private Thread worker = null;

    /**
     * keeper of statistics
     */
    private Statistics statistics = new Statistics();

    /**
     * who listens to the tests? This is potentially remote
     */
    private RunnerConfiguration configuration = new RunnerConfiguration();

    public static final String ERROR_TESTS_IN_PROGRESS = "Component is already running tests";
    public static final String TESTS_FAILED = "Tests Failed";
    private static final String TEST_WAS_INTERRUPTED = "Test was interrupted";

    /**
     * constructor
     *
     * @throws RemoteException for network problems
     */
    public TestRunnerImpl() throws RemoteException {
        helper = new ComponentHelper(this);
    }

    private synchronized Thread getWorker() {
        return worker;
    }

    private synchronized void setWorker(Thread worker) {
        this.worker = worker;
    }

    /**
     * validate our settings, bail out if they are invalid
     *
     * @throws SmartFrogInitException if the configuration is invalid
     */
    private void validate() throws SmartFrogInitException {
        if (threadPriority < Thread.MIN_PRIORITY ||
                threadPriority > Thread.MAX_PRIORITY) {
            throw new SmartFrogInitException(ATTR_THREAD_PRIORITY +
                    " is out of range -must be within "
                    + Thread.MIN_PRIORITY + " and " + Thread.MAX_PRIORITY);
        }
    }

    /**
     * Deploy the compound. Deployment is defined as iterating over the context
     * and deploying any parsed eager components.
     *
     * @throws SmartFrogException     failure deploying compound or
     *                                  sub-component
     * @throws RemoteException In case of Remote/nework error
     */
    public synchronized void sfDeploy() throws SmartFrogException,
            RemoteException {
        super.sfDeploy();
        log = helper.getLogger();
    }

    /**
     * Starts the compound. This sends a synchronous sfStart to all managed
     * components in the compound context. Any failure will cause the compound
     * to terminate
     *
     * @throws SmartFrogException failed to start compound
     * @throws RemoteException In case of Remote/nework error
     */
    public synchronized void sfStart() throws SmartFrogException,
            RemoteException {
        //this will deploy all our children, including the test suites
        super.sfStart();
        name = sfCompleteName();
        Object o = sfResolve(ATTR_LISTENER,
                configuration.getListenerFactory(),
                true);
        if (!(o instanceof TestListenerFactory)) {
            throw new SmartFrogException("The attribute " +
                    ATTR_LISTENER
                    + "must refer to an implementation of TestListenerFactory");
        }
        TestListenerFactory listenerFactory = (TestListenerFactory) o;
        String listenerName = ((Prim) listenerFactory).sfResolve(
                TestListenerFactory.ATTR_NAME,
                "",
                true);
        log.info("Test Listener is of type " + listenerName);
        configuration.setListenerFactory(listenerFactory);
        configuration.setKeepGoing(
                sfResolve(ATTR_KEEPGOING, configuration.getKeepGoing(), false));
        failOnError = sfResolve(ATTR_FAILONERROR, failOnError, false);
        threadPriority = sfResolve(ATTR_THREAD_PRIORITY,
                threadPriority,
                false);
        shouldTerminate = sfResolve(
                ShouldDetachOrTerminate.ATTR_SHOULD_TERMINATE, shouldTerminate, false);
        shouldDetach = sfResolve(
                ShouldDetachOrTerminate.ATTR_SHOULD_DETACH, shouldDetach, false);
        singleTest = sfResolve(ATTR_SINGLE_TEST,singleTest,false);
        TestListenerLog testLog = (TestListenerLog) sfResolve(ATTR_TESTLOG, (Prim) null, false);
        configuration.setTestLog(testLog);

        validate();
        //execute the tests in all the suites attached to this class
        boolean runTests = sfResolve(ATTR_RUN_TESTS_ON_STARTUP, true, true);
        if (runTests) {
            startTests();
        } else {
            log.info("Tests will only start when directly invoked");
        }
    }

    /**
     * Liveness tests first delegates to the parent, then considers itself live
     * unless all of the following conditions are met <ol> <li>We are finished
     * <li>There was an exception <li>failOnError is set </ol> In which case the
     * cached exception gets thrown.
     *
     * @param source source of ping
     * @throws SmartFrogLivenessException liveness failed
     */
    public void sfPing(Object source) throws SmartFrogLivenessException,
            RemoteException {
        //check the substuff
        super.sfPing(source);
        //then look to see if we had a failure with our tests
        synchronized(this) {
            if (failOnError && isFinished() && getCachedException() != null) {
                SmartFrogLivenessException.forward(getCachedException());
            }
        }
    }


    /**
     * Performs the compound termination behaviour. Based on sfSyncTerminate
     * flag this gets forwarded to sfSyncTerminate or sfASyncTerminateWith
     * method. Terminates children before self.
     *
     * @param status termination status
     */
    public synchronized void sfTerminateWith(TerminationRecord status) {
        super.sfTerminateWith(status);
        Thread thread = getWorker();
        if (thread != null && thread.isAlive()) {
            thread.interrupt();
        }
    }

    /**
     * run the test
     *
     * @throws java.rmi.RemoteException
     */
    public synchronized boolean startTests() throws RemoteException,
            SmartFrogException {
        if (getWorker() != null) {
            throw new SmartFrogException(ERROR_TESTS_IN_PROGRESS);
        }
        Thread thread = new Thread(this);
        thread.setName("tester");
        thread.setPriority(threadPriority);
        log.info("Starting new tester at priority " + threadPriority);
        setWorker(thread);
        thread.start();
        return true;
    }

    /**
     * this is the thread entry point; runs the tests in a new thread.
     *
     * @see Thread#run()
     */
    public void run() {
        setFinished(false);
        log.info("Beginning tests");
        try {
            if (!executeTests()) {
                throw new SmartFrogException(TESTS_FAILED);
            }
        } catch (InterruptedException e) {
            catchException(e);
        } catch (RemoteException e) {
            catchException(e);
        } catch (SmartFrogException e) {
            catchException(e);
        } finally {
            boolean testFailed = getCachedException() != null;
            log.info("Completed tests "
                +(testFailed?"with errors ":"successfully"));
            //declare ourselves finished
            setFinished(true);
            //unset the worker field
            setWorker(null);

            //now look at our termination actions
            if (shouldTerminate) {
                TerminationRecord record;
                if (!testFailed || !failOnError) {
                    record = TerminationRecord.normal(name);
                } else {
                    record = TerminationRecord.abnormal("Test failure", name, getCachedException());
                }
                log.info("terminating test component"+record.toString());
                helper.targetForTermination(record, shouldDetach, false);
            }
        }
    }


    /**
     * run all the tests; this is the routine run in the worker thread. Break
     * out (between suites) if we are interrupted. Sets the {@link
     * TestResultAttributes#ATTR_FINISHED} attribute to true on completion.
     *
     * @return true if the tests worked
     * @throws SmartFrogException for problems
     * @throws RemoteException for network problems
     * @throws InterruptedException if the tests get blocked
     */
    public boolean executeTests() throws SmartFrogException, RemoteException, InterruptedException {

        try {
            if(singleTest==null || singleTest.length()==0) {
                return executeBatchTests();
            } else {
                return executeSingleTest();
            }
        } finally {
            //this is here as it can throw an exception
            sfReplaceAttribute(TestRunner.ATTR_FINISHED, Boolean.TRUE);
        }
    }


    private boolean executeBatchTests() throws RemoteException, SmartFrogException, InterruptedException {
        boolean successful = true;
        Enumeration e = sfChildren();
        while (e.hasMoreElements()) {
            Object child = e.nextElement();
            if (child instanceof TestSuite) {
                TestSuite suiteComponent = (TestSuite) child;
                successful &= executeTestSuite(suiteComponent);
            }
            //break out if the thread is interrupted
            Thread thisThread = Thread.currentThread();
            synchronized(thisThread) {
                if (thisThread.isInterrupted()) {
                    thisThread.interrupt();
                    log.info(TEST_WAS_INTERRUPTED);
                    throw new InterruptedException(TEST_WAS_INTERRUPTED);
                }
            }
            if(!successful && !configuration.getKeepGoing()) {
                //we have failed and asked to stop in this situation
                log.info("Stopping tests after a failure");
                return false;
            }
        }
        return successful;
    }

    /**
     * if a single test was asked for, run it.
     * @return true iff it worked
     * @throws RemoteException network trouble
     * @throws SmartFrogException other problems
     * @throws InterruptedException if the test run is interrupted
     */
    private boolean executeSingleTest()
            throws SmartFrogException, RemoteException, InterruptedException {
        Prim child=null;
        child=sfResolve(singleTest,child,false);
        if(child==null) {
            log.info("No test suite called "+singleTest);
            return false;
        }
        TestSuite suiteComponent = (TestSuite) child;
        return executeTestSuite(suiteComponent);
    }

    /**
     * Execute a single test
     * @param suiteComponent the suite to run
     * @return true if the tests were successful
     * @throws RemoteException network trouble
     * @throws SmartFrogException other problems
     * @throws InterruptedException if the test run is interrupted
     */
    private boolean executeTestSuite(TestSuite suiteComponent)
            throws RemoteException, SmartFrogException, InterruptedException {
        //bind to the configuration. This will set the static properties.
        suiteComponent.bind(getConfiguration());
        boolean result;
        try {
            result = suiteComponent.runTests();
        } finally {
            //unbind from this test
            suiteComponent.bind(null);
            updateResultAttributes((Prim) suiteComponent);
        }
        return result;
    }


    /**
     * fetch the test results from the Test suite, then update our own values
     *
     * @param testSuite test suite to patch
     * @throws RemoteException network trouble
     * @throws SmartFrogException other problems
     */
    private synchronized void updateResultAttributes(Prim testSuite)
            throws SmartFrogRuntimeException, RemoteException {
        statistics.retrieveAndAdd(testSuite);
        statistics.updateResultAttributes(this, false);
    }


    public TestListenerFactory getListenerFactory() throws RemoteException {
        return configuration.getListenerFactory();
    }

    public void setListenerFactory(TestListenerFactory listener) {
        configuration.setListenerFactory(listener);
    }


    public boolean getKeepGoing() {
        return configuration.getKeepGoing();
    }

    public void setKeepGoing(boolean keepGoing) {
        configuration.setKeepGoing(keepGoing);
    }

    public TestListenerLog getTestLog() {
        return configuration.getTestLog();
    }

    public void setTestLog(TestListenerLog testLog) {
        configuration.setTestLog(testLog);
    }

    public RunnerConfiguration getConfiguration() {
        return configuration;
    }


    public synchronized Throwable getCachedException() {
        return cachedException;
    }

    public synchronized void catchException(Throwable caught) {
        if (caught != null) {
            ThrowableTraceInfo tti = new ThrowableTraceInfo(caught);
            log.info("Caught exception in tests " + tti, caught);
        }
        cachedException = caught;
    }

    public synchronized boolean isFinished() throws RemoteException {
        return finished;
    }

    public synchronized void setFinished(boolean finished) {
        this.finished = finished;
    }

    /**
     * Get test execution statistics
     *
     * @return stats
     * @throws java.rmi.RemoteException
     */
    public Statistics getStatistics() throws RemoteException {
        return statistics;
    }


    /**
     * @return true only if the test has finished and failed
     * @throws RemoteException    on network trouble
     * @throws SmartFrogException on other problems
     */
    public boolean isFailed() throws RemoteException, SmartFrogException {
        return !isSucceeded();
    }

    /**
     * @return true iff the test succeeded
     * @throws RemoteException    on network trouble
     * @throws SmartFrogException on other problems
     */

    public boolean isSucceeded() throws RemoteException, SmartFrogException {
        return statistics.isSuccessful();
    }

    /**
     * Get the exit record
     *
     * @return the exit record, will be null for an unfinished child
     * @throws RemoteException    on network trouble
     * @throws SmartFrogException on other problems
     */
    public TerminationRecord getStatus() throws RemoteException, SmartFrogException {
        return null;
    }

    /**
     * return the current action
     *
     * @return the child component. this will be null after termination.
     * @throws RemoteException    on network trouble
     * @throws SmartFrogException on other problems
     */
    public Prim getAction() throws RemoteException, SmartFrogException {
        return null;
    }

    /**
     * turn true if a test is skipped; if some condition caused it not to run
     *
     * @return whether or not the test block skipped deployment of children.
     */
    public boolean isSkipped() throws RemoteException, SmartFrogException {
        return false;
    }
}




See more files for this project here

SmartFrog

SmartFrog (Smart Framework for Object Groups) is a framework for configuring and automatically activating distributed applications. \r\nThe SmartFrog framework is released under LGPL license.\r\nMore info at: www.smartfrog.org

Project homepage: http://sourceforge.net/projects/smartfrog
Programming language(s): Java,XML
License: other

  AbstractTestSuite.java
  LogListener.java
  PackagedTestSuite.java
  RunnerConfiguration.java
  TestContextInjector.java
  TestListener.java
  TestListenerFactory.java
  TestResultAttributes.java
  TestRunner.java
  TestRunnerImpl.java
  TestSuite.java
  components.sf
  testrunner.sf
  testsuite.sf