001package org.junit.runner.notification; 002 003import static java.util.Arrays.asList; 004 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.Iterator; 008import java.util.List; 009 010import org.junit.internal.AssumptionViolatedException; 011import org.junit.runner.Description; 012import org.junit.runner.Result; 013 014/** 015 * If you write custom runners, you may need to notify JUnit of your progress running tests. 016 * Do this by invoking the <code>RunNotifier</code> passed to your implementation of 017 * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to 018 * move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)} 019 * to a separate class since they should only be called once per run. 020 * 021 * @since 4.0 022 */ 023public class RunNotifier { 024 private final List<RunListener> fListeners = 025 Collections.synchronizedList(new ArrayList<RunListener>()); 026 private volatile boolean fPleaseStop = false; 027 028 /** 029 * Internal use only 030 */ 031 public void addListener(RunListener listener) { 032 fListeners.add(listener); 033 } 034 035 /** 036 * Internal use only 037 */ 038 public void removeListener(RunListener listener) { 039 fListeners.remove(listener); 040 } 041 042 private abstract class SafeNotifier { 043 private final List<RunListener> fCurrentListeners; 044 045 SafeNotifier() { 046 this(fListeners); 047 } 048 049 SafeNotifier(List<RunListener> currentListeners) { 050 fCurrentListeners = currentListeners; 051 } 052 053 void run() { 054 synchronized (fListeners) { 055 List<RunListener> safeListeners = new ArrayList<RunListener>(); 056 List<Failure> failures = new ArrayList<Failure>(); 057 for (Iterator<RunListener> all = fCurrentListeners.iterator(); all 058 .hasNext(); ) { 059 try { 060 RunListener listener = all.next(); 061 notifyListener(listener); 062 safeListeners.add(listener); 063 } catch (Exception e) { 064 failures.add(new Failure(Description.TEST_MECHANISM, e)); 065 } 066 } 067 fireTestFailures(safeListeners, failures); 068 } 069 } 070 071 abstract protected void notifyListener(RunListener each) throws Exception; 072 } 073 074 /** 075 * Do not invoke. 076 */ 077 public void fireTestRunStarted(final Description description) { 078 new SafeNotifier() { 079 @Override 080 protected void notifyListener(RunListener each) throws Exception { 081 each.testRunStarted(description); 082 } 083 084 ; 085 }.run(); 086 } 087 088 /** 089 * Do not invoke. 090 */ 091 public void fireTestRunFinished(final Result result) { 092 new SafeNotifier() { 093 @Override 094 protected void notifyListener(RunListener each) throws Exception { 095 each.testRunFinished(result); 096 } 097 098 ; 099 }.run(); 100 } 101 102 /** 103 * Invoke to tell listeners that an atomic test is about to start. 104 * 105 * @param description the description of the atomic test (generally a class and method name) 106 * @throws StoppedByUserException thrown if a user has requested that the test run stop 107 */ 108 public void fireTestStarted(final Description description) throws StoppedByUserException { 109 if (fPleaseStop) { 110 throw new StoppedByUserException(); 111 } 112 new SafeNotifier() { 113 @Override 114 protected void notifyListener(RunListener each) throws Exception { 115 each.testStarted(description); 116 } 117 118 ; 119 }.run(); 120 } 121 122 /** 123 * Invoke to tell listeners that an atomic test failed. 124 * 125 * @param failure the description of the test that failed and the exception thrown 126 */ 127 public void fireTestFailure(Failure failure) { 128 fireTestFailures(fListeners, asList(failure)); 129 } 130 131 private void fireTestFailures(List<RunListener> listeners, 132 final List<Failure> failures) { 133 if (!failures.isEmpty()) { 134 new SafeNotifier(listeners) { 135 @Override 136 protected void notifyListener(RunListener listener) 137 throws Exception { 138 for (Failure each : failures) { 139 listener.testFailure(each); 140 } 141 } 142 143 ; 144 }.run(); 145 } 146 } 147 148 /** 149 * Invoke to tell listeners that an atomic test flagged that it assumed 150 * something false. 151 * 152 * @param failure the description of the test that failed and the 153 * {@link AssumptionViolatedException} thrown 154 */ 155 public void fireTestAssumptionFailed(final Failure failure) { 156 new SafeNotifier() { 157 @Override 158 protected void notifyListener(RunListener each) throws Exception { 159 each.testAssumptionFailure(failure); 160 } 161 162 ; 163 }.run(); 164 } 165 166 /** 167 * Invoke to tell listeners that an atomic test was ignored. 168 * 169 * @param description the description of the ignored test 170 */ 171 public void fireTestIgnored(final Description description) { 172 new SafeNotifier() { 173 @Override 174 protected void notifyListener(RunListener each) throws Exception { 175 each.testIgnored(description); 176 } 177 }.run(); 178 } 179 180 /** 181 * Invoke to tell listeners that an atomic test finished. Always invoke 182 * {@link #fireTestFinished(Description)} if you invoke {@link #fireTestStarted(Description)} 183 * as listeners are likely to expect them to come in pairs. 184 * 185 * @param description the description of the test that finished 186 */ 187 public void fireTestFinished(final Description description) { 188 new SafeNotifier() { 189 @Override 190 protected void notifyListener(RunListener each) throws Exception { 191 each.testFinished(description); 192 } 193 194 ; 195 }.run(); 196 } 197 198 /** 199 * Ask that the tests run stop before starting the next test. Phrased politely because 200 * the test currently running will not be interrupted. It seems a little odd to put this 201 * functionality here, but the <code>RunNotifier</code> is the only object guaranteed 202 * to be shared amongst the many runners involved. 203 */ 204 public void pleaseStop() { 205 fPleaseStop = true; 206 } 207 208 /** 209 * Internal use only. The Result's listener must be first. 210 */ 211 public void addFirstListener(RunListener listener) { 212 fListeners.add(0, listener); 213 } 214}