1 package org.apache.maven.plugin.surefire.booterclient;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
23 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
24 import org.apache.maven.plugin.surefire.util.Relocator;
25 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
26 import org.apache.maven.surefire.booter.Classpath;
27 import org.apache.maven.surefire.booter.ClasspathConfiguration;
28 import org.apache.maven.surefire.booter.ForkedBooter;
29 import org.apache.maven.surefire.booter.StartupConfiguration;
30 import org.apache.maven.surefire.booter.SurefireBooterForkException;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.powermock.core.classloader.annotations.PowerMockIgnore;
35 import org.powermock.core.classloader.annotations.PrepareForTest;
36 import org.powermock.modules.junit4.PowerMockRunner;
37
38 import javax.annotation.Nonnull;
39 import java.io.File;
40 import java.util.HashMap;
41 import java.util.Map;
42 import java.util.Properties;
43
44 import static java.util.Collections.singleton;
45 import static org.fest.assertions.Assertions.assertThat;
46 import static org.mockito.ArgumentMatchers.anyString;
47 import static org.mockito.ArgumentMatchers.eq;
48 import static org.mockito.Mockito.never;
49 import static org.mockito.Mockito.times;
50 import static org.mockito.Mockito.verify;
51 import static org.powermock.api.mockito.PowerMockito.mock;
52 import static org.powermock.api.mockito.PowerMockito.mockStatic;
53 import static org.powermock.api.mockito.PowerMockito.spy;
54 import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
55 import static org.powermock.api.mockito.PowerMockito.verifyStatic;
56 import static org.powermock.api.mockito.PowerMockito.when;
57 import static org.powermock.reflect.Whitebox.invokeMethod;
58
59
60
61
62
63
64
65 @RunWith( PowerMockRunner.class )
66 @PrepareForTest( { DefaultForkConfiguration.class, Relocator.class } )
67 @PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
68 public class DefaultForkConfigurationTest
69 {
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
84 @Before
85 public void setup()
86 {
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 }
101
102 @Test
103 public void shouldBeNullArgLine() throws Exception
104 {
105 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
106 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
107 debug, forkCount, reuseForks, pluginPlatform, log )
108 {
109
110 @Override
111 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
112 @Nonnull String booterThatHasMainMethod,
113 @Nonnull StartupConfiguration config,
114 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
115 {
116
117 }
118 };
119
120 DefaultForkConfiguration mockedConfig = spy( config );
121 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
122 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
123 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
124 assertThat( newArgLine ).isEmpty();
125 }
126
127 @Test
128 public void shouldBeEmptyArgLine() throws Exception
129 {
130 argLine = "";
131 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
132 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
133 debug, forkCount, reuseForks, pluginPlatform, log )
134 {
135
136 @Override
137 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
138 @Nonnull String booterThatHasMainMethod,
139 @Nonnull StartupConfiguration config,
140 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
141 {
142
143 }
144 };
145
146 DefaultForkConfiguration mockedConfig = spy( config );
147 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
148 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
149 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
150 assertThat( newArgLine ).isEmpty();
151 }
152
153 @Test
154 public void shouldBeEmptyArgLineInsteadOfNewLines() throws Exception
155 {
156 argLine = "\n\r";
157 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
158 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
159 debug, forkCount, reuseForks, pluginPlatform, log )
160 {
161
162 @Override
163 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
164 @Nonnull String booterThatHasMainMethod,
165 @Nonnull StartupConfiguration config,
166 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
167 {
168
169 }
170 };
171
172 DefaultForkConfiguration mockedConfig = spy( config );
173 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
174 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
175 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
176 assertThat( newArgLine ).isEmpty();
177 }
178
179 @Test
180 public void shouldBeWithoutEscaping() throws Exception
181 {
182 argLine = "-Dfile.encoding=UTF-8";
183 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
184 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
185 debug, forkCount, reuseForks, pluginPlatform, log )
186 {
187
188 @Override
189 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
190 @Nonnull String booterThatHasMainMethod,
191 @Nonnull StartupConfiguration config,
192 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
193 {
194
195 }
196 };
197
198 DefaultForkConfiguration mockedConfig = spy( config );
199 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
200 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
201 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
202 assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
203 }
204
205 @Test
206 public void shouldBeWithEscaping() throws Exception
207 {
208 modelProperties.put( "encoding", "UTF-8" );
209 argLine = "-Dfile.encoding=@{encoding}";
210 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
211 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
212 debug, forkCount, reuseForks, pluginPlatform, log )
213 {
214
215 @Override
216 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
217 @Nonnull String booterThatHasMainMethod,
218 @Nonnull StartupConfiguration config,
219 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
220 {
221
222 }
223 };
224
225 DefaultForkConfiguration mockedConfig = spy( config );
226 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
227 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
228 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
229 assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
230 }
231
232 @Test
233 public void shouldBeWhitespaceInsteadOfNewLines() throws Exception
234 {
235 argLine = "a\n\rb";
236 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
237 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
238 debug, forkCount, reuseForks, pluginPlatform, log )
239 {
240
241 @Override
242 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
243 @Nonnull String booterThatHasMainMethod,
244 @Nonnull StartupConfiguration config,
245 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
246 {
247
248 }
249 };
250
251 DefaultForkConfiguration mockedConfig = spy( config );
252 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
253 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
254 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "a b" ) );
255 assertThat( newArgLine ).isEqualTo( "a b" );
256 }
257
258 @Test
259 public void shouldEscapeThreadNumber() throws Exception
260 {
261 argLine = "-Dthread=${surefire.threadNumber}";
262 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
263 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
264 debug, forkCount, reuseForks, pluginPlatform, log )
265 {
266
267 @Override
268 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
269 @Nonnull String booterThatHasMainMethod,
270 @Nonnull StartupConfiguration config,
271 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
272 {
273
274 }
275 };
276
277 DefaultForkConfiguration mockedConfig = spy( config );
278 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
279 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
280 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
281 assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
282 }
283
284 @Test
285 public void shouldEscapeForkNumber() throws Exception
286 {
287 argLine = "-Dthread=${surefire.forkNumber}";
288 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
289 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
290 debug, forkCount, reuseForks, pluginPlatform, log )
291 {
292
293 @Override
294 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
295 @Nonnull String booterThatHasMainMethod,
296 @Nonnull StartupConfiguration config,
297 @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
298 {
299
300 }
301 };
302
303 DefaultForkConfiguration mockedConfig = spy( config );
304 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
305 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
306 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
307 assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
308 }
309
310 @Test
311 public void shouldRelocateBooterClassWhenShadefire() throws Exception
312 {
313 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
314 ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
315 StartupConfiguration conf = new StartupConfiguration( "org.apache.maven.shadefire.surefire.MyProvider",
316 cc, clc, false, false, null );
317 StartupConfiguration confMock = spy( conf );
318 mockStatic( Relocator.class );
319 when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
320
321 String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
322
323 verify( confMock, times( 1 ) ).isShadefire();
324 verifyStatic( Relocator.class, times( 1 ) );
325 Relocator.relocate( eq( ForkedBooter.class.getName() ) );
326
327 assertThat( cls ).isEqualTo( "org.apache.maven.shadefire.surefire.booter.ForkedBooter" );
328 assertThat( confMock.isShadefire() ).isTrue();
329 }
330
331 @Test
332 public void shouldNotRelocateBooterClass() throws Exception
333 {
334 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
335 ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
336 StartupConfiguration conf =
337 new StartupConfiguration( "org.apache.maven.surefire.MyProvider", cc, clc, false, false, null );
338 StartupConfiguration confMock = spy( conf );
339 mockStatic( Relocator.class );
340 when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
341
342 String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
343
344 verify( confMock, times( 1 ) ).isShadefire();
345 verifyStatic( Relocator.class, never() );
346 Relocator.relocate( eq( ForkedBooter.class.getName() ) );
347
348 assertThat( cls ).isEqualTo( "org.apache.maven.surefire.booter.ForkedBooter" );
349 assertThat( confMock.isShadefire() ).isFalse();
350 }
351 }