org.springframework.load.AbstractTest.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.load.AbstractTest.java

Source

/*
 * The Spring Framework is published under the terms
 * of the Apache Software License.
 */

package org.springframework.load;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.util.ResponseTimeMonitor;
import org.springframework.util.ResponseTimeMonitorImpl;
import org.springframework.util.StopWatch;

/**
 * Convenient superclass that makes it very easy to implement Tests.
 * 
 * Uses the Template Method design pattern. Concrete subclasses have
 * only to implement the abstract runPass(i) method to execute each test.
 * 
 * This class exposes bean properties controlling test execution.
 * 
 * @author  Rod Johnson
 * @since February 9, 2001
 */
public abstract class AbstractTest implements Test, BeanNameAware {

    //---------------------------------------------------------------------
    // Instance variables
    //---------------------------------------------------------------------
    /** Used to calculate random delays if a subclass wants this behavior */
    private static Random rand = new Random();

    protected final Log logger = LogFactory.getLog(getClass());

    private long maxPause = -1L;

    private int nbPasses;

    private StopWatch runningTimer;

    private StopWatch pauseTimer;

    private StopWatch elapsedTimer;

    /**
     * List of failures encountered by this test so far
     */
    private List testFailedExceptions;

    /**
     * Whether toString should generate a verbose format.
     * This property is inherited from the managing TestSuite unless
     * it's overridden as a bean property.
     */
    private boolean longReports;

    /** Number of tests completed so far */
    private int completedTests;

    /** Number of instances of this test. Use for weighting */
    private int instances = 1;

    /** Descriptive name of this test */
    private String name;

    /**
     * Helper object used to capture performance information
     */
    private ResponseTimeMonitorImpl responseTimeMonitorImpl = new ResponseTimeMonitorImpl();

    /** Suite that manages this test */
    private AbstractTestSuite suite;

    //---------------------------------------------------------------------
    // Constructor
    //---------------------------------------------------------------------
    /**
     * Construct a new AbstractTest. The object must be further configured
     * by its JavaBean properties and/or by inheritance of properties
     * from the TestSuite it runs it. The setTestSuite() method must
     * be invoked by the managing AbstractTestSuite.
     */
    protected AbstractTest() {
        testFailedExceptions = new LinkedList();
        runningTimer = new StopWatch();
        runningTimer.setKeepTaskList(false);

        pauseTimer = new StopWatch();
        pauseTimer.setKeepTaskList(false);

        elapsedTimer = new StopWatch();
        elapsedTimer.setKeepTaskList(false);
    }

    /**
     * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
     */
    public void setBeanName(String name) {
        setName(name);
    }

    //---------------------------------------------------------------------
    // Implementation of Test
    //---------------------------------------------------------------------
    /**
     * Subclasses can override this to save a test
     * fixture to run application-specific tests against.
     * This implementation doesn't know anything about fixtures, so
     * it always throws an exception.
     * @param context application-specific object to test
     */
    public void setFixture(Object context) {
        throw new UnsupportedOperationException("AbstractTest.setFixture");
    }

    /**
     * @return the test fixture used by this object.
     * Subclasses that require test fixtures must override this method,
     * which always throws UnsupportedOperationException
     */
    public Object getFixture() {
        throw new UnsupportedOperationException("AbstractTest.getFixture");
    }

    /**
     * Set the number of instances of this Test. This enables weighting
     * of tests.
     */
    public void setInstances(int instances) {
        this.instances = instances;
    }

    public int getInstances() {
        return instances;
    }

    /**
     * The TestSuite object that manages this test thread invokes this
     * method. It is used to enable properties to be inherited from the
     * test suite unless overriden by bean properties on this class.
     */
    public void setTestSuite(AbstractTestSuite suite) {
        this.suite = suite;
        // Set defaults if not set via this class's bean properties
        if (getPasses() == 0)
            this.setPasses(suite.getPasses());
        if (getMaxPause() < 0L)
            this.setMaxPause(suite.getMaxPause());
        this.setLongReports(suite.getLongReports());
    }

    public final void setName(String name) {
        this.name = name;
    }

