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 javax.annotation.Nonnull;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Properties;
33
34 import org.apache.maven.plugin.surefire.JdkAttributes;
35 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Commandline;
36 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
37 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
38 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
39 import org.apache.maven.surefire.booter.Classpath;
40 import org.apache.maven.surefire.booter.ClasspathConfiguration;
41 import org.apache.maven.surefire.booter.ModularClasspath;
42 import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
43 import org.apache.maven.surefire.booter.StartupConfiguration;
44 import org.apache.maven.surefire.booter.SurefireBooterForkException;
45 import org.apache.maven.surefire.extensions.ForkNodeFactory;
46 import org.apache.maven.surefire.shared.io.FileUtils;
47 import org.junit.After;
48 import org.junit.Before;
49 import org.junit.Test;
50
51 import static java.lang.String.join;
52 import static java.nio.file.Files.readAllBytes;
53 import static java.util.Collections.singletonList;
54 import static org.apache.commons.io.FileUtils.getTempDirectory;
55 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
56 import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
57 import static org.apache.maven.surefire.booter.ProcessCheckerType.ALL;
58 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
59 import static org.assertj.core.api.Assertions.assertThat;
60 import static org.junit.Assert.assertEquals;
61 import static org.junit.Assert.assertTrue;
62 import static org.junit.Assert.fail;
63 import static org.mockito.Mockito.mock;
64
65
66
67
68 public class ForkConfigurationTest {
69 private static final StartupConfiguration STARTUP_CONFIG = new StartupConfiguration(
70 "",
71 new ClasspathConfiguration(true, true),
72 new ClassLoaderConfiguration(true, true),
73 ALL,
74 Collections.<String[]>emptyList());
75
76 private static int idx = 0;
77
78 private File basedir;
79
80 @Before
81 public void setupDirectories() throws IOException {
82 File target = new File(System.getProperty("user.dir"), "target");
83 basedir = new File(target, "SUREFIRE-1136-" + ++idx);
84 FileUtils.deleteDirectory(basedir);
85 assertTrue(basedir.mkdirs());
86 }
87
88 @After
89 public void deleteDirectories() throws IOException {
90 FileUtils.deleteDirectory(basedir);
91 }
92
93 @Test
94 public void testEnv() throws Exception {
95 Map<String, String> env = new HashMap<>();
96 env.put("key1", "val1");
97 env.put("key2", "val2");
98 env.put("key3", "val3");
99 String[] exclEnv = {"PATH"};
100
101 String jvm = new File(new File(System.getProperty("java.home"), "bin"), "java").getCanonicalPath();
102 Platform platform = new Platform().withJdkExecAttributesForTests(new JdkAttributes(jvm, false));
103
104 ForkConfiguration config =
105 new DefaultForkConfiguration(
106 emptyClasspath(),
107 basedir,
108 "",
109 basedir,
110 new Properties(),
111 "",
112 env,
113 exclEnv,
114 false,
115 1,
116 true,
117 platform,
118 new NullConsoleLogger(),
119 mock(ForkNodeFactory.class)) {
120
121 @Override
122 protected void resolveClasspath(
123 @Nonnull Commandline cli,
124 @Nonnull String booterThatHasMainMethod,
125 @Nonnull StartupConfiguration config,
126 @Nonnull File dumpLogDirectory) {}
127 };
128
129 List<String[]> providerJpmsArgs = new ArrayList<>();
130 providerJpmsArgs.add(new String[] {"arg2", "arg3"});
131
132 File cpElement = getTempClasspathFile();
133 List<String> cp = singletonList(cpElement.getAbsolutePath());
134
135 ClasspathConfiguration cpConfig =
136 new ClasspathConfiguration(new Classpath(cp), emptyClasspath(), emptyClasspath(), true, true);
137 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
138 StartupConfiguration startup = new StartupConfiguration("cls", cpConfig, clc, ALL, providerJpmsArgs);
139
140 org.apache.maven.surefire.shared.utils.cli.Commandline cli =
141 config.createCommandLine(startup, 1, getTempDirectory());
142
143 assertThat(cli.getEnvironmentVariables())
144 .contains("key1=val1", "key2=val2", "key3=val3")
145 .doesNotContain("PATH=")
146 .doesNotHaveDuplicates();
147 }
148
149 @Test
150 public void testEnvInterpolateForkNumber() throws Exception {
151 Map<String, String> env = new HashMap<>();
152 env.put("FORK_ID", "${surefire.forkNumber}");
153 String[] exclEnv = {"PATH"};
154
155 String jvm = new File(new File(System.getProperty("java.home"), "bin"), "java").getCanonicalPath();
156 Platform platform = new Platform().withJdkExecAttributesForTests(new JdkAttributes(jvm, false));
157
158 ForkConfiguration config =
159 new DefaultForkConfiguration(
160 emptyClasspath(),
161 basedir,
162 "",
163 basedir,
164 new Properties(),
165 "",
166 env,
167 exclEnv,
168 false,
169 2,
170 true,
171 platform,
172 new NullConsoleLogger(),
173 mock(ForkNodeFactory.class)) {
174
175 @Override
176 protected void resolveClasspath(
177 @Nonnull Commandline cli,
178 @Nonnull String booterThatHasMainMethod,
179 @Nonnull StartupConfiguration config,
180 @Nonnull File dumpLogDirectory) {}
181 };
182
183 List<String[]> providerJpmsArgs = new ArrayList<>();
184 providerJpmsArgs.add(new String[] {"arg2", "arg3"});
185
186 File cpElement = getTempClasspathFile();
187 List<String> cp = singletonList(cpElement.getAbsolutePath());
188
189 ClasspathConfiguration cpConfig =
190 new ClasspathConfiguration(new Classpath(cp), emptyClasspath(), emptyClasspath(), true, true);
191 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
192 StartupConfiguration startup = new StartupConfiguration("cls", cpConfig, clc, ALL, providerJpmsArgs);
193
194 org.apache.maven.surefire.shared.utils.cli.Commandline cliFork1 =
195 config.createCommandLine(startup, 1, getTempDirectory());
196
197 assertThat(cliFork1.getEnvironmentVariables())
198 .contains("FORK_ID=1")
199 .doesNotContain("PATH=")
200 .doesNotHaveDuplicates();
201
202 org.apache.maven.surefire.shared.utils.cli.Commandline cliFork2 =
203 config.createCommandLine(startup, 2, getTempDirectory());
204
205 assertThat(cliFork2.getEnvironmentVariables())
206 .contains("FORK_ID=2")
207 .doesNotContain("PATH=")
208 .doesNotHaveDuplicates();
209 }
210
211 @Test
212 public void testCliArgs() throws Exception {
213 String jvm = new File(new File(System.getProperty("java.home"), "bin"), "java").getCanonicalPath();
214 Platform platform = new Platform().withJdkExecAttributesForTests(new JdkAttributes(jvm, false));
215
216 ModularClasspathForkConfiguration config = new ModularClasspathForkConfiguration(
217 emptyClasspath(),
218 basedir,
219 "",
220 basedir,
221 new Properties(),
222 "arg1",
223 Collections.<String, String>emptyMap(),
224 new String[0],
225 false,
226 1,
227 true,
228 platform,
229 new NullConsoleLogger(),
230 mock(ForkNodeFactory.class));
231
232 assertThat(config.isDebug()).isFalse();
233
234 List<String[]> providerJpmsArgs = new ArrayList<>();
235 providerJpmsArgs.add(new String[] {"arg2", "arg3"});
236
237 ModularClasspath modulepath = new ModularClasspath(
238 "test.module", Collections.<String>emptyList(), Collections.<String>emptyList(), null, false);
239 ModularClasspathConfiguration cpConfig = new ModularClasspathConfiguration(
240 modulepath, emptyClasspath(), emptyClasspath(), emptyClasspath(), false, true);
241 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
242 StartupConfiguration startup = new StartupConfiguration("cls", cpConfig, clc, ALL, providerJpmsArgs);
243
244 org.apache.maven.surefire.shared.utils.cli.Commandline cli =
245 config.createCommandLine(startup, 1, getTempDirectory());
246 String cliAsString = cli.toString();
247
248 assertThat(cliAsString).contains("arg1");
249
250
251 int beginOfFileArg = cliAsString.indexOf('@', cliAsString.lastIndexOf("arg1"));
252 assertThat(beginOfFileArg).isPositive();
253 int endOfFileArg = cliAsString.indexOf(IS_OS_WINDOWS ? '"' : '\'', beginOfFileArg);
254 assertThat(endOfFileArg).isPositive();
255 Path argFile = Paths.get(cliAsString.substring(beginOfFileArg + 1, endOfFileArg));
256 String argFileText = new String(readAllBytes(argFile));
257 assertThat(argFileText).contains("arg2").contains("arg3").contains("--add-modules" + NL + "ALL-MODULE-PATH");
258 }
259
260 @Test
261 public void testDebugLine() throws Exception {
262 String jvm = new File(new File(System.getProperty("java.home"), "bin"), "java").getCanonicalPath();
263 Platform platform = new Platform().withJdkExecAttributesForTests(new JdkAttributes(jvm, false));
264
265 ConsoleLogger logger = mock(ConsoleLogger.class);
266 ForkNodeFactory forkNodeFactory = mock(ForkNodeFactory.class);
267
268 ForkConfiguration config =
269 new DefaultForkConfiguration(
270 emptyClasspath(),
271 basedir,
272 "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005",
273 basedir,
274 new Properties(),
275 "",
276 Collections.<String, String>emptyMap(),
277 new String[0],
278 true,
279 1,
280 true,
281 platform,
282 logger,
283 forkNodeFactory) {
284
285 @Override
286 protected void resolveClasspath(
287 @Nonnull Commandline cli,
288 @Nonnull String booterThatHasMainMethod,
289 @Nonnull StartupConfiguration config,
290 @Nonnull File dumpLogDirectory) {}
291 };
292
293 assertThat(config.isDebug()).isTrue();
294
295 assertThat(config.getDebugLine())
296 .isEqualTo("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005");
297
298 assertThat(config.getForkCount()).isEqualTo(1);
299
300 assertThat(config.isReuseForks()).isTrue();
301
302 assertThat(config.getForkNodeFactory()).isSameAs(forkNodeFactory);
303
304 File cpElement = getTempClasspathFile();
305 List<String> cp = singletonList(cpElement.getAbsolutePath());
306
307 ClasspathConfiguration cpConfig =
308 new ClasspathConfiguration(new Classpath(cp), emptyClasspath(), emptyClasspath(), true, true);
309 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
310 StartupConfiguration startup = new StartupConfiguration(
311 "org.apache.maven.surefire.JUnitProvider#main", cpConfig, clc, ALL, Collections.<String[]>emptyList());
312
313 assertThat(startup.isProviderMainClass()).isTrue();
314
315 assertThat(startup.getProviderClassName()).isEqualTo("org.apache.maven.surefire.JUnitProvider#main");
316
317 assertThat(startup.isShadefire()).isFalse();
318
319 org.apache.maven.surefire.shared.utils.cli.Commandline cli =
320 config.createCommandLine(startup, 1, getTempDirectory());
321
322 assertThat(cli.toString()).contains("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005");
323 }
324
325 @Test
326 @SuppressWarnings({"checkstyle:methodname", "checkstyle:magicnumber"})
327 public void testCreateCommandLine_UseSystemClassLoaderForkOnce_ShouldConstructManifestOnlyJar()
328 throws IOException, SurefireBooterForkException {
329 ForkConfiguration config = getForkConfiguration(basedir, null);
330 File cpElement = getTempClasspathFile();
331
332 List<String> cp = singletonList(cpElement.getAbsolutePath());
333 ClasspathConfiguration cpConfig =
334 new ClasspathConfiguration(new Classpath(cp), emptyClasspath(), emptyClasspath(), true, true);
335 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
336 StartupConfiguration startup =
337 new StartupConfiguration("", cpConfig, clc, ALL, Collections.<String[]>emptyList());
338
339 org.apache.maven.surefire.shared.utils.cli.Commandline cli =
340 config.createCommandLine(startup, 1, getTempDirectory());
341
342 String line = join(" ", cli.getCommandline());
343 assertTrue(line.contains("-jar"));
344 }
345
346 @Test
347 public void testArglineWithNewline() throws IOException, SurefireBooterForkException {
348
349 ForkConfiguration config = getForkConfiguration(basedir, "abc\ndef");
350 File cpElement = getTempClasspathFile();
351
352 List<String> cp = singletonList(cpElement.getAbsolutePath());
353 ClasspathConfiguration cpConfig =
354 new ClasspathConfiguration(new Classpath(cp), emptyClasspath(), emptyClasspath(), true, true);
355 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
356 StartupConfiguration startup =
357 new StartupConfiguration("", cpConfig, clc, ALL, Collections.<String[]>emptyList());
358
359 org.apache.maven.surefire.shared.utils.cli.Commandline commandLine =
360 config.createCommandLine(startup, 1, getTempDirectory());
361 assertThat(commandLine.toString()).contains(IS_OS_WINDOWS ? "abc def" : "'abc' 'def'");
362 }
363
364 @Test
365 public void testCurrentWorkingDirectoryPropagationIncludingForkNumberExpansion()
366 throws IOException, SurefireBooterForkException {
367 File cwd = new File(basedir, "fork_${surefire.forkNumber}");
368
369 ClasspathConfiguration cpConfig =
370 new ClasspathConfiguration(emptyClasspath(), emptyClasspath(), emptyClasspath(), true, true);
371 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
372 StartupConfiguration startup =
373 new StartupConfiguration("", cpConfig, clc, ALL, Collections.<String[]>emptyList());
374 ForkConfiguration config = getForkConfiguration(cwd.getCanonicalFile());
375 org.apache.maven.surefire.shared.utils.cli.Commandline commandLine =
376 config.createCommandLine(startup, 1, getTempDirectory());
377
378 File forkDirectory = new File(basedir, "fork_1");
379
380 String shellWorkDir = commandLine.getShell().getWorkingDirectory().getCanonicalPath();
381 assertEquals(shellWorkDir, forkDirectory.getCanonicalPath());
382 }
383
384 @Test
385 public void testExceptionWhenCurrentDirectoryIsNotRealDirectory() throws IOException {
386 File cwd = new File(basedir, "cwd.txt");
387 FileUtils.touch(cwd);
388
389 try {
390 ForkConfiguration config = getForkConfiguration(cwd.getCanonicalFile());
391 config.createCommandLine(STARTUP_CONFIG, 1, getTempDirectory());
392 } catch (SurefireBooterForkException e) {
393
394 String absolutePath = cwd.getCanonicalPath();
395 assertEquals("WorkingDirectory " + absolutePath + " exists and is not a directory", e.getMessage());
396 return;
397 } finally {
398 assertTrue(cwd.delete());
399 }
400
401 fail();
402 }
403
404 @Test
405 public void testExceptionWhenCurrentDirectoryCannotBeCreated() throws IOException {
406
407
408
409 File cwd = new File(basedir, "?\u0000InvalidDirectoryName");
410
411 try {
412 ForkConfiguration config = getForkConfiguration(cwd.getAbsoluteFile());
413 config.createCommandLine(STARTUP_CONFIG, 1, getTempDirectory());
414 } catch (SurefireBooterForkException sbfe) {
415 assertEquals("Cannot create workingDirectory " + cwd.getAbsolutePath(), sbfe.getMessage());
416 return;
417 } finally {
418 FileUtils.deleteDirectory(cwd);
419 }
420
421 if (IS_OS_WINDOWS || isJavaVersionAtLeast7u60()) {
422 fail();
423 }
424 }
425
426 private File getTempClasspathFile() throws IOException {
427 File cpElement = new File(basedir, "ForkConfigurationTest." + idx + ".file");
428 FileUtils.deleteDirectory(cpElement);
429 return cpElement;
430 }
431
432 static ForkConfiguration getForkConfiguration(File basedir, String argLine) throws IOException {
433 File jvm = new File(new File(System.getProperty("java.home"), "bin"), "java");
434 return getForkConfiguration(basedir, argLine, jvm.getAbsolutePath(), new File(".").getCanonicalFile());
435 }
436
437 private ForkConfiguration getForkConfiguration(File cwd) throws IOException {
438 File jvm = new File(new File(System.getProperty("java.home"), "bin"), "java");
439 return getForkConfiguration(basedir, null, jvm.getAbsolutePath(), cwd);
440 }
441
442 private static ForkConfiguration getForkConfiguration(File basedir, String argLine, String jvm, File cwd)
443 throws IOException {
444 Platform platform = new Platform().withJdkExecAttributesForTests(new JdkAttributes(jvm, false));
445 File tmpDir = new File(new File(basedir, "target"), "surefire");
446 FileUtils.deleteDirectory(tmpDir);
447 assertTrue(tmpDir.mkdirs());
448 return new JarManifestForkConfiguration(
449 emptyClasspath(),
450 tmpDir,
451 null,
452 cwd,
453 new Properties(),
454 argLine,
455 Collections.<String, String>emptyMap(),
456 new String[0],
457 false,
458 1,
459 false,
460 platform,
461 new NullConsoleLogger(),
462 mock(ForkNodeFactory.class));
463 }
464
465
466 @SuppressWarnings("checkstyle:magicnumber")
467 private static boolean isJavaVersionAtLeast7u60() {
468 String[] javaVersionElements =
469 System.getProperty("java.runtime.version").split("\\.|_|-b");
470 return Integer.parseInt(javaVersionElements[1]) >= 7 && Integer.parseInt(javaVersionElements[3]) >= 60;
471 }
472 }