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