-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
Copy pathConcurrentRunNotifierTest.java
173 lines (146 loc) · 5.81 KB
/
ConcurrentRunNotifierTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package org.junit.runner.notification;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.testsupport.EventCollectorMatchers.hasNumberOfTestsStarted;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.testsupport.EventCollector;
/**
* Testing RunNotifier in concurrent access.
*
* @author Tibor Digana (tibor17)
* @version 4.12
* @since 4.12
*/
public final class ConcurrentRunNotifierTest {
private static final long TIMEOUT = 3;
private final RunNotifier fNotifier = new RunNotifier();
@Test
public void realUsage() throws Exception {
EventCollector listener1 = new EventCollector();
EventCollector listener2 = new EventCollector();
fNotifier.addListener(listener1);
fNotifier.addListener(listener2);
final int numParallelTests = 4;
ExecutorService pool = Executors.newFixedThreadPool(numParallelTests);
for (int i = 0; i < numParallelTests; ++i) {
pool.submit(new Runnable() {
public void run() {
fNotifier.fireTestStarted(null);
}
});
}
pool.shutdown();
assertTrue(pool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
fNotifier.removeListener(listener1);
fNotifier.removeListener(listener2);
assertThat(listener1, hasNumberOfTestsStarted(numParallelTests));
assertThat(listener2, hasNumberOfTestsStarted(numParallelTests));
}
private static class ExaminedListener extends RunListener {
final boolean throwFromTestStarted;
volatile boolean hasTestFailure = false;
ExaminedListener(boolean throwFromTestStarted) {
this.throwFromTestStarted = throwFromTestStarted;
}
@Override
public void testStarted(Description description) throws Exception {
if (throwFromTestStarted) {
throw new Exception();
}
}
@Override
public void testFailure(Failure failure) throws Exception {
hasTestFailure = true;
}
}
private abstract class AbstractConcurrentFailuresTest {
protected abstract void addListener(ExaminedListener listener);
public void test() throws Exception {
int totalListenersFailures = 0;
Random random = new Random(42);
ExaminedListener[] examinedListeners = new ExaminedListener[1000];
for (int i = 0; i < examinedListeners.length; ++i) {
boolean fail = random.nextDouble() >= 0.5d;
if (fail) {
++totalListenersFailures;
}
examinedListeners[i] = new ExaminedListener(fail);
}
final AtomicBoolean condition = new AtomicBoolean(true);
final CyclicBarrier trigger = new CyclicBarrier(2);
final CountDownLatch latch = new CountDownLatch(10);
ExecutorService notificationsPool = Executors.newFixedThreadPool(4);
notificationsPool.submit(new Callable<Void>() {
public Void call() throws Exception {
trigger.await();
while (condition.get()) {
fNotifier.fireTestStarted(null);
latch.countDown();
}
fNotifier.fireTestStarted(null);
return null;
}
});
// Wait for callable to start
trigger.await(TIMEOUT, TimeUnit.SECONDS);
// Wait for callable to fire a few events
latch.await(TIMEOUT, TimeUnit.SECONDS);
for (ExaminedListener examinedListener : examinedListeners) {
addListener(examinedListener);
}
notificationsPool.shutdown();
condition.set(false);
assertTrue(notificationsPool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
if (totalListenersFailures != 0) {
// If no listener failures, then all the listeners do not report any failure.
int countTestFailures = examinedListeners.length - countReportedTestFailures(examinedListeners);
assertThat(totalListenersFailures, is(countTestFailures));
}
}
}
/**
* Verifies that listeners added while tests are run concurrently are
* notified about test failures.
*/
@Test
public void reportConcurrentFailuresAfterAddListener() throws Exception {
new AbstractConcurrentFailuresTest() {
@Override
protected void addListener(ExaminedListener listener) {
fNotifier.addListener(listener);
}
}.test();
}
/**
* Verifies that listeners added with addFirstListener() while tests are run concurrently are
* notified about test failures.
*/
@Test
public void reportConcurrentFailuresAfterAddFirstListener() throws Exception {
new AbstractConcurrentFailuresTest() {
@Override
protected void addListener(ExaminedListener listener) {
fNotifier.addFirstListener(listener);
}
}.test();
}
private static int countReportedTestFailures(ExaminedListener[] listeners) {
int count = 0;
for (ExaminedListener listener : listeners) {
if (listener.hasTestFailure) {
++count;
}
}
return count;
}
}