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 }