1
package com.clarkware.junitperf;
3
import junit.framework.Test;
4
import junit.framework.TestResult;
5
import junit.extensions.RepeatedTest;
8
* The <code>LoadTest</code> is a test decorator that runs
9
* a test with a simulated number of concurrent users and
12
* In its simplest form, a <code>LoadTest</code> is constructed
13
* with a test to decorate and the number of concurrent users.
16
* For example, to create a load test of 10 concurrent users
17
* with each user running <code>ExampleTest</code> once and
18
* all users started simultaneously, use:
21
* Test loadTest = new LoadTest(new TestSuite(ExampleTest.class), 10);
24
* or, to load test a single test method, use:
27
* Test loadTest = new LoadTest(new ExampleTest("testSomething"), 10);
32
* The load can be ramped by specifying a pluggable
33
* <code>Timer</code> instance which prescribes the delay
34
* between the addition of each concurrent user. A
35
* <code>ConstantTimer</code> has a constant delay, with
36
* a zero value indicating that all users will be started
37
* simultaneously. A <code>RandomTimer</code> has a random
38
* delay with a uniformly distributed variation.
41
* For example, to create a load test of 10 concurrent users
42
* with each user running <code>ExampleTest.testSomething()</code> once and
43
* with a one second delay between the addition of users, use:
46
* Timer timer = new ConstantTimer(1000);
47
* Test loadTest = new LoadTest(new ExampleTest("testSomething"), 10, timer);
52
* In order to simulate each concurrent user running a test for a
53
* specified number of iterations, a <code>LoadTest</code> can be
54
* constructed to decorate a <code>RepeatedTest</code>.
55
* Alternatively, a <code>LoadTest</code> convenience constructor
56
* specifying the number of iterations is provided which creates a
57
* <code>RepeatedTest</code>.
60
* For example, to create a load test of 10 concurrent users
61
* with each user running <code>ExampleTest.testSomething()</code> for 20 iterations,
62
* and with a one second delay between the addition of users, use:
65
* Timer timer = new ConstantTimer(1000);
66
* Test repeatedTest = new RepeatedTest(new ExampleTest("testSomething"), 20);
67
* Test loadTest = new LoadTest(repeatedTest, 10, timer);
70
* or, alternatively, use:
73
* Timer timer = new ConstantTimer(1000);
74
* Test loadTest = new LoadTest(new ExampleTest("testSomething"), 10, 20, timer);
77
* A <code>LoadTest</code> can be decorated as a <code>TimedTest</code>
78
* to test the elapsed time of the load test. For example, to decorate
79
* the load test constructed above as a timed test with a maximum elapsed
80
* time of 2 seconds, use:
83
* Test timedTest = new TimedTest(loadTest, 2000);
88
* By default, a <code>LoadTest</code> does not enforce test
89
* atomicity (as defined in transaction processing) if its decorated
90
* test spawns threads, either directly or indirectly. In other words,
91
* if a decorated test spawns a thread and then returns control without
92
* waiting for its spawned thread to complete, then the test is assumed
93
* to be transactionally complete.
96
* If threads are integral to the successful completion of
97
* a decorated test, meaning that the decorated test should not be
98
* treated as complete until all of its threads complete, then
99
* <code>setEnforceTestAtomicity(true)</code> should be invoked to
100
* enforce test atomicity. This effectively causes the load test to
101
* wait for the completion of all threads belonging to the same
102
* <code>ThreadGroup</code> as the thread running the decorated test.
104
* @author <a href="mailto:mike@clarkware.com">Mike Clark</a>
105
* @author <a href="http://www.clarkware.com">Clarkware Consulting, Inc.</a>
106
* @author Ervin Varga
108
* @see junit.framework.Test
111
public class LoadTest implements Test {
113
private final int _users;
114
private final Timer _timer;
115
private final ThreadedTest _test;
116
private final ThreadedTestGroup _group;
117
private final ThreadBarrier _barrier;
118
private boolean _enforceTestAtomicity;
121
* Constructs a <code>LoadTest</code> to decorate
122
* the specified test using the specified number
123
* of concurrent users starting simultaneously.
125
* @param test Test to decorate.
126
* @param users Number of concurrent users.
128
public LoadTest(Test test, int users) {
129
this(test, users, new ConstantTimer(0));
133
* Constructs a <code>LoadTest</code> to decorate
134
* the specified test using the specified number
135
* of concurrent users starting simultaneously and
136
* the number of iterations per user.
138
* @param test Test to decorate.
139
* @param users Number of concurrent users.
140
* @param iteration Number of iterations per user.
142
public LoadTest(Test test, int users, int iterations) {
143
this(test, users, iterations, new ConstantTimer(0));
147
* Constructs a <code>LoadTest</code> to decorate
148
* the specified test using the specified number
149
* of concurrent users, number of iterations per
150
* user, and delay timer.
152
* @param test Test to decorate.
153
* @param users Number of concurrent users.
154
* @param iteration Number of iterations per user.
155
* @param timer Delay timer.
157
public LoadTest(Test test, int users, int iterations, Timer timer) {
158
this(new RepeatedTest(test, iterations), users, timer);
162
* Constructs a <code>LoadTest</code> to decorate
163
* the specified test using the specified number
164
* of concurrent users and delay timer.
166
* @param test Test to decorate.
167
* @param users Number of concurrent users.
168
* @param timer Delay timer.
170
public LoadTest(Test test, int users, Timer timer) {
173
throw new IllegalArgumentException("Number of users must be > 0");
174
} else if (timer == null) {
175
throw new IllegalArgumentException("Delay timer is null");
176
} else if (test == null) {
177
throw new IllegalArgumentException("Decorated test is null");
182
setEnforceTestAtomicity(false);
183
_barrier = new ThreadBarrier(users);
184
_group = new ThreadedTestGroup(this);
185
_test = new ThreadedTest(test, _group, _barrier);
189
* Indicates whether test atomicity should be enforced.
191
* If threads are integral to the successful completion of
192
* a decorated test, meaning that the decorated test should not be
193
* treated as complete until all of its threads complete, then
194
* <code>setEnforceTestAtomicity(true)</code> should be invoked to
195
* enforce test atomicity. This effectively causes the load test to
196
* wait for the completion of all threads belonging to the same
197
* <code>ThreadGroup</code> as the thread running the decorated test.
199
* @param isAtomic <code>true</code> to enforce test atomicity;
200
* <code>false</code> otherwise.
202
public void setEnforceTestAtomicity(boolean isAtomic) {
203
_enforceTestAtomicity = isAtomic;
207
* Returns the number of tests in this load test.
209
* @return Number of tests.
211
public int countTestCases() {
212
return _test.countTestCases() * _users;
218
* @param result Test result.
220
public void run(TestResult result) {
222
_group.setTestResult(result);
224
for (int i=0; i < _users; i++) {
226
if (result.shouldStop()) {
227
_barrier.cancelThreads(_users - i);
235
Thread.sleep(getDelay());
237
} catch(InterruptedException ignored) {}
240
waitForTestCompletion();
245
protected void waitForTestCompletion() {
247
// TODO: May require a strategy pattern
248
// if other algorithms emerge.
250
if (_enforceTestAtomicity) {
251
waitForAllThreadsToComplete();
253
waitForThreadedTestThreadsToComplete();
257
protected void waitForThreadedTestThreadsToComplete() {
259
while (!_barrier.isReached()) {
264
protected void waitForAllThreadsToComplete() {
266
while (_group.activeCount() > 0) {
271
protected void sleep(long time) {
276
} catch(Exception ignored) { }
279
protected void cleanup() {
284
} catch (Throwable ignored) { }
287
public String toString() {
288
if (_enforceTestAtomicity) {
289
return "LoadTest (ATOMIC): " + _test.toString();
291
return "LoadTest (NON-ATOMIC): " + _test.toString();
295
protected long getDelay() {
296
return _timer.getDelay();