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.
15
* For example, to create a load test of 10 concurrent users
16
* with each user running <code>ExampleTest</code> once and
17
* all users started simultaneously, use:
20
* Test loadTest = new LoadTest(new ExampleTest(), 10);
24
* The load can be ramped by specifying a pluggable
25
* <code>Timer</code> instance which prescribes the delay
26
* between the addition of each concurrent user. A
27
* <code>ConstantTimer</code> has a constant delay, with
28
* a zero value indicating that all users will be started
29
* simultaneously. A <code>RandomTimer</code> has a random
30
* delay with a uniformly distributed variation.
32
* For example, to create a load test of 10 concurrent users
33
* with each user running <code>ExampleTest</code> once and
34
* with a one second delay between the addition of users, use:
37
* Timer timer = new ConstantTimer(1000);
38
* Test loadTest = new LoadTest(new ExampleTest(), 10, timer);
42
* In order to simulate each concurrent user running a test for a
43
* specified number of iterations, a <code>LoadTest</code> can be
44
* constructed to decorate a <code>RepeatedTest</code>.
45
* Alternatively, a <code>LoadTest</code> convenience constructor
46
* specifying the number of iterations is provided which creates a
47
* <code>RepeatedTest</code>.
49
* For example, to create a load test of 10 concurrent users
50
* with each user running <code>ExampleTest</code> for 20 iterations,
51
* and with a one second delay between the addition of users, use:
54
* Timer timer = new ConstantTimer(1000);
55
* Test repeatedTest = new RepeatedTest(new ExampleTest(), 20);
56
* Test loadTest = new LoadTest(repeatedTest, 10, timer);
59
* or, alternatively, use:
62
* Timer timer = new ConstantTimer(1000);
63
* Test loadTest = new LoadTest(new ExampleTest(), 10, 20, timer);
66
* A <code>LoadTest</code> can be decorated as a <code>TimedTest</code>
67
* to test the elapsed time of the load test. For example, to decorate
68
* the load test constructed above as a timed test with a maximum elapsed
69
* time of 2 seconds, use:
72
* Test timedTest = new TimedTest(loadTest, 2000);
76
* By default, a <code>LoadTest</code> does not enforce test
77
* atomicity (as defined in transaction processing) if its decorated
78
* test spawns threads, either directly or indirectly. In other words,
79
* if a decorated test spawns a thread and then returns control without
80
* waiting for its spawned thread to complete, then the test is assumed
81
* to be transactionally complete.
83
* If threads are integral to the successful completion of
84
* a decorated test, meaning that the decorated test should not be
85
* treated as complete until all of its threads complete, then
86
* <code>setEnforceTestAtomicity(true)</code> should be invoked to
87
* enforce test atomicity. This effectively causes the load test to
88
* wait for the completion of all threads belonging to the same
89
* <code>ThreadGroup</code> as the thread running the decorated test.
91
* @author <a href="mailto:mike@clarkware.com">Mike Clark</a>
92
* @author <a href="http://www.clarkware.com">Clarkware Consulting, Inc.</a>
95
* @see junit.framework.Test
98
public class LoadTest implements Test {
100
private final int _users;
101
private final Timer _timer;
102
private final ThreadedTest _test;
103
private final ThreadedTestGroup _group;
104
private final ThreadBarrier _barrier;
105
private boolean _enforceTestAtomicity;
108
* Constructs a <code>LoadTest</code> to decorate
109
* the specified test using the specified number
110
* of concurrent users and a delay of 0 ms.
112
* @param test Test to decorate.
113
* @param users Number of concurrent users.
115
public LoadTest(Test test, int users) {
116
this(test, users, new ConstantTimer(0));
120
* Constructs a <code>LoadTest</code> to decorate
121
* the specified test using the specified number
122
* of concurrent users, number of iterations per
123
* user, and a delay of 0 ms.
125
* @param test Test to decorate.
126
* @param users Number of concurrent users.
127
* @param iteration Number of iterations per user.
129
public LoadTest(Test test, int users, int iterations) {
130
this(test, users, iterations, new ConstantTimer(0));
134
* Constructs a <code>LoadTest</code> to decorate
135
* the specified test using the specified number
136
* of concurrent users, number of iterations per
137
* user, and delay timer.
139
* @param test Test to decorate.
140
* @param users Number of concurrent users.
141
* @param iteration Number of iterations per user.
142
* @timer Delay timer.
144
public LoadTest(Test test, int users, int iterations, Timer timer) {
145
this(new RepeatedTest(test, iterations), users, timer);
149
* Constructs a <code>LoadTest</code> to decorate
150
* the specified test using the specified number
151
* of concurrent users and delay timer.
153
* @param test Test to decorate.
154
* @param users Number of concurrent users.
155
* @timer Delay timer.
156
* @throws IllegalArgumentException If an invalid argument is
157
* specified, such as a non-positive number of concurrent users.
159
public LoadTest(Test test, int users, Timer timer) {
162
throw new IllegalArgumentException("Number of users must be > 0");
163
} else if (timer == null) {
164
throw new IllegalArgumentException("Delay timer is null");
165
} else if (test == null) {
166
throw new IllegalArgumentException("Decorated test is null");
171
setEnforceTestAtomicity(false);
172
_barrier = new ThreadBarrier(users);
173
_group = new ThreadedTestGroup(this);
174
_test = new ThreadedTest(test, _group, _barrier);
178
* Indicates whether test atomicity should be enforced.
180
* @param isAtomic <code>true</code> to enforce test atomicity;
181
* <code>false</code> otherwise.
183
public void setEnforceTestAtomicity(boolean isAtomic) {
184
_enforceTestAtomicity = isAtomic;
188
* Returns the number of tests in this test.
190
* @return Number of tests.
192
public int countTestCases() {
193
return _test.countTestCases() * _users;
199
* @param result Test result.
201
public void run(TestResult result) {
203
_group.setTestResult(result);
205
for (int i=0; i < _users; i++) {
207
if (result.shouldStop()) {
208
_barrier.cancelThreads(_users - i);
216
Thread.sleep(getDelay());
218
} catch(InterruptedException ignored) { }
221
waitForTestCompletion();
227
* Waits for test completion.
229
protected void waitForTestCompletion() {
231
// TODO: May require a strategy pattern
232
// if other algorithms emerge.
234
if (_enforceTestAtomicity) {
235
waitForAllThreadsToComplete();
237
waitForThreadedTestThreadsToComplete();
242
* Waits for all threads spawned by
243
* <code>ThreadedTest</code> instances
246
protected void waitForThreadedTestThreadsToComplete() {
248
while (!_barrier.isReached()) {
254
* Waits for all threads in the
255
* <code>ThreadedTestGroup</code> to complete.
257
protected void waitForAllThreadsToComplete() {
259
while (_group.activeCount() > 0) {
267
protected void sleep(long time) {
272
} catch(Exception ignored) { }
276
* Cleans up thread resources.
278
protected void cleanup() {
283
} catch (Throwable ignored) { }
287
* Returns the test description.
289
* @return Description.
291
public String toString() {
292
if (_enforceTestAtomicity) {
293
return "LoadTest (ATOMIC): " + _test.toString();
295
return "LoadTest (NON-ATOMIC): " + _test.toString();
300
* Returns the test delay.
302
* @return Delay in milliseconds.
304
protected long getDelay() {
305
return _timer.getDelay();
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();