View Javadoc
1   package org.apache.maven.plugin.surefire.booterclient;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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.PrepareForTest;
35  import org.powermock.modules.junit4.PowerMockRunner;
36  
37  import javax.annotation.Nonnull;
38  import java.io.File;
39  import java.util.HashMap;
40  import java.util.Map;
41  import java.util.Properties;
42  
43  import static java.util.Collections.singleton;
44  import static org.fest.assertions.Assertions.assertThat;
45  import static org.mockito.ArgumentMatchers.anyString;
46  import static org.mockito.ArgumentMatchers.eq;
47  import static org.mockito.Mockito.never;
48  import static org.mockito.Mockito.times;
49  import static org.mockito.Mockito.verify;
50  import static org.powermock.api.mockito.PowerMockito.*;
51  import static org.powermock.reflect.Whitebox.invokeMethod;
52  
53  /**
54   * Unit tests for {@link DefaultForkConfiguration}.
55   *
56   * @author Tibor Digana (tibor17)
57   * @since 2.21
58   */
59  @RunWith( PowerMockRunner.class )
60  @PrepareForTest( { DefaultForkConfiguration.class, Relocator.class } )
61  public class DefaultForkConfigurationTest
62  {
63      private Classpath booterClasspath;
64      private File tempDirectory;
65      private String debugLine;
66      private File workingDirectory;
67      private Properties modelProperties;
68      private String argLine;
69      private Map<String, String> environmentVariables;
70      private boolean debug;
71      private int forkCount;
72      private boolean reuseForks;
73      private Platform pluginPlatform;
74      private ConsoleLogger log;
75  
76      @Before
77      public void setup()
78      {
79          booterClasspath = new Classpath( singleton( "provider.jar" ) );
80          tempDirectory = new File( "target/surefire" );
81          debugLine = "";
82          workingDirectory = new File( "." );
83          modelProperties = new Properties();
84          argLine = null;
85          environmentVariables = new HashMap<>();
86          debug = true;
87          forkCount = 2;
88          reuseForks = true;
89          pluginPlatform = new Platform();
90          log = mock( ConsoleLogger.class );
91      }
92  
93      @Test
94      public void shouldBeNullArgLine() throws Exception
95      {
96          DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
97                  workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
98                  pluginPlatform, log )
99          {
100 
101             @Override
102             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
103                                              @Nonnull String booterThatHasMainMethod,
104                                              @Nonnull StartupConfiguration config,
105                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
106             {
107 
108             }
109         };
110 
111         DefaultForkConfiguration mockedConfig = spy( config );
112         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
113         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
114         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
115         assertThat( newArgLine ).isEmpty();
116     }
117 
118     @Test
119     public void shouldBeEmptyArgLine() throws Exception
120     {
121         argLine = "";
122         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
123                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
124                 pluginPlatform, log )
125         {
126 
127             @Override
128             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
129                                              @Nonnull String booterThatHasMainMethod,
130                                              @Nonnull StartupConfiguration config,
131                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
132             {
133 
134             }
135         };
136 
137         DefaultForkConfiguration mockedConfig = spy( config );
138         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
139         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
140         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
141         assertThat( newArgLine ).isEmpty();
142     }
143 
144     @Test
145     public void shouldBeEmptyArgLineInsteadOfNewLines() throws Exception
146     {
147         argLine = "\n\r";
148         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
149                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
150                 pluginPlatform, log )
151         {
152 
153             @Override
154             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
155                                              @Nonnull String booterThatHasMainMethod,
156                                              @Nonnull StartupConfiguration config,
157                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
158             {
159 
160             }
161         };
162 
163         DefaultForkConfiguration mockedConfig = spy( config );
164         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
165         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
166         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
167         assertThat( newArgLine ).isEmpty();
168     }
169 
170     @Test
171     public void shouldBeWithoutEscaping() throws Exception
172     {
173         argLine = "-Dfile.encoding=UTF-8";
174         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
175                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
176                 pluginPlatform, log )
177         {
178 
179             @Override
180             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
181                                              @Nonnull String booterThatHasMainMethod,
182                                              @Nonnull StartupConfiguration config,
183                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
184             {
185 
186             }
187         };
188 
189         DefaultForkConfiguration mockedConfig = spy( config );
190         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
191         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
192         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
193         assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
194     }
195 
196     @Test
197     public void shouldBeWithEscaping() throws Exception
198     {
199         modelProperties.put( "encoding", "UTF-8" );
200         argLine = "-Dfile.encoding=@{encoding}";
201         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
202                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
203                 pluginPlatform, log )
204         {
205 
206             @Override
207             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
208                                              @Nonnull String booterThatHasMainMethod,
209                                              @Nonnull StartupConfiguration config,
210                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
211             {
212 
213             }
214         };
215 
216         DefaultForkConfiguration mockedConfig = spy( config );
217         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
218         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
219         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
220         assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
221     }
222 
223     @Test
224     public void shouldBeWhitespaceInsteadOfNewLines() throws Exception
225     {
226         argLine = "a\n\rb";
227         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
228                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
229                 pluginPlatform, log )
230         {
231 
232             @Override
233             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
234                                              @Nonnull String booterThatHasMainMethod,
235                                              @Nonnull StartupConfiguration config,
236                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
237             {
238 
239             }
240         };
241 
242         DefaultForkConfiguration mockedConfig = spy( config );
243         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
244         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
245         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "a  b" ) );
246         assertThat( newArgLine ).isEqualTo( "a  b" );
247     }
248 
249     @Test
250     public void shouldEscapeThreadNumber() throws Exception
251     {
252         argLine = "-Dthread=${surefire.threadNumber}";
253         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
254                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
255                 pluginPlatform, log )
256         {
257 
258             @Override
259             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
260                                              @Nonnull String booterThatHasMainMethod,
261                                              @Nonnull StartupConfiguration config,
262                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
263             {
264 
265             }
266         };
267 
268         DefaultForkConfiguration mockedConfig = spy( config );
269         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
270         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
271         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
272         assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
273     }
274 
275     @Test
276     public void shouldEscapeForkNumber() throws Exception
277     {
278         argLine = "-Dthread=${surefire.forkNumber}";
279         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
280                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
281                 pluginPlatform, log )
282         {
283 
284             @Override
285             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
286                                              @Nonnull String booterThatHasMainMethod,
287                                              @Nonnull StartupConfiguration config,
288                                              @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
289             {
290 
291             }
292         };
293 
294         DefaultForkConfiguration mockedConfig = spy( config );
295         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
296         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
297         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
298         assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
299     }
300 
301     @Test
302     public void shouldRelocateBooterClassWhenShadefire() throws Exception
303     {
304         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
305         ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
306         StartupConfiguration conf =
307                 new StartupConfiguration( "org.apache.maven.shadefire.surefire.MyProvider", cc, clc, false, false );
308         StartupConfiguration confMock = spy( conf );
309         mockStatic( Relocator.class );
310         when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
311 
312         String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
313 
314         verify( confMock, times( 1 ) ).isShadefire();
315         verifyStatic( Relocator.class, times( 1 ) );
316         Relocator.relocate( eq( ForkedBooter.class.getName() ) );
317 
318         assertThat( cls ).isEqualTo( "org.apache.maven.shadefire.surefire.booter.ForkedBooter" );
319         assertThat( confMock.isShadefire() ).isTrue();
320     }
321 
322     @Test
323     public void shouldNotRelocateBooterClass() throws Exception
324     {
325         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
326         ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
327         StartupConfiguration conf =
328                 new StartupConfiguration( "org.apache.maven.surefire.MyProvider", cc, clc, false, false );
329         StartupConfiguration confMock = spy( conf );
330         mockStatic( Relocator.class );
331         when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
332 
333         String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
334 
335         verify( confMock, times( 1 ) ).isShadefire();
336         verifyStatic( Relocator.class, never() );
337         Relocator.relocate( eq( ForkedBooter.class.getName() ) );
338 
339         assertThat( cls ).isEqualTo( "org.apache.maven.surefire.booter.ForkedBooter" );
340         assertThat( confMock.isShadefire() ).isFalse();
341     }
342 }