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 ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity); 69 ArrayList<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 abstract protected 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 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 */ fireTestStarted(final Description description)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 */ fireTestFailure(Failure failure)131 public void fireTestFailure(Failure failure) { 132 fireTestFailures(listeners, asList(failure)); 133 } 134 fireTestFailures(List<RunListener> listeners, final List<Failure> failures)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 */ fireTestAssumptionFailed(final Failure failure)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 */ fireTestIgnored(final Description description)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 */ fireTestFinished(final Description description)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 */ pleaseStop()201 public void pleaseStop() { 202 pleaseStop = true; 203 } 204 205 /** 206 * Internal use only. The Result's listener must be first. 207 */ addFirstListener(RunListener listener)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 } 215