Skip to content

Commit 21a926e

Browse files
author
Tibor Digana
committed
problem to remove synchronized listener
1 parent 20c6b53 commit 21a926e

File tree

5 files changed

+236
-12
lines changed

5 files changed

+236
-12
lines changed

Diff for: pom.xml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="UTF-8"?>
1+
<?xml version="1.0" encoding="UTF-8"?>
22
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
33
<modelVersion>4.0.0</modelVersion>
44

@@ -93,6 +93,11 @@
9393
<artifactId>hamcrest-core</artifactId>
9494
<version>1.3</version>
9595
</dependency>
96+
<dependency>
97+
<groupId>net.jcip</groupId>
98+
<artifactId>jcip-annotations</artifactId>
99+
<version>1.0</version>
100+
</dependency>
96101
</dependencies>
97102

98103
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.junit.runner.notification;
2+
3+
import org.junit.runner.Description;
4+
import org.junit.runner.Result;
5+
6+
/**
7+
* See the equals method.
8+
*
9+
* @author Tibor Digana (tibor17)
10+
* @version 4.12
11+
* @since 4.12
12+
*/
13+
final class EqualRunListener extends RunListener {
14+
private final RunListener listener;
15+
16+
EqualRunListener(RunListener listener) {
17+
if (listener == null) {
18+
throw new NullPointerException("null listener");
19+
}
20+
this.listener = listener;
21+
}
22+
23+
@Override
24+
public void testRunStarted(Description description) throws Exception {
25+
listener.testRunStarted(description);
26+
}
27+
28+
@Override
29+
public void testRunFinished(Result result) throws Exception {
30+
listener.testRunFinished(result);
31+
}
32+
33+
@Override
34+
public void testStarted(Description description) throws Exception {
35+
listener.testStarted(description);
36+
}
37+
38+
@Override
39+
public void testFinished(Description description) throws Exception {
40+
listener.testFinished(description);
41+
}
42+
43+
@Override
44+
public void testFailure(Failure failure) throws Exception {
45+
listener.testFailure(failure);
46+
}
47+
48+
@Override
49+
public void testAssumptionFailure(Failure failure) {
50+
listener.testAssumptionFailure(failure);
51+
}
52+
53+
@Override
54+
public void testIgnored(Description description) throws Exception {
55+
listener.testIgnored(description);
56+
}
57+
58+
@Override
59+
public int hashCode() {
60+
return listener.hashCode();
61+
}
62+
63+
@Override
64+
public boolean equals(Object o) {
65+
return o != null && o.equals(listener);
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return listener.toString();
71+
}
72+
}

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package org.junit.runner.notification;
22

3+
import net.jcip.annotations.ThreadSafe;
4+
import org.junit.internal.AssumptionViolatedException;
5+
import org.junit.runner.Description;
6+
import org.junit.runner.Result;
7+
38
import java.util.ArrayList;
49
import java.util.Arrays;
510
import java.util.Collection;
611
import java.util.List;
712
import java.util.concurrent.CopyOnWriteArrayList;
8-
import org.junit.internal.AssumptionViolatedException;
9-
import org.junit.runner.Description;
10-
import org.junit.runner.Result;
1113

1214
/**
1315
* If you write custom runners, you may need to notify JUnit of your progress running tests.
@@ -26,16 +28,23 @@ public class RunNotifier {
2628
* Internal use only
2729
*/
2830
public void addListener(RunListener listener) {
31+
listener = wrapSynchronizedIfNotThreadSafe(listener);
2932
fListeners.add(listener);
3033
}
3134

3235
/**
3336
* Internal use only
3437
*/
3538
public void removeListener(RunListener listener) {
39+
listener = new EqualRunListener(listener);
3640
fListeners.remove(listener);
3741
}
3842

