Contract maven plugin - incremental test generation (#1361)
* Contract maven plugin - incremental test generation * incremental stubs and stub jar generation
This commit is contained in:
@@ -253,6 +253,9 @@ extends `com.example.base.BaseClass`. This setting takes precedence over
|
||||
components. Those properties might be used by (for example) built-in or custom Stub Downloaders.
|
||||
* `failOnNoContracts`: When enabled, will throw an exception when no contracts were found. Defaults to `true`.
|
||||
* `failOnInProgress`: If set to true then if any contracts that are in progress are found, will break the build. On the producer side you need to be explicit about the fact that you have contracts in progress and take into consideration that you might be causing false positive test execution results on the consumer side.. Defaults to `true`.
|
||||
* `incrementalContractTests`: When enabled, tests are created only when contracts have changed since last build. Defaults to `true`.
|
||||
* `incrementalContractStubs`: When enabled, stubs are created only when contracts have changed since last build. Defaults to `true`.
|
||||
* `incrementalContractStubsJar`: When enabled, stubs jar is created only when stubs have changed since last build. Defaults to `true`.
|
||||
|
||||
If you want to download your contract definitions from a Maven repository, you can use
|
||||
the following options:
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<maven-site-plugin.version>3.8.2</maven-site-plugin.version>
|
||||
<plexus-component-metadata.version>2.0.0</plexus-component-metadata.version>
|
||||
<maven-plugin-testing-harness.version>3.3.0</maven-plugin-testing-harness.version>
|
||||
<maven-shared-incremental.version>1.1</maven-shared-incremental.version>
|
||||
</properties>
|
||||
|
||||
<inceptionYear>2016</inceptionYear>
|
||||
@@ -367,6 +368,11 @@
|
||||
<artifactId>plexus-archiver</artifactId>
|
||||
<version>${plexus-archiver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.shared</groupId>
|
||||
<artifactId>maven-shared-incremental</artifactId>
|
||||
<version>${maven-shared-incremental.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.maven.verifier;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.plugin.MojoExecution;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.shared.incremental.IncrementalBuildHelper;
|
||||
import org.apache.maven.shared.utils.io.DirectoryScanner;
|
||||
|
||||
final class ChangeDetector {
|
||||
|
||||
private ChangeDetector() {
|
||||
}
|
||||
|
||||
static boolean inputFilesChangeDetected(File contractsDirectory,
|
||||
MojoExecution mojoExecution, MavenSession session)
|
||||
throws MojoExecutionException {
|
||||
|
||||
IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper(
|
||||
mojoExecution, session);
|
||||
|
||||
DirectoryScanner scanner = incrementalBuildHelper.getDirectoryScanner();
|
||||
scanner.setBasedir(contractsDirectory);
|
||||
scanner.scan();
|
||||
boolean changeDetected = incrementalBuildHelper.inputFileTreeChanged(scanner);
|
||||
if (scanner.getIncludedFiles().length == 0) {
|
||||
// at least one input file must exist to consider changed/unchanged
|
||||
// return true to skip incremental build and make visible no input file at all
|
||||
return true;
|
||||
}
|
||||
return changeDetected;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import java.util.Map;
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecution;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
@@ -37,6 +38,8 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig
|
||||
import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter;
|
||||
import org.springframework.cloud.contract.verifier.converter.ToYamlConverter;
|
||||
|
||||
import static org.springframework.cloud.contract.maven.verifier.ChangeDetector.inputFilesChangeDetected;
|
||||
|
||||
/**
|
||||
* Convert Spring Cloud Contract Verifier contracts into stubs mappings.
|
||||
* <p>
|
||||
@@ -188,6 +191,19 @@ public class ConvertMojo extends AbstractMojo {
|
||||
@Parameter(property = "failOnNoContracts", defaultValue = "true")
|
||||
private boolean failOnNoContracts;
|
||||
|
||||
/**
|
||||
* If set to true then stubs are created only when contracts have changed since last
|
||||
* build.
|
||||
*/
|
||||
@Parameter(property = "incrementalContractStubs", defaultValue = "true")
|
||||
private boolean incrementalContractStubs = true;
|
||||
|
||||
@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
|
||||
private MojoExecution mojoExecution;
|
||||
|
||||
@Parameter(defaultValue = "${session}", readonly = true, required = true)
|
||||
private MavenSession session;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException {
|
||||
if (this.skip) {
|
||||
@@ -205,7 +221,17 @@ public class ConvertMojo extends AbstractMojo {
|
||||
config.setExcludeBuildFolders(this.excludeBuildFolders);
|
||||
File contractsDirectory = locationOfContracts(config);
|
||||
contractsDirectory = contractSubfolderIfPresent(contractsDirectory);
|
||||
|
||||
if (this.incrementalContractStubs && !inputFilesChangeDetected(contractsDirectory,
|
||||
mojoExecution, session)) {
|
||||
getLog().info("Nothing to generate - all stubs are up to date");
|
||||
return;
|
||||
}
|
||||
|
||||
File contractsDslDir = contractsDslDir(contractsDirectory);
|
||||
LeftOverPrevention leftOverPrevention = new LeftOverPrevention(
|
||||
this.stubsDirectory, mojoExecution, session);
|
||||
|
||||
File copiedContracts = copyContracts(rootPath, config, contractsDirectory);
|
||||
if (this.convertToYaml) {
|
||||
contractsDslDir = copiedContracts;
|
||||
@@ -220,6 +246,7 @@ public class ConvertMojo extends AbstractMojo {
|
||||
config.getExcludedFiles(), config.getIncludedContracts(),
|
||||
config.isExcludeBuildFolders());
|
||||
converter.processFiles();
|
||||
leftOverPrevention.deleteLeftOvers();
|
||||
}
|
||||
|
||||
private void convertBackedUpDslsToYaml(String rootPath,
|
||||
|
||||
@@ -21,7 +21,9 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecution;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
@@ -33,6 +35,8 @@ import org.apache.maven.project.MavenProjectHelper;
|
||||
import org.codehaus.plexus.archiver.Archiver;
|
||||
import org.codehaus.plexus.archiver.jar.JarArchiver;
|
||||
|
||||
import static org.springframework.cloud.contract.maven.verifier.ChangeDetector.inputFilesChangeDetected;
|
||||
|
||||
/**
|
||||
* Picks the converted .json files and creates a jar. Requires convert to be executed
|
||||
* first.
|
||||
@@ -86,6 +90,19 @@ public class GenerateStubsMojo extends AbstractMojo {
|
||||
@Parameter(defaultValue = "stubs")
|
||||
private String classifier;
|
||||
|
||||
/**
|
||||
* If set to true then stubs jar is created only when stubs have changed since last
|
||||
* build.
|
||||
*/
|
||||
@Parameter(property = "incrementalContractStubsJar", defaultValue = "true")
|
||||
private boolean incrementalContractStubsJar = true;
|
||||
|
||||
@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
|
||||
private MojoExecution mojoExecution;
|
||||
|
||||
@Parameter(defaultValue = "${session}", readonly = true, required = true)
|
||||
private MavenSession session;
|
||||
|
||||
/**
|
||||
* When enabled, this flag will tell stub runner to throw an exception when no stubs /
|
||||
* contracts were found.
|
||||
@@ -111,6 +128,11 @@ public class GenerateStubsMojo extends AbstractMojo {
|
||||
+ this.outputDirectory.getAbsolutePath()
|
||||
+ "] .\nPlease make sure that spring-cloud-contract:convert was invoked");
|
||||
}
|
||||
if (this.incrementalContractStubsJar
|
||||
&& !inputFilesChangeDetected(outputDirectory, mojoExecution, session)) {
|
||||
getLog().info("Nothing to generate - stubs jar is up to date");
|
||||
return;
|
||||
}
|
||||
File stubsJarFile = createStubJar(this.outputDirectory);
|
||||
this.projectHelper.attachArtifact(this.project, "jar", this.classifier,
|
||||
stubsJarFile);
|
||||
|
||||
@@ -21,9 +21,11 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.apache.maven.model.Resource;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecution;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
@@ -245,6 +247,19 @@ public class GenerateTestsMojo extends AbstractMojo {
|
||||
@Parameter(property = "failOnInProgress", defaultValue = "true")
|
||||
private boolean failOnInProgress = true;
|
||||
|
||||
/**
|
||||
* If set to true then tests are created only when contracts have changed since last
|
||||
* build.
|
||||
*/
|
||||
@Parameter(property = "incrementalContractTests", defaultValue = "true")
|
||||
private boolean incrementalContractTests = true;
|
||||
|
||||
@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
|
||||
private MojoExecution mojoExecution;
|
||||
|
||||
@Parameter(defaultValue = "${session}", readonly = true, required = true)
|
||||
private MavenSession session;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
if (this.skip || this.mavenTestSkip || this.skipTests) {
|
||||
@@ -280,6 +295,13 @@ public class GenerateTestsMojo extends AbstractMojo {
|
||||
this.contractsDirectory);
|
||||
getLog().info(
|
||||
"Directory with contract is present at [" + contractsDirectory + "]");
|
||||
|
||||
if (this.incrementalContractTests && !ChangeDetector
|
||||
.inputFilesChangeDetected(contractsDirectory, mojoExecution, session)) {
|
||||
getLog().info("Nothing to generate - all classes are up to date");
|
||||
return;
|
||||
}
|
||||
|
||||
setupConfig(config, contractsDirectory);
|
||||
this.project
|
||||
.addTestCompileSourceRoot(this.generatedTestSourcesDir.getAbsolutePath());
|
||||
@@ -297,9 +319,12 @@ public class GenerateTestsMojo extends AbstractMojo {
|
||||
+ this.baseClassMappings);
|
||||
}
|
||||
try {
|
||||
LeftOverPrevention leftOverPrevention = new LeftOverPrevention(
|
||||
this.generatedTestSourcesDir, mojoExecution, session);
|
||||
TestGenerator generator = new TestGenerator(config);
|
||||
int generatedClasses = generator.generate();
|
||||
getLog().info("Generated " + generatedClasses + " test classes.");
|
||||
leftOverPrevention.deleteLeftOvers();
|
||||
}
|
||||
catch (ContractVerifierException e) {
|
||||
throw new MojoExecutionException(
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.maven.verifier;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.plugin.MojoExecution;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.shared.incremental.IncrementalBuildHelper;
|
||||
import org.apache.maven.shared.incremental.IncrementalBuildHelperRequest;
|
||||
|
||||
/**
|
||||
* Prevents the following scenario:
|
||||
*
|
||||
* 1. Contract is added
|
||||
*
|
||||
* 2. Derived file is generated
|
||||
*
|
||||
* 3. Contract is deleted
|
||||
*
|
||||
* 4. Derived file still exists
|
||||
*/
|
||||
class LeftOverPrevention {
|
||||
|
||||
private final IncrementalBuildHelper incrementalBuildHelper;
|
||||
|
||||
private final File generatedDirectory;
|
||||
|
||||
LeftOverPrevention(File generatedDirectory, MojoExecution mojoExecution,
|
||||
MavenSession session) throws MojoExecutionException {
|
||||
this.generatedDirectory = generatedDirectory;
|
||||
this.incrementalBuildHelper = new IncrementalBuildHelper(mojoExecution, session);
|
||||
this.incrementalBuildHelper.beforeRebuildExecution(
|
||||
new IncrementalBuildHelperRequest().outputDirectory(generatedDirectory));
|
||||
}
|
||||
|
||||
void deleteLeftOvers() throws MojoExecutionException {
|
||||
if (generatedDirectory.exists()) {
|
||||
incrementalBuildHelper
|
||||
.afterRebuildExecution(new IncrementalBuildHelperRequest()
|
||||
.outputDirectory(generatedDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user