This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
The following commit(s) were added to refs/heads/master by this push: new e0304d9 [SUREFIRE-1234] Allow to configure JVM for tests by referencing a toolchain entry e0304d9 is described below commit e0304d973bcb24a020aa8fb0d05e6230b217a577 Author: Tibor Digana <tibordig...@apache.org> AuthorDate: Fri Apr 24 21:27:20 2020 +0200 [SUREFIRE-1234] Allow to configure JVM for tests by referencing a toolchain entry --- .../plugin/surefire/AbstractSurefireMojo.java | 95 ++++++++++- .../AbstractSurefireMojoToolchainsTest.java | 176 +++++++++++++++++++++ .../org/apache/maven/surefire/JUnit4SuiteTest.java | 2 + .../src/site/apt/examples/toolchains.apt.vm | 56 +++++++ maven-surefire-plugin/src/site/markdown/java9.md | 2 + maven-surefire-plugin/src/site/site.xml | 1 + 6 files changed, 330 insertions(+), 2 deletions(-) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index b26e981..112de82 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -97,6 +97,7 @@ import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult; import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Paths; @@ -141,6 +142,8 @@ import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJvmExec; import static org.apache.maven.surefire.booter.SystemUtils.toJdkVersionFromReleaseFile; import static org.apache.maven.surefire.suite.RunResult.failure; import static org.apache.maven.surefire.suite.RunResult.noTestsRun; +import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray; +import static org.apache.maven.surefire.util.ReflectionUtils.tryGetMethod; /** * Abstract base class for running tests using Surefire. @@ -765,6 +768,42 @@ public abstract class AbstractSurefireMojo private String[] dependenciesToScan; /** + * <p> + * Allow for configuration of the test jvm via maven toolchains. + * This permits a configuration where the project is built with one jvm and tested with another. + * This is similar to {@link #jvm}, but avoids hardcoding paths. + * The two parameters are mutually exclusive (jvm wins) + * </p> + * + * <p>Examples:</p> + * (see <a href="https://maven.apache.org/guides/mini/guide-using-toolchains.html"> + * Guide to Toolchains</a> for more info) + * + * <pre> + * {@code + * <configuration> + * ... + * <jdkToolchain> + * <version>1.11</version> + * </jdkToolchain> + * </configuration> + * + * <configuration> + * ... + * <jdkToolchain> + * <version>1.8</version> + * <vendor>zulu</vendor> + * </jdkToolchain> + * </configuration> + * } + * </pre> + * + * @since 3.0.0-M5 and Maven 3.3.x + */ + @Parameter + private Map<String, String> jdkToolchain; + + /** * */ @Component @@ -909,7 +948,49 @@ public abstract class AbstractSurefireMojo return consoleLogger; } - private void setupStuff() + private static <T extends ToolchainManager> Toolchain getToolchainMaven33x( Class<T> toolchainManagerType, + T toolchainManager, + MavenSession session, + Map<String, String> toolchainArgs ) + throws MojoFailureException + { + Method getToolchainsMethod = + tryGetMethod( toolchainManagerType, "getToolchains", MavenSession.class, String.class, Map.class ); + if ( getToolchainsMethod != null ) + { + //noinspection unchecked + List<Toolchain> tcs = (List<Toolchain>) invokeMethodWithArray( toolchainManager, + getToolchainsMethod, session, "jdk", toolchainArgs ); + if ( tcs.isEmpty() ) + { + throw new MojoFailureException( + "Requested toolchain specification did not match any configured toolchain: " + toolchainArgs ); + } + return tcs.get( 0 ); + } + return null; + } + + //TODO remove the part with ToolchainManager lookup once we depend on + //3.0.9 (have it as prerequisite). Define as regular component field then. + private Toolchain getToolchain() throws MojoFailureException + { + Toolchain tc = null; + + if ( getJdkToolchain() != null ) + { + tc = getToolchainMaven33x( ToolchainManager.class, getToolchainManager(), getSession(), getJdkToolchain() ); + } + + if ( tc == null ) + { + tc = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() ); + } + + return tc; + } + + private void setupStuff() throws MojoFailureException { surefireDependencyResolver = new SurefireDependencyResolver( getRepositorySystem(), getConsoleLogger(), getLocalRepository(), @@ -925,7 +1006,7 @@ public abstract class AbstractSurefireMojo if ( getToolchainManager() != null ) { - toolchain = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() ); + toolchain = getToolchain(); } } @@ -3865,6 +3946,16 @@ public abstract class AbstractSurefireMojo SurefireHelper.logDebugOrCliShowErrors( s, getConsoleLogger(), cli ); } + public Map<String, String> getJdkToolchain() + { + return jdkToolchain; + } + + public void setJdkToolchain( Map<String, String> jdkToolchain ) + { + this.jdkToolchain = jdkToolchain; + } + public String getTempDir() { return tempDir; diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java new file mode 100644 index 0000000..7589718 --- /dev/null +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java @@ -0,0 +1,176 @@ +package org.apache.maven.plugin.surefire; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.toolchain.Toolchain; +import org.apache.maven.toolchain.ToolchainManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static junit.framework.TestCase.assertNull; +import static org.fest.assertions.Assertions.assertThat; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.reflect.Whitebox.invokeMethod; + +/** + * Test for {@link AbstractSurefireMojo}. jdkToolchain parameter + */ +@RunWith( PowerMockRunner.class ) +@PrepareForTest( {AbstractSurefireMojo.class} ) +@PowerMockIgnore( {"org.jacoco.agent.rt.*", "com.vladium.emma.rt.*"} ) +public class AbstractSurefireMojoToolchainsTest +{ + + /** + * Ensure that we use the toolchain found by getToolchainMaven33x() + * when the jdkToolchain parameter is set. + */ + @Test + public void shouldCallMaven33xMethodWhenSpecSet() throws Exception + { + AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo(); + Toolchain expectedFromMaven33Method = mock( Toolchain.class ); + MockToolchainManager toolchainManager = new MockToolchainManager( null, null ); + mojo.setToolchainManager( toolchainManager ); + mojo.setJdkToolchain( singletonMap( "version", "1.8" ) ); + + mockStatic( AbstractSurefireMojo.class ); + when( + AbstractSurefireMojo.class, + "getToolchainMaven33x", + ToolchainManager.class, + toolchainManager, + mojo.getSession(), mojo.getJdkToolchain() ).thenReturn( expectedFromMaven33Method ); + Toolchain actual = invokeMethod( mojo, "getToolchain" ); + assertThat( actual ) + .isSameAs( expectedFromMaven33Method ); + } + + /** + * Ensure that we use the toolchain from build context when + * no jdkToolchain map is configured in mojo parameters. + * getToolchain() returns the main maven toolchain from the build context + */ + @Test + public void shouldFallthroughToBuildContextWhenNoSpecSet() throws Exception + { + AbstractSurefireMojoTest.Mojo mojo = new AbstractSurefireMojoTest.Mojo(); + Toolchain expectedFromContext = mock( Toolchain.class ); + Toolchain expectedFromSpec = mock( Toolchain.class ); //ensure it still behaves correctly even if not null + mojo.setToolchainManager( new MockToolchainManager( expectedFromSpec, expectedFromContext ) ); + Toolchain actual = invokeMethod( mojo, "getToolchain" ); + assertThat( actual ) + .isSameAs( expectedFromContext ); + } + + @Test + public void shouldReturnNoToolchainInMaven32() throws Exception + { + Toolchain toolchain = invokeMethod( AbstractSurefireMojo.class, + "getToolchainMaven33x", + MockToolchainManagerMaven32.class, + new MockToolchainManagerMaven32( null ), + mock( MavenSession.class ), + emptyMap() ); + assertNull( toolchain ); + } + + @Test( expected = MojoFailureException.class ) + public void shouldThrowMaven33xToolchain() throws Exception + { + invokeMethod( + AbstractSurefireMojo.class, + "getToolchainMaven33x", + MockToolchainManager.class, + new MockToolchainManager( null, null ), + mock( MavenSession.class ), + emptyMap() ); + } + + @Test + public void shouldGetMaven33xToolchain() throws Exception + { + Toolchain expected = mock( Toolchain.class ); + Toolchain actual = invokeMethod( + AbstractSurefireMojo.class, + "getToolchainMaven33x", + MockToolchainManager.class, + new MockToolchainManager( expected, null ), + mock( MavenSession.class ), + emptyMap() ); + + assertThat( actual ) + .isSameAs( expected ); + } + + /** + * Mocks a ToolchainManager + */ + public static final class MockToolchainManager extends MockToolchainManagerMaven32 + { + private final Toolchain specToolchain; + + public MockToolchainManager( Toolchain specToolchain, Toolchain buildContextToolchain ) + { + super( buildContextToolchain ); + this.specToolchain = specToolchain; + } + + public List<Toolchain> getToolchains( MavenSession session, String type, Map<String, String> requirements ) + { + return specToolchain == null ? Collections.<Toolchain>emptyList() : singletonList( specToolchain ); + } + } + + /** + * Mocks an older version that does not implement getToolchains() + * returns provided toolchain + */ + public static class MockToolchainManagerMaven32 implements ToolchainManager + { + + private final Toolchain buildContextToolchain; + + public MockToolchainManagerMaven32( Toolchain buildContextToolchain ) + { + this.buildContextToolchain = buildContextToolchain; + } + + @Override + public Toolchain getToolchainFromBuildContext( String type, MavenSession context ) + { + return buildContextToolchain; + } + } +} diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java index 22bf702..36425ed 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java @@ -25,6 +25,7 @@ import junit.framework.TestCase; import junit.framework.TestSuite; import org.apache.maven.plugin.surefire.AbstractSurefireMojoJava7PlusTest; import org.apache.maven.plugin.surefire.AbstractSurefireMojoTest; +import org.apache.maven.plugin.surefire.AbstractSurefireMojoToolchainsTest; import org.apache.maven.plugin.surefire.CommonReflectorTest; import org.apache.maven.plugin.surefire.MojoMocklessTest; import org.apache.maven.plugin.surefire.SurefireHelperTest; @@ -97,6 +98,7 @@ public class JUnit4SuiteTest extends TestCase suite.addTest( new JUnit4TestAdapter( JarManifestForkConfigurationTest.class ) ); suite.addTest( new JUnit4TestAdapter( ModularClasspathForkConfigurationTest.class ) ); suite.addTest( new JUnit4TestAdapter( AbstractSurefireMojoJava7PlusTest.class ) ); + suite.addTest( new JUnit4TestAdapter( AbstractSurefireMojoToolchainsTest.class ) ); suite.addTest( new JUnit4TestAdapter( ScannerUtilTest.class ) ); suite.addTest( new JUnit4TestAdapter( MojoMocklessTest.class ) ); suite.addTest( new JUnit4TestAdapter( ForkClientTest.class ) ); diff --git a/maven-surefire-plugin/src/site/apt/examples/toolchains.apt.vm b/maven-surefire-plugin/src/site/apt/examples/toolchains.apt.vm new file mode 100644 index 0000000..891f8b1 --- /dev/null +++ b/maven-surefire-plugin/src/site/apt/examples/toolchains.apt.vm @@ -0,0 +1,56 @@ + ------ + Using Maven Toolchains + ------ + Akom <akom> + ------ + 2020-04-17 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + + For general information about Maven Toolchains, see + {{{https://maven.apache.org/guides/mini/guide-using-toolchains.html}Guide to Using Toolchains}} + + +Using Maven Toolchains with ${thisPlugin}. + + By default, if the pom configures the toolchains plugin as specified in the aforementioned + guide, ${thisPlugin} will launch the test jvm using the main toolchain + configured in Maven. + + In some cases, it may be desirable to compile and test using different jvms. + While the <<<jvm>>> option can achieve this, it requires hardcoding system-specific paths. + Configuration option <<<jdkToolchain>>> can be used to supply an alternate toolchain specification. + +* Configuring a different jvm for running tests using toolchains + ++---+ +<configuration> + [...] + <jdkToolchain> + <version>1.11</version> + <vendor>zulu</vendor> + </jdkToolchain> + [...] +</configuration> ++---+ + + The above example assumes that your toolchains.xml contains a valid entry with these values. diff --git a/maven-surefire-plugin/src/site/markdown/java9.md b/maven-surefire-plugin/src/site/markdown/java9.md index 4ba2567..17157ab 100644 --- a/maven-surefire-plugin/src/site/markdown/java9.md +++ b/maven-surefire-plugin/src/site/markdown/java9.md @@ -125,3 +125,5 @@ Your POM should specify the plugin which activates only particular JDK in *toolc </plugin> Now you can run the build with tests on the top of Java 9. + +Also see the [full documentation for surefire toolchains](examples/toolchains.html) configuration options. diff --git a/maven-surefire-plugin/src/site/site.xml b/maven-surefire-plugin/src/site/site.xml index 2cec467..cbee436 100644 --- a/maven-surefire-plugin/src/site/site.xml +++ b/maven-surefire-plugin/src/site/site.xml @@ -62,6 +62,7 @@ <item name="Shutdown of Forked JVM" href="examples/shutdown.html"/> <item name="Run tests with Java 9" href="java9.html"/> <item name="Run tests in Docker" href="docker.html"/> + <item name="Run tests in a different JVM using toolchains" href="examples/toolchains.html"/> </menu> </body> </project>