    001    package org.junit.runner.notification;
    002
    003    import static java.util.Arrays.asList;
    004
    005    import java.util.ArrayList;
    006    import java.util.List;
    007    import java.util.concurrent.CopyOnWriteArrayList;
    008
    009    import org.junit.runner.Description;
    010    import 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     */
    021    public 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            abstract protected 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    }