    public final void setLongReports(boolean longReports) {
        this.longReports = longReports;
    }

    public final void setPasses(int nbPasses) {
        this.nbPasses = nbPasses;
    }

    public final void setMaxPause(long maxPause) {
        this.maxPause = maxPause;
    }

    public final long getMaxPause() {
        return this.maxPause;
    }

    public final int getPasses() {
        return nbPasses;
    }

    public final int getErrorCount() {
        return testFailedExceptions.size();
    }

    public final String getName() {
        return name;
    }

    public final int getTestsCompletedCount() {
        return completedTests;
    }

    public final boolean isComplete() {
        return getPasses() == getTestsCompletedCount();
    }

    /**
     * @see org.springframework.load.TestStatus#getAverageResponseTime()
     */
    public int getAverageResponseTime() {
        return this.responseTimeMonitorImpl.getAverageResponseTimeMillis();
    }

    /**
     * @see org.springframework.load.TestStatus#getTestsPerSecondCount()
     */
    public final double getTestsPerSecondCount() {
        double res = 0.0;
        double totalTime = runningTimer.getTotalTimeMillis();
        double testCompleted = getTestsCompletedCount();

        if (testCompleted == 0.0)
            return 0.0;

        if (totalTime != 0.0)
            res = 1000.0 / (totalTime / testCompleted);
        else {
            // No time!!!!
            //return testCompleted;
            return Double.POSITIVE_INFINITY;
        }
        return res;
    }

    /**
     * @see org.springframework.load.TestStatus#getTotalWorkingTime()
     */
    public final long getTotalWorkingTime() {
        return runningTimer.getTotalTimeMillis();
    }

    /**
     * @see org.springframework.load.TestStatus#getElapsedTime()
     */
    public final long getElapsedTime() {
        return elapsedTimer.getTotalTimeMillis();
    }

    /**
     * @see org.springframework.load.TestStatus#getTotalPauseTime()
     */
    public final long getTotalPauseTime() {
        return pauseTimer.getTotalTimeMillis();
    }

    public final TestFailedException[] getFailureExceptions() {
        TestFailedException[] fails = new TestFailedException[testFailedExceptions.size()];
        for (int i = 0; i < fails.length; i++)
            fails[i] = (TestFailedException) testFailedExceptions.get(i);
        return fails;
    }

    /**
     * @see org.springframework.load.Test#reset()
     */
    public void reset() {
        elapsedTimer = new StopWatch("elapsed timer");
        completedTests = 0;
        runningTimer = new StopWatch("running timer");
        pauseTimer = new StopWatch("pause timer");
        testFailedExceptions.clear();
    }

    /**
     * @return additional stats information.
     */
    public ResponseTimeMonitor getTargetResponse() {
        return responseTimeMonitorImpl;
    }

    /** 
     * Run all the tests in this thread
     * @see java.lang.Runnable#run()
     */
    public final void run() {
        elapsedTimer.start(null);//"run");
        for (int i = 0; i < getPasses(); i++) {
            try {
                pause();
                runningTimer.start(null);//"run");
                runPass(i);
            } catch (AbortTestException ex) {
                // Terminate the for loop to end the work of this test thread
                System.err.println("Abortion!: " + ex);
                break;
            } catch (TestFailedException ex) {
                // We don't need to wrap this         
                testFailedExceptions.add(ex);
                onTestPassFailed(ex);
            } catch (Exception ex) {
                // Wrap the uncaught exception in a new TestFailedException
                TestFailedException tfe = new TestFailedException("Uncaught exception: " + ex.getMessage(), ex);
                testFailedExceptions.add(tfe);
                onTestPassFailed(ex);
            } finally {
                ++completedTests;
                runningTimer.stop();
                responseTimeMonitorImpl.recordResponseTime(runningTimer.getLastTaskTimeMillis());
            }
        } // for each test
        elapsedTimer.stop();
    } // run

