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.extensions.ForkNodeFactory;
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.Collections;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.Properties;
44
45 import static java.util.Collections.singleton;
46 import static org.fest.assertions.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 {
71 private Classpath booterClasspath;
72 private File tempDirectory;
73 private String debugLine;
74 private File workingDirectory;
75 private Properties modelProperties;
76 private String argLine;
77 private Map<String, String> environmentVariables;
78 private String[] excludedEnvironmentVariables;
79 private boolean debug;
80 private int forkCount;
81 private boolean reuseForks;
82 private Platform pluginPlatform;
83 private ConsoleLogger log;
84 private ForkNodeFactory forkNodeFactory;
85
86 @Before
87 public void setup()
88 {
89 booterClasspath = new Classpath( singleton( "provider.jar" ) );
90 tempDirectory = new File( "target/surefire" );
91 debugLine = "";
92 workingDirectory = new File( "." );
93 modelProperties = new Properties();
94 argLine = null;
95 environmentVariables = new HashMap<>();
96 excludedEnvironmentVariables = new String[0];
97 debug = true;
98 forkCount = 2;
99 reuseForks = true;
100 pluginPlatform = new Platform();
101 log = mock( ConsoleLogger.class );
102 forkNodeFactory = mock( ForkNodeFactory.class );
103 }
104
105 @Test
106 public void shouldBeNullArgLine() throws Exception
107 {
108 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
109 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
110 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
111 {
112
113 @Override
114 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
115 @Nonnull String booterThatHasMainMethod,
116 @Nonnull StartupConfiguration config,
117 @Nonnull File dumpLogDirectory )
118 {
119 }
120 };
121
122 DefaultForkConfiguration mockedConfig = spy( config );
123 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
124 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
125 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
126 assertThat( newArgLine ).isEmpty();
127 }
128
129 @Test
130 public void shouldBeEmptyArgLine() throws Exception
131 {
132 argLine = "";
133 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
134 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
135 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
136 {
137
138 @Override
139 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
140 @Nonnull String booterThatHasMainMethod,
141 @Nonnull StartupConfiguration config,
142 @Nonnull File dumpLogDirectory )
143 {
144 }
145 };
146
147 DefaultForkConfiguration mockedConfig = spy( config );
148 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
149 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
150 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
151 assertThat( newArgLine ).isEmpty();
152 }
153
154 @Test
155 public void shouldBeEmptyArgLineInsteadOfNewLines() throws Exception
156 {
157 argLine = "\n\r";
158 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
159 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
160 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
161 {
162
163 @Override
164 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
165 @Nonnull String booterThatHasMainMethod,
166 @Nonnull StartupConfiguration config,
167 @Nonnull File dumpLogDirectory )
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, forkNodeFactory )
186 {
187
188 @Override
189 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
190 @Nonnull String booterThatHasMainMethod,
191 @Nonnull StartupConfiguration config,
192 @Nonnull File dumpLogDirectory )
193 {
194 }
195 };
196
197 DefaultForkConfiguration mockedConfig = spy( config );
198 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
199 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
200 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
201 assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
202 }
203
204 @Test
205 public void shouldBeWithEscaping() throws Exception
206 {
207 modelProperties.put( "encoding", "UTF-8" );
208 argLine = "-Dfile.encoding=@{encoding}";
209 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
210 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
211 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
212 {
213
214 @Override
215 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
216 @Nonnull String booterThatHasMainMethod,
217 @Nonnull StartupConfiguration config,
218 @Nonnull File dumpLogDirectory )
219 {
220 }
221 };
222
223 DefaultForkConfiguration mockedConfig = spy( config );
224 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
225 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
226 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
227 assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
228 }
229
230 @Test
231 public void shouldBeWhitespaceInsteadOfNewLines() throws Exception
232 {
233 argLine = "a\n\rb";
234 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
235 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
236 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
237 {
238
239 @Override
240 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
241 @Nonnull String booterThatHasMainMethod,
242 @Nonnull StartupConfiguration config,
243 @Nonnull File dumpLogDirectory )
244 {
245 }
246 };
247
248 DefaultForkConfiguration mockedConfig = spy( config );
249 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
250 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
251 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "a b" ) );
252 assertThat( newArgLine ).isEqualTo( "a b" );
253 }
254
255 @Test
256 public void shouldEscapeThreadNumber() throws Exception
257 {
258 argLine = "-Dthread=${surefire.threadNumber}";
259 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
260 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
261 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
262 {
263
264 @Override
265 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
266 @Nonnull String booterThatHasMainMethod,
267 @Nonnull StartupConfiguration config,
268 @Nonnull File dumpLogDirectory )
269 {
270 }
271 };
272
273 DefaultForkConfiguration mockedConfig = spy( config );
274 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
275 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
276 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
277 assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
278 }
279
280 @Test
281 public void shouldEscapeForkNumber() throws Exception
282 {
283 argLine = "-Dthread=${surefire.forkNumber}";
284 DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
285 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
286 debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
287 {
288
289 @Override
290 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
291 @Nonnull String booterThatHasMainMethod,
292 @Nonnull StartupConfiguration config,
293 @Nonnull File dumpLogDirectory )
294 {
295 }
296 };
297
298 DefaultForkConfiguration mockedConfig = spy( config );
299 String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
300 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
301 verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
302 assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
303 }
304
305 @Test
306 public void shouldRelocateBooterClassWhenShadefire() throws Exception
307 {
308 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
309 ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
310 StartupConfiguration conf = new StartupConfiguration( "org.apache.maven.shadefire.surefire.MyProvider",
311 cc, clc, null, Collections.<String[]>emptyList() );
312 StartupConfiguration confMock = spy( conf );
313 mockStatic( Relocator.class );
314 when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
315
316 String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
317
318 verify( confMock, times( 1 ) ).isShadefire();
319 verifyStatic( Relocator.class, times( 1 ) );
320 Relocator.relocate( eq( ForkedBooter.class.getName() ) );
321
322 assertThat( cls ).isEqualTo( "org.apache.maven.shadefire.surefire.booter.ForkedBooter" );
323 assertThat( confMock.isShadefire() ).isTrue();
324 }
325
326 @Test
327 public void shouldNotRelocateBooterClass() throws Exception
328 {
329 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
330 ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
331 StartupConfiguration conf = new StartupConfiguration( "org.apache.maven.surefire.MyProvider",
332 cc, clc, null, Collections.<String[]>emptyList() );
333 StartupConfiguration confMock = spy( conf );
334 mockStatic( Relocator.class );
335 when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
336
337 String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
338
339 verify( confMock, times( 1 ) ).isShadefire();
340 verifyStatic( Relocator.class, never() );
341 Relocator.relocate( eq( ForkedBooter.class.getName() ) );
342
343 assertThat( cls ).isEqualTo( "org.apache.maven.surefire.booter.ForkedBooter" );
344 assertThat( confMock.isShadefire() ).isFalse();
345 }
346 }