• 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             List<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
69             List<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         protected abstract 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 a test suite is about to start. Runners are strongly
110      * encouraged--but not required--to call this method. If this method is called for
111      * a given {@link Description} then {@link #fireTestSuiteFinished(Description)} MUST
112      * be called for the same {@code Description}.
113      *
114      * @param description the description of the suite test (generally a class name)
115      * @since 4.13
116      */
fireTestSuiteStarted(final Description description)117     public void fireTestSuiteStarted(final Description description) {
118         new SafeNotifier() {
119             @Override
120             protected void notifyListener(RunListener each) throws Exception {
121                 each.testSuiteStarted(description);
122             }
123         }.run();
124     }
125 
126     /**
127      * Invoke to tell listeners that a test suite is about to finish. Always invoke
128      * this method if you invoke {@link #fireTestSuiteStarted(Description)}
129      * as listeners are likely to expect them to come in pairs.
130      *
131      * @param description the description of the suite test (generally a class name)
132      * @since 4.13
133      */
fireTestSuiteFinished(final Description description)134     public void fireTestSuiteFinished(final Description description) {
135         new SafeNotifier() {
136             @Override
137             protected void notifyListener(RunListener each) throws Exception {
138                 each.testSuiteFinished(description);
139             }
140         }.run();
141     }
142 
143     /**
144      * Invoke to tell listeners that an atomic test is about to start.
145      *
146      * @param description the description of the atomic test (generally a class and method name)
147      * @throws StoppedByUserException thrown if a user has requested that the test run stop
148      */
fireTestStarted(final Description description)149     public void fireTestStarted(final Description description) throws StoppedByUserException {
150         if (pleaseStop) {
151             throw new StoppedByUserException();
152         }
153         new SafeNotifier() {
154             @Override
155             protected void notifyListener(RunListener each) throws Exception {
156                 each.testStarted(description);
157             }
158         }.run();
159     }
160 
161     /**
162      * Invoke to tell listeners that an atomic test failed.
163      *
164      * @param failure the description of the test that failed and the exception thrown
165      */
fireTestFailure(Failure failure)166     public void fireTestFailure(Failure failure) {
167         fireTestFailures(listeners, asList(failure));
168     }
169 
fireTestFailures(List<RunListener> listeners, final List<Failure> failures)170     private void fireTestFailures(List<RunListener> listeners,
171             final List<Failure> failures) {
172         if (!failures.isEmpty()) {
173             new SafeNotifier(listeners) {
174                 @Override
175                 protected void notifyListener(RunListener listener) throws Exception {
176                     for (Failure each : failures) {
177                         listener.testFailure(each);
178                     }
179                 }
180             }.run();
181         }
182     }
183 
184     /**
185      * Invoke to tell listeners that an atomic test flagged that it assumed
186      * something false.
187      *
188      * @param failure the description of the test that failed and the
189      * {@link org.junit.AssumptionViolatedException} thrown
190      */
fireTestAssumptionFailed(final Failure failure)191     public void fireTestAssumptionFailed(final Failure failure) {
192         new SafeNotifier() {
193             @Override
194             protected void notifyListener(RunListener each) throws Exception {
195                 each.testAssumptionFailure(failure);
196             }
197         }.run();
198     }
199 
200     /**
201      * Invoke to tell listeners that an atomic test was ignored.
202      *
203      * @param description the description of the ignored test
204      */
fireTestIgnored(final Description description)205     public void fireTestIgnored(final Description description) {
206         new SafeNotifier() {
207             @Override
208             protected void notifyListener(RunListener each) throws Exception {
209                 each.testIgnored(description);
210             }
211         }.run();
212     }
213 
214     /**
215      * Invoke to tell listeners that an atomic test finished. Always invoke
216      * this method if you invoke {@link #fireTestStarted(Description)}
217      * as listeners are likely to expect them to come in pairs.
218      *
219      * @param description the description of the test that finished
220      */
fireTestFinished(final Description description)221     public void fireTestFinished(final Description description) {
222         new SafeNotifier() {
223             @Override
224             protected void notifyListener(RunListener each) throws Exception {
225                 each.testFinished(description);
226             }
227         }.run();
228     }
229 
230     /**
231      * Ask that the tests run stop before starting the next test. Phrased politely because
232      * the test currently running will not be interrupted. It seems a little odd to put this
233      * functionality here, but the <code>RunNotifier</code> is the only object guaranteed
234      * to be shared amongst the many runners involved.
235      */
pleaseStop()236     public void pleaseStop() {
237         pleaseStop = true;
238     }
239 
240     /**
241      * Internal use only. The Result's listener must be first.
242      */
addFirstListener(RunListener listener)243     public void addFirstListener(RunListener listener) {
244         if (listener == null) {
245             throw new NullPointerException("Cannot add a null listener");
246         }
247         listeners.add(0, wrapIfNotThreadSafe(listener));
248     }
249 }
250