1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.surefire.booter;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.PrintStream;
25 import java.util.Iterator;
26 import java.util.NoSuchElementException;
27 import java.util.concurrent.BlockingQueue;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.FutureTask;
31 import java.util.concurrent.LinkedBlockingQueue;
32 import java.util.concurrent.TimeUnit;
33
34 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
35 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
36 import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
37 import org.apache.maven.surefire.api.booter.Shutdown;
38 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
39 import org.apache.maven.surefire.api.testset.TestSetFailedException;
40 import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
41 import org.apache.maven.surefire.booter.spi.CommandChannelDecoder;
42 import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47
48 import static java.nio.charset.StandardCharsets.US_ASCII;
49 import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
50 import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.hamcrest.MatcherAssert.assertThat;
53 import static org.hamcrest.Matchers.is;
54 import static org.junit.Assert.assertFalse;
55 import static org.junit.Assert.assertTrue;
56 import static org.junit.Assert.fail;
57
58
59
60
61
62
63
64 @RunWith(NewClassLoaderRunner.class)
65 @SuppressWarnings("checkstyle:magicnumber")
66 public class CommandReaderTest {
67 private static final long DELAY = 200L;
68 private static final long TEST_TIMEOUT = 15_000L;
69
70 private final BlockingQueue<Byte> blockingStream = new LinkedBlockingQueue<>();
71 private CommandReader reader;
72
73 static class A {}
74
75 static class B {}
76
77 static class C {}
78
79 static class D {}
80
81 @Before
82 public void init() {
83
84 Thread.interrupted();
85 InputStream realInputStream = new SystemInputStream();
86 addTestToPipeline(getClass().getName());
87 ConsoleLogger logger = new NullConsoleLogger();
88 ForkNodeArguments args = new ForkedNodeArg(1, false);
89 MasterProcessChannelDecoder decoder = new CommandChannelDecoder(newChannel(realInputStream), args);
90 reader = new CommandReader(decoder, Shutdown.DEFAULT, logger);
91 }
92
93 @After
94 public void deinit() {
95 reader.stop();
96 }
97
98 @Test
99 public void readJustOneClass() {
100 Iterator<String> it =
101 reader.getIterableClasses(new EventChannelEncoder(nul())).iterator();
102 assertTrue(it.hasNext());
103 assertThat(it.next(), is(getClass().getName()));
104 reader.stop();
105 assertFalse(it.hasNext());
106 try {
107 it.next();
108 fail();
109 } catch (NoSuchElementException e) {
110
111 }
112 }
113
114 @Test
115 public void manyClasses() {
116 Iterator<String> it1 =
117 reader.getIterableClasses(new EventChannelEncoder(nul())).iterator();
118 assertThat(it1.next(), is(getClass().getName()));
119 addTestToPipeline(A.class.getName());
120 assertThat(it1.next(), is(A.class.getName()));
121 addTestToPipeline(B.class.getName());
122 assertThat(it1.next(), is(B.class.getName()));
123 addTestToPipeline(C.class.getName());
124 assertThat(it1.next(), is(C.class.getName()));
125 addEndOfPipeline();
126 addTestToPipeline(D.class.getName());
127 assertFalse(it1.hasNext());
128 }
129
130 @Test
131 public void twoIterators() throws Exception {
132 Iterator<String> it1 =
133 reader.getIterableClasses(new EventChannelEncoder(nul())).iterator();
134
135 assertThat(it1.next(), is(getClass().getName()));
136 addTestToPipeline(A.class.getName());
137 assertThat(it1.next(), is(A.class.getName()));
138 addTestToPipeline(B.class.getName());
139
140 TimeUnit.MILLISECONDS.sleep(DELAY);
141
142 Iterator<String> it2 = reader.iterated();
143
144 assertThat(it1.next(), is(B.class.getName()));
145 addTestToPipeline(C.class.getName());
146
147 assertThat(it2.hasNext(), is(true));
148 assertThat(it2.next(), is(getClass().getName()));
149 assertThat(it2.hasNext(), is(true));
150 assertThat(it2.next(), is(A.class.getName()));
151 assertThat(it2.hasNext()).isFalse();
152
153 assertThat(it1.next(), is(C.class.getName()));
154 addEndOfPipeline();
155 assertThat(it1.hasNext()).isFalse();
156 }
157
158 @Test(expected = NoSuchElementException.class)
159 public void stopBeforeReadInThread() throws Throwable {
160 Runnable runnable = new Runnable() {
161 @Override
162 public void run() {
163 Iterator<String> it = reader.getIterableClasses(new EventChannelEncoder(nul()))
164 .iterator();
165 assertThat(it.next(), is(CommandReaderTest.class.getName()));
166 }
167 };
168 FutureTask<Object> futureTask = new FutureTask<>(runnable, null);
169 Thread t = new Thread(futureTask);
170 reader.stop();
171 t.start();
172 try {
173 futureTask.get();
174 } catch (ExecutionException e) {
175 throw e.getCause();
176 }
177 }
178
179 @Test
180 public void readTwoClassesInThread() throws Throwable {
181 final CountDownLatch counter = new CountDownLatch(1);
182 Runnable runnable = new Runnable() {
183 @Override
184 public void run() {
185 Iterator<String> it = reader.getIterableClasses(new EventChannelEncoder(nul()))
186 .iterator();
187 assertThat(it.next(), is(CommandReaderTest.class.getName()));
188 counter.countDown();
189 assertThat(it.next(), is(Foo.class.getName()));
190 }
191 };
192 FutureTask<Object> futureTask = new FutureTask<>(runnable, null);
193 Thread t = new Thread(futureTask);
194 t.start();
195 counter.await();
196 addTestToPipeline(Foo.class.getName());
197 try {
198 futureTask.get();
199 } catch (ExecutionException e) {
200 throw e.getCause();
201 }
202 }
203
204 @Test(timeout = TEST_TIMEOUT)
205 public void shouldAwaitReaderUp() throws TestSetFailedException {
206 assertTrue(reader.awaitStarted());
207 reader.stop();
208 assertFalse(reader.awaitStarted());
209 }
210
211 private class SystemInputStream extends InputStream {
212 @Override
213 public int read() throws IOException {
214 try {
215 return CommandReaderTest.this.blockingStream.take();
216 } catch (InterruptedException e) {
217 throw new IOException(e);
218 }
219 }
220 }
221
222 private void addTestToPipeline(String cls) {
223 int clsLength = cls.length();
224 String cmd = new StringBuilder(512)
225 .append(":maven-surefire-command:")
226 .append((char) 13)
227 .append(":run-testclass:")
228 .append((char) 5)
229 .append(":UTF-8:")
230 .append((char) (clsLength >> 24))
231 .append((char) ((clsLength >> 16) & 0xff))
232 .append((char) ((clsLength >> 8) & 0xff))
233 .append((char) (clsLength & 0xff))
234 .append(":")
235 .append(cls)
236 .append(":")
237 .toString();
238
239 for (byte cmdByte : cmd.getBytes(US_ASCII)) {
240 blockingStream.add(cmdByte);
241 }
242 }
243
244 private void addEndOfPipeline() {
245 for (byte cmdByte : (":maven-surefire-command:" + (char) 16 + ":testset-finished:").getBytes(US_ASCII)) {
246 blockingStream.add(cmdByte);
247 }
248 }
249
250 private static WritableBufferedByteChannel nul() {
251 return newBufferedChannel(new PrintStream(new ByteArrayOutputStream()));
252 }
253 }