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 false,
169 xmlReporter,
170 outputReporter,
171 statelessTestsetInfoReporter,
172 new ReporterFactoryOptions());
173
174 ConsoleLogger logger = mock(ConsoleLogger.class);
175
176 ForkStarter forkStarter = new ForkStarter(
177 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
178
179 DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
180
181 e.expect(SurefireBooterForkException.class);
182 e.expectMessage(containsString("Process Exit Code: 1"));
183 e.expectMessage(containsString("The forked VM terminated without properly saying goodbye."));
184 e.expectMessage(containsString("VM crash or System.exit called?"));
185
186 Class<?>[] types = {
187 Object.class,
188 PropertiesWrapper.class,
189 ForkClient.class,
190 SurefireProperties.class,
191 int.class,
192 AbstractCommandReader.class,
193 ForkNodeFactory.class,
194 boolean.class
195 };
196 TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream(new ArrayDeque<String>());
197 invokeMethod(
198 forkStarter,
199 "fork",
200 types,
201 null,
202 new PropertiesWrapper(Collections.<String, String>emptyMap()),
203 new ForkClient(reporterFactory, null, 1),
204 new SurefireProperties(),
205 1,
206 testProvidingInputStream,
207 new LegacyForkNodeFactory(),
208 true);
209 testProvidingInputStream.close();
210 }
211
212 @Test
213 public void processShouldWaitForAck() throws Exception {
214 ReporterConfiguration reporterConfiguration = new ReporterConfiguration(tmp, true);
215
216 ProviderConfiguration providerConfiguration = mock(ProviderConfiguration.class);
217 when(providerConfiguration.getReporterConfiguration()).thenReturn(reporterConfiguration);
218 when(providerConfiguration.getShutdown()).thenReturn(Shutdown.EXIT);
219
220 StartupConfiguration startupConfiguration = mock(StartupConfiguration.class);
221 when(startupConfiguration.getClasspathConfiguration()).thenReturn(new ClasspathConfig());
222 when(startupConfiguration.getClassLoaderConfiguration()).thenReturn(new ClassLoaderConfiguration(false, false));
223 when(startupConfiguration.getProviderClassName()).thenReturn(MainClass.class.getName());
224
225 ForkConfiguration forkConfiguration = mock(ForkConfiguration.class);
226 when(forkConfiguration.getWorkingDirectory()).thenReturn(tmp);
227 when(forkConfiguration.getTempDirectory()).thenReturn(tmp);
228 when(forkConfiguration.getPluginPlatform()).thenReturn(new Platform());
229 Commandline cli = new Commandline();
230 cli.setWorkingDirectory(tmp);
231 cli.setExecutable(System.getProperty("java.home") + "/bin/java");
232 cli.createArg().setLine("-jar");
233 cli.createArg().setLine("surefirebooter.jar");
234 when(forkConfiguration.createCommandLine(eq(startupConfiguration), eq(1), eq(tmp)))
235 .thenReturn(cli);
236
237 SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
238 SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
239 SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter(true, "3");
240
241 StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration(
242 true,
243 true,
244 null,
245 false,
246 tmp,
247 true,
248 "",
249 null,
250 false,
251 0,
252 null,
253 null,
254 true,
255 true,
256 true,
257 false,
258 xmlReporter,
259 outputReporter,
260 statelessTestsetInfoReporter,
261 new ReporterFactoryOptions());
262
263 ConsoleLogger logger = mock(ConsoleLogger.class);
264
265 ForkStarter forkStarter = new ForkStarter(
266 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
267
268 DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
269
270 Class<?>[] types = {
271 Object.class,
272 PropertiesWrapper.class,
273 ForkClient.class,
274 SurefireProperties.class,
275 int.class,
276 AbstractCommandReader.class,
277 ForkNodeFactory.class,
278 boolean.class
279 };
280 TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
281 invokeMethod(
282 forkStarter,
283 "fork",
284 types,
285 null,
286 new PropertiesWrapper(Collections.<String, String>emptyMap()),
287 new ForkClient(reporterFactory, testLessInputStream, 1),
288 new SurefireProperties(),
289 1,
290 testLessInputStream,
291 new LegacyForkNodeFactory(),
292 true);
293 testLessInputStream.close();
294 }
295
296 private static class ClasspathConfig extends AbstractPathConfiguration {
297 ClasspathConfig() {
298 this(Classpath.emptyClasspath(), false, false);
299 }
300
301 private ClasspathConfig(Classpath surefireClasspathUrls, boolean enableAssertions, boolean childDelegation) {
302 super(surefireClasspathUrls, enableAssertions, childDelegation);
303 }
304
305 @Override
306 public Classpath getTestClasspath() {
307 return Classpath.emptyClasspath();
308 }
309
310 @Override
311 public boolean isModularPathConfig() {
312 return false;
313 }
314
315 @Override
316 public boolean isClassPathConfig() {
317 return true;
318 }
319
320 @Override
321 protected Classpath getInprocClasspath() {
322 return Classpath.emptyClasspath();
323 }
324 }
325 }