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 }