1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.surefire.booterclient;
20
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayDeque;
28 import java.util.Collections;
29 import java.util.jar.Manifest;
30 import java.util.zip.Deflater;
31
32 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
33 import org.apache.maven.plugin.surefire.SurefireProperties;
34 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractCommandReader;
35 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Commandline;
36 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
37 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
38 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
39 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
40 import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
41 import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
42 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
43 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
44 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
45 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
46 import org.apache.maven.surefire.api.booter.Shutdown;
47 import org.apache.maven.surefire.api.report.ReporterConfiguration;
48 import org.apache.maven.surefire.api.report.ReporterFactoryOptions;
49 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
50 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
51 import org.apache.maven.surefire.booter.Classpath;
52 import org.apache.maven.surefire.booter.PropertiesWrapper;
53 import org.apache.maven.surefire.booter.ProviderConfiguration;
54 import org.apache.maven.surefire.booter.StartupConfiguration;
55 import org.apache.maven.surefire.booter.SurefireBooterForkException;
56 import org.apache.maven.surefire.extensions.ForkNodeFactory;
57 import org.apache.maven.surefire.shared.compress.archivers.zip.Zip64Mode;
58 import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveEntry;
59 import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveOutputStream;
60 import org.junit.AfterClass;
61 import org.junit.BeforeClass;
62 import org.junit.Rule;
63 import org.junit.Test;
64 import org.junit.rules.ExpectedException;
65
66 import static org.apache.commons.io.FileUtils.deleteQuietly;
67 import static org.hamcrest.Matchers.containsString;
68 import static org.mockito.ArgumentMatchers.eq;
69 import static org.mockito.Mockito.mock;
70 import static org.mockito.Mockito.when;
71 import static org.powermock.reflect.Whitebox.invokeMethod;
72
73
74
75
76 public class ForkStarterTest {
77 private static String baseDir = System.getProperty("user.dir");
78 private static File tmp;
79
80 @Rule
81 public final ExpectedException e = ExpectedException.none();
82
83 @BeforeClass
84 public static void prepareFiles() throws IOException {
85 File target = new File(baseDir, "target");
86 tmp = new File(target, "tmp");
87 tmp.mkdirs();
88 File booter = new File(tmp, "surefirebooter.jar");
89 booter.createNewFile();
90
91 try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(new FileOutputStream(booter))) {
92 zos.setUseZip64(Zip64Mode.Never);
93 zos.setLevel(Deflater.NO_COMPRESSION);
94
95 ZipArchiveEntry ze = new ZipArchiveEntry("META-INF/MANIFEST.MF");
96 zos.putArchiveEntry(ze);
97
98 Manifest man = new Manifest();
99
100 man.getMainAttributes().putValue("Manifest-Version", "1.0");
101 man.getMainAttributes().putValue("Main-Class", MainClass.class.getName());
102
103 man.write(zos);
104
105 zos.closeArchiveEntry();
106
107 ze = new ZipArchiveEntry("org/apache/maven/plugin/surefire/booterclient/MainClass.class");
108 zos.putArchiveEntry(ze);
109 String classesDir = Paths.get(target.getPath(), "test-classes").toString();
110 Path cls = Paths.get(
111 classesDir, "org", "apache", "maven", "plugin", "surefire", "booterclient", "MainClass.class");
112 zos.write(Files.readAllBytes(cls));
113 zos.closeArchiveEntry();
114 }
115 }
116
117 @AfterClass
118 public static void deleteTmp() {
119 deleteQuietly(tmp);
120 }
121
122 @Test
123 public void processShouldExitWithoutSayingGoodBye() throws Exception {
124 ReporterConfiguration reporterConfiguration = new ReporterConfiguration(tmp, true);
125
126 ProviderConfiguration providerConfiguration = mock(ProviderConfiguration.class);
127 when(providerConfiguration.getReporterConfiguration()).thenReturn(reporterConfiguration);
128 when(providerConfiguration.getShutdown()).thenReturn(Shutdown.EXIT);
129
130 StartupConfiguration startupConfiguration = mock(StartupConfiguration.class);
131 when(startupConfiguration.getClasspathConfiguration()).thenReturn(new ClasspathConfig());
132 when(startupConfiguration.getClassLoaderConfiguration()).thenReturn(new ClassLoaderConfiguration(false, false));
133 when(startupConfiguration.getProviderClassName()).thenReturn(MainClass.class.getName());
134
135 ForkConfiguration forkConfiguration = mock(ForkConfiguration.class);
136 when(forkConfiguration.getWorkingDirectory()).thenReturn(tmp);
137 when(forkConfiguration.getTempDirectory()).thenReturn(tmp);
138 when(forkConfiguration.getPluginPlatform()).thenReturn(new Platform());
139 Commandline cli = new Commandline();
140 cli.setWorkingDirectory(tmp);
141 cli.setExecutable(System.getProperty("java.home") + "/bin/java");
142 cli.createArg().setLine("-jar");
143 cli.createArg().setLine("surefirebooter.jar");
144 cli.createArg().setLine("fail");
145 when(forkConfiguration.createCommandLine(eq(startupConfiguration), eq(1), eq(tmp)))
146 .thenReturn(cli);
147
148 SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
149 SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
150 SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter(true, "3");
151
152 StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration(
153 true,
154 true,
155 null,
156 false,
157 tmp,
158 true,
159 "",
160 null,
161 false,
162 0,
163 null,
164 null,
165 true,
166 true,
167 true,
168 xmlReporter,
169 outputReporter,
170 statelessTestsetInfoReporter,
171 new ReporterFactoryOptions());
172
173 ConsoleLogger logger = mock(ConsoleLogger.class);
174
175 ForkStarter forkStarter = new ForkStarter(
176 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
177
178 DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
179
180 e.expect(SurefireBooterForkException.class);
181 e.expectMessage(containsString("Process Exit Code: 1"));
182 e.expectMessage(containsString("The forked VM terminated without properly saying goodbye."));
183 e.expectMessage(containsString("VM crash or System.exit called?"));
184
185 Class<?>[] types = {
186 Object.class,
187 PropertiesWrapper.class,
188 ForkClient.class,
189 SurefireProperties.class,
190 int.class,
191 AbstractCommandReader.class,
192 ForkNodeFactory.class,
193 boolean.class
194 };
195 TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream(new ArrayDeque<String>());
196 invokeMethod(
197 forkStarter,
198 "fork",
199 types,
200 null,
201 new PropertiesWrapper(Collections.<String, String>emptyMap()),
202 new ForkClient(reporterFactory, null, 1),
203 new SurefireProperties(),
204 1,
205 testProvidingInputStream,
206 new LegacyForkNodeFactory(),
207 true);
208 testProvidingInputStream.close();
209 }
210
211 @Test
212 public void processShouldWaitForAck() throws Exception {
213 ReporterConfiguration reporterConfiguration = new ReporterConfiguration(tmp, true);
214
215 ProviderConfiguration providerConfiguration = mock(ProviderConfiguration.class);
216 when(providerConfiguration.getReporterConfiguration()).thenReturn(reporterConfiguration);
217 when(providerConfiguration.getShutdown()).thenReturn(Shutdown.EXIT);
218
219 StartupConfiguration startupConfiguration = mock(StartupConfiguration.class);
220 when(startupConfiguration.getClasspathConfiguration()).thenReturn(new ClasspathConfig());
221 when(startupConfiguration.getClassLoaderConfiguration()).thenReturn(new ClassLoaderConfiguration(false, false));
222 when(startupConfiguration.getProviderClassName()).thenReturn(MainClass.class.getName());
223
224 ForkConfiguration forkConfiguration = mock(ForkConfiguration.class);
225 when(forkConfiguration.getWorkingDirectory()).thenReturn(tmp);
226 when(forkConfiguration.getTempDirectory()).thenReturn(tmp);
227 when(forkConfiguration.getPluginPlatform()).thenReturn(new Platform());
228 Commandline cli = new Commandline();
229 cli.setWorkingDirectory(tmp);
230 cli.setExecutable(System.getProperty("java.home") + "/bin/java");
231 cli.createArg().setLine("-jar");
232 cli.createArg().setLine("surefirebooter.jar");
233 when(forkConfiguration.createCommandLine(eq(startupConfiguration), eq(1), eq(tmp)))
234 .thenReturn(cli);
235
236 SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
237 SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
238 SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter(true, "3");
239
240 StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration(
241 true,
242 true,
243 null,
244 false,
245 tmp,
246 true,
247 "",
248 null,
249 false,
250 0,
251 null,
252 null,
253 true,
254 true,
255 true,
256 xmlReporter,
257 outputReporter,
258 statelessTestsetInfoReporter,
259 new ReporterFactoryOptions());
260
261 ConsoleLogger logger = mock(ConsoleLogger.class);
262
263 ForkStarter forkStarter = new ForkStarter(
264 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
265
266 DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
267
268 Class<?>[] types = {
269 Object.class,
270 PropertiesWrapper.class,
271 ForkClient.class,
272 SurefireProperties.class,
273 int.class,
274 AbstractCommandReader.class,
275 ForkNodeFactory.class,
276 boolean.class
277 };
278 TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
279 invokeMethod(
280 forkStarter,
281 "fork",
282 types,
283 null,
284 new PropertiesWrapper(Collections.<String, String>emptyMap()),
285 new ForkClient(reporterFactory, testLessInputStream, 1),
286 new SurefireProperties(),
287 1,
288 testLessInputStream,
289 new LegacyForkNodeFactory(),
290 true);
291 testLessInputStream.close();
292 }
293
294 private static class ClasspathConfig extends AbstractPathConfiguration {
295 ClasspathConfig() {
296 this(Classpath.emptyClasspath(), false, false);
297 }
298
299 private ClasspathConfig(Classpath surefireClasspathUrls, boolean enableAssertions, boolean childDelegation) {
300 super(surefireClasspathUrls, enableAssertions, childDelegation);
301 }
302
303 @Override
304 public Classpath getTestClasspath() {
305 return Classpath.emptyClasspath();
306 }
307
308 @Override
309 public boolean isModularPathConfig() {
310 return false;
311 }
312
313 @Override
314 public boolean isClassPathConfig() {
315 return true;
316 }
317
318 @Override
319 protected Classpath getInprocClasspath() {
320 return Classpath.emptyClasspath();
321 }
322 }
323 }