    /**
     * @return diagnostic information about this Test.
     * The verbosity of the format will depend on the value of the
     * longReports bean property.
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append(getName() + "\t");
        sb.append(getTestsCompletedCount() + "/" + getPasses());
        sb.append("\terrs=" + getErrorCount());
        sb.append("\t" + suite.getDecimalFormat().format(getTestsPerSecondCount()) + "hps");
        sb.append("\tavg=" + responseTimeMonitorImpl.getAverageResponseTimeMillis() + "ms");
        if (longReports) {
            sb.append("\tworkt=" + getTotalWorkingTime());
            sb.append("\telt=" + getElapsedTime());
            sb.append("\best=" + responseTimeMonitorImpl.getBestResponseTimeMillis() + "ms");
            sb.append("\tworst=" + responseTimeMonitorImpl.getWorstResponseTimeMillis() + "ms");
            sb.append("\tpause=" + getTotalPauseTime());
        }
        if (isComplete() && this.longReports) {
            sb.append("\tCOMPLETED\n");
            sb.append(getErrorReport());
        }
        return sb.toString();
    } // toString

    /**
     * @return an error report string
     */
    public final String getErrorReport() {
        String s = "";
        TestFailedException[] fails = getFailureExceptions();
        if (fails == null || fails.length == 0)
            return "No errors\n";
        else {
            for (int i = 0; i < fails.length; i++) {
                s += fails[i] + "\n";
            }
        }
        return s;
    }

    public String getGroup() {
        return null;
    }

    /**
     * Subclasses must implement this method
     * @throws TestFailedException if the test failed
     * @throws AbortTestException if the test run should abort
     * @throws Exception if there's any other unspecified error. 
     * This will not cause the test run to abort.
     * @param i index of test pass, indexed from 0
     */
    protected abstract void runPass(int i) throws TestFailedException, AbortTestException, Exception;

    /**
     * For handling any exceptions thrown by a Test pass. Override as desired.
     * This implementation does nothing.
     * This method is invoked on test failures, whether caused by an uncaught exception
     * or a TestAssertionFailure
     */
    protected void onTestPassFailed(Exception ex) {
        ex.printStackTrace();
    }

    //---------------------------------------------------------------------
    // Implementation methods and convenience methods for subclasses
    //---------------------------------------------------------------------
    /**
     * Pause for up to maxPause milliseconds
     */
    private void pause() {
        if (this.maxPause > 0L) {
            try {
                pauseTimer.start(null);//"pause");
                long p = Math.abs(rand.nextLong() % this.maxPause);
                Thread.sleep(p);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                pauseTimer.stop();
            }
        }
    } // pause

    /**
     * Convenience method to simulate delay for between min and max ms
     * @param min minimum number of milliseconds to delay
     * @param max maximum number of milliseconds to delay
     */
    public static void simulateDelay(long min, long max) {
        if (max - min > 0L) {
            try {
                long p = Math.abs(min + rand.nextLong() % (max - min));
                //System.out.println("delay for " + p + "ms");
                Thread.sleep(p);
            } catch (InterruptedException ex) {
                // Ignore it   
            }
        }
    } // simulateDelay

    /** 
     * Convenience method for subclasses
     * @param sz size of array to index
     * @return a random array of list index from 0 up to sz-1
     */
    public static int randomIndex(int sz) {
        return Math.abs(rand.nextInt(sz));
    }

    /**
     * Like JUnit assertion
     * @param s
     */
    protected void assertTrue(String s, boolean condition) throws TestFailedException {
        if (!condition)
            throw new TestFailedException(s);
    }

    protected void assertEquals(String s, Object a, Object b) throws TestFailedException {
        if (a == b)
            return;
        if (a == null) {
            if (b != null)
                throw new TestFailedException(s);
        } else if (b == null) {
            // a isn't null
            throw new TestFailedException(s);
        }
        if (!a.equals(b)) {
            throw new TestFailedException(s);
        }
    }

    protected void assertEquals(String s, int a, int b) throws TestFailedException {
        if (a != b)
            throw new TestFailedException(s + ": expected " + a + " but found " + b);
    }

    protected void assertEquals(String s, long a, long b) throws TestFailedException {
        if (a != b)
            throw new TestFailedException(s);
    }

} // class AbstractTest