View Javadoc
1   package org.apache.maven.plugin.surefire;
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.execution.MavenSession;
23  import org.apache.maven.plugin.MojoFailureException;
24  import org.apache.maven.surefire.shared.io.FilenameUtils;
25  import org.apache.maven.toolchain.Toolchain;
26  import org.apache.maven.toolchain.ToolchainManager;
27  import org.apache.maven.toolchain.java.DefaultJavaToolChain;
28  import org.codehaus.plexus.logging.Logger;
29  import org.fest.assertions.MapAssert;
30  import org.junit.Rule;
31  import org.junit.Test;
32  import org.junit.rules.ExpectedException;
33  import org.junit.runner.RunWith;
34  import org.mockito.ArgumentCaptor;
35  import org.powermock.core.classloader.annotations.PowerMockIgnore;
36  import org.powermock.core.classloader.annotations.PrepareForTest;
37  import org.powermock.modules.junit4.PowerMockRunner;
38  
39  import java.io.File;
40  import java.nio.file.Path;
41  import java.nio.file.Paths;
42  import java.util.Collections;
43  import java.util.List;
44  import java.util.Map;
45  
46  import static java.io.File.separatorChar;
47  import static java.util.Collections.emptyMap;
48  import static java.util.Collections.singletonList;
49  import static java.util.Collections.singletonMap;
50  import static junit.framework.TestCase.assertNull;
51  import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJre;
52  import static org.fest.assertions.Assertions.assertThat;
53  import static org.hamcrest.CoreMatchers.startsWith;
54  import static org.mockito.Mockito.times;
55  import static org.mockito.Mockito.verify;
56  import static org.powermock.api.mockito.PowerMockito.mock;
57  import static org.powermock.api.mockito.PowerMockito.mockStatic;
58  import static org.powermock.api.mockito.PowerMockito.when;
59  import static org.powermock.reflect.Whitebox.invokeMethod;
60  
61  /**
62   * Test for {@link AbstractSurefireMojo}. jdkToolchain parameter
63   */
64  @RunWith( PowerMockRunner.class )
65  @PrepareForTest( {AbstractSurefireMojo.class} )
66  @PowerMockIgnore( {"org.jacoco.agent.rt.*", "com.vladium.emma.rt.*"} )
67  public class AbstractSurefireMojoToolchainsTest
68  {
69      @Rule
70      public final ExpectedException e = ExpectedException.none();
71  
72      /**
73       * Ensure that we use the toolchain found by getToolchainMaven33x()
74       * when the jdkToolchain parameter is set.
75       */
76      @Test
77      public void shouldCallMaven33xMethodWhenSpecSet() throws Exception
78      {
79          AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
80          Toolchain expectedFromMaven33Method = mock( Toolchain.class );
81          MockToolchainManager toolchainManager = new MockToolchainManager( null, null );
82          mojo.setToolchainManager( toolchainManager );
83          mojo.setJdkToolchain( singletonMap( "version", "1.8" ) );
84  
85          mockStatic( AbstractSurefireMojo.class );
86          when(
87              AbstractSurefireMojo.class,
88              "getToolchainMaven33x",
89              ToolchainManager.class,
90              toolchainManager,
91              mojo.getSession(), mojo.getJdkToolchain() ).thenReturn( expectedFromMaven33Method );
92          Toolchain actual = invokeMethod( mojo, "getToolchain" );
93          assertThat( actual )
94              .isSameAs( expectedFromMaven33Method );
95      }
96  
97      /**
98       * Ensure that we use the toolchain from build context when
99       * no jdkToolchain map is configured in mojo parameters.
100      * getToolchain() returns the main maven toolchain from the build context
101      */
102     @Test
103     public void shouldFallthroughToBuildContextWhenNoSpecSet() throws Exception
104     {
105         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
106         Toolchain expectedFromContext = mock( Toolchain.class );
107         Toolchain expectedFromSpec = mock( Toolchain.class ); //ensure it still behaves correctly even if not null
108         mojo.setToolchainManager( new MockToolchainManager( expectedFromSpec, expectedFromContext ) );
109         Toolchain actual = invokeMethod( mojo, "getToolchain" );
110         assertThat( actual )
111             .isSameAs( expectedFromContext );
112     }
113 
114     @Test
115     public void shouldReturnNoToolchainInMaven32() throws Exception
116     {
117         Toolchain toolchain = invokeMethod( AbstractSurefireMojo.class,
118             "getToolchainMaven33x",
119             MockToolchainManagerMaven32.class,
120             new MockToolchainManagerMaven32( null ),
121             mock( MavenSession.class ),
122             emptyMap() );
123         assertNull( toolchain );
124     }
125 
126     @Test( expected = MojoFailureException.class )
127     public void shouldThrowMaven33xToolchain() throws Exception
128     {
129         invokeMethod(
130             AbstractSurefireMojo.class,
131             "getToolchainMaven33x",
132             MockToolchainManager.class,
133             new MockToolchainManager( null, null ),
134             mock( MavenSession.class ),
135             emptyMap() );
136     }
137 
138     @Test
139     public void shouldGetMaven33xToolchain() throws Exception
140     {
141         Toolchain expected = mock( Toolchain.class );
142         Toolchain actual = invokeMethod(
143             AbstractSurefireMojo.class,
144             "getToolchainMaven33x",
145             MockToolchainManager.class,
146             new MockToolchainManager( expected, null ),
147             mock( MavenSession.class ),
148             emptyMap() );
149 
150         assertThat( actual )
151             .isSameAs( expected );
152     }
153 
154     /**
155      * Ensures that the environmentVariables map for launching a test jvm
156      * contains a Toolchain-driven entry when toolchain is set.
157      */
158     @Test
159     public void shouldChangeJavaHomeFromToolchain() throws Exception
160     {
161         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
162         DefaultJavaToolChain toolchain = mock( DefaultJavaToolChain.class );
163         when( toolchain.findTool( "java" ) ).thenReturn( "/path/from/toolchain" );
164         when( toolchain.getJavaHome() ).thenReturn( "/some/path" );
165         mojo.setToolchain( toolchain );
166 
167         assertThat( mojo.getEnvironmentVariables() ).isEmpty();
168         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
169         assertThat( mojo.getEnvironmentVariables() ).includes( MapAssert.entry( "JAVA_HOME", "/some/path" ) );
170         assertThat( effectiveJvm.getJvmExecutable().getPath() )
171             .contains( "/path/from/toolchain".replace( '/', separatorChar ) );
172     }
173 
174     @Test
175     public void shouldNotChangeJavaHomeFromToolchainIfAlreadySet() throws Exception
176     {
177         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
178         mojo.setEnvironmentVariables( singletonMap( "JAVA_HOME", "/already/set/path" ) );
179 
180         DefaultJavaToolChain toolchain = mock( DefaultJavaToolChain.class );
181         when( toolchain.findTool( "java" ) ).thenReturn( "/path/from/toolchain" );
182         when( toolchain.getJavaHome() ).thenReturn( "/some/path" );
183         mojo.setToolchain( toolchain );
184 
185         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
186         assertThat( mojo.getEnvironmentVariables() ).includes( MapAssert.entry( "JAVA_HOME", "/already/set/path" ) );
187         assertThat( effectiveJvm.getJvmExecutable().getPath() )
188             .contains( "/path/from/toolchain".replace( '/', separatorChar ) );
189     }
190 
191     /**
192      * Ensures that the environmentVariables map for launching a test jvm
193      * contains a jvm parameter-driven entry when jvm is set.
194      */
195     @Test
196     public void shouldChangeJavaHomeFromJvm() throws Exception
197     {
198         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
199 
200         File currentJdkHome = toJdkHomeFromJre();
201         String javaExecutablePath = FilenameUtils.concat(
202             currentJdkHome.getAbsolutePath(), "bin/java" );
203 
204         mojo.setJvm( javaExecutablePath );
205 
206         assertThat( mojo.getEnvironmentVariables() ).isEmpty();
207         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
208         assertThat( mojo.getEnvironmentVariables() )
209             .includes( MapAssert.entry( "JAVA_HOME", currentJdkHome.getAbsolutePath() ) );
210         assertThat( effectiveJvm.getJvmExecutable().getPath() ).contains( javaExecutablePath );
211     }
212 
213     /**
214      * Ensures that users can manually configure a value for JAVA_HOME
215      * and we will not override it
216      */
217     @Test
218     public void shouldNotChangeJavaHomeFromJvmIfAlreadySet() throws Exception
219     {
220         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
221         mojo.setEnvironmentVariables( singletonMap( "JAVA_HOME", "/already/set/path" ) );
222 
223         File currentJdkHome = toJdkHomeFromJre();
224         String javaExecutablePath = FilenameUtils.concat( currentJdkHome.getAbsolutePath(), "bin/java" );
225 
226         mojo.setJvm( javaExecutablePath );
227 
228         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
229         assertThat( mojo.getEnvironmentVariables() )
230             .includes( MapAssert.entry( "JAVA_HOME", "/already/set/path" ) );
231         assertThat( effectiveJvm.getJvmExecutable().getPath() ).contains( javaExecutablePath );
232     }
233 
234     @Test
235     public void withoutJvmAndToolchain() throws Exception
236     {
237         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
238         Logger logger = mock( Logger.class );
239         mojo.setLogger( logger );
240         ArgumentCaptor<String> argument = ArgumentCaptor.forClass( String.class );
241         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
242 
243         assertThat( mojo.getJvm() )
244             .isNull();
245 
246         assertThat( mojo.getEnvironmentVariables() )
247             .isEmpty();
248 
249         assertThat( effectiveJvm )
250             .isNotNull();
251 
252         assertThat( effectiveJvm.getJvmExecutable() )
253             .isNotNull();
254 
255         Path javaHome = Paths.get( System.getProperty( "java.home" ) ).normalize();
256         boolean isLocalJvm = effectiveJvm.getJvmExecutable().toPath().normalize().startsWith( javaHome );
257         assertThat( isLocalJvm )
258             .isTrue();
259 
260         verify( logger, times( 1 ) )
261             .debug( argument.capture() );
262 
263         assertThat( argument.getValue() )
264             .startsWith( "Using JVM: " + System.getProperty( "java.home" ) );
265     }
266 
267     @Test
268     public void shouldFailWithWrongJvmExecPath() throws Exception
269     {
270         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
271         mojo.setLogger( mock( Logger.class ) );
272         mojo.setJvm( System.getProperty( "user.dir" ) );
273 
274         e.expect( MojoFailureException.class );
275         e.expectMessage( startsWith( "Given path does not end with java executor" ) );
276 
277         invokeMethod( mojo, "getEffectiveJvm" );
278     }
279 
280     /**
281      * Mocks a ToolchainManager
282      */
283     public static final class MockToolchainManager extends MockToolchainManagerMaven32
284     {
285         private final Toolchain specToolchain;
286 
287         public MockToolchainManager( Toolchain specToolchain, Toolchain buildContextToolchain )
288         {
289             super( buildContextToolchain );
290             this.specToolchain = specToolchain;
291         }
292 
293         public List<Toolchain> getToolchains( MavenSession session, String type, Map<String, String> requirements )
294         {
295             return specToolchain == null ? Collections.<Toolchain>emptyList() : singletonList( specToolchain );
296         }
297     }
298 
299     /**
300      * Mocks an older version that does not implement getToolchains()
301      * returns provided toolchain
302      */
303     public static class MockToolchainManagerMaven32 implements ToolchainManager
304     {
305 
306         private final Toolchain buildContextToolchain;
307 
308         public MockToolchainManagerMaven32( Toolchain buildContextToolchain )
309         {
310             this.buildContextToolchain = buildContextToolchain;
311         }
312 
313         @Override
314         public Toolchain getToolchainFromBuildContext( String type, MavenSession context )
315         {
316             return buildContextToolchain;
317         }
318     }
319 }