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<String, String>();
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 ) throws SurefireBooterForkException
105             {
106 
107             }
108         };
109 
110         DefaultForkConfiguration mockedConfig = spy( config );
111         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
112         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
113         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
114         assertThat( newArgLine ).isEmpty();
115     }
116 
117     @Test
118     public void shouldBeEmptyArgLine() throws Exception
119     {
120         argLine = "";
121         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
122                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
123                 pluginPlatform, log )
124         {
125 
126             @Override
127             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
128                                              @Nonnull String booterThatHasMainMethod,
129                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
130             {
131 
132             }
133         };
134 
135         DefaultForkConfiguration mockedConfig = spy( config );
136         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
137         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
138         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
139         assertThat( newArgLine ).isEmpty();
140     }
141 
142     @Test
143     public void shouldBeEmptyArgLineInsteadOfNewLines() throws Exception
144     {
145         argLine = "\n\r";
146         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
147                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
148                 pluginPlatform, log )
149         {
150 
151             @Override
152             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
153                                              @Nonnull String booterThatHasMainMethod,
154                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
155             {
156 
157             }
158         };
159 
160         DefaultForkConfiguration mockedConfig = spy( config );
161         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
162         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
163         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "" ) );
164         assertThat( newArgLine ).isEmpty();
165     }
166 
167     @Test
168     public void shouldBeWithoutEscaping() throws Exception
169     {
170         argLine = "-Dfile.encoding=UTF-8";
171         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
172                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
173                 pluginPlatform, log )
174         {
175 
176             @Override
177             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
178                                              @Nonnull String booterThatHasMainMethod,
179                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
180             {
181 
182             }
183         };
184 
185         DefaultForkConfiguration mockedConfig = spy( config );
186         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
187         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
188         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
189         assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
190     }
191 
192     @Test
193     public void shouldBeWithEscaping() throws Exception
194     {
195         modelProperties.put( "encoding", "UTF-8" );
196         argLine = "-Dfile.encoding=@{encoding}";
197         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
198                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
199                 pluginPlatform, log )
200         {
201 
202             @Override
203             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
204                                              @Nonnull String booterThatHasMainMethod,
205                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
206             {
207 
208             }
209         };
210 
211         DefaultForkConfiguration mockedConfig = spy( config );
212         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
213         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
214         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dfile.encoding=UTF-8" ) );
215         assertThat( newArgLine ).isEqualTo( "-Dfile.encoding=UTF-8" );
216     }
217 
218     @Test
219     public void shouldBeWhitespaceInsteadOfNewLines() throws Exception
220     {
221         argLine = "a\n\rb";
222         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
223                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
224                 pluginPlatform, log )
225         {
226 
227             @Override
228             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
229                                              @Nonnull String booterThatHasMainMethod,
230                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
231             {
232 
233             }
234         };
235 
236         DefaultForkConfiguration mockedConfig = spy( config );
237         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
238         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
239         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "a  b" ) );
240         assertThat( newArgLine ).isEqualTo( "a  b" );
241     }
242 
243     @Test
244     public void shouldEscapeThreadNumber() throws Exception
245     {
246         argLine = "-Dthread=${surefire.threadNumber}";
247         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
248                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
249                 pluginPlatform, log )
250         {
251 
252             @Override
253             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
254                                              @Nonnull String booterThatHasMainMethod,
255                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
256             {
257 
258             }
259         };
260 
261         DefaultForkConfiguration mockedConfig = spy( config );
262         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
263         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
264         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
265         assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
266     }
267 
268     @Test
269     public void shouldEscapeForkNumber() throws Exception
270     {
271         argLine = "-Dthread=${surefire.forkNumber}";
272         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
273                 workingDirectory, modelProperties, argLine, environmentVariables, debug, forkCount, reuseForks,
274                 pluginPlatform, log )
275         {
276 
277             @Override
278             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
279                                              @Nonnull String booterThatHasMainMethod,
280                                              @Nonnull StartupConfiguration config ) throws SurefireBooterForkException
281             {
282 
283             }
284         };
285 
286         DefaultForkConfiguration mockedConfig = spy( config );
287         String newArgLine = invokeMethod( mockedConfig, "newJvmArgLine", new Class[] { int.class }, 2 );
288         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "interpolateArgLineWithPropertyExpressions" );
289         verifyPrivate( mockedConfig, times( 1 ) ).invoke( "extendJvmArgLine", eq( "-Dthread=" + forkCount ) );
290         assertThat( newArgLine ).isEqualTo( "-Dthread=" + forkCount );
291     }
292 
293     @Test
294     public void shouldRelocateBooterClassWhenShadefire() throws Exception
295     {
296         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
297         ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
298         StartupConfiguration conf =
299                 new StartupConfiguration( "org.apache.maven.surefire.shadefire.MyProvider", cc, clc, false, false );
300         StartupConfiguration confMock = spy( conf );
301         mockStatic( Relocator.class );
302         when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
303 
304         String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
305 
306         verify( confMock, times( 1 ) ).isShadefire();
307         verifyStatic( Relocator.class, times( 1 ) );
308         Relocator.relocate( eq( ForkedBooter.class.getName() ) );
309 
310         assertThat( cls ).isEqualTo( "org.apache.maven.surefire.shadefire.booter.ForkedBooter" );
311         assertThat( confMock.isShadefire() ).isTrue();
312     }
313 
314     @Test
315     public void shouldNotRelocateBooterClass() throws Exception
316     {
317         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
318         ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
319         StartupConfiguration conf =
320                 new StartupConfiguration( "org.apache.maven.surefire.MyProvider", cc, clc, false, false );
321         StartupConfiguration confMock = spy( conf );
322         mockStatic( Relocator.class );
323         when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
324 
325         String cls = invokeMethod( DefaultForkConfiguration.class, "findStartClass", confMock );
326 
327         verify( confMock, times( 1 ) ).isShadefire();
328         verifyStatic( Relocator.class, never() );
329         Relocator.relocate( eq( ForkedBooter.class.getName() ) );
330 
331         assertThat( cls ).isEqualTo( "org.apache.maven.surefire.booter.ForkedBooter" );
332         assertThat( confMock.isShadefire() ).isFalse();
333     }
334 }