Skip to content

Commit 4b8efa1

Browse files
committed
Update in response to code review commends by dsaff and Tibor17
1 parent a6fb342 commit 4b8efa1

File tree

5 files changed

+100
-95
lines changed

5 files changed

+100
-95
lines changed

Diff for: src/main/java/org/junit/runner/notification/RunListener.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@
5353
public class RunListener {
5454

5555
/**
56-
* Called before any tests have been run. This may not necessarily
57-
* be called by the same thread as started the test run.
56+
* Called before any tests have been run. This may be called on an
57+
* arbitrary thread.
5858
*
5959
* @param description describes the tests to be run
6060
*/
6161
public void testRunStarted(Description description) throws Exception {
6262
}
6363

6464
/**
65-
* Called when all tests have finished. This may not necessarily
66-
* be called by the same thread as started the test run.
65+
* Called when all tests have finished. This may be called on an
66+
* arbitrary thread.
6767
*
6868
* @param result the summary of the test run, including all the tests that failed
6969
*/
@@ -96,9 +96,8 @@ public void testFinished(Description description) throws Exception {
9696
* {@link #testStarted(Description)}.
9797
*
9898
* <p>In the case of a listener throwing an exception, this will be called with
99-
* a {@code Description} of {@link Description#TEST_MECHANISM}. This call
100-
* may or may not be called in the same thread as the failing listener was
101-
* called
99+
* a {@code Description} of {@link Description#TEST_MECHANISM}, and may be called
100+
* on an arbitrary thread.
102101
*
103102
* @param failure describes the test that failed and the exception that was thrown
104103
*/

Diff for: src/main/java/org/junit/runner/notification/RunNotifier.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public void pleaseStop() {
199199
*/
200200
public void addFirstListener(RunListener listener) {
201201
if (listener == null) {
202-
throw new NullPointerException("Cannot remove a null listener");
202+
throw new NullPointerException("Cannot add a null listener");
203203
}
204204
fListeners.add(0, SynchronizedRunListener.wrapIfNotThreadSafe(listener));
205205
}

Diff for: src/main/java/org/junit/runner/notification/SynchronizedRunListener.java

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
* Thread-safe decorator for {@link RunListener} implementations that synchronizes
88
* calls to the delegate.
99
*
10+
* <p>This class synchronizes all listener calls on a global monitor. This is done because
11+
* prior to JUnit 4.12, all listeners were called in a synchronized block in RunNotifier,
12+
* so no two listeners were ever called concurrently. If we instead made the methods here
13+
* sychronized, clients that added multiple listeners that called common code might see
14+
* issues due to the reduced synchronization.
15+
*
1016
* @author Tibor Digana (tibor17)
1117
* @author Kevin Cooney (kcooney)
1218
* @since 4.12

Diff for: src/test/java/org/junit/runner/notification/ConcurrentRunNotifierTest.java

+77-77
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
import org.junit.Test;
44
import org.junit.runner.Description;
55

6-
import java.util.concurrent.*;
6+
import java.util.Random;
7+
import java.util.concurrent.Callable;
8+
import java.util.concurrent.CountDownLatch;
9+
import java.util.concurrent.CyclicBarrier;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.ExecutorService;
12+
import java.util.concurrent.TimeUnit;
713
import java.util.concurrent.atomic.AtomicBoolean;
814
import java.util.concurrent.atomic.AtomicInteger;
915

@@ -58,16 +64,16 @@ public void run() {
5864
}
5965

6066
private static class ExaminedListener extends RunListener {
61-
volatile boolean useMe = false;
67+
final boolean throwFromTestStarted;
6268
volatile boolean hasTestFailure = false;
6369

64-
ExaminedListener(boolean useMe) {
65-
this.useMe = useMe;
70+
ExaminedListener(boolean throwFromTestStarted) {
71+
this.throwFromTestStarted = throwFromTestStarted;
6672
}
6773

6874
@Override
6975
public void testStarted(Description description) throws Exception {
70-
if (!useMe) {
76+
if (!throwFromTestStarted) {
7177
throw new Exception();
7278
}
7379
}
@@ -78,94 +84,88 @@ public void testFailure(Failure failure) throws Exception {
7884
}
7985
}
8086

81-
@Test
82-
public void reportConcurrentFailuresAfterAddListener() throws Exception {
83-
int totalListenersFailures = 0;
87+
private abstract class AbstractConcurrentFailuresTest {
8488

85-
final ExaminedListener[] examinedListeners = new ExaminedListener[1000];
86-
for (int i = 0; i < examinedListeners.length; ++i) {
87-
boolean fail = StrictMath.random() >= 0.5d;
88-
if (fail) {
89-
++totalListenersFailures;
90-
}
91-
examinedListeners[i] = new ExaminedListener(!fail);
92-
}
89+
protected abstract void addListener(ExaminedListener listener);
9390

94-
final CyclicBarrier trigger = new CyclicBarrier(2);
95-
final AtomicBoolean condition = new AtomicBoolean(true);
91+
public void test() throws Exception {
92+
int totalListenersFailures = 0;
9693

97-
ExecutorService notificationsPool = Executors.newFixedThreadPool(4);
98-
notificationsPool.submit(new Callable<Void>() {
99-
public Void call() throws Exception {
100-
trigger.await();
101-
while (condition.get()) {
102-
notifier.fireTestStarted(null);
94+
Random random = new Random(42);
95+
ExaminedListener[] examinedListeners = new ExaminedListener[1000];
96+
for (int i = 0; i < examinedListeners.length; ++i) {
97+
boolean fail = random.nextDouble() >= 0.5d;
98+
if (fail) {
99+
++totalListenersFailures;
103100
}
104-
notifier.fireTestStarted(null);
105-
return null;
101+
examinedListeners[i] = new ExaminedListener(!fail);
106102
}
107-
});
108-
109-
trigger.await();
110-
111-
for (ExaminedListener examinedListener : examinedListeners) {
112-
notifier.addListener(examinedListener);
113-
}
114103

115-
notificationsPool.shutdown();
116-
condition.set(false);
117-
assertTrue(notificationsPool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
104+
final AtomicBoolean condition = new AtomicBoolean(true);
105+
final CyclicBarrier trigger = new CyclicBarrier(2);
106+
final CountDownLatch latch = new CountDownLatch(10);
107+
108+
ExecutorService notificationsPool = Executors.newFixedThreadPool(4);
109+
notificationsPool.submit(new Callable<Void>() {
110+
public Void call() throws Exception {
111+
trigger.await();
112+
while (condition.get()) {
113+
notifier.fireTestStarted(null);
114+
latch.countDown();
115+
}
116+
notifier.fireTestStarted(null);
117+
return null;
118+
}
119+
});
118120

119-
if (totalListenersFailures != 0) {
120-
// If no listener failures, then all the listeners do not report any failure.
121-
int countTestFailures = examinedListeners.length - countReportedTestFailures(examinedListeners);
122-
assertThat(totalListenersFailures, is(countTestFailures));
123-
}
124-
}
121+
// Wait for callable to start
122+
trigger.await(TIMEOUT, TimeUnit.SECONDS);
125123

126-
@Test
127-
public void reportConcurrentFailuresAfterAddFirstListener() throws Exception {
128-
int totalListenersFailures = 0;
124+
// Wait for callable to fire a few events
125+
latch.await(TIMEOUT, TimeUnit.SECONDS);
129126

130-
final ExaminedListener[] examinedListeners = new ExaminedListener[1000];
131-
for (int i = 0; i < examinedListeners.length; ++i) {
132-
boolean fail = StrictMath.random() >= 0.5d;
133-
if (fail) {
134-
++totalListenersFailures;
127+
for (ExaminedListener examinedListener : examinedListeners) {
128+
addListener(examinedListener);
135129
}
136-
examinedListeners[i] = new ExaminedListener(!fail);
137-
}
138130

139-
final CyclicBarrier trigger = new CyclicBarrier(2);
140-
final AtomicBoolean condition = new AtomicBoolean(true);
131+
notificationsPool.shutdown();
132+
condition.set(false);
133+
assertTrue(notificationsPool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
141134

142-
ExecutorService notificationsPool = Executors.newFixedThreadPool(4);
143-
notificationsPool.submit(new Callable<Void>() {
144-
public Void call() throws Exception {
145-
trigger.await();
146-
while (condition.get()) {
147-
notifier.fireTestStarted(null);
148-
}
149-
notifier.fireTestStarted(null);
150-
return null;
135+
if (totalListenersFailures != 0) {
136+
// If no listener failures, then all the listeners do not report any failure.
137+
int countTestFailures = examinedListeners.length - countReportedTestFailures(examinedListeners);
138+
assertThat(totalListenersFailures, is(countTestFailures));
151139
}
152-
});
153-
154-
trigger.await();
155-
156-
for (ExaminedListener examinedListener : examinedListeners) {
157-
notifier.addFirstListener(examinedListener);
158140
}
141+
}
159142

160-
notificationsPool.shutdown();
161-
condition.set(false);
162-
assertTrue(notificationsPool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
143+
/**
144+
* Verifies that listeners added while tests are run concurrently are
145+
* notified about test failures.
146+
*/
147+
@Test
148+
public void reportConcurrentFailuresAfterAddListener() throws Exception {
149+
new AbstractConcurrentFailuresTest() {
150+
@Override
151+
protected void addListener(ExaminedListener listener) {
152+
notifier.addListener(listener);
153+
}
154+
}.test();
155+
}
163156

164-
if (totalListenersFailures != 0) {
165-
// If no listener failures, then all the listeners do not report any failure.
166-
int countTestFailures = examinedListeners.length - countReportedTestFailures(examinedListeners);
167-
assertThat(totalListenersFailures, is(countTestFailures));
168-
}
157+
/**
158+
* Verifies that listeners added with addFirstListener() while tests are run concurrently are
159+
* notified about test failures.
160+
*/
161+
@Test
162+
public void reportConcurrentFailuresAfterAddFirstListener() throws Exception {
163+
new AbstractConcurrentFailuresTest() {
164+
@Override
165+
protected void addListener(ExaminedListener listener) {
166+
notifier.addFirstListener(listener);
167+
}
168+
}.test();
169169
}
170170

171171
private static int countReportedTestFailures(ExaminedListener[] listeners) {

Diff for: src/test/java/org/junit/runner/notification/SynchronizedRunListenerTest.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ private static class MethodSignature {
2929
private final List<Class<?>> parameterTypes;
3030

3131
public MethodSignature(Method method) {
32-
this.method= method;
33-
name= method.getName();
34-
parameterTypes= Arrays.asList(method.getParameterTypes());
32+
this.method = method;
33+
name = method.getName();
34+
parameterTypes = Arrays.asList(method.getParameterTypes());
3535
}
3636

3737
@Override
@@ -67,8 +67,8 @@ private Set<MethodSignature> getAllDeclaredMethods(Class<?> type) {
6767

6868
@Test
6969
public void overridesAllMethodsInRunListener() {
70-
Set<MethodSignature> runListenerMethods= getAllDeclaredMethods(RunListener.class);
71-
Set<MethodSignature> synchronizedRunListenerMethods= getAllDeclaredMethods(
70+
Set<MethodSignature> runListenerMethods = getAllDeclaredMethods(RunListener.class);
71+
Set<MethodSignature> synchronizedRunListenerMethods = getAllDeclaredMethods(
7272
SynchronizedRunListener.class);
7373

7474
assertTrue(synchronizedRunListenerMethods.containsAll(runListenerMethods));
@@ -78,7 +78,7 @@ private static class NamedListener extends RunListener {
7878
private final String name;
7979

8080
public NamedListener(String name) {
81-
this.name= name;
81+
this.name = name;
8282
}
8383

8484
@Override
@@ -94,7 +94,7 @@ public boolean equals(Object obj) {
9494
if (!(obj instanceof NamedListener)) {
9595
return false;
9696
}
97-
NamedListener that= (NamedListener) obj;
97+
NamedListener that = (NamedListener) obj;
9898
return this.name.equals(that.name);
9999
}
100100
}
@@ -149,7 +149,7 @@ public void hashCodeDelegates() {
149149

150150
@Test
151151
public void wrapIfNotThreadSafeShouldNotWrapThreadSafeListeners() {
152-
ThreadSafeRunListener listener= new ThreadSafeRunListener();;
152+
ThreadSafeRunListener listener = new ThreadSafeRunListener();;
153153
assertSame(listener, SynchronizedRunListener.wrapIfNotThreadSafe(listener));
154154
}
155155

@@ -160,8 +160,8 @@ public void wrapIfNotThreadSafeShouldNotWrapThreadSafeListeners() {
160160
*/
161161
@Test
162162
public void wrapIfNotThreadSafeShouldWrapNonThreadSafeListeners() {
163-
NamedListener listener= new NamedListener("name");
164-
RunListener wrappedListener= SynchronizedRunListener.wrapIfNotThreadSafe(listener);
163+
NamedListener listener = new NamedListener("name");
164+
RunListener wrappedListener = SynchronizedRunListener.wrapIfNotThreadSafe(listener);
165165
assertThat(wrappedListener, instanceOf(SynchronizedRunListener.class));
166166
}
167167
}

0 commit comments

Comments
 (0)