001package org.junit.runner.notification;
002
003import static java.util.Arrays.asList;
004
005import java.util.ArrayList;
006import java.util.Collections;
007import java.util.Iterator;
008import java.util.List;
009
010import org.junit.internal.AssumptionViolatedException;
011import org.junit.runner.Description;
012import org.junit.runner.Result;
013
014/**
015 * If you write custom runners, you may need to notify JUnit of your progress running tests.
016 * Do this by invoking the <code>RunNotifier</code> passed to your implementation of
017 * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to
018 * move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)}
019 * to a separate class since they should only be called once per run.
020 *
021 * @since 4.0
022 */
023public class RunNotifier {
024    private final List<RunListener> fListeners =
025            Collections.synchronizedList(new ArrayList<RunListener>());
026    private volatile boolean fPleaseStop = false;
027
028    /**
029     * Internal use only
030     */
031    public void addListener(RunListener listener) {
032        fListeners.add(listener);
033    }
034
035    /**
036     * Internal use only
037     */
038    public void removeListener(RunListener listener) {
039        fListeners.remove(listener);
040    }
041
042    private abstract class SafeNotifier {
043        private final List<RunListener> fCurrentListeners;
044
045        SafeNotifier() {
046            this(fListeners);
047        }
048
049        SafeNotifier(List<RunListener> currentListeners) {
050            fCurrentListeners = currentListeners;
051        }
052
053        void run() {
054            synchronized (fListeners) {
055                List<RunListener> safeListeners = new ArrayList<RunListener>();
056                List<Failure> failures = new ArrayList<Failure>();
057                for (Iterator<RunListener> all = fCurrentListeners.iterator(); all
058                        .hasNext(); ) {
059                    try {
060                        RunListener listener = all.next();
061                        notifyListener(listener);
062                        safeListeners.add(listener);
063                    } catch (Exception e) {
064                        failures.add(new Failure(Description.TEST_MECHANISM, e));
065                    }
066                }
067                fireTestFailures(safeListeners, failures);
068            }
069        }
070
071        abstract protected void notifyListener(RunListener each) throws Exception;
072    }
073
074    /**
075     * Do not invoke.
076     */
077    public void fireTestRunStarted(final Description description) {
078        new SafeNotifier() {
079            @Override
080            protected void notifyListener(RunListener each) throws Exception {
081                each.testRunStarted(description);
082            }
083
084            ;
085        }.run();
086    }
087
088    /**
089     * Do not invoke.
090     */
091    public void fireTestRunFinished(final Result result) {
092        new SafeNotifier() {
093            @Override
094            protected void notifyListener(RunListener each) throws Exception {
095                each.testRunFinished(result);
096            }
097
098            ;
099        }.run();
100    }
101
102    /**
103     * Invoke to tell listeners that an atomic test is about to start.
104     *
105     * @param description the description of the atomic test (generally a class and method name)
106     * @throws StoppedByUserException thrown if a user has requested that the test run stop
107     */
108    public void fireTestStarted(final Description description) throws StoppedByUserException {
109        if (fPleaseStop) {
110            throw new StoppedByUserException();
111        }
112        new SafeNotifier() {
113            @Override
114            protected void notifyListener(RunListener each) throws Exception {
115                each.testStarted(description);
116            }
117
118            ;
119        }.run();
120    }
121
122    /**
123     * Invoke to tell listeners that an atomic test failed.
124     *
125     * @param failure the description of the test that failed and the exception thrown
126     */
127    public void fireTestFailure(Failure failure) {
128        fireTestFailures(fListeners, asList(failure));
129    }
130
131    private void fireTestFailures(List<RunListener> listeners,
132            final List<Failure> failures) {
133        if (!failures.isEmpty()) {
134            new SafeNotifier(listeners) {
135                @Override
136                protected void notifyListener(RunListener listener)
137                        throws Exception {
138                    for (Failure each : failures) {
139                        listener.testFailure(each);
140                    }
141                }
142
143                ;
144            }.run();
145        }
146    }
147
148    /**
149     * Invoke to tell listeners that an atomic test flagged that it assumed
150     * something false.
151     *
152     * @param failure the description of the test that failed and the
153     * {@link AssumptionViolatedException} thrown
154     */
155    public void fireTestAssumptionFailed(final Failure failure) {
156        new SafeNotifier() {
157            @Override
158            protected void notifyListener(RunListener each) throws Exception {
159                each.testAssumptionFailure(failure);
160            }
161
162            ;
163        }.run();
164    }
165
166    /**
167     * Invoke to tell listeners that an atomic test was ignored.
168     *
169     * @param description the description of the ignored test
170     */
171    public void fireTestIgnored(final Description description) {
172        new SafeNotifier() {
173            @Override
174            protected void notifyListener(RunListener each) throws Exception {
175                each.testIgnored(description);
176            }
177        }.run();
178    }
179
180    /**
181     * Invoke to tell listeners that an atomic test finished. Always invoke
182     * {@link #fireTestFinished(Description)} if you invoke {@link #fireTestStarted(Description)}
183     * as listeners are likely to expect them to come in pairs.
184     *
185     * @param description the description of the test that finished
186     */
187    public void fireTestFinished(final Description description) {
188        new SafeNotifier() {
189            @Override
190            protected void notifyListener(RunListener each) throws Exception {
191                each.testFinished(description);
192            }
193
194            ;
195        }.run();
196    }
197
198    /**
199     * Ask that the tests run stop before starting the next test. Phrased politely because
200     * the test currently running will not be interrupted. It seems a little odd to put this
201     * functionality here, but the <code>RunNotifier</code> is the only object guaranteed
202     * to be shared amongst the many runners involved.
203     */
204    public void pleaseStop() {
205        fPleaseStop = true;
206    }
207
208    /**
209     * Internal use only. The Result's listener must be first.
210     */
211    public void addFirstListener(RunListener listener) {
212        fListeners.add(0, listener);
213    }
214}