1 package org.junit.runner.notification;
2
3 import org.junit.Test;
4 import org.junit.runner.Description;
5
6 import java.util.Random;
7 import java.util.concurrent.Callable;
8 import java.util.concurrent.CountDownLatch;
9 import java.util.concurrent.CyclicBarrier;
10 import java.util.concurrent.Executors;
11 import java.util.concurrent.ExecutorService;
12 import java.util.concurrent.TimeUnit;
13 import java.util.concurrent.atomic.AtomicBoolean;
14 import java.util.concurrent.atomic.AtomicInteger;
15
16 import static org.hamcrest.core.Is.is;
17 import static org.junit.Assert.assertThat;
18 import static org.junit.Assert.assertTrue;
19
20
21
22
23
24
25
26
27 public final class ConcurrentRunNotifierTest {
28 private static final long TIMEOUT = 3;
29 private final RunNotifier fNotifier = new RunNotifier();
30
31 private static class ConcurrentRunListener extends RunListener {
32 final AtomicInteger fTestStarted = new AtomicInteger(0);
33
34 @Override
35 public void testStarted(Description description) throws Exception {
36 fTestStarted.incrementAndGet();
37 }
38 }
39
40 @Test
41 public void realUsage() throws Exception {
42 ConcurrentRunListener listener1 = new ConcurrentRunListener();
43 ConcurrentRunListener listener2 = new ConcurrentRunListener();
44 fNotifier.addListener(listener1);
45 fNotifier.addListener(listener2);
46
47 final int numParallelTests = 4;
48 ExecutorService pool = Executors.newFixedThreadPool(numParallelTests);
49 for (int i = 0; i < numParallelTests; ++i) {
50 pool.submit(new Runnable() {
51 public void run() {
52 fNotifier.fireTestStarted(null);
53 }
54 });
55 }
56 pool.shutdown();
57 assertTrue(pool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
58
59 fNotifier.removeListener(listener1);
60 fNotifier.removeListener(listener2);
61
62 assertThat(listener1.fTestStarted.get(), is(numParallelTests));
63 assertThat(listener2.fTestStarted.get(), is(numParallelTests));
64 }
65
66 private static class ExaminedListener extends RunListener {
67 final boolean throwFromTestStarted;
68 volatile boolean hasTestFailure = false;
69
70 ExaminedListener(boolean throwFromTestStarted) {
71 this.throwFromTestStarted = throwFromTestStarted;
72 }
73
74 @Override
75 public void testStarted(Description description) throws Exception {
76 if (throwFromTestStarted) {
77 throw new Exception();
78 }
79 }
80
81 @Override
82 public void testFailure(Failure failure) throws Exception {
83 hasTestFailure = true;
84 }
85 }
86
87 private abstract class AbstractConcurrentFailuresTest {
88
89 protected abstract void addListener(ExaminedListener listener);
90
91 public void test() throws Exception {
92 int totalListenersFailures = 0;
93
94 Random random = new Random(42);
95 ExaminedListener[] examinedListeners = new ExaminedListener[1000];
96 for (int i = 0; i < examinedListeners.length; ++i) {
97 boolean fail = random.nextDouble() >= 0.5d;
98 if (fail) {
99 ++totalListenersFailures;
100 }
101 examinedListeners[i] = new ExaminedListener(fail);
102 }
103
104 final AtomicBoolean condition = new AtomicBoolean(true);
105 final CyclicBarrier trigger = new CyclicBarrier(2);
106 final CountDownLatch latch = new CountDownLatch(10);
107
108 ExecutorService notificationsPool = Executors.newFixedThreadPool(4);
109 notificationsPool.submit(new Callable<Void>() {
110 public Void call() throws Exception {
111 trigger.await();
112 while (condition.get()) {
113 fNotifier.fireTestStarted(null);
114 latch.countDown();
115 }
116 fNotifier.fireTestStarted(null);
117 return null;
118 }
119 });
120
121
122 trigger.await(TIMEOUT, TimeUnit.SECONDS);
123
124
125 latch.await(TIMEOUT, TimeUnit.SECONDS);
126
127 for (ExaminedListener examinedListener : examinedListeners) {
128 addListener(examinedListener);
129 }
130
131 notificationsPool.shutdown();
132 condition.set(false);
133 assertTrue(notificationsPool.awaitTermination(TIMEOUT, TimeUnit.SECONDS));
134
135 if (totalListenersFailures != 0) {
136
137 int countTestFailures = examinedListeners.length - countReportedTestFailures(examinedListeners);
138 assertThat(totalListenersFailures, is(countTestFailures));
139 }
140 }
141 }
142
143
144
145
146
147 @Test
148 public void reportConcurrentFailuresAfterAddListener() throws Exception {
149 new AbstractConcurrentFailuresTest() {
150 @Override
151 protected void addListener(ExaminedListener listener) {
152 fNotifier.addListener(listener);
153 }
154 }.test();
155 }
156
157
158
159
160
161 @Test
162 public void reportConcurrentFailuresAfterAddFirstListener() throws Exception {
163 new AbstractConcurrentFailuresTest() {
164 @Override
165 protected void addListener(ExaminedListener listener) {
166 fNotifier.addFirstListener(listener);
167 }
168 }.test();
169 }
170
171 private static int countReportedTestFailures(ExaminedListener[] listeners) {
172 int count = 0;
173 for (ExaminedListener listener : listeners) {
174 if (listener.hasTestFailure) {
175 ++count;
176 }
177 }
178 return count;
179 }
180 }