001: package com.clarkware.junitperf;
002:
003: import junit.framework.Test;
004: import junit.framework.TestCase;
005: import junit.framework.TestResult;
006: import junit.framework.TestSuite;
007:
008: /**
009: * The <code>TestFactory</code> class creates thread-local
010: * <code>TestSuite</code> instances.
011: * <p>
012: * This factory class should be used in cases when a stateful test
013: * is intended to be decorated by a <code>LoadTest</code>. A stateful
014: * test is defined as any test that defines test-specific state in
015: * its <code>setUp()</code> method.
016: * </p>
017: * <p>
018: * Use of the <code>TestFactory</code> ensures that each thread spawned
019: * by a <code>LoadTest</code> contains its own <code>TestSuite</code>
020: * instance containing all tests defined in the specified
021: * <code>TestCase</code> class.
022: * </p>
023: * <p>
024: * A typical usage scenario is as follows:
025: * <blockquote>
026: * <pre>
027: * Test factory = new TestFactory(YourTestCase.class);
028: * LoadTest test = new LoadTest(factory, numberOfUsers, ...);
029: * ...
030: * </pre>
031: * </blockquote>
032: * </p>
033: * <p>
034: * Of course, static variables cannot be protected externally, so tests
035: * intended to be run in a multi-threaded environment should ensure
036: * that the use of static variables is thread-safe.
037: * </p>
038: * <p>
039: * This class is dependent on Java 2. For earlier platforms a
040: * local cache implementation should be changed to use, for example,
041: * a HashMap to track thread-local information.
042: * </p>
043: * @author <b>Mike Clark</b>
044: * @author Clarkware Consulting, Inc.
045: * @author Ervin Varga
046: *
047: * @see com.clarkware.junitperf.LoadTest
048: */
049:
050: public class TestFactory implements Test {
051:
052: protected final Class testClass;
053: private TestSuite suite;
054: private final TestCache testCache;
055:
056: /**
057: * Constructs a <code>TestFactory</code> instance.
058: *
059: * @param testClass The <code>TestCase</code> class to load test.
060: */
061: public TestFactory(Class testClass) {
062:
063: if (!(TestCase.class.isAssignableFrom(testClass))) {
064: throw new IllegalArgumentException("TestFactory must "
065: + "be constructed with a TestCase class.");
066: }
067:
068: this .testClass = testClass;
069: this .testCache = new TestCache();
070: }
071:
072: /**
073: * Runs an instance of the <code>Test</code> class and
074: * collects its result in the specified <code>TestResult</code>.
075: * <p>
076: * Each invocation of this method triggers the creation of a
077: * new <code>Test</code> class instance as specified in the
078: * construction of this <code>TestFactory</code>.
079: *
080: * @param result Test result.
081: */
082: public void run(TestResult result) {
083: getTest().run(result);
084: }
085:
086: /**
087: * Returns the number of tests in this test.
088: *
089: * @return Number of tests.
090: */
091: public int countTestCases() {
092: return getTestSuite().countTestCases();
093: }
094:
095: /**
096: * Returns the test description.
097: *
098: * @return Description.
099: */
100: public String toString() {
101: return "TestFactory: " + getTestSuite().toString();
102: }
103:
104: protected Test getTest() {
105: return testCache.getTest();
106: }
107:
108: protected TestSuite getTestSuite() {
109: if (suite == null) {
110: suite = makeTestSuite();
111: }
112:
113: return suite;
114: }
115:
116: protected TestSuite makeTestSuite() {
117: return new TestSuite(testClass);
118: }
119:
120: /*
121: * The <code>TestCache</code> class provides thread-local
122: * instances of a <code>TestSuite</code> class containing
123: * tests defined in the <code>TestCase</code> class
124: * specified in the <code>TestFactory</code>.
125: */
126: private final class TestCache {
127:
128: private final ThreadLocal _localCache = new ThreadLocal() {
129:
130: protected Object initialValue() {
131: return makeTestSuite();
132: }
133: };
134:
135: /*
136: * Returns the <code>Test</code> instance local to the
137: * calling thread.
138: *
139: * @return Thread-local <code>Test</code> instance.
140: */
141: Test getTest() {
142: return (Test) _localCache.get();
143: }
144: }
145: }
|