8
8
import java .util .ArrayList ;
9
9
import java .util .Arrays ;
10
10
import java .util .Collection ;
11
- import java .util .List ;
12
- import java .util .concurrent .CopyOnWriteArrayList ;
11
+ import java .util .concurrent .locks .ReentrantLock ;
13
12
14
13
/**
15
14
* If you write custom runners, you may need to notify JUnit of your progress running tests.
21
20
* @since 4.0
22
21
*/
23
22
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
+ }
26
48
27
49
/**
28
50
* Internal use only
29
51
*/
30
52
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
+ }
33
71
}
34
72
35
73
/**
36
74
* Internal use only
37
75
*/
38
76
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
+ }
46
102
}
47
103
48
104
private abstract class SafeNotifier {
49
- private final Collection <RunListener > fCurrentListeners ;
105
+ private final Collection <RunListener > currentListeners ;
50
106
51
107
SafeNotifier () {
52
- this (fListeners );
108
+ this (Arrays . asList ( listeners ) );
53
109
}
54
110
55
111
SafeNotifier (Collection <RunListener > currentListeners ) {
56
- fCurrentListeners = currentListeners ;
112
+ this . currentListeners = currentListeners ;
57
113
}
58
114
59
115
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 ) {
64
120
try {
65
121
notifyListener (listener );
66
122
safeListeners .add (listener );
@@ -105,7 +161,7 @@ protected void notifyListener(RunListener each) throws Exception {
105
161
* @throws StoppedByUserException thrown if a user has requested that the test run stop
106
162
*/
107
163
public void fireTestStarted (final Description description ) throws StoppedByUserException {
108
- if (fPleaseStop ) {
164
+ if (pleaseStop ) {
109
165
throw new StoppedByUserException ();
110
166
}
111
167
new SafeNotifier () {
@@ -122,10 +178,10 @@ protected void notifyListener(RunListener each) throws Exception {
122
178
* @param failure the description of the test that failed and the exception thrown
123
179
*/
124
180
public void fireTestFailure (Failure failure ) {
125
- fireTestFailures (fListeners , Arrays .asList (failure ));
181
+ fireTestFailures (Arrays . asList ( listeners ) , Arrays .asList (failure ));
126
182
}
127
183
128
- private void fireTestFailures (Collection <RunListener > listeners , final List <Failure > failures ) {
184
+ private void fireTestFailures (Collection <RunListener > listeners , final Collection <Failure > failures ) {
129
185
if (!failures .isEmpty ()) {
130
186
new SafeNotifier (listeners ) {
131
187
@ Override
@@ -191,13 +247,29 @@ protected void notifyListener(RunListener each) throws Exception {
191
247
* to be shared amongst the many runners involved.
192
248
*/
193
249
public void pleaseStop () {
194
- fPleaseStop = true ;
250
+ pleaseStop = true ;
195
251
}
196
252
197
253
/**
198
254
* Internal use only. The Result's listener must be first.
199
255
*/
200
256
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
+ }
202
274
}
203
275
}
0 commit comments