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