43+
private static RunListener wrapSynchronizedIfNotThreadSafe(RunListener listener) {
44+
boolean isThreadSafe = listener.getClass().isAnnotationPresent(ThreadSafe.class);
45+
return isThreadSafe ? new EqualRunListener(listener) : new SynchronizedRunListener(listener);
46+
}
47+
3948
private abstract class SafeNotifier {
4049
private final Collection<RunListener> fCurrentListeners;
4150

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.junit.runner.notification;
2+
3+
import org.junit.runner.Description;
4+
import org.junit.runner.Result;
5+
6+
/**
7+
* SynchronizedRunListener decorates {@link RunListener} and
8+
* has all methods synchronized.
9+
*
10+
* @author Tibor Digana (tibor17)
11+
* @version 4.12
12+
* @since 4.12
13+
*/
14+
final class SynchronizedRunListener extends RunListener {
15+
private final RunListener listener;
16+
17+
SynchronizedRunListener(RunListener listener) {
18+
if (listener == null) {
19+
throw new NullPointerException("null listener");
20+
}
21+
this.listener = listener;
22+
}
23+
24+
@Override
25+
public synchronized void testRunStarted(Description description) throws Exception {
26+
listener.testRunStarted(description);
27+
}
28+
29+
@Override
30+
public synchronized void testRunFinished(Result result) throws Exception {
31+
listener.testRunFinished(result);
32+
}
33+
34+
@Override
35+
public synchronized void testStarted(Description description) throws Exception {
36+
listener.testStarted(description);
37+
}
38+
39+
@Override
40+
public synchronized void testFinished(Description description) throws Exception {
41+
listener.testFinished(description);
42+
}
43+
44+
@Override
45+
public synchronized void testFailure(Failure failure) throws Exception {
46+
listener.testFailure(failure);
47+
}
48+
49+
@Override
50+
public synchronized void testAssumptionFailure(Failure failure) {
51+
listener.testAssumptionFailure(failure);
52+
}
53+
54+
@Override
55+
public synchronized void testIgnored(Description description) throws Exception {
56+
listener.testIgnored(description);
57+
}
58+
59+
@Override
60+
public int hashCode() {
61+
return listener.hashCode();
62+
}
63+
64+
@Override
65+
public boolean equals(Object o) {
66+
return listener.equals(o);
67+
}
68+
69+
@Override
70+
public String toString() {
71+
return listener.toString();
72+
}
73+
}

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

+73-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package org.junit.runner.notification;
22

3-
import java.util.concurrent.BrokenBarrierException;
4-
import java.util.concurrent.Callable;
5-
import java.util.concurrent.CyclicBarrier;
6-
import java.util.concurrent.ExecutorService;
7-
import java.util.concurrent.Executors;
8-
import java.util.concurrent.TimeUnit;
9-
import java.util.concurrent.atomic.AtomicBoolean;
10-
import java.util.concurrent.atomic.AtomicInteger;
3+
import net.jcip.annotations.ThreadSafe;
114
import org.junit.Test;
125
import org.junit.runner.Description;
136

7+
import java.util.concurrent.*;
8+
import java.util.concurrent.atomic.AtomicBoolean;
9+
import java.util.concurrent.atomic.AtomicInteger;
10+
1411
import static org.hamcrest.core.Is.is;
1512
import static org.junit.Assert.assertThat;
1613
import static org.junit.Assert.assertTrue;
@@ -177,6 +174,74 @@ public Object call() throws Exception {
177174
}
178175
}
179176

177+
@Test
178+
public void keepContractOnEqualsNegative() {
179+
RunNotifier notifier = new RunNotifier();
180+
final ConcurrentRunListener listener = new ConcurrentRunListener();
181+
ConcurrentRunListener wrappedListener = new ConcurrentRunListener() {
182+
@Override
183+
public boolean equals(Object o) {
184+
return listener.equals(o);
185+
}
186+
};
187+
notifier.addListener(wrappedListener);
188+
assertThat(wrappedListener.testStarted.get(), is(0));
189+
notifier.fireTestStarted(null);
190+
assertThat(wrappedListener.testStarted.get(), is(1));
191+
notifier.removeListener(listener);
192+
notifier.fireTestStarted(null);
193+
assertThat(wrappedListener.testStarted.get(), is(2));
194+
}
195+
196+
@Test
197+
public void keepContractOnEquals() {
198+
RunNotifier notifier = new RunNotifier();
199+
final ConcurrentRunListener listener = new ConcurrentRunListener();
200+
ConcurrentRunListener wrappedListener = new ConcurrentRunListener() {
201+
@Override
202+
public boolean equals(Object o) {
203+
return listener.equals(o);
204+
}
205+
};
206+
notifier.addListener(listener);
207+
assertThat(listener.testStarted.get(), is(0));
208+
notifier.fireTestStarted(null);
209+
assertThat(listener.testStarted.get(), is(1));
210+
notifier.removeListener(wrappedListener);
211+
notifier.fireTestStarted(null);
212+
assertThat(listener.testStarted.get(), is(1));
213+
}
214+
215+
@Test
216+
public void wrapSynchronizedIfNotThreadSafe() {
217+
RunNotifier notifier = new RunNotifier();
218+
ConcurrentRunListener listener = new ConcurrentRunListener();
219+
notifier.addListener(listener);
220+
assertThat(listener.testStarted.get(), is(0));
221+
notifier.fireTestStarted(null);
222+
assertThat(listener.testStarted.get(), is(1));
223+
notifier.removeListener(listener);
224+
notifier.fireTestStarted(null);
225+
assertThat(listener.testStarted.get(), is(1));
226+
}
227+
228+
@ThreadSafe
229+
private static class ThreadSafeRunListener extends ConcurrentRunListener {
230+
}
231+
232+
@Test
233+
public void doNotWrapIfThreadSafe() {
234+
RunNotifier notifier = new RunNotifier();
235+
ThreadSafeRunListener listener = new ThreadSafeRunListener();
236+
notifier.addListener(listener);
237+
assertThat(listener.testStarted.get(), is(0));
238+
notifier.fireTestStarted(null);
239+
assertThat(listener.testStarted.get(), is(1));
240+
notifier.removeListener(listener);
241+
notifier.fireTestStarted(null);
242+
assertThat(listener.testStarted.get(), is(1));
243+
}
244+
180245
private static int countReportedTestFailures(ExaminedListener[] listeners) {
181246
int count = 0;
182247
for (ExaminedListener listener : listeners) {

0 commit comments

Comments
 (0)