• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.junit.runner.notification;
2 
3 import static java.util.Arrays.asList;
4 
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.concurrent.CopyOnWriteArrayList;
8 
9 import org.junit.runner.Description;
10 import org.junit.runner.Result;
11 
12 /**
13  * If you write custom runners, you may need to notify JUnit of your progress running tests.
14  * Do this by invoking the <code>RunNotifier</code> passed to your implementation of
15  * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to
16  * move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)}
17  * to a separate class since they should only be called once per run.
18  *
19  * @since 4.0
20  */
21 public class RunNotifier {
22     private final List<RunListener> listeners = new CopyOnWriteArrayList<RunListener>();
23     private volatile boolean pleaseStop = false;
24 
25     /**
26      * Internal use only
27      */
addListener(RunListener listener)28     public void addListener(RunListener listener) {
29         if (listener == null) {
30             throw new NullPointerException("Cannot add a null listener");
31         }
32         listeners.add(wrapIfNotThreadSafe(listener));
33     }
34 
35     /**
36      * Internal use only
37      */
removeListener(RunListener listener)38     public void removeListener(RunListener listener) {
39         if (listener == null) {
40             throw new NullPointerException("Cannot remove a null listener");
41         }
42         listeners.remove(wrapIfNotThreadSafe(listener));
43     }
44 
45     /**
46      * Wraps the given listener with {@link SynchronizedRunListener} if
47      * it is not annotated with {@link RunListener.ThreadSafe}.
48      */
wrapIfNotThreadSafe(RunListener listener)49     RunListener wrapIfNotThreadSafe(RunListener listener) {
50         return listener.getClass().isAnnotationPresent(RunListener.ThreadSafe.class) ?
51                 listener : new SynchronizedRunListener(listener, this);
52     }
53 
54 
55     private abstract class SafeNotifier {
56         private final List<RunListener> currentListeners;
57 
SafeNotifier()58         SafeNotifier() {
59             this(listeners);
60         }
61 
SafeNotifier(List<RunListener> currentListeners)62         SafeNotifier(List<RunListener> currentListeners) {
63             this.currentListeners = currentListeners;
64         }
65 
run()66         void run() {
67             int capacity = currentListeners.size();
68             ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
69             ArrayList<Failure> failures = new ArrayList<Failure>(capacity);
70             for (RunListener listener : currentListeners) {
71                 try {
72                     notifyListener(listener);
73                     safeListeners.add(listener);
74                 } catch (Exception e) {
75                     failures.add(new Failure(Description.TEST_MECHANISM, e));
76                 }
77             }
78             fireTestFailures(safeListeners, failures);
79         }
80 
notifyListener(RunListener each)81         abstract protected void notifyListener(RunListener each) throws Exception;
82     }
83 
84     /**
85      * Do not invoke.
86      */
fireTestRunStarted(final Description description)87     public void fireTestRunStarted(final Description description) {
88         new SafeNotifier() {
89             @Override
90             protected void notifyListener(RunListener each) throws Exception {
91                 each.testRunStarted(description);
92             }
93         }.run();
94     }
95 
96     /**
97      * Do not invoke.
98      */
fireTestRunFinished(final Result result)99     public void fireTestRunFinished(final Result result) {
100         new SafeNotifier() {
101             @Override
102             protected void notifyListener(RunListener each) throws Exception {
103                 each.testRunFinished(result);
104             }
105         }.run();
106     }
107 
108     /**
109      * Invoke to tell listeners that an atomic test is about to start.
110      *
111      * @param description the description of the atomic test (generally a class and method name)
112      * @throws StoppedByUserException thrown if a user has requested that the test run stop
113      */
fireTestStarted(final Description description)114     public void fireTestStarted(final Description description) throws StoppedByUserException {
115         if (pleaseStop) {
116             throw new StoppedByUserException();
117         }
118         new SafeNotifier() {
119             @Override
120             protected void notifyListener(RunListener each) throws Exception {
121                 each.testStarted(description);
122             }
123         }.run();
124     }
125 
126     /**
127      * Invoke to tell listeners that an atomic test failed.
128      *
129      * @param failure the description of the test that failed and the exception thrown
130      */
fireTestFailure(Failure failure)131     public void fireTestFailure(Failure failure) {
132         fireTestFailures(listeners, asList(failure));
133     }
134 
fireTestFailures(List<RunListener> listeners, final List<Failure> failures)135     private void fireTestFailures(List<RunListener> listeners,
136             final List<Failure> failures) {
137         if (!failures.isEmpty()) {
138             new SafeNotifier(listeners) {
139                 @Override
140                 protected void notifyListener(RunListener listener) throws Exception {
141                     for (Failure each : failures) {
142                         listener.testFailure(each);
143                     }
144                 }
145             }.run();
146         }
147     }
148 
149     /**
150      * Invoke to tell listeners that an atomic test flagged that it assumed
151      * something false.
152      *
153      * @param failure the description of the test that failed and the
154      * {@link org.junit.AssumptionViolatedException} thrown
155      */
fireTestAssumptionFailed(final Failure failure)156     public void fireTestAssumptionFailed(final Failure failure) {
157         new SafeNotifier() {
158             @Override
159             protected void notifyListener(RunListener each) throws Exception {
160                 each.testAssumptionFailure(failure);
161             }
162         }.run();
163     }
164 
165     /**
166      * Invoke to tell listeners that an atomic test was ignored.
167      *
168      * @param description the description of the ignored test
169      */
fireTestIgnored(final Description description)170     public void fireTestIgnored(final Description description) {
171         new SafeNotifier() {
172             @Override
173             protected void notifyListener(RunListener each) throws Exception {
174                 each.testIgnored(description);
175             }
176         }.run();
177     }
178 
179     /**
180      * Invoke to tell listeners that an atomic test finished. Always invoke
181      * this method if you invoke {@link #fireTestStarted(Description)}
182      * as listeners are likely to expect them to come in pairs.
183      *
184      * @param description the description of the test that finished
185      */
fireTestFinished(final Description description)186     public void fireTestFinished(final Description description) {
187         new SafeNotifier() {
188             @Override
189             protected void notifyListener(RunListener each) throws Exception {
190                 each.testFinished(description);
191             }
192         }.run();
193     }
194 
195     /**
196      * Ask that the tests run stop before starting the next test. Phrased politely because
197      * the test currently running will not be interrupted. It seems a little odd to put this
198      * functionality here, but the <code>RunNotifier</code> is the only object guaranteed
199      * to be shared amongst the many runners involved.
200      */
pleaseStop()201     public void pleaseStop() {
202         pleaseStop = true;
203     }
204 
205     /**
206      * Internal use only. The Result's listener must be first.
207      */
addFirstListener(RunListener listener)208     public void addFirstListener(RunListener listener) {
209         if (listener == null) {
210             throw new NullPointerException("Cannot add a null listener");
211         }
212         listeners.add(0, wrapIfNotThreadSafe(listener));
213     }
214 }
215