View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.surefire;
20  
21  import java.io.File;
22  import java.nio.file.Path;
23  import java.nio.file.Paths;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.maven.execution.MavenSession;
29  import org.apache.maven.plugin.MojoFailureException;
30  import org.apache.maven.surefire.shared.io.FilenameUtils;
31  import org.apache.maven.toolchain.Toolchain;
32  import org.apache.maven.toolchain.ToolchainManager;
33  import org.apache.maven.toolchain.java.DefaultJavaToolChain;
34  import org.codehaus.plexus.logging.Logger;
35  import org.junit.Ignore;
36  import org.junit.Rule;
37  import org.junit.Test;
38  import org.junit.rules.ExpectedException;
39  import org.junit.runner.RunWith;
40  import org.mockito.ArgumentCaptor;
41  import org.powermock.core.classloader.annotations.PowerMockIgnore;
42  import org.powermock.core.classloader.annotations.PrepareForTest;
43  import org.powermock.modules.junit4.PowerMockRunner;
44  
45  import static java.io.File.separatorChar;
46  import static java.util.Collections.singletonList;
47  import static java.util.Collections.singletonMap;
48  import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJre;
49  import static org.assertj.core.api.Assertions.assertThat;
50  import static org.hamcrest.CoreMatchers.startsWith;
51  import static org.mockito.Mockito.times;
52  import static org.mockito.Mockito.verify;
53  import static org.powermock.api.mockito.PowerMockito.mock;
54  import static org.powermock.api.mockito.PowerMockito.when;
55  import static org.powermock.reflect.Whitebox.invokeMethod;
56  
57  /**
58   * Test for {@link AbstractSurefireMojo}. jdkToolchain parameter
59   */
60  @RunWith(PowerMockRunner.class)
61  @PrepareForTest({AbstractSurefireMojo.class})
62  @PowerMockIgnore({"org.jacoco.agent.rt.*", "com.vladium.emma.rt.*"})
63  public class AbstractSurefireMojoToolchainsTest {
64      @Rule
65      public final ExpectedException e = ExpectedException.none();
66  
67      /**
68       * Ensure that we use the toolchain found by getToolchain()
69       * when the jdkToolchain parameter is set.
70       */
71      @Test
72      public void shouldCallMethodWhenSpecSet() throws Exception {
73          AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
74          Toolchain expectedMethod = mock(Toolchain.class);
75          MockToolchainManager toolchainManager = new MockToolchainManager(expectedMethod, null);
76          mojo.setToolchainManager(toolchainManager);
77          mojo.setJdkToolchain(singletonMap("version", "1.8"));
78          Toolchain actual = invokeMethod(mojo, "getToolchain");
79          assertThat(actual).isSameAs(expectedMethod);
80      }
81  
82      /**
83       * Ensure that we use the toolchain from build context when
84       * no jdkToolchain map is configured in mojo parameters.
85       * getToolchain() returns the main maven toolchain from the build context
86       */
87      @Test
88      public void shouldFallthroughToBuildContextWhenNoSpecSet() throws Exception {
89          AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
90          Toolchain expectedFromContext = mock(Toolchain.class);
91          Toolchain expectedFromSpec = mock(Toolchain.class); // ensure it still behaves correctly even if not null
92          mojo.setToolchainManager(new MockToolchainManager(expectedFromSpec, expectedFromContext));
93          Toolchain actual = invokeMethod(mojo, "getToolchain");
94          assertThat(actual).isSameAs(expectedFromContext);
95      }
96  
97      // TODO Is this still required?
98      @Test(expected = MojoFailureException.class)
99      @Ignore
100     public void shouldThrowToolchain() throws Exception {
101         invokeMethod(AbstractSurefireMojo.class, "getToolchain");
102     }
103 
104     // TODO Is this still required?
105     @Test
106     @Ignore
107     public void shouldGetToolchain() throws Exception {
108         Toolchain expected = mock(Toolchain.class);
109         Toolchain actual = invokeMethod(AbstractSurefireMojo.class, "getToolchain");
110 
111         assertThat(actual).isSameAs(expected);
112     }
113 
114     /**
115      * Ensures that the environmentVariables map for launching a test jvm
116      * contains a Toolchain-driven entry when toolchain is set.
117      */
118     @Test
119     public void shouldChangeJavaHomeFromToolchain() throws Exception {
120         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
121         DefaultJavaToolChain toolchain = mock(DefaultJavaToolChain.class);
122         when(toolchain.findTool("java")).thenReturn("/path/from/toolchain");
123         when(toolchain.getJavaHome()).thenReturn("/some/path");
124         mojo.setToolchain(toolchain);
125 
126         assertThat(mojo.getEnvironmentVariables()).isEmpty();
127         JdkAttributes effectiveJvm = invokeMethod(mojo, "getEffectiveJvm");
128         assertThat(mojo.getEnvironmentVariables()).containsEntry("JAVA_HOME", "/some/path");
129         assertThat(effectiveJvm.getJvmExecutable().getPath())
130                 .contains("/path/from/toolchain".replace('/', separatorChar));
131     }
132 
133     @Test
134     public void shouldNotChangeJavaHomeFromToolchainIfAlreadySet() throws Exception {
135         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
136         mojo.setEnvironmentVariables(singletonMap("JAVA_HOME", "/already/set/path"));
137 
138         DefaultJavaToolChain toolchain = mock(DefaultJavaToolChain.class);
139         when(toolchain.findTool("java")).thenReturn("/path/from/toolchain");
140         when(toolchain.getJavaHome()).thenReturn("/some/path");
141         mojo.setToolchain(toolchain);
142 
143         JdkAttributes effectiveJvm = invokeMethod(mojo, "getEffectiveJvm");
144         assertThat(mojo.getEnvironmentVariables()).containsEntry("JAVA_HOME", "/already/set/path");
145         assertThat(effectiveJvm.getJvmExecutable().getPath())
146                 .contains("/path/from/toolchain".replace('/', separatorChar));
147     }
148 
149     /**
150      * Ensures that the environmentVariables map for launching a test jvm
151      * contains a jvm parameter-driven entry when jvm is set.
152      */
153     @Test
154     public void shouldChangeJavaHomeFromJvm() throws Exception {
155         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
156 
157         File currentJdkHome = toJdkHomeFromJre();
158         String javaExecutablePath = FilenameUtils.concat(currentJdkHome.getAbsolutePath(), "bin/java");
159 
160         mojo.setJvm(javaExecutablePath);
161 
162         assertThat(mojo.getEnvironmentVariables()).isEmpty();
163         JdkAttributes effectiveJvm = invokeMethod(mojo, "getEffectiveJvm");
164         assertThat(mojo.getEnvironmentVariables()).containsEntry("JAVA_HOME", currentJdkHome.getAbsolutePath());
165         assertThat(effectiveJvm.getJvmExecutable().getPath()).contains(javaExecutablePath);
166     }
167 
168     /**
169      * Ensures that users can manually configure a value for JAVA_HOME
170      * and we will not override it
171      */
172     @Test
173     public void shouldNotChangeJavaHomeFromJvmIfAlreadySet() throws Exception {
174         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
175         mojo.setEnvironmentVariables(singletonMap("JAVA_HOME", "/already/set/path"));
176 
177         File currentJdkHome = toJdkHomeFromJre();
178         String javaExecutablePath = FilenameUtils.concat(currentJdkHome.getAbsolutePath(), "bin/java");
179 
180         mojo.setJvm(javaExecutablePath);
181 
182         JdkAttributes effectiveJvm = invokeMethod(mojo, "getEffectiveJvm");
183         assertThat(mojo.getEnvironmentVariables()).containsEntry("JAVA_HOME", "/already/set/path");
184         assertThat(effectiveJvm.getJvmExecutable().getPath()).contains(javaExecutablePath);
185     }
186 
187     @Test
188     public void withoutJvmAndToolchain() throws Exception {
189         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
190         Logger logger = mock(Logger.class);
191         mojo.setLogger(logger);
192         ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
193         JdkAttributes effectiveJvm = invokeMethod(mojo, "getEffectiveJvm");
194 
195         assertThat(mojo.getJvm()).isNull();
196 
197         assertThat(mojo.getEnvironmentVariables()).isEmpty();
198 
199         assertThat(effectiveJvm).isNotNull();
200 
201         assertThat(effectiveJvm.getJvmExecutable()).isNotNull();
202 
203         Path javaHome = Paths.get(System.getProperty("java.home")).normalize();
204         boolean isLocalJvm =
205                 effectiveJvm.getJvmExecutable().toPath().normalize().startsWith(javaHome);
206         assertThat(isLocalJvm).isTrue();
207 
208         verify(logger, times(1)).debug(argument.capture());
209 
210         assertThat(argument.getValue()).startsWith("Using JVM: " + System.getProperty("java.home"));
211     }
212 
213     @Test
214     public void shouldFailWithWrongJvmExecPath() throws Exception {
215         AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo();
216         mojo.setLogger(mock(Logger.class));
217         mojo.setJvm(System.getProperty("user.dir"));
218 
219         e.expect(MojoFailureException.class);
220         e.expectMessage(startsWith("Given path does not end with java executor"));
221 
222         invokeMethod(mojo, "getEffectiveJvm");
223     }
224 
225     /**
226      * Mocks a ToolchainManager
227      */
228     public static final class MockToolchainManager implements ToolchainManager {
229 
230         private final Toolchain specToolchain;
231         private final Toolchain buildContextToolchain;
232 
233         public MockToolchainManager(Toolchain specToolchain, Toolchain buildContextToolchain) {
234             this.specToolchain = specToolchain;
235             this.buildContextToolchain = buildContextToolchain;
236         }
237 
238         @Override
239         public Toolchain getToolchainFromBuildContext(String type, MavenSession context) {
240             return buildContextToolchain;
241         }
242 
243         @Override
244         public List<Toolchain> getToolchains(MavenSession session, String type, Map<String, String> requirements) {
245             return specToolchain == null ? Collections.<Toolchain>emptyList() : singletonList(specToolchain);
246         }
247     }
248 }