You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ru...@apache.org on 2008/02/20 17:05:08 UTC

svn commit: r629518 [4/7] - in /incubator/qpid/branches/M2.1/java: ./ broker/src/main/java/org/apache/qpid/server/plugins/ broker/src/main/java/org/apache/qpid/server/txn/ client-java14/ client/ client/src/main/java/org/apache/qpid/client/ client/src/t...

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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.qpid.junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import junit.runner.Version;
+
+import junit.textui.ResultPrinter;
+import junit.textui.TestRunner;
+
+import org.apache.log4j.Logger;
+
+import java.io.PrintStream;
+
+/**
+ * The {@link junit.textui.TestRunner} does not provide very good error handling. It does not wrap exceptions and
+ * does not print out stack traces, losing valuable error tracing information. This class overrides methods in it
+ * in order to improve their error handling. The {@link TKTestRunner} is then built on top of this.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class TestRunnerImprovedErrorHandling extends TestRunner
+{
+    /** Used for logging. */
+    Logger log = Logger.getLogger(TestRunnerImprovedErrorHandling.class);
+
+    /**
+     * Delegates to the super constructor.
+     */
+    public TestRunnerImprovedErrorHandling()
+    {
+        super();
+    }
+
+    /**
+     * Delegates to the super constructor.
+     *
+     * @param printStream The location to write test results to.
+     */
+    public TestRunnerImprovedErrorHandling(PrintStream printStream)
+    {
+        super(printStream);
+    }
+
+    /**
+     * Delegates to the super constructor.
+     *
+     * @param resultPrinter The location to write test results to.
+     */
+    public TestRunnerImprovedErrorHandling(ResultPrinter resultPrinter)
+    {
+        super(resultPrinter);
+    }
+
+    /**
+     * Starts a test run. Analyzes the command line arguments
+     * and runs the given test suite.
+     *
+     * @param args The command line arguments.
+     *
+     * @return The test results.
+     *
+     * @throws Exception Any exceptions falling through the tests are wrapped in Exception and rethrown.
+     */
+    protected TestResult start(String[] args) throws Exception
+    {
+        String testCase = "";
+        boolean wait = false;
+
+        for (int i = 0; i < args.length; i++)
+        {
+            if (args[i].equals("-wait"))
+            {
+                wait = true;
+            }
+            else if (args[i].equals("-c"))
+            {
+                testCase = extractClassName(args[++i]);
+            }
+            else if (args[i].equals("-v"))
+            {
+                System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
+            }
+            else
+            {
+                testCase = args[i];
+            }
+        }
+
+        if (testCase.equals(""))
+        {
+            throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+        }
+
+        try
+        {
+            Test suite = getTest(testCase);
+
+            return doRun(suite, wait);
+        }
+        catch (Exception e)
+        {
+            log.warn("Got exception whilst creating and running test suite.", e);
+            throw new Exception("Could not create and run the test suite.", e);
+        }
+    }
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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.qpid.junit.extensions;
+
+/**
+ * This interface can be implemented by tests that want to know if they are being run concurrently. It provides
+ * lifecycle notification events to tell the test implementation when test threads are being created and destroyed.
+ * This can assist tests in creating and destroying resources that exist over the life of a test thread. A single
+ * test thread can excute the same test many times, and often it is convenient to keep resources, for example network
+ * connections, open over many test calls.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Set up per thread test fixtures.
+ * <tr><td> Clean up per thread test fixtures.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TestThreadAware
+{
+    /**
+     * Called when a test thread is created.
+     */
+    public void threadSetUp();
+
+    /**
+     * Called when a test thread is destroyed.
+     */
+    public void threadTearDown();
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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.qpid.junit.extensions;
+
+/**
+ * Throttle is an interface that supplies a {@link #throttle} method, that can only be called at the rate specified
+ * in a call to the {@link #setRate} method. This can be used to restict processing to run at a certain number
+ * of operations per second.
+ *
+ * <p/>Throttle also supplies a method to check the throttle rate, without waiting. This could be used to update a user
+ * interface every time an event occurs, but only up to a maximum rate. For example, as elements are added to a list,
+ * a count of elements is updated for the user to see, but only up to a maximum rate of ten updates a second, as updating
+ * faster than that slows the processing of element-by-element additions to the list unnecessarily.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Accept throttling rate in operations per second.
+ * <tr><td> Inject short pauses to fill-out processing cycles to a specified rate.
+ * <tr><td> Check against a throttle speed without waiting.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface Throttle
+{
+    /**
+     * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse
+     * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer.
+     * The value must also be larger than zero.
+     *
+     * @param hertz The throttling rate in cycles per second.
+     */
+    public void setRate(float hertz);
+
+    /**
+     * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this
+     * it will inject short pauses to restrict the call rate to that rate.
+     *
+     * <p/>If the thread executing this method is interrupted, it must ensure that the threads interrupt thread
+     * remains set upon exit from the method. This method does not expose InterruptedException, to indicate interruption
+     * of the throttle during a timed wait. It may be changed so that it does.
+     */
+    public void throttle();
+
+    /**
+     * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater
+     * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt>
+     *
+     * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has
+     *         passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very
+     *         first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above
+     *         self-referential definition.
+     */
+    public boolean checkThrottle();
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,175 @@
+/*
+ *
+ * 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.qpid.junit.extensions;
+
+/**
+ * A TimingController is a interface that a test that is aware of the fact that it is being timed can use to manage
+ * the timer. Using this interface tests can suspend and resume test timers. This is usefull if you want to exclude
+ * some expensive preparation from being timed as part of a test. In general when timing tests to measure the
+ * performance of code, you should try to set up data in the #setUp where possible, or as static members in the test
+ * class. This is not always convenient, this interface gives you a way to suspend and resume, or event completely
+ * restart test timers, to get accurate measurements.
+ *
+ * <p/>The interface can also be used to register multiple test pass/fails and timings from a single test method.
+ * In some cases it is easier to write tests in this way. For example a concurrent and asynchronous test may make
+ * many asynchronous requests and then wait for replies to all its requests. Writing such a test with one send/reply
+ * per test method and trying to scale up using many threads will quickly run into limitations if more than about
+ * 100 asynchronous calls need to be made at once. A better way to write such a test is as a single method that sends
+ * many (perhaps thousands or millions) and waits for replies in two threads, one for send, one for replies. It can
+ * then log pass/fails and timings on each individual reply as they come back in, even though the test has been written
+ * to send thousands of requests per test method in order to do volume testing.
+ *
+ * <p/>If when the {@link #completeTest(boolean)} is called, the test runner decides that testing should stop (perhaps
+ * because a duration test has expired), it throws an InterruptedException to indicate that the test method should stop
+ * immediately. The test method can do this by allowing this exception to fall through, if no other clean-up handling
+ * is necessary, or it can simply return as soon as it possibly can. The test runner will still call the tearDown
+ * method in the usual way when this happens.
+ *
+ * <p/>Below are some examples of how this can be used. Not how checking that the timing controller is really available
+ * rather than assuming it is, means that the test can run as an ordinary JUnit test under the default test runners. In
+ * general code should be written to take advantage of the extended capabilities of junit toolkit, without assuming they
+ * are going to be run under its test runner.
+ *
+ * <pre>
+ * public class MyTest extends TestCase implements TimingControllerAware {
+ * ...
+ *
+ *    timingUtils = this.getTimingController();
+ *
+ *    // Do expensive data preparation here...
+ *
+ *    if (timingUtils != null)
+ *        timingUtils.restart();
+ * </pre>
+ *
+ * <pre>
+ * public class MyTest extends TestCase implements TimingControllerAware {
+ * ...
+ *
+ *   public void myVolumeTest(int size) {
+ *
+ *    timingUtils = this.getTimingController();
+ *
+ *    boolean stopNow = false;
+ *
+ *    // In Sender thread.
+ *      for(int i = 0; !stopNow && i < size; i++)
+ *        // Send request i.
+ *        ...
+ *
+ *    // In Receiver thread.
+ *    onReceive(Object o) {
+ *      try {
+ *      // Check o is as expected.
+ *      if (....)
+ *      {
+ *        if (timingUtils != null)
+ *          timingUtils.completeTest(true);
+ *      }
+ *      else
+ *      {
+ *        if (timingUtils != null)
+ *          timingUtils.completeTest(false);
+ *      }
+ *      } catch (InterruptedException e) {
+ *        stopNow = true;
+ *        return;
+ *      }
+ *    }
+ * </pre>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Allow test timers to be suspended, restarted or reset.
+ * <tr><td> Allow tests to register multiple pass/fails and timings.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TimingController
+{
+    /**
+     * Gets the timing controller associated with the current test thread. Tests that use timing controller should
+     * always get the timing controller from this method in the same thread that called the setUp, tearDown or test
+     * method. The controller returned by this method may be called from any thread because it remembers the thread
+     * id of the original test thread.
+     *
+     * @return The timing controller associated with the current test thread.
+     */
+    public TimingController getControllerForCurrentThread();
+
+    /**
+     * Suspends the test timer.
+     *
+     * @return The current time in nanoseconds.
+     */
+    public long suspend();
+
+    /**
+     * Allows the test timer to continue running after a suspend.
+     *
+     * @return The current time in nanoseconds.
+     */
+    public long resume();
+
+    /**
+     * Completely restarts the test timer from zero.
+     *
+     * @return The current time in nanoseconds.
+     */
+    public long restart();
+
+    /**
+     * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of
+     * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters.
+     *
+     * @param testPassed Whether or not this timing is for a test pass or fail.
+     *
+     * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+     *                              indicate to the test method that it should stop immediately.
+     */
+    public void completeTest(boolean testPassed) throws InterruptedException;
+
+    /**
+     * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
+     * 'size' parmeter.
+     *
+     * @param testPassed Whether or not this timing is for a test pass or fail.
+     * @param param      The test parameter size for parameterized tests.
+     *
+     * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+     *                              indicate to the test method that it should stop immediately.
+     */
+    public void completeTest(boolean testPassed, int param) throws InterruptedException;
+
+    /**
+     * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
+     * 'size' parmeter and allows the caller to sepecify the timing to log.
+     *
+     * @param testPassed Whether or not this timing is for a test pass or fail.
+     * @param param      The test parameter size for parameterized tests.
+     * @param timeNanos  The time in nano seconds to log the test result with.
+     *
+     * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+     *                              indicate to the test method that it should stop immediately.
+     */
+    public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException;
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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.qpid.junit.extensions;
+
+/**
+ * TimingControllerAware is an interface that tests that manipulate the timing controller should implement. It enables
+ * the TK test runner to set the test up with a handle on the timing controller which the test can use to call back
+ * to the test runner to manage the timers.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide timing controller insertion point for tests.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TimingControllerAware
+{
+    /**
+     * Used by test runners that can supply a {@link TimingController} to set the controller on an aware test.
+     *
+     * @param controller The timing controller.
+     */
+    public void setTimingController(TimingController controller);
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,134 @@
+/*
+ *
+ * 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.qpid.junit.extensions;
+
+import junit.extensions.TestDecorator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * WrappedSuiteTestDecorator is a test decorator that wraps a test suite, or another wrapped suite, but provides the
+ * same functionality for the {@link junit.extensions.TestDecorator#countTestCases()}  and {@link TestSuite#testAt(int)}
+ * methods as the underlying suite. It returns the values that these methods provide, to enable classes using decorated
+ * tests to drill down to the underlying tests in the suite. That is to say that it indexes and reports the number of
+ * distinct tests in the suite, not the number of test runs that would result from, for example, wrapping the suite in a
+ * repeating decorator.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide access to the underlying tests in a suite.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class WrappedSuiteTestDecorator extends TestDecorator
+{
+    /** Used for logging. */
+    private static Logger log = Logger.getLogger(WrappedSuiteTestDecorator.class);
+
+    /** Holds the test suite that this supplies access to. */
+    protected Test suite;
+
+    /**
+     * Creates a wrappred suite test decorator from a test suite.
+     *
+     * @param suite The test suite.
+     */
+    public WrappedSuiteTestDecorator(TestSuite suite)
+    {
+        super(suite);
+        this.suite = suite;
+    }
+
+    /**
+     * Creates a wrapped suite test decorator from another one.
+     *
+     * @param suite The test suite.
+     */
+    public WrappedSuiteTestDecorator(WrappedSuiteTestDecorator suite)
+    {
+        super(suite);
+        this.suite = suite;
+    }
+
+    /**
+     * Returns the test count of the wrapped suite.
+     *
+     * @return The test count of the wrapped suite.
+     */
+    public int countTestCases()
+    {
+        return suite.countTestCases();
+    }
+
+    /**
+     * Gets the ith test from the test suite.
+     *
+     * @param i The index of the test within the suite to get.
+     *
+     * @return The test with the specified index.
+     */
+    public Test testAt(int i)
+    {
+        log.debug("public Test testAt(int i = " + i + "): called");
+
+        if (suite instanceof WrappedSuiteTestDecorator)
+        {
+            return ((WrappedSuiteTestDecorator) suite).testAt(i);
+        }
+        else if (suite instanceof TestSuite)
+        {
+            return ((TestSuite) suite).testAt(i);
+        }
+
+        // This should never happen.
+        return null;
+    }
+
+    /**
+     * Gets all the tests from the underlying test suite.
+     *
+     * @return All the tests from the underlying test suite.
+     */
+    public Collection<Test> getAllUnderlyingTests()
+    {
+        log.debug("public Collection<Test> getAllUnderlyingTests(): called");
+
+        List<Test> tests = new ArrayList<Test>();
+
+        int numTests = countTestCases();
+        log.debug("numTests = " + numTests);
+
+        for (int i = 0; i < numTests; i++)
+        {
+            tests.add(testAt(i));
+        }
+
+        return tests;
+    }
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,532 @@
+/*
+ *
+ * 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.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.ShutdownHookable;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * CSVTestListener is both a test listener, a timings listener, a memory listener and a parameter listener. It listens for test completion events and
+ * then writes out all the data that it has listened to into a '.csv' (comma seperated values) file.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Listen to test events; start, end, fail, error.
+ * <tr><td> Listen to test timings.
+ * <tr><td> Listen to test memory usage.
+ * <tr><td> Listen to parameterized test parameters.
+ * <tr><td> Output all test data to a CSV file.
+ * </table>
+ *
+ * @author Rupert Smith
+ *
+ * @todo Write an XML output class. Write a transform to convert it into an HTML page with timings as graphs.
+ */
+public class CSVTestListener implements TestListener, TKTestListener, ShutdownHookable
+{
+    /** Used for logging. */
+    private static final Logger log = Logger.getLogger(CSVTestListener.class);
+
+    /** The timings file writer. */
+    private Writer timingsWriter;
+
+    /**
+     * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an
+     * explicit thread id must be used, where notifications come from different threads than the ones that called
+     * the test method.
+     */
+    Map<Long, TestResult> threadLocalResults = Collections.synchronizedMap(new HashMap<Long, TestResult>());
+
+    /** Used to record the start time of a complete test run, for outputing statistics at the end of the test run. */
+    private long batchStartTime;
+
+    /** Used to record the number of errors accross a complete test run. */
+    private int numError;
+
+    /** Used to record the number of failures accross a complete test run. */
+    private int numFailed;
+
+    /** Used to record the number of passes accross a complete test run. */
+    private int numPassed;
+
+    /** Used to record the total tests run accross a complete test run. Always equal to passes + errors + fails. */
+    private int totalTests;
+
+    /** Used to recrod the current concurrency level for the test batch. */
+    private int concurrencyLevel;
+
+    /**
+     * Used to record the total 'size' of the tests run, this is the number run times the average value of the test
+     * size parameters.
+     */
+    private int totalSize;
+
+    /**
+     * Used to record the summation of all of the individual test timgings. Note that total time and summed time
+     * are unlikely to be in agreement, exception for a single threaded test (with no setup time). Total time is
+     * the time taken to run all the tests, summed time is the added up time that each individual test took. So if
+     * two tests run in parallel and take one second each, total time will be one seconds, summed time will be two
+     * seconds.
+     */
+    private long summedTime;
+
+    /** Flag to indicate when batch has been started but not ended to ensure end batch stats are output only once. */
+    private boolean batchStarted = false;
+
+    /**
+     * Creates a new CSVTestListener object.
+     *
+     * @param writer A writer where this CSV listener should write out its output to.
+     */
+    public CSVTestListener(Writer writer)
+    {
+        // log.debug("public CSVTestListener(Writer writer): called");
+
+        // Keep the writer.
+        this.timingsWriter = writer;
+    }
+
+    /**
+     * Resets the test results to the default state of time zero, memory usage zero, test passed.
+     *
+     * @param test     The test to resest any results for.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void reset(Test test, Long threadId)
+    {
+        // log.debug("public void reset(Test test = \"" + test + "\", Long threadId = " + threadId + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.testTime = 0L;
+        r.testStartMem = 0L;
+        r.testEndMem = 0L;
+        r.testState = "Pass";
+        r.testParam = 0;
+    }
+
+    /**
+     * Called when a test results in an error.
+     *
+     * @param test The test which is in error.
+     * @param t Any Throwable raised by the test in error.
+     */
+    public void addError(Test test, Throwable t)
+    {
+        // log.debug("public void addError(Test test, Throwable t): called");
+
+        TestResult r = threadLocalResults.get(Thread.currentThread().getId());
+        r.testState = "Error";
+    }
+
+    /**
+     * Called when a test results in a failure.
+     *
+     * @param test The test which failed.
+     * @param t The AssertionFailedError that encapsulates the test failure.
+     */
+    public void addFailure(Test test, AssertionFailedError t)
+    {
+        // log.debug("public void addFailure(Test \"" + test + "\", AssertionFailedError t): called");
+
+        TestResult r = threadLocalResults.get(Thread.currentThread().getId());
+        r.testState = "Failure";
+    }
+
+    /**
+     * Called when a test completes to mark it as a test fail. This method should be used when registering a
+     * failure from a different thread than the one that started the test.
+     *
+     * @param test     The test which failed.
+     * @param e        The assertion that failed the test.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void addFailure(Test test, AssertionFailedError e, Long threadId)
+    {
+        // log.debug("public void addFailure(Test test = \"" + test + "\", AssertionFailedError e, Long threadId = " + threadId
+        // + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.testState = "Failure";
+    }
+
+    /**
+     * Called when a test completes. Success, failure and errors.
+     *
+     * @param test The test which completed.
+     */
+    public void endTest(Test test)
+    {
+        // log.debug("public void endTest(Test \"" + test + "\"): called");
+
+        TestResult r = threadLocalResults.get(Thread.currentThread().getId());
+
+        writeTestResults(r, test);
+
+        // Clear all the test results for the thread.
+        threadLocalResults.remove(Thread.currentThread().getId());
+    }
+
+    /**
+     * Called when a test starts.
+     *
+     * @param test The test wich has started.
+     */
+    public void startTest(Test test)
+    {
+        // log.debug("public void startTest(Test \"" + test + "\"): called");
+
+        // Initialize the thread local test results.
+        threadLocalResults.put(Thread.currentThread().getId(), new TestResult());
+    }
+
+    /**
+     * Should be called every time a test completes with the run time of that test.
+     *
+     * @param test     The name of the test.
+     * @param nanos   The run time of the test in nanoseconds.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void timing(Test test, long nanos, Long threadId)
+    {
+        // log.debug("public void timing(String \"" + test + "\", long " + nanos + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.testTime = nanos;
+        summedTime += nanos;
+    }
+
+    /**
+     * Should be called every time a test completed with the amount of memory used before and after the test was run.
+     *
+     * @param test     The test which memory was measured for.
+     * @param memStart The total JVM memory used before the test was run.
+     * @param memEnd   The total JVM memory used after the test was run.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
+    {
+        // log.debug("public void memoryUsed(Test \"" + test + "\", long " + memStart + ", long " + memEnd + ", Long "
+        // + threadId + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.testStartMem = memStart;
+        r.testEndMem = memEnd;
+    }
+
+    /**
+     * Should be called every time a parameterized test completed with the int value of its test parameter.
+     *
+     * @param test      The test which memory was measured for.
+     * @param parameter The int parameter value.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void parameterValue(Test test, int parameter, Long threadId)
+    {
+        // log.debug("public void parameterValue(Test test = \"" + test + "\", int parameter = " + parameter + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.testParam = parameter;
+        totalSize += parameter;
+    }
+
+    /**
+     * Should be called every time a test completes with the current number of test threads running. This should not
+     * change within a test batch, therefore it is safe to take this as a batch level property value too.
+     *
+     * @param test     The test for which the measurement is being generated.
+     * @param threads  The number of tests being run concurrently.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void concurrencyLevel(Test test, int threads, Long threadId)
+    {
+        // log.debug("public void concurrencyLevel(Test test = \"" + test + "\", int threads = " + threads + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.testConcurrency = threads;
+        concurrencyLevel = threads;
+
+    }
+
+    /**
+     * Called when a test completes. Success, failure and errors. This method should be used when registering an
+     * end test from a different thread than the one that started the test.
+     *
+     * @param test     The test which completed.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void endTest(Test test, Long threadId)
+    {
+        // log.debug("public void endTest(Test test = \"" + test + "\", Long threadId " + threadId + "): called");
+
+        TestResult r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        writeTestResults(r, test);
+    }
+
+    /**
+     * Takes a time stamp for the beginning of the batch and resets stats counted for the batch.
+     */
+    public synchronized void startBatch()
+    {
+        numError = 0;
+        numFailed = 0;
+        numPassed = 0;
+        totalTests = 0;
+        totalSize = 0;
+        batchStartTime = System.nanoTime();
+        summedTime = 0;
+        batchStarted = true;
+
+        // Write out the column headers for the batch.
+        writeColumnHeaders();
+    }
+
+    /**
+     * Takes a time stamp for the end of the batch to calculate the total run time.
+     * Write this and other stats out to the tail of the csv file.
+     *
+     * @param parameters The optional test parameters, may be null.
+     */
+    public synchronized void endBatch(Properties parameters)
+    {
+        boolean noParams = (parameters == null) || (parameters.size() == 0);
+
+        // Check that a batch has been started but not ended.
+        if (batchStarted)
+        {
+            long batchEndTime = System.nanoTime();
+            float totalTimeMillis = ((float) (batchEndTime - batchStartTime)) / 1000000f;
+            float summedTimeMillis = ((float) summedTime) / 1000000f;
+
+            // Write the stats for the batch out.
+            try
+            {
+                synchronized (this.getClass())
+                {
+                    timingsWriter.write("Total Tests:, " + totalTests + ", ");
+                    timingsWriter.write("Total Passed:, " + numPassed + ", ");
+                    timingsWriter.write("Total Failed:, " + numFailed + ", ");
+                    timingsWriter.write("Total Error:, " + numError + ", ");
+                    timingsWriter.write("Total Size:, " + totalSize + ", ");
+                    timingsWriter.write("Summed Time:, " + summedTimeMillis + ", ");
+                    timingsWriter.write("Concurrency Level:, " + concurrencyLevel + ", ");
+                    timingsWriter.write("Total Time:, " + totalTimeMillis + ", ");
+                    timingsWriter.write("Test Throughput:, " + (((float) totalTests) / totalTimeMillis) + ", ");
+                    timingsWriter.write("Test * Size Throughput:, " + (((float) totalSize) / totalTimeMillis)
+                        + (noParams ? "\n\n" : ", "));
+
+                    // Write out the test parameters if there are any specified.
+                    if (!noParams)
+                    {
+                        properties(parameters);
+                    }
+
+                    timingsWriter.flush();
+                }
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException("Unable to write out end batch statistics: " + e, e);
+            }
+        }
+
+        // Reset the batch started flag to ensure stats are only output once.
+        batchStarted = false;
+    }
+
+    /**
+     * Notifies listeners of the tests read/set properties.
+     *
+     * @param properties The tests read/set properties.
+     */
+    public void properties(Properties properties)
+    {
+        // log.debug("public void properties(Properties properties): called");
+
+        // Write the properties out to the results file.
+        try
+        {
+            synchronized (this.getClass())
+            {
+                Set keySet = new TreeSet(properties.keySet());
+
+                // timingsWriter.write("\n");
+
+                for (Object key : keySet)
+                {
+                    timingsWriter.write(key + " = , " + properties.getProperty((String) key) + ", ");
+                }
+
+                timingsWriter.write("\n\n");
+                // timingsWriter.flush();
+            }
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Unable to write out test parameters: " + e, e);
+        }
+
+        // Write out the column headers after the properties.
+        // writeColumnHeaders();
+    }
+
+    /**
+     * Writes out and flushes the column headers for raw test data.
+     */
+    private void writeColumnHeaders()
+    {
+        // Write the column headers for the CSV file. Any IO exceptions are ignored.
+        try
+        {
+            timingsWriter.write("Class, ");
+            timingsWriter.write("Method, ");
+            timingsWriter.write("Thread, ");
+            timingsWriter.write("Test Outcome, ");
+            timingsWriter.write("Time (milliseconds), ");
+            timingsWriter.write("Memory Used (bytes), ");
+            timingsWriter.write("Concurrency level, ");
+            timingsWriter.write("Test Size\n");
+
+            timingsWriter.flush();
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Unable to write out column headers: " + e, e);
+        }
+    }
+
+    /**
+     * Writes out the test results for the specified test. This outputs a single line of results to the csv file.
+     *
+     * @param r    The test results to write out.
+     * @param test The test to write them out for.
+     */
+    private void writeTestResults(TestResult r, Test test)
+    {
+        // Update the running stats for this batch.
+        if ("Error".equals(r.testState))
+        {
+            numError++;
+        }
+        else if ("Failure".equals(r.testState))
+        {
+            numFailed++;
+        }
+        else if ("Pass".equals(r.testState))
+        {
+            numPassed++;
+        }
+
+        totalTests++;
+
+        // Write the test name and thread information plus all instrumenation a line of the CSV ouput. Any IO
+        // exceptions are ignored.
+        try
+        {
+            synchronized (this.getClass())
+            {
+                timingsWriter.write(test.getClass().getName() + ", ");
+                timingsWriter.write(((test instanceof TestCase) ? ((TestCase) test).getName() : "") + ", ");
+                timingsWriter.write(Thread.currentThread().getName() + ", ");
+                timingsWriter.write(r.testState + ", ");
+                timingsWriter.write((((float) r.testTime) / 1000000f) + ", ");
+                timingsWriter.write((r.testEndMem - r.testStartMem) + ", ");
+                timingsWriter.write(r.testConcurrency + ", ");
+                timingsWriter.write(r.testParam + "\n");
+            }
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Unable to write out test results: " + e, e);
+        }
+    }
+
+    /**
+     * Supplies the shutdown hook. This attempts to flush the results in the event of the test runner being prematurely
+     * suspended before the end of the current test batch.
+     *
+     * @return The shut down hook.
+     */
+    public Thread getShutdownHook()
+    {
+        return new Thread(new Runnable()
+                {
+                    public void run()
+                    {
+                        log.debug("CSVTestListener::ShutdownHook: called");
+
+                        // Complete the current test batch stats.
+                        endBatch(TestContextProperties.getInstance());
+                    }
+                });
+    }
+
+    /** Captures test results packaged into a single object, so that it can be set up as a thread local. */
+    private static class TestResult
+    {
+        /** Used to hold the test timing. */
+        public long testTime;
+
+        /** Used to hold the test start memory usage. */
+        public long testStartMem;
+
+        /** Used to hold the test end memory usage. */
+        public long testEndMem;
+
+        /** Used to hold the test pass/fail/error state. */
+        public String testState = "Pass";
+
+        /** Used to hold the test parameter value. */
+        public int testParam;
+
+        /** Used to hold the concurrency level under which the test was run. */
+        public int testConcurrency;
+    }
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,264 @@
+/*
+ *
+ * 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.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import org.apache.qpid.junit.extensions.SleepThrottle;
+import org.apache.qpid.junit.extensions.Throttle;
+
+import java.util.Properties;
+
+/**
+ * ConsoleTestListener provides feedback to the console, as test timings are taken, by drawing a '.', or an 'E', or an
+ * 'F', for each test that passes, is in error or fails. It does this for every test result registered with the framework,
+ * not just on the completion of each test method as the JUnit one does. It also uses a throttle to cap the rate of
+ * dot drawing, as exessively high rates can degrade test performance without providing much usefull feedback to the user.
+ * Unlike the JUnit dot drawing feedback, this one will correctly wrap lines when tests are run concurrently (the
+ * rate capping ensures that this does not become a hot-spot for thread contention).
+ *
+ * <p/>Where rate capping causes the conflation of multiple requested dots into a single dot, the dot that is actually
+ * drawn will be the worst result within the conflation period, that is, error is worse than fail which is worse than pass.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Draw dots as each test result completes, at a capped rate.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ConsoleTestListener implements TestListener, TKTestListener
+{
+    /** Used to indicate a test pass. */
+    private static final int PASS = 1;
+
+    /** Used to indicate a test failure. */
+    private static final int FAIL = 2;
+
+    /** Used to indicate a test error. */
+    private static final int ERROR = 3;
+
+    /** Defines the maximum number of columns of dots to print. */
+    private static final int MAX_COLUMNS = 80;
+
+    /** Used to throttle the dot writing rate. */
+    Throttle throttle;
+
+    /** Tracks the worst test result so far, when the throttled print method must conflate results due to throttling. */
+    private int conflatedResult = 0;
+
+    /** Tracks the column count as dots are printed, so that newlines can be inserted at the right margin. */
+    private int columnCount = 0;
+
+    /** Used as a monitor on the print method criticial section, to ensure that line ends always happen in the right place. */
+    private final Object printMonitor = new Object();
+
+    /**
+     * Creates a dot drawing feedback test listener, set by default to 80 columns at 80 dots per second capped rate.
+     */
+    public ConsoleTestListener()
+    {
+        throttle = new SleepThrottle();
+        throttle.setRate(80f);
+    }
+
+    /**
+     * Prints dots at a capped rate, conflating the requested type of dot to draw if this method is called at a rate
+     * higher than the capped rate. The conflation works by always printing the worst result that occurs within the
+     * conflation period, that is, error is worse than fail which is worse than a pass.
+     *
+     * @param result The type of dot to draw, {@link #PASS}, {@link #FAIL} or {@link #ERROR}.
+     */
+    private void throttledPrint(int result)
+    {
+        conflatedResult = (result > conflatedResult) ? result : conflatedResult;
+
+        if (throttle.checkThrottle())
+        {
+            synchronized (printMonitor)
+            {
+                switch (conflatedResult)
+                {
+                default:
+                case PASS:
+                    System.out.print('.');
+                    break;
+
+                case FAIL:
+                    System.out.print('F');
+                    break;
+
+                case ERROR:
+                    System.out.print('E');
+                    break;
+                }
+
+                columnCount = (columnCount >= MAX_COLUMNS) ? 0 : (columnCount + 1);
+
+                if (columnCount == 0)
+                {
+                    System.out.print('\n');
+                }
+
+                conflatedResult = 0;
+            }
+        }
+    }
+
+    /**
+     * An error occurred.
+     *
+     * @param test The test in error. Ignored.
+     * @param t    The error that the test threw. Ignored.
+     */
+    public void addError(Test test, Throwable t)
+    {
+        throttledPrint(ERROR);
+    }
+
+    /**
+     * A failure occurred.
+     *
+     * @param test The test that failed. Ignored.
+     * @param t    The assertion failure that the test threw. Ignored.
+     */
+    public void addFailure(Test test, AssertionFailedError t)
+    {
+        throttledPrint(FAIL);
+    }
+
+    /**
+     * A test ended.
+     *
+     * @param test The test that ended. Ignored.
+     */
+    public void endTest(Test test)
+    {
+        throttledPrint(PASS);
+    }
+
+    /**
+     * A test started.
+     *
+     * @param test The test that started. Ignored.
+     */
+    public void startTest(Test test)
+    { }
+
+    /**
+     * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
+     *
+     * @param test     The test to resest any results for.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void reset(Test test, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a test completes with the run time of that test.
+     *
+     * @param test     The name of the test.
+     * @param nanos    The run time of the test in nanoseconds.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void timing(Test test, long nanos, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a test completed with the amount of memory used before and after the test was run.
+     *
+     * @param test     The test which memory was measured for.
+     * @param memStart The total JVM memory used before the test was run.
+     * @param memEnd   The total JVM memory used after the test was run.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a parameterized test completed with the int value of its test parameter.
+     *
+     * @param test      The test which memory was measured for.
+     * @param parameter The int parameter value.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void parameterValue(Test test, int parameter, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a test completes with the current number of test threads running.
+     *
+     * @param test     The test for which the measurement is being generated.
+     * @param threads  The number of tests being run concurrently.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void concurrencyLevel(Test test, int threads, Long threadId)
+    { }
+
+    /**
+     * Called when a test completes. Success, failure and errors. This method should be used when registering an
+     * end test from a different thread than the one that started the test.
+     *
+     * @param test     The test which completed.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void endTest(Test test, Long threadId)
+    {
+        throttledPrint(PASS);
+    }
+
+    /**
+     * Called when a test completes to mark it as a test fail. This method should be used when registering a
+     * failure from a different thread than the one that started the test.
+     *
+     * @param test     The test which failed.
+     * @param e        The assertion that failed the test.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void addFailure(Test test, AssertionFailedError e, Long threadId)
+    {
+        throttledPrint(FAIL);
+    }
+
+    /**
+     * Notifies listeners of the start of a complete run of tests.
+     */
+    public void startBatch()
+    { }
+
+    /**
+     * Notifies listeners of the end of a complete run of tests.
+     *
+     * @param parameters The optional test parameters to log out with the batch results.
+     */
+    public void endBatch(Properties parameters)
+    { }
+
+    /**
+     * Notifies listeners of the tests read/set properties.
+     *
+     * @param properties The tests read/set properties.
+     */
+    public void properties(Properties properties)
+    { }
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,132 @@
+/*
+ *
+ * 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.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import java.util.Properties;
+
+/**
+ * TKTestListener is a listener interface for listeners that want to be informed of the run times of tests, the memory
+ * usage of tests, the 'size' parameters of parameterized tests and the begin and end events of complete test runs.
+ * {@link org.apache.qpid.junit.extensions.TKTestResult} is an example of a test result class that listeners
+ * interested in these events can be attached to.
+ *
+ * The {@link #timing(junit.framework.Test, long, Long)}, {@link #memoryUsed(junit.framework.Test, long, long, Long)},
+ * {@link #parameterValue(junit.framework.Test, int, Long)} and {@link #endTest(junit.framework.Test, Long)} methods
+ * all accept on optional thread id parameter.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Listen to test timings.
+ * <tr><td> Listen to test memory usages.
+ * <tr><td> Listen to parameterized test parameters.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TKTestListener extends TestListener
+{
+    /**
+     * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
+     *
+     * @param test     The test to resest any results for.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void reset(Test test, Long threadId);
+
+    /**
+     * Should be called every time a test completes with the run time of that test.
+     *
+     * @param test     The name of the test.
+     * @param nanos   The run time of the test in nanoseconds.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void timing(Test test, long nanos, Long threadId);
+
+    /**
+     * Should be called every time a test completed with the amount of memory used before and after the test was run.
+     *
+     * @param test     The test which memory was measured for.
+     * @param memStart The total JVM memory used before the test was run.
+     * @param memEnd   The total JVM memory used after the test was run.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void memoryUsed(Test test, long memStart, long memEnd, Long threadId);
+
+    /**
+     * Should be called every time a parameterized test completed with the int value of its test parameter.
+     *
+     * @param test      The test which memory was measured for.
+     * @param parameter The int parameter value.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void parameterValue(Test test, int parameter, Long threadId);
+
+    /**
+     * Should be called every time a test completes with the current number of test threads running.
+     *
+     * @param test    The test for which the measurement is being generated.
+     * @param threads The number of tests being run concurrently.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void concurrencyLevel(Test test, int threads, Long threadId);
+
+    /**
+     * Called when a test completes. Success, failure and errors. This method should be used when registering an
+     * end test from a different thread than the one that started the test.
+     *
+     * @param test The test which completed.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void endTest(Test test, Long threadId);
+
+    /**
+     * Called when a test completes to mark it as a test fail. This method should be used when registering a
+     * failure from a different thread than the one that started the test.
+     *
+     * @param test      The test which failed.
+     * @param e         The assertion that failed the test.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void addFailure(Test test, AssertionFailedError e, Long threadId);
+
+    /**
+     * Notifies listeners of the start of a complete run of tests.
+     */
+    public void startBatch();
+
+    /**
+     * Notifies listeners of the end of a complete run of tests.
+     *
+     * @param parameters The optional test parameters to log out with the batch results.
+     */
+    public void endBatch(Properties parameters);
+
+    /**
+     * Notifies listeners of the tests read/set properties.
+     *
+     * @param properties The tests read/set properties.
+     */
+    public void properties(Properties properties);
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java Wed Feb 20 08:04:25 2008
@@ -0,0 +1,400 @@
+/*
+ *
+ * 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.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.ShutdownHookable;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.*;
+
+/**
+ * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified
+ * writer.
+ *
+ * <p/>The API for this listener accepts notifications about different aspects of a tests results through different
+ * methods, so some assumption needs to be made as to which test result a notification refers to. For example
+ * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is
+ * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may
+ * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used
+ * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest}
+ * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur
+ * between the start and end and will be given with the same thread id as the start and end, so the thread id provides
+ * a unqiue value to identify a particular test run against.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Listen to test lifecycle notifications.
+ * <tr><td> Listen to test errors and failures.
+ * <tr><td> Listen to test timings.
+ * <tr><td> Listen to test memory usages.
+ * <tr><td> Listen to parameterized test parameters.
+ * <tr><th> Responsibilities
+ * </table>
+ *
+ * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring
+ *       out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as
+ *       the ant XML formatter, and a more structured one for outputing results with timings and summaries from
+ *       performance tests.
+ *
+ * @author Rupert Smith
+ */
+public class XMLTestListener implements TKTestListener, ShutdownHookable
+{
+    /** Used for debugging. */
+    private static final Logger log = Logger.getLogger(XMLTestListener.class);
+
+    /** The results file writer. */
+    protected Writer writer;
+
+    /** Holds the results for individual tests. */
+    // protected Map<Result, Result> results = new LinkedHashMap<Result, Result>();
+    // protected List<Result> results = new ArrayList<Result>();
+
+    /**
+     * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an
+     * explicit thread id must be used, where notifications come from different threads than the ones that called
+     * the test method.
+     */
+    Map<Long, Result> threadLocalResults = Collections.synchronizedMap(new LinkedHashMap<Long, Result>());
+
+    /**
+     * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means
+     * that the thread id is freed for the thread to generate more results.
+     */
+    List<Result> results = new ArrayList<Result>();
+
+    /** Holds the overall error count. */
+    protected int errors = 0;
+
+    /** Holds the overall failure count. */
+    protected int failures = 0;
+
+    /** Holds the overall tests run count. */
+    protected int runs = 0;
+
+    /** Holds the name of the class that tests are being run for. */
+    String testClassName;
+
+    /**
+     * Creates a new XML results output listener that writes to the specified location.
+     *
+     * @param writer        The location to write results to.
+     * @param testClassName The name of the test class to include in the test results.
+     */
+    public XMLTestListener(Writer writer, String testClassName)
+    {
+        log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called");
+
+        this.writer = writer;
+        this.testClassName = testClassName;
+    }
+
+    /**
+     * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
+     *
+     * @param test     The test to resest any results for.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void reset(Test test, Long threadId)
+    {
+        log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called");
+
+        XMLTestListener.Result r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+        r.error = null;
+        r.failure = null;
+
+    }
+
+    /**
+     * Notification that a test started.
+     *
+     * @param test The test that started.
+     */
+    public void startTest(Test test)
+    {
+        log.debug("public void startTest(Test test = " + test + "): called");
+
+        Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName());
+
+        // Initialize the thread local test results.
+        threadLocalResults.put(Thread.currentThread().getId(), newResult);
+        runs++;
+    }
+
+    /**
+     * Should be called every time a test completes with the run time of that test.
+     *
+     * @param test     The name of the test.
+     * @param nanos    The run time of the test in nanoseconds.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void timing(Test test, long nanos, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a test completed with the amount of memory used before and after the test was run.
+     *
+     * @param test     The test which memory was measured for.
+     * @param memStart The total JVM memory used before the test was run.
+     * @param memEnd   The total JVM memory used after the test was run.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a parameterized test completed with the int value of its test parameter.
+     *
+     * @param test      The test which memory was measured for.
+     * @param parameter The int parameter value.
+     * @param threadId  Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void parameterValue(Test test, int parameter, Long threadId)
+    { }
+
+    /**
+     * Should be called every time a test completes with the current number of test threads running.
+     *
+     * @param test     The test for which the measurement is being generated.
+     * @param threads  The number of tests being run concurrently.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void concurrencyLevel(Test test, int threads, Long threadId)
+    { }
+
+    /**
+     * Notifies listeners of the tests read/set properties.
+     *
+     * @param properties The tests read/set properties.
+     */
+    public void properties(Properties properties)
+    { }
+
+    /**
+     * Notification that a test ended.
+     *
+     * @param test The test that ended.
+     */
+    public void endTest(Test test)
+    {
+        log.debug("public void endTest(Test test = " + test + "): called");
+
+        // Move complete test results into the completed tests list.
+        Result r = threadLocalResults.get(Thread.currentThread().getId());
+        results.add(r);
+
+        // Clear all the test results for the thread.
+        threadLocalResults.remove(Thread.currentThread().getId());
+    }
+
+    /**
+     * Called when a test completes. Success, failure and errors. This method should be used when registering an
+     * end test from a different thread than the one that started the test.
+     *
+     * @param test     The test which completed.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void endTest(Test test, Long threadId)
+    {
+        log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called");
+
+        // Move complete test results into the completed tests list.
+        Result r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+        results.add(r);
+
+        // Clear all the test results for the thread.
+        threadLocalResults.remove(Thread.currentThread().getId());
+    }
+
+    /**
+     * An error occurred.
+     *
+     * @param test The test in which the error occurred.
+     * @param t    The throwable that resulted from the error.
+     */
+    public void addError(Test test, Throwable t)
+    {
+        log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called");
+
+        Result r = threadLocalResults.get(Thread.currentThread().getId());
+        r.error = t;
+        errors++;
+    }
+
+    /**
+     * A failure occurred.
+     *
+     * @param test The test in which the failure occurred.
+     * @param t    The JUnit assertions that led to the failure.
+     */
+    public void addFailure(Test test, AssertionFailedError t)
+    {
+        log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called");
+
+        Result r = threadLocalResults.get(Thread.currentThread().getId());
+        r.failure = t;
+        failures++;
+    }
+
+    /**
+     * Called when a test completes to mark it as a test fail. This method should be used when registering a
+     * failure from a different thread than the one that started the test.
+     *
+     * @param test     The test which failed.
+     * @param e        The assertion that failed the test.
+     * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+     */
+    public void addFailure(Test test, AssertionFailedError e, Long threadId)
+    {
+        log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called");
+
+        Result r =
+            (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+        r.failure = e;
+        failures++;
+    }
+
+    /**
+     * Notifies listeners of the start of a complete run of tests.
+     */
+    public void startBatch()
+    {
+        log.debug("public void startBatch(): called");
+
+        // Reset all results counts.
+        threadLocalResults = Collections.synchronizedMap(new HashMap<Long, Result>());
+        errors = 0;
+        failures = 0;
+        runs = 0;
+
+        // Write out the file header.
+        try
+        {
+            writer.write("<?xml version=\"1.0\" ?>\n");
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Unable to write the test results.", e);
+        }
+    }
+
+    /**
+     * Notifies listeners of the end of a complete run of tests.
+     *
+     * @param parameters The optional test parameters to log out with the batch results.
+     */
+    public void endBatch(Properties parameters)
+    {
+        log.debug("public void endBatch(Properties parameters = " + parameters + "): called");
+
+        // Write out the results.
+        try
+        {
+            // writer.write("<?xml version=\"1.0\" ?>\n");
+            writer.write("<testsuite errors=\"" + errors + "\" failures=\"" + failures + "\" tests=\"" + runs + "\" name=\""
+                + testClassName + "\">\n");
+
+            for (Result result : results)
+            {
+                writer.write("  <testcase classname=\"" + result.testClass + "\" name=\"" + result.testName + "\">\n");
+
+                if (result.error != null)
+                {
+                    writer.write("    <error type=\"" + result.error.getClass() + "\">");
+                    result.error.printStackTrace(new PrintWriter(writer));
+                    writer.write("    </error>");
+                }
+                else if (result.failure != null)
+                {
+                    writer.write("    <failure type=\"" + result.failure.getClass() + "\">");
+                    result.failure.printStackTrace(new PrintWriter(writer));
+                    writer.write("    </failure>");
+                }
+
+                writer.write("  </testcase>\n");
+            }
+
+            writer.write("</testsuite>\n");
+            writer.flush();
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Unable to write the test results.", e);
+        }
+    }
+
+    /**
+     * Supplies the shutdown hook.
+     *
+     * @return The shut down hook.
+     */
+    public Thread getShutdownHook()
+    {
+        return new Thread(new Runnable()
+                {
+                    public void run()
+                    {
+                        log.debug("XMLTestListener::ShutdownHook: called");
+                    }
+                });
+    }
+
+    /**
+     * Used to capture the results of a particular test run.
+     */
+    protected static class Result
+    {
+        /** Holds the name of the test class. */
+        public String testClass;
+
+        /** Holds the name of the test method. */
+        public String testName;
+
+        /** Holds the exception that caused error in this test. */
+        public Throwable error;
+
+        /** Holds the assertion exception that caused failure in this test. */
+        public AssertionFailedError failure;
+
+        /**
+         * Creates a placeholder for the results of a test.
+         *
+         * @param testClass The test class.
+         * @param testName  The name of the test that was run.
+         */
+        public Result(String testClass, String testName)
+        {
+            this.testClass = testClass;
+            this.testName = testName;
+        }
+    }
+}

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html Wed Feb 20 08:04:25 2008
@@ -0,0 +1,6 @@
+<html>
+<body>
+Listners for test statistics are defined in this package. At the moment there is only one listener which writes all test
+statistics out to a CSV (comma seperated values) file which can be loaded by most spread sheets.
+</body>
+</html>
\ No newline at end of file

Added: incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html?rev=629518&view=auto
==============================================================================
--- incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html (added)
+++ incubator/qpid/branches/M2.1/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html Wed Feb 20 08:04:25 2008
@@ -0,0 +1,12 @@
+<html>
+<body>
+Basic JUnit is enahanced with test runners to run tests repeatedly, simultaneously in many threads and with increasing
+test sizes for asymptotic performance measurements. There are features to measure the time and amount of memory that
+tests use as well as to record the asymptotic test size parameters. There are some utilities to write these test
+statistics to various file formats too and these can be found in the listeners package.
+
+</p>The main test runner class is TKTestRunner which can be called with command line parameters to specify how tests
+should be run.
+
+</body>
+</html>
\ No newline at end of file