001package org.junit.runner.notification;
002
003import static java.util.Arrays.asList;
004
005import java.util.ArrayList;
006import java.util.List;
007import java.util.concurrent.CopyOnWriteArrayList;
008
009import org.junit.runner.Description;
010import org.junit.runner.Result;
011
012/**
013 * If you write custom runners, you may need to notify JUnit of your progress running tests.
014 * Do this by invoking the <code>RunNotifier</code> passed to your implementation of
015 * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to
016 * move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)}
017 * to a separate class since they should only be called once per run.
018 *
019 * @since 4.0
020 */
021public class RunNotifier {
022    private final List<RunListener> listeners = new CopyOnWriteArrayList<RunListener>();
023    private volatile boolean pleaseStop = false;
024
025    /**
026     * Internal use only
027     */
028    public void addListener(RunListener listener) {
029        if (listener == null) {
030            throw new NullPointerException("Cannot add a null listener");
031        }
032        listeners.add(wrapIfNotThreadSafe(listener));
033    }
034
035    /**
036     * Internal use only
037     */
038    public void removeListener(RunListener listener) {
039        if (listener == null) {
040            throw new NullPointerException("Cannot remove a null listener");
041        }
042        listeners.remove(wrapIfNotThreadSafe(listener));
043    }
044
045    /**
046     * Wraps the given listener with {@link SynchronizedRunListener} if
047     * it is not annotated with {@link RunListener.ThreadSafe}.
048     */
049    RunListener wrapIfNotThreadSafe(RunListener listener) {
050        return listener.getClass().isAnnotationPresent(RunListener.ThreadSafe.class) ?
051                listener : new SynchronizedRunListener(listener, this);
052    }
053
054
055    private abstract class SafeNotifier {
056        private final List<RunListener> currentListeners;
057
058        SafeNotifier() {
059            this(listeners);
060        }
061
062        SafeNotifier(List<RunListener> currentListeners) {
063            this.currentListeners = currentListeners;
064        }
065
066        void run() {
067            int capacity = currentListeners.size();
068            List<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
069            List<Failure> failures = new ArrayList<Failure>(capacity);
070            for (RunListener listener : currentListeners) {
071                try {
072                    notifyListener(listener);
073                    safeListeners.add(listener);
074                } catch (Exception e) {
075                    failures.add(new Failure(Description.TEST_MECHANISM, e));
076                }
077            }
078            fireTestFailures(safeListeners, failures);
079        }
080
081        protected abstract void notifyListener(RunListener each) throws Exception;
082    }
083
084    /**
085     * Do not invoke.
086     */
087    public void fireTestRunStarted(final Description description) {
088        new SafeNotifier() {
089            @Override
090            protected void notifyListener(RunListener each) throws Exception {
091                each.testRunStarted(description);
092            }
093        }.run();
094    }
095
096    /**
097     * Do not invoke.
098     */
099    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     */
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     */
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     */
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     */
166    public void fireTestFailure(Failure failure) {
167        fireTestFailures(listeners, asList(failure));
168    }
169
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     */
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     */
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     */
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     */
236    public void pleaseStop() {
237        pleaseStop = true;
238    }
239
240    /**
241     * Internal use only. The Result's listener must be first.
242     */
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}