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.util.Collections;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Properties;
28
29 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Commandline;
30 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
31 import org.apache.maven.plugin.surefire.util.Relocator;
32 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
33 import org.apache.maven.surefire.booter.Classpath;
34 import org.apache.maven.surefire.booter.ClasspathConfiguration;
35 import org.apache.maven.surefire.booter.ForkedBooter;
36 import org.apache.maven.surefire.booter.StartupConfiguration;
37 import org.apache.maven.surefire.extensions.ForkNodeFactory;
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.powermock.core.classloader.annotations.PowerMockIgnore;
42 import org.powermock.core.classloader.annotations.PrepareForTest;
43 import org.powermock.modules.junit4.PowerMockRunner;
44
45 import static java.util.Collections.singleton;
46 import static org.assertj.core.api.Assertions.assertThat;
47 import static org.mockito.ArgumentMatchers.anyString;
48 import static org.mockito.ArgumentMatchers.eq;
49 import static org.mockito.Mockito.never;
50 import static org.mockito.Mockito.times;
51 import static org.mockito.Mockito.verify;
52 import static org.powermock.api.mockito.PowerMockito.mock;
53 import static org.powermock.api.mockito.PowerMockito.mockStatic;
54 import static org.powermock.api.mockito.PowerMockito.spy;
55 import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
56 import static org.powermock.api.mockito.PowerMockito.verifyStatic;
57 import static org.powermock.api.mockito.PowerMockito.when;
58 import static org.powermock.reflect.Whitebox.invokeMethod;
59
60
61
62
63
64
65
66 @RunWith(PowerMockRunner.class)
67 @PrepareForTest({DefaultForkConfiguration.class, Relocator.class})
68 @PowerMockIgnore({"org.jacoco.agent.rt.*", "com.vladium.emma.rt.*"})
69 public class DefaultForkConfigurationTest {
70 private Classpath booterClasspath;
71 private File tempDirectory;
72 private String debugLine;
73 private File workingDirectory;
74 private Properties modelProperties;
75 private String argLine;
76 private Map<String, String> environmentVariables;
77 private String[] excludedEnvironmentVariables;
78 private boolean debug;
79 private int forkCount;
80 private boolean reuseForks;
81 private Platform pluginPlatform;
82 private ConsoleLogger log;
83 private ForkNodeFactory forkNodeFactory;
84
85 @Before
86 public void setup() {
87 booterClasspath = new Classpath(singleton("provider.jar"));
88 tempDirectory = new File("target/surefire");
89 debugLine = "";
90 workingDirectory = new File(".");
91 modelProperties = new Properties();
92 argLine = null;
93 environmentVariables = new HashMap<>();
94 excludedEnvironmentVariables = new String[0];
95 debug = true;
96 forkCount = 2;
97 reuseForks = true;
98 pluginPlatform = new Platform();
99 log = mock(ConsoleLogger.class);
100 forkNodeFactory = mock(ForkNodeFactory.class);
101 }
102
103 @Test
104 public void shouldBeNullArgLine() throws Exception {
105 DefaultForkConfiguration config =
106 new DefaultForkConfiguration(
107 booterClasspath,
108 tempDirectory,
109 debugLine,
110 workingDirectory,
111 modelProperties,
112 argLine,
113 environmentVariables,
114 excludedEnvironmentVariables,
115 debug,
116 forkCount,
117 reuseForks,
118 pluginPlatform,
119 log,
120 forkNodeFactory) {
121
122 @Override
123 protected void resolveClasspath(
124 @Nonnull Commandline cli,
125 @Nonnull String booterThatHasMainMethod,
126 @Nonnull StartupConfiguration config,
127 @Nonnull File dumpLogDirectory) {}
128 };
129
130 DefaultForkConfiguration mockedConfig = spy(config);
131 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
132 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
133 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq(""));
134 assertThat(newArgLine).isEmpty();
135 }
136
137 @Test
138 public void shouldBeEmptyArgLine() throws Exception {
139 argLine = "";
140 DefaultForkConfiguration config =
141 new DefaultForkConfiguration(
142 booterClasspath,
143 tempDirectory,
144 debugLine,
145 workingDirectory,
146 modelProperties,
147 argLine,
148 environmentVariables,
149 excludedEnvironmentVariables,
150 debug,
151 forkCount,
152 reuseForks,
153 pluginPlatform,
154 log,
155 forkNodeFactory) {
156
157 @Override
158 protected void resolveClasspath(
159 @Nonnull Commandline cli,
160 @Nonnull String booterThatHasMainMethod,
161 @Nonnull StartupConfiguration config,
162 @Nonnull File dumpLogDirectory) {}
163 };
164
165 DefaultForkConfiguration mockedConfig = spy(config);
166 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
167 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
168 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq(""));
169 assertThat(newArgLine).isEmpty();
170 }
171
172 @Test
173 public void shouldBeEmptyArgLineInsteadOfNewLines() throws Exception {
174 argLine = "\n\r";
175 DefaultForkConfiguration config =
176 new DefaultForkConfiguration(
177 booterClasspath,
178 tempDirectory,
179 debugLine,
180 workingDirectory,
181 modelProperties,
182 argLine,
183 environmentVariables,
184 excludedEnvironmentVariables,
185 debug,
186 forkCount,
187 reuseForks,
188 pluginPlatform,
189 log,
190 forkNodeFactory) {
191
192 @Override
193 protected void resolveClasspath(
194 @Nonnull Commandline cli,
195 @Nonnull String booterThatHasMainMethod,
196 @Nonnull StartupConfiguration config,
197 @Nonnull File dumpLogDirectory) {}
198 };
199
200 DefaultForkConfiguration mockedConfig = spy(config);
201 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
202 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
203 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq(""));
204 assertThat(newArgLine).isEmpty();
205 }
206
207 @Test
208 public void shouldBeWithoutEscaping() throws Exception {
209 argLine = "-Dfile.encoding=UTF-8";
210 DefaultForkConfiguration config =
211 new DefaultForkConfiguration(
212 booterClasspath,
213 tempDirectory,
214 debugLine,
215 workingDirectory,
216 modelProperties,
217 argLine,
218 environmentVariables,
219 excludedEnvironmentVariables,
220 debug,
221 forkCount,
222 reuseForks,
223 pluginPlatform,
224 log,
225 forkNodeFactory) {
226
227 @Override
228 protected void resolveClasspath(
229 @Nonnull Commandline cli,
230 @Nonnull String booterThatHasMainMethod,
231 @Nonnull StartupConfiguration config,
232 @Nonnull File dumpLogDirectory) {}
233 };
234
235 DefaultForkConfiguration mockedConfig = spy(config);
236 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
237 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
238 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq("-Dfile.encoding=UTF-8"));
239 assertThat(newArgLine).isEqualTo("-Dfile.encoding=UTF-8");
240 }
241
242 @Test
243 public void shouldBeWithEscaping() throws Exception {
244 modelProperties.put("encoding", "UTF-8");
245 argLine = "-Dfile.encoding=@{encoding}";
246 DefaultForkConfiguration config =
247 new DefaultForkConfiguration(
248 booterClasspath,
249 tempDirectory,
250 debugLine,
251 workingDirectory,
252 modelProperties,
253 argLine,
254 environmentVariables,
255 excludedEnvironmentVariables,
256 debug,
257 forkCount,
258 reuseForks,
259 pluginPlatform,
260 log,
261 forkNodeFactory) {
262
263 @Override
264 protected void resolveClasspath(
265 @Nonnull Commandline cli,
266 @Nonnull String booterThatHasMainMethod,
267 @Nonnull StartupConfiguration config,
268 @Nonnull File dumpLogDirectory) {}
269 };
270
271 DefaultForkConfiguration mockedConfig = spy(config);
272 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
273 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
274 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq("-Dfile.encoding=UTF-8"));
275 assertThat(newArgLine).isEqualTo("-Dfile.encoding=UTF-8");
276 }
277
278 @Test
279 public void shouldBeWhitespaceInsteadOfNewLines() throws Exception {
280 argLine = "a\n\rb";
281 DefaultForkConfiguration config =
282 new DefaultForkConfiguration(
283 booterClasspath,
284 tempDirectory,
285 debugLine,
286 workingDirectory,
287 modelProperties,
288 argLine,
289 environmentVariables,
290 excludedEnvironmentVariables,
291 debug,
292 forkCount,
293 reuseForks,
294 pluginPlatform,
295 log,
296 forkNodeFactory) {
297
298 @Override
299 protected void resolveClasspath(
300 @Nonnull Commandline cli,
301 @Nonnull String booterThatHasMainMethod,
302 @Nonnull StartupConfiguration config,
303 @Nonnull File dumpLogDirectory) {}
304 };
305
306 DefaultForkConfiguration mockedConfig = spy(config);
307 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
308 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
309 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq("a b"));
310 assertThat(newArgLine).isEqualTo("a b");
311 }
312
313 @Test
314 public void shouldEscapeThreadNumber() throws Exception {
315 argLine = "-Dthread=${surefire.threadNumber}";
316 DefaultForkConfiguration config =
317 new DefaultForkConfiguration(
318 booterClasspath,
319 tempDirectory,
320 debugLine,
321 workingDirectory,
322 modelProperties,
323 argLine,
324 environmentVariables,
325 excludedEnvironmentVariables,
326 debug,
327 forkCount,
328 reuseForks,
329 pluginPlatform,
330 log,
331 forkNodeFactory) {
332
333 @Override
334 protected void resolveClasspath(
335 @Nonnull Commandline cli,
336 @Nonnull String booterThatHasMainMethod,
337 @Nonnull StartupConfiguration config,
338 @Nonnull File dumpLogDirectory) {}
339 };
340
341 DefaultForkConfiguration mockedConfig = spy(config);
342 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
343 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
344 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq("-Dthread=" + forkCount));
345 assertThat(newArgLine).isEqualTo("-Dthread=" + forkCount);
346 }
347
348 @Test
349 public void shouldEscapeForkNumber() throws Exception {
350 argLine = "-Dthread=${surefire.forkNumber}";
351 DefaultForkConfiguration config =
352 new DefaultForkConfiguration(
353 booterClasspath,
354 tempDirectory,
355 debugLine,
356 workingDirectory,
357 modelProperties,
358 argLine,
359 environmentVariables,
360 excludedEnvironmentVariables,
361 debug,
362 forkCount,
363 reuseForks,
364 pluginPlatform,
365 log,
366 forkNodeFactory) {
367
368 @Override
369 protected void resolveClasspath(
370 @Nonnull Commandline cli,
371 @Nonnull String booterThatHasMainMethod,
372 @Nonnull StartupConfiguration config,
373 @Nonnull File dumpLogDirectory) {}
374 };
375
376 DefaultForkConfiguration mockedConfig = spy(config);
377 String newArgLine = invokeMethod(mockedConfig, "newJvmArgLine", new Class[] {int.class}, 2);
378 verifyPrivate(mockedConfig, times(1)).invoke("interpolateArgLineWithPropertyExpressions");
379 verifyPrivate(mockedConfig, times(1)).invoke("extendJvmArgLine", eq("-Dthread=" + forkCount));
380 assertThat(newArgLine).isEqualTo("-Dthread=" + forkCount);
381 }
382
383 @Test
384 public void shouldRelocateBooterClassWhenShadefire() throws Exception {
385 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
386 ClasspathConfiguration cc = new ClasspathConfiguration(true, true);
387 StartupConfiguration conf = new StartupConfiguration(
388 "org.apache.maven.shadefire.surefire.MyProvider", cc, clc, null, Collections.<String[]>emptyList());
389 StartupConfiguration confMock = spy(conf);
390 mockStatic(Relocator.class);
391 when(Relocator.relocate(anyString())).thenCallRealMethod();
392
393 String cls = invokeMethod(DefaultForkConfiguration.class, "findStartClass", confMock);
394
395 verify(confMock, times(1)).isShadefire();
396 verifyStatic(Relocator.class, times(1));
397 Relocator.relocate(eq(ForkedBooter.class.getName()));
398
399 assertThat(cls).isEqualTo("org.apache.maven.shadefire.surefire.booter.ForkedBooter");
400 assertThat(confMock.isShadefire()).isTrue();
401 }
402
403 @Test
404 public void shouldNotRelocateBooterClass() throws Exception {
405 ClassLoaderConfiguration clc = new ClassLoaderConfiguration(true, true);
406 ClasspathConfiguration cc = new ClasspathConfiguration(true, true);
407 StartupConfiguration conf = new StartupConfiguration(
408 "org.apache.maven.surefire.MyProvider", cc, clc, null, Collections.<String[]>emptyList());
409 StartupConfiguration confMock = spy(conf);
410 mockStatic(Relocator.class);
411 when(Relocator.relocate(anyString())).thenCallRealMethod();
412
413 String cls = invokeMethod(DefaultForkConfiguration.class, "findStartClass", confMock);
414
415 verify(confMock, times(1)).isShadefire();
416 verifyStatic(Relocator.class, never());
417 Relocator.relocate(eq(ForkedBooter.class.getName()));
418
419 assertThat(cls).isEqualTo("org.apache.maven.surefire.booter.ForkedBooter");
420 assertThat(confMock.isShadefire()).isFalse();
421 }
422 }