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 ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
069 ArrayList<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 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 */
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 */
131 public void fireTestFailure(Failure failure) {
132 fireTestFailures(listeners, asList(failure));
133 }
134
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 */
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 */
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 */
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 */
201 public void pleaseStop() {
202 pleaseStop = true;
203 }
204
205 /**
206 * Internal use only. The Result's listener must be first.
207 */
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 }