Skip to content

Commit 1d6914b

Browse files
author
Tibor Digana
committed
backward compatible, SynchronizedRunListener, removed EqualRunListener
1 parent 21a926e commit 1d6914b

File tree

5 files changed

+280
-172
lines changed

5 files changed

+280
-172
lines changed

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

-72
This file was deleted.

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

+97-25
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
import java.util.ArrayList;
99
import java.util.Arrays;
1010
import java.util.Collection;
11-
import java.util.List;
12-
import java.util.concurrent.CopyOnWriteArrayList;
11+
import java.util.concurrent.locks.ReentrantLock;
1312

1413
/**
1514
* If you write custom runners, you may need to notify JUnit of your progress running tests.
@@ -21,46 +20,103 @@
2120
* @since 4.0
2221
*/
2322
public class RunNotifier {
24-
private final CopyOnWriteArrayList<RunListener> fListeners= new CopyOnWriteArrayList<RunListener>();
25-
private volatile boolean fPleaseStop= false;
23+
private final ReentrantLock lock = new ReentrantLock();
24+
25+
private volatile RunListener[] listeners = new RunListener[0];
26+
private volatile boolean pleaseStop = false;
27+
28+
private static RunListener wrapSynchronizedIfNotThreadSafe(RunListener listener) {
29+
boolean isThreadSafe = listener.getClass().isAnnotationPresent(ThreadSafe.class);
30+
return isThreadSafe ? listener : new SynchronizedRunListener(listener);
31+
}
32+
33+
/**
34+
* Satisfies <tt>(o == null ? e == null : o.equals(e)</tt>
35+
* in {@link java.util.List#remove(Object)}.
36+
*
37+
* @param o listener to remove
38+
* @param e element in <code>listeners</code> which was previously added
39+
* @return {@code true} if <code>o</code> is equal with <code>e</code>
40+
*/
41+
private static boolean equalListeners(Object o, Object e) {
42+
if (o == null) {
43+
return e == null;
44+
} else {
45+
return e.getClass() == SynchronizedRunListener.class ? e.equals(o) : o.equals(e);
46+
}
47+
}
2648

2749
/**
2850
* Internal use only
2951
*/
3052
public void addListener(RunListener listener) {
31-
listener = wrapSynchronizedIfNotThreadSafe(listener);
32-
fListeners.add(listener);
53+
if (listener != null) {
54+
listener = wrapSynchronizedIfNotThreadSafe(listener);
55+
final ReentrantLock lock = this.lock;
56+
lock.lock();
57+
try {
58+
// same behavior as List#add(Object)
59+
RunListener[] elements = this.listeners;
60+
int length = elements.length;
61+
RunListener[] listeners = new RunListener[1 + length];
62+
for (int i = 0; i < length; ++i) {
63+
listeners[i] = elements[i];
64+
}
65+
listeners[length] = listener;
66+
this.listeners = listeners;
67+
} finally {
68+
lock.unlock();
69+
}
70+
}
3371
}
3472

3573
/**
3674
* Internal use only
3775
*/
3876
public void removeListener(RunListener listener) {
39-
listener = new EqualRunListener(listener);
40-
fListeners.remove(listener);
41-
}
42-
43-
private static RunListener wrapSynchronizedIfNotThreadSafe(RunListener listener) {
44-
boolean isThreadSafe = listener.getClass().isAnnotationPresent(ThreadSafe.class);
45-
return isThreadSafe ? new EqualRunListener(listener) : new SynchronizedRunListener(listener);
77+
if (listener != null) {
78+
final ReentrantLock lock = this.lock;
79+
lock.lock();
80+
try {
81+
// same behavior as List#remove(Object)
82+
RunListener[] elements = this.listeners;
83+
int length = elements.length;
84+
if (length > 0) {
85+
RunListener[] listeners = new RunListener[length - 1];
86+
for (int i = 0, newLength = listeners.length; i < length; ++i) {
87+
if (equalListeners(listener, elements[i])) {
88+
for (int k = 1 + i; k != Integer.MAX_VALUE && k < length; ++k) {
89+
listeners[k - 1] = elements[k];
90+
}
91+
this.listeners = listeners;
92+
return;
93+
} else if (i < newLength) {
94+
listeners[i] = elements[i];
95+
}
96+
}
97+
}
98+
} finally {
99+
lock.unlock();
100+
}
101+
}
46102
}
47103

48104
private abstract class SafeNotifier {
49-
private final Collection<RunListener> fCurrentListeners;
105+
private final Collection<RunListener> currentListeners;
50106

51107
SafeNotifier() {
52-
this(fListeners);
108+
this(Arrays.asList(listeners));
53109
}
54110

55111
SafeNotifier(Collection<RunListener> currentListeners) {
56-
fCurrentListeners= currentListeners;
112+
this.currentListeners = currentListeners;
57113
}
58114

59115
void run() {
60-
int capacity= fCurrentListeners.size();
61-
ArrayList<RunListener> safeListeners= new ArrayList<RunListener>(capacity);
62-
ArrayList<Failure> failures= new ArrayList<Failure>(capacity);
63-
for (RunListener listener : fCurrentListeners) {
116+
int capacity = currentListeners.size();
117+
ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
118+
ArrayList<Failure> failures = new ArrayList<Failure>(capacity);
119+
for (RunListener listener : currentListeners) {
64120
try {
65121
notifyListener(listener);
66122
safeListeners.add(listener);
@@ -105,7 +161,7 @@ protected void notifyListener(RunListener each) throws Exception {
105161
* @throws StoppedByUserException thrown if a user has requested that the test run stop
106162
*/
107163
public void fireTestStarted(final Description description) throws StoppedByUserException {
108-
if (fPleaseStop) {
164+
if (pleaseStop) {
109165
throw new StoppedByUserException();
110166
}
111167
new SafeNotifier() {
@@ -122,10 +178,10 @@ protected void notifyListener(RunListener each) throws Exception {
122178
* @param failure the description of the test that failed and the exception thrown
123179
*/
124180
public void fireTestFailure(Failure failure) {
125-
fireTestFailures(fListeners, Arrays.asList(failure));
181+
fireTestFailures(Arrays.asList(listeners), Arrays.asList(failure));
126182
}
127183

128-
private void fireTestFailures(Collection<RunListener> listeners, final List<Failure> failures) {
184+
private void fireTestFailures(Collection<RunListener> listeners, final Collection<Failure> failures) {
129185
if (!failures.isEmpty()) {
130186
new SafeNotifier(listeners) {
131187
@Override
@@ -191,13 +247,29 @@ protected void notifyListener(RunListener each) throws Exception {
191247
* to be shared amongst the many runners involved.
192248
*/
193249
public void pleaseStop() {
194-
fPleaseStop= true;
250+
pleaseStop = true;
195251
}
196252

197253
/**
198254
* Internal use only. The Result's listener must be first.
199255
*/
200256
public void addFirstListener(RunListener listener) {
201-
fListeners.add(0, listener);
257+
if (listener != null) {
258+
listener = wrapSynchronizedIfNotThreadSafe(listener);
259+
final ReentrantLock lock = this.lock;
260+
lock.lock();
261+
try {
262+
// same behavior as List#add(0, Object)
263+
RunListener[] elements = this.listeners;
264+
RunListener[] listeners = new RunListener[1 + elements.length];
265+
listeners[0] = listener;
266+
for (int i = 0, length = elements.length; i < length; ++i) {
267+
listeners[1 + i] = elements[i];
268+
}
269+
this.listeners = listeners;
270+
} finally {
271+
lock.unlock();
272+
}
273+
}
202274
}
203275
}

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ final class SynchronizedRunListener extends RunListener {
1515
private final RunListener listener;
1616

1717
SynchronizedRunListener(RunListener listener) {
18-
if (listener == null) {
19-
throw new NullPointerException("null listener");
20-
}
2118
this.listener = listener;
2219
}
2320

@@ -63,7 +60,7 @@ public int hashCode() {
6360

6461
@Override
6562
public boolean equals(Object o) {
66-
return listener.equals(o);
63+
return o.equals(listener);
6764
}
6865

6966
@Override

0 commit comments

Comments
 (0)