From 797217766d6eef05a53143dd24305fe3cd1803ac Mon Sep 17 00:00:00 2001 From: Anatolii Balakiriev Date: Tue, 27 Aug 2019 14:21:09 +0200 Subject: [PATCH] Replace custom caching with Gradle's incremental build Replaced custom caching of the downloaded contracts with Gradle's incremental build. After that `InitContractsTask` is not needed any more, as its main purpose was to be a bridge between custom caching and Gradle's one. Simplified code to download contracts is now part of the `ContractsCopyTask`. Also fixed `PublishStubsToScmTask` and removed incremental stuff from there for now. Fixes gh-1133 --- .../plugin/ContractVerifierExtension.groovy | 15 +- .../verifier/plugin/ContractsCopyTask.groovy | 151 ++++++++-- .../plugin/GenerateServerTestsTask.groovy | 12 +- .../plugin/GradleContractsDownloader.groovy | 139 ---------- .../GradleContractsDownloaderHelper.groovy | 33 +++ .../verifier/plugin/InitContractsTask.groovy | 91 ------- .../plugin/PublishStubsToScmTask.groovy | 141 ++++++++-- ...ngCloudContractVerifierGradlePlugin.groovy | 29 +- .../plugin/ContractVerifierSpec.groovy | 10 - ...GradleContractsDownloaderHelperSpec.groovy | 86 ++++++ .../GradleContractsDownloaderSpec.groovy | 257 ------------------ 11 files changed, 388 insertions(+), 576 deletions(-) delete mode 100644 spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy delete mode 100644 spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/InitContractsTask.groovy create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy delete mode 100644 spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderSpec.groovy diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy index b346bdc39d..2b23bd42a7 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy @@ -16,6 +16,8 @@ package org.springframework.cloud.contract.verifier.plugin +import org.gradle.api.tasks.Internal + import javax.inject.Inject import groovy.transform.CompileStatic @@ -517,10 +519,11 @@ class ContractVerifierExtension { @Optional Property proxyHost /** - * If set to true then will cache the folder where non snapshot contract artifacts got downloaded. + * Not used any more, as we switched to Gradle's incremental build. */ - @Input - Property cacheDownloadedContracts + @Internal + @Deprecated + boolean cacheDownloadedContracts @Inject ContractRepository(ObjectFactory objects) { @@ -529,7 +532,6 @@ class ContractVerifierExtension { this.password = objects.property(String) this.proxyHost = objects.property(String) this.proxyPort = objects.property(Integer) - this.cacheDownloadedContracts = objects.property(Boolean).convention(true) } @Override @@ -540,7 +542,6 @@ class ContractVerifierExtension { ", password=" + password.getOrNull() + ", proxyPort=" + proxyPort.getOrNull() + ", proxyHost=" + proxyHost.getOrNull() + - ", cacheDownloadedContracts=" + cacheDownloadedContracts.get() + '}' } @@ -575,5 +576,9 @@ class ContractVerifierExtension { void setProxyHost(GString proxyHost) { this.proxyHost.set(proxyHost.toString()) } + + void setProxyPort(Integer proxyPort) { + this.proxyPort.set(proxyPort) + } } } \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy index c4fd8c5911..b3210a46b0 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy @@ -18,6 +18,8 @@ package org.springframework.cloud.contract.verifier.plugin import groovy.transform.CompileStatic +import groovy.transform.Immutable +import groovy.transform.ImmutableOptions import groovy.transform.PackageScope import org.gradle.api.Action import org.gradle.api.DefaultTask @@ -26,18 +28,28 @@ import org.gradle.api.Project import org.gradle.api.file.CopySpec import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty +import org.gradle.api.logging.Logger +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.WorkResult - +import org.springframework.cloud.contract.stubrunner.ContractDownloader +import org.springframework.cloud.contract.stubrunner.StubConfiguration +import org.springframework.cloud.contract.stubrunner.StubDownloader +import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilderProvider +import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties import org.springframework.cloud.contract.verifier.converter.ToYamlConverter import org.springframework.util.StringUtils + +import java.time.Instant + // TODO: Convert to incremental task: https://docs.gradle.org/current/userguide/custom_tasks.html#incremental_tasks /** * Task that copies the contracts in order for the jar task to @@ -65,12 +77,54 @@ class ContractsCopyTask extends DefaultTask { Provider excludeBuildFolders @Input Provider failOnNoContracts - @Input - Provider includedRootFolderAntPattern - @InputDirectory Provider contractsDirectory + // All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build: + @Nested + @Optional + ContractVerifierExtension.Dependency contractDependency + @Nested + @Optional + ContractVerifierExtension.ContractRepository contractRepository + @Input + @Optional + Property contractsMode + @Input + Property deleteStubsAfterTest @Input - Provider contractsRepository + MapProperty contractsProperties + @Input + @Optional + Property contractsPath + @Input + @Optional + Instant getForceDownloadOfTheLatestContracts() { + // If we have `dynamic` version (`+` or `SNAPSHOT`) - we should mark this task as out of date for every run: + if (shouldDownloadContracts() && getStubConfiguration().isVersionChanging()) { + return Instant.now() // This will trigger re-download of contracts + } else { + return null // This will not trigger re-download of contracts + } + } + @Optional + @InputDirectory + Provider getContractsDirectory() { + if (shouldDownloadContracts()) { + return null + } else { + return contractsDirectory + } + } + + @Internal + boolean shouldDownloadContracts() { + return StringUtils.hasText(contractDependency.getArtifactId().getOrNull()) || + StringUtils.hasText(contractDependency.getStringNotation().getOrNull()) || + StringUtils.hasText(contractRepository.repositoryUrl.getOrNull()) + } + @Internal + StubConfiguration getStubConfiguration() { + return GradleContractsDownloaderHelper.stubConfiguration(contractDependency) + } @OutputDirectory DirectoryProperty copiedContractsFolder @@ -81,13 +135,23 @@ class ContractsCopyTask extends DefaultTask { @TaskAction void sync() { - File contractsDirectory = config.contractsDirectory.get().asFile - String contractsRepository = config.contractsRepository.get() + final DownloadedData downloadedData = downloadContractsIfNeeded() + final File contractsDirectory + final String antPattern + if (downloadedData) { + contractsDirectory = downloadedData.downloadedContracts + antPattern = "${downloadedData.inclusionProperties.includedRootFolderAntPattern}*.*" + } else { + contractsDirectory = config.contractsDirectory.get().asFile + antPattern = "**/" + } + logger.info("For project [{}] will use contracts provided in the folder [{}]", project.name, contractsDirectory) + final String contractsRepository = config.contractRepository.repositoryUrl.isPresent() ? config.contractRepository.repositoryUrl : "" throwExceptionWhenFailOnNoContracts(contractsDirectory, contractsRepository) - String antPattern = "${config.includedRootFolderAntPattern.get()}*.*" - String slashSeparatedGroupId = project.group.toString().replace(".", File.separator) - String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString()) - File output = config.copiedContractsFolder.get().asFile + + final String slashSeparatedGroupId = project.group.toString().replace(".", File.separator) + final String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString()) + final File output = config.copiedContractsFolder.get().asFile logger.info("Downloading and unpacking files from [${contractsDirectory}] to [$output]. The inclusion ant patterns are [${antPattern}] and [${slashSeparatedAntPattern}]") sync(contractsDirectory, antPattern, slashSeparatedAntPattern, config.excludeBuildFolders.get(), output) if (config.convertToYaml.get()) { @@ -95,16 +159,20 @@ class ContractsCopyTask extends DefaultTask { } } - static Config fromExtension(ContractVerifierExtension extension, TaskProvider initContractsTask, String root, Project project) { + static Config fromExtension(ContractVerifierExtension extension, String root, Project project) { return new Config( convertToYaml: extension.convertToYaml, excludeBuildFolders: extension.excludeBuildFolders, failOnNoContracts: extension.failOnNoContracts, - includedRootFolderAntPattern: initContractsTask.flatMap { it.config.includedRootFolderAntPattern }, - contractsDirectory: initContractsTask.flatMap { it.config.initialisedContractsDirectory }, - contractsRepository: extension.contractRepository.repositoryUrl.isPresent() ? extension.contractRepository.repositoryUrl : project.provider({ String s -> "" }), + contractsDirectory: extension.contractsDslDir, copiedContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.CONTRACTS, project), - backupContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.BACKUP, project) + backupContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.BACKUP, project), + contractDependency: extension.contractDependency, + contractRepository: extension.contractRepository, + contractsMode: extension.contractsMode, + deleteStubsAfterTest: extension.deleteStubsAfterTest, + contractsProperties: extension.contractsProperties, + contractsPath: extension.contractsPath ) } @@ -115,8 +183,6 @@ class ContractsCopyTask extends DefaultTask { } protected WorkResult sync(File file, String antPattern, String slashSeparatedAntPattern, boolean excludeBuildFolders, File outputContractsFolder) { - // TODO: Is there any better way to make it statically compiled, avoiding explicit creation of new Action? - // sync will remove files from target if they are removed from source. So using it here instead of copy: return project.sync(new Action() { @Override void execute(final CopySpec spec) { @@ -135,6 +201,35 @@ class ContractsCopyTask extends DefaultTask { }) } + private DownloadedData downloadContractsIfNeeded() { + if (config.shouldDownloadContracts()) { + logger.info("Project has group id [{}], artifact id [{}]", project.group, project.name) + logger.info("For project [${project.name}] Download dependency is provided - will download contract jars") + logger.info("Contract dependency [{}]", config.contractDependency) + StubConfiguration configuration = config.getStubConfiguration() + logger.info("Got the following contract dependency to download [{}]", configuration) + logger.info("The contract dependency is a changing one [{}]", configuration.isVersionChanging()) + + final StubDownloader downloader = new StubDownloaderBuilderProvider().get( + StubRunnerOptionsFactory.createStubRunnerOptions(config.contractRepository, + config.contractsMode.getOrNull(), config.deleteStubsAfterTest.get(), + config.contractsProperties.get(), config.failOnNoContracts.get())) + final ContractDownloader contractDownloader = new ContractDownloader(downloader, configuration, + config.contractsPath.getOrNull(), project.group as String, project.name, project.version as String) + final File downloadedContracts = contractDownloader.unpackAndDownloadContracts(); + final ContractDownloader.InclusionProperties inclusionProperties = + contractDownloader.createNewInclusionProperties(downloadedContracts) + + // TODO: inclusionProperties.includedContracts is never used eventually. Review this: + return new DownloadedData( + downloadedContracts: contractsSubDirIfPresent(downloadedContracts, logger), + inclusionProperties: inclusionProperties + ) + } else { + return null + } + } + private static DirectoryProperty createTaskOutput(String root, DirectoryProperty stubsOutputDir, String suffix, Project project) { Provider provider = stubsOutputDir.flatMap { Directory dir = it @@ -162,4 +257,22 @@ class ContractsCopyTask extends DefaultTask { + "] .\nPlease make sure that the contracts were defined, or set the [failOnNoContracts] flag to [false]") } } + + private static File contractsSubDirIfPresent(File contractsDirectory, Logger logger) { + File contracts = new File(contractsDirectory, "contracts") + if (contracts.exists()) { + if (logger.isDebugEnabled()) { + logger.debug("Contracts folder found [" + contracts + "]") + } + contractsDirectory = contracts + } + return contractsDirectory + } + + @ImmutableOptions(knownImmutableClasses = [File, ContractDownloader.InclusionProperties]) + @Immutable + private static class DownloadedData { + final File downloadedContracts + final ContractDownloader.InclusionProperties inclusionProperties + } } \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy index 04e95866a7..5085546c5f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy @@ -54,8 +54,6 @@ class GenerateServerTestsTask extends DefaultTask { @InputDirectory Provider contractsDslDir @Input - Provider includedContracts - @Input @Optional Provider nameSuffixForTests @Input @@ -101,12 +99,6 @@ class GenerateServerTestsTask extends DefaultTask { logger.info("Generated test sources dir [${generatedTestSources}]") logger.info("Generated test resources dir [${generatedTestResources}]") File contractsDslDir = config.contractsDslDir.get().asFile - // There used to be some bug in old code, which was always setting `includedContracts` to ".*" instead of - // getting it from the config (which is coming from contracts downloader). In fact, that `correct` behaviour - // doesn't even make sense here as contracts are already copied, so that inclusion filter will not include - // anything. So for now just restoring this old behaviour, but it must be reviewed. Probably that - // `includedContracts` should be used in the `copyContracts` task instead? - // String includedContracts = config.includedContracts.get() String includedContracts = ".*" project.logger.info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation") project.logger.info("Contracts are unpacked to [${contractsDslDir}]") @@ -145,11 +137,9 @@ class GenerateServerTestsTask extends DefaultTask { } } - static Config fromExtension(ContractVerifierExtension extension, TaskProvider initContractsTask, - TaskProvider copyContractsTask) { + static Config fromExtension(ContractVerifierExtension extension, TaskProvider copyContractsTask) { return new Config( contractsDslDir: copyContractsTask.flatMap { it.config.copiedContractsFolder }, - includedContracts: initContractsTask.flatMap { it.config.includedContracts }, nameSuffixForTests: extension.nameSuffixForTests, basePackageForTests: extension.basePackageForTests, baseClassForTests: extension.baseClassForTests, diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy deleted file mode 100644 index 40c7aab394..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy +++ /dev/null @@ -1,139 +0,0 @@ -package org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import groovy.transform.Immutable -import groovy.transform.ImmutableOptions -import groovy.transform.PackageScope -import org.gradle.api.Project -import org.gradle.api.logging.Logger -import org.springframework.cloud.contract.stubrunner.ContractDownloader -import org.springframework.cloud.contract.stubrunner.StubConfiguration -import org.springframework.cloud.contract.stubrunner.StubDownloader -import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilderProvider -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties -import org.springframework.util.StringUtils - -import java.util.concurrent.ConcurrentHashMap - -/** - * @author Marcin Grzejszczak - */ -@PackageScope -@CompileStatic -class GradleContractsDownloader { - - private static final String LATEST_VERSION = '+' - - private final Project project - private final Logger logger - protected static final Map downloadedContract = new ConcurrentHashMap<>() - - GradleContractsDownloader(Project project, Logger logger) { - this.project = project - this.logger = logger - } - - DownloadedData downloadAndUnpackContractsIfRequired(ContractVerifierExtension.Dependency contractDependency, - ContractVerifierExtension.ContractRepository contractRepository, String contractsPath, - StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest, - Map contractsProperties, boolean failOnNoContracts) { - if (!shouldDownloadContracts(contractDependency, contractRepository)) { - return null - } - logger.info("Project has group id [{}], artifact id [{}]", project.group, project.name) - // download contracts, unzip them and pass as output directory - - logger.info("For project [${project.name}] Download dependency is provided - will download contract jars") - logger.info("Contract dependency [{}]", contractDependency) - StubConfiguration configuration = stubConfiguration(contractDependency) - logger.info("Got the following contract dependency to download [{}]", configuration) - logger.info("The contract dependency is a changing one [{}] and cache download switch is set to [{}]", - configuration.isVersionChanging(), contractRepository.cacheDownloadedContracts.get()) - if (!configuration.isVersionChanging() && contractRepository.cacheDownloadedContracts.get()) { - logger.info("Resolved a non changing version - will try to return the folder from a cache") - File cachedFolder = downloadedContract.get(configuration) - if (cachedFolder) { - logger.info("For project [{}] returning the cached location of the contracts", project.name) - final ContractDownloader.InclusionProperties inclusionProperties = - contractDownloader(configuration, contractRepository, contractsPath, contractsMode, - deleteStubsAfterTest, contractsProperties, failOnNoContracts).createNewInclusionProperties(cachedFolder) - - return new DownloadedData( - downloadedContracts: contractsSubDirIfPresent(cachedFolder, logger), - inclusionProperties: inclusionProperties - ) - } - } - final ContractDownloader contractDownloader = - contractDownloader(configuration, contractRepository, contractsPath, contractsMode, deleteStubsAfterTest, - contractsProperties, failOnNoContracts); - final File downloadedContracts = contractDownloader.unpackAndDownloadContracts(); - final ContractDownloader.InclusionProperties inclusionProperties = - contractDownloader.createNewInclusionProperties(downloadedContracts) - - downloadedContract.put(configuration, downloadedContracts) - - return new DownloadedData( - downloadedContracts: contractsSubDirIfPresent(downloadedContracts, logger), - inclusionProperties: inclusionProperties - ) - } - - private static File contractsSubDirIfPresent(File contractsDirectory, Logger logger) { - File contracts = new File(contractsDirectory, "contracts") - if (contracts.exists()) { - if (logger.isDebugEnabled()) { - logger.debug("Contracts folder found [" + contracts + "]") - } - contractsDirectory = contracts - } - return contractsDirectory - } - - @PackageScope - StubConfiguration stubConfiguration(ContractVerifierExtension.Dependency contractDependency) { - String groupId = contractDependency.groupId.getOrNull() - String artifactId = contractDependency.artifactId.getOrNull() - String version = StringUtils.hasText(contractDependency.version.getOrNull()) ? - contractDependency.version.getOrNull() : LATEST_VERSION - String classifier = contractDependency.classifier.getOrNull() - String stringNotation = contractDependency.stringNotation.getOrNull() - if (StringUtils.hasText(stringNotation)) { - StubConfiguration stubConfiguration = new StubConfiguration(stringNotation) - return new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId, - stubConfiguration.version, stubConfiguration.classifier) - } - return new StubConfiguration(groupId, artifactId, version, classifier) - } - - protected ContractDownloader contractDownloader(StubConfiguration configuration, - ContractVerifierExtension.ContractRepository contractRepository, - String contractsPath, StubRunnerProperties.StubsMode contractsMode, - boolean deleteStubsAfterTest, Map contractsProperties, - boolean failOnNoContracts) { - return new ContractDownloader(stubDownloader(contractRepository, contractsMode, deleteStubsAfterTest, contractsProperties, failOnNoContracts), - configuration, contractsPath, project.group as String, project.name, project.version as String) - } - - protected StubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository, - StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest, - Map contractsProperties, boolean failOnNoContracts) { - StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider() - return provider.get(StubRunnerOptionsFactory.createStubRunnerOptions(contractRepository, contractsMode, - deleteStubsAfterTest, contractsProperties, failOnNoContracts)) - } - - private static boolean shouldDownloadContracts(ContractVerifierExtension.Dependency contractDependency, - ContractVerifierExtension.ContractRepository contractRepository) { - return StringUtils.hasText(contractDependency.getArtifactId().getOrNull()) || - StringUtils.hasText(contractDependency.getStringNotation().getOrNull()) || - StringUtils.hasText(contractRepository.repositoryUrl.getOrNull()) - } - - @ImmutableOptions(knownImmutableClasses = [File, ContractDownloader.InclusionProperties]) - @Immutable - static class DownloadedData { - final File downloadedContracts - final ContractDownloader.InclusionProperties inclusionProperties - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy new file mode 100644 index 0000000000..45d3855a0f --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy @@ -0,0 +1,33 @@ +package org.springframework.cloud.contract.verifier.plugin + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope +import org.springframework.cloud.contract.stubrunner.StubConfiguration +import org.springframework.util.StringUtils + +/** + * @author Anatoliy Balakirev + */ +@PackageScope +@CompileStatic +class GradleContractsDownloaderHelper { + + private static final String LATEST_VERSION = '+' + + @PackageScope + static StubConfiguration stubConfiguration(ContractVerifierExtension.Dependency contractDependency) { + String groupId = contractDependency.groupId.getOrNull() + String artifactId = contractDependency.artifactId.getOrNull() + String version = StringUtils.hasText(contractDependency.version.getOrNull()) ? + contractDependency.version.getOrNull() : LATEST_VERSION + String classifier = contractDependency.classifier.getOrNull() + String stringNotation = contractDependency.stringNotation.getOrNull() + if (StringUtils.hasText(stringNotation)) { + StubConfiguration stubConfiguration = new StubConfiguration(stringNotation) + return new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId, + stubConfiguration.version, stubConfiguration.classifier) + } + return new StubConfiguration(groupId, artifactId, version, classifier) + } + +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/InitContractsTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/InitContractsTask.groovy deleted file mode 100644 index a669e4f054..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/InitContractsTask.groovy +++ /dev/null @@ -1,91 +0,0 @@ -package org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.gradle.api.DefaultTask -import org.gradle.api.Project -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties - -/** - * @author Anatoliy Balakirev - */ -@PackageScope -@CompileStatic -class InitContractsTask extends DefaultTask { - - static final String TASK_NAME = 'initContracts' - protected final GradleContractsDownloader contractDownloader = new GradleContractsDownloader(project, logger) - @Nested - Config config - - static class Config { - // All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build: - @Nested - @Optional - ContractVerifierExtension.Dependency contractDependency - @Nested - @Optional - ContractVerifierExtension.ContractRepository contractRepository - @Input - @Optional - Property contractsMode - @Input - Property deleteStubsAfterTest - @Input - Property failOnNoContracts - @Input - MapProperty contractsProperties - @Input - @Optional - Property contractsPath - - @Internal - Property includedContracts - @Internal - Property includedRootFolderAntPattern - - // TODO: Enable it when all caching from `GradleContractsDownloader` is replaced with Gradle's one here: - // @OutputDirectory - DirectoryProperty initialisedContractsDirectory - } - - @TaskAction - void initContracts() { - GradleContractsDownloader.DownloadedData downloaded = - contractDownloader.downloadAndUnpackContractsIfRequired(config.contractDependency, - config.contractRepository, config.contractsPath.getOrNull(), config.contractsMode.getOrNull(), - config.deleteStubsAfterTest.get(), config.contractsProperties.get(), config.failOnNoContracts.get()) - if (downloaded) { - config.includedContracts.set(downloaded.inclusionProperties.includedContracts) - config.includedRootFolderAntPattern.set(downloaded.inclusionProperties.includedRootFolderAntPattern) - config.initialisedContractsDirectory.set(downloaded.downloadedContracts) - } - logger.info("For project [{}] will use contracts provided in the folder [{}]", project.name, - config.initialisedContractsDirectory.get()) - } - - static Config fromExtension(ContractVerifierExtension extension, Project project) { - DirectoryProperty initialisedContractsDirectory = project.objects.directoryProperty() - initialisedContractsDirectory.set(extension.contractsDslDir) - return new Config( - contractDependency: extension.contractDependency, - contractRepository: extension.contractRepository, - contractsMode: extension.contractsMode, - deleteStubsAfterTest: extension.deleteStubsAfterTest, - failOnNoContracts: extension.failOnNoContracts, - contractsProperties: extension.contractsProperties, - contractsPath: extension.contractsPath, - includedContracts: project.objects.property(String).convention(".*"), - includedRootFolderAntPattern: project.objects.property(String).convention("**/"), - initialisedContractsDirectory: initialisedContractsDirectory - ) - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy index df5c019c8c..3a64df0f94 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy @@ -17,14 +17,12 @@ package org.springframework.cloud.contract.verifier.plugin import groovy.transform.CompileStatic +import groovy.transform.PackageScope import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.provider.Property import org.gradle.api.tasks.TaskAction import org.springframework.cloud.contract.stubrunner.ContractProjectUpdater import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder @@ -44,30 +42,95 @@ import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties class PublishStubsToScmTask extends DefaultTask { static final String TASK_NAME = 'publishStubsToScm' - @Nested Config config + private Closure customizationClosure = Closure.IDENTITY static class Config { - // All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build: - @Nested - @Optional + private ObjectFactory objects ContractVerifierExtension.ContractRepository contractRepository - @Input - @Optional - Provider contractsMode - @Input - Provider deleteStubsAfterTest - @Input - Provider failOnNoContracts - @Input + Property contractsMode + Property deleteStubsAfterTest + Property failOnNoContracts MapProperty contractsProperties - - @OutputDirectory DirectoryProperty stubsOutputDir + + void setContractsMode(Property contractsMode) { + this.contractsMode = contractsMode + } + + void setContractsMode(String contractsMode) { + if (contractsMode) { + this.contractsMode = objects.property(StubRunnerProperties.StubsMode).convention(StubRunnerProperties.StubsMode.valueOf(contractsMode.toUpperCase())) + } + } + + void setDeleteStubsAfterTest(Property deleteStubsAfterTest) { + this.deleteStubsAfterTest = deleteStubsAfterTest + } + + void setDeleteStubsAfterTest(Boolean deleteStubsAfterTest) { + if (deleteStubsAfterTest != null) { + this.deleteStubsAfterTest = objects.property(Boolean).convention(deleteStubsAfterTest) + } + } + + void setFailOnNoContracts(Property failOnNoContracts) { + this.failOnNoContracts = failOnNoContracts + } + + void setFailOnNoContracts(Boolean failOnNoContracts) { + if (failOnNoContracts != null) { + this.failOnNoContracts = objects.property(Boolean).convention(failOnNoContracts) + } + } + + void setContractsProperties(MapProperty contractsProperties) { + this.contractsProperties = contractsProperties + } + + void setContractsProperties(Map contractsProperties) { + if (contractsProperties) { + this.contractsProperties = objects.mapProperty(String, String).convention(contractsProperties) + } + } + + void setStubsOutputDir(DirectoryProperty stubsOutputDir) { + this.stubsOutputDir = stubsOutputDir + } + + void setStubsOutputDir(String stubsOutputDir) { + if (stubsOutputDir) { + this.stubsOutputDir = objects.directoryProperty() + this.stubsOutputDir.set(new File(stubsOutputDir)) + } + } + + void setStubsOutputDir(GString stubsOutputDir) { + if (stubsOutputDir) { + this.stubsOutputDir = objects.directoryProperty() + this.stubsOutputDir.set(new File(stubsOutputDir.toString())) + } + } + + void contractRepository(@DelegatesTo(ContractVerifierExtension.ContractRepository) Closure closure) { + closure.delegate = contractRepository + closure.call() + } + + @Deprecated + void contractDependency(Closure closure) { + // Here only to preserve backward compatibility. Was never used here. + } + + @PackageScope + void setObjects(ObjectFactory objects) { + this.objects = objects + } } @TaskAction void publishStubsToScm() { + applyConfigCustomizations() if (!shouldRun()) { return } @@ -79,15 +142,15 @@ class PublishStubsToScmTask extends DefaultTask { new ContractProjectUpdater(stubRunnerOptions).updateContractProject(projectName, config.stubsOutputDir.get().asFile.toPath()) } - static Config fromExtension(ContractVerifierExtension extension) { + static Config fromExtension(ContractVerifierExtension extension, ObjectFactory objects) { return new Config( contractRepository: extension.contractRepository, contractsMode: extension.contractsMode, failOnNoContracts: extension.failOnNoContracts, deleteStubsAfterTest: extension.deleteStubsAfterTest, contractsProperties: extension.contractsProperties, - - stubsOutputDir: extension.stubsOutputDir + stubsOutputDir: extension.stubsOutputDir, + objects: objects ) } @@ -99,4 +162,38 @@ class PublishStubsToScmTask extends DefaultTask { } return true } + + /** + * Some builds might want to customize this task only, overriding settings from plugin. + */ + void customize(@DelegatesTo(Config) Closure closure) { + customizationClosure = closure + } + + private void applyConfigCustomizations() { + // Needs to be copied, otherwise properties won't be updated: + config.contractRepository = copy(config.contractRepository, project.objects) + customizationClosure.delegate = config + customizationClosure.call() + } + + private static ContractVerifierExtension.ContractRepository copy(ContractVerifierExtension.ContractRepository original, ObjectFactory objects) { + ContractVerifierExtension.ContractRepository copied = new ContractVerifierExtension.ContractRepository(objects) + if (original.repositoryUrl.getOrNull()) { + copied.repositoryUrl.set(original.repositoryUrl.get()) + } + if (original.username.getOrNull()) { + copied.username.set(original.username.get()) + } + if (original.password.getOrNull()) { + copied.password.set(original.password.get()) + } + if (original.proxyPort.getOrNull()) { + copied.proxyPort.set(original.proxyPort.get()) + } + if (original.proxyHost.getOrNull()) { + copied.proxyHost.set(original.proxyHost.get()) + } + return copied + } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy index 20924ffb74..16b23e1ed5 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy @@ -57,12 +57,11 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin { ContractVerifierExtension extension = project.extensions.create(EXTENSION_NAME, ContractVerifierExtension) setConfigurationDefaults(extension) - TaskProvider initContracts = createAndConfigureContractsInitTask(extension) - TaskProvider copyContracts = createAndConfigureCopyContractsTask(initContracts, extension) + TaskProvider copyContracts = createAndConfigureCopyContractsTask(extension) TaskProvider generateClientStubs = createAndConfigureGenerateClientStubs(extension, copyContracts) createAndConfigureStubsJarTasks(extension, copyContracts, generateClientStubs) - createGenerateTestsTask(extension, copyContracts, initContracts) + createGenerateTestsTask(extension, copyContracts) createAndConfigurePublishStubsToScmTask(extension, generateClientStubs) project.afterEvaluate { addIdeaTestSources(project, extension) @@ -112,14 +111,13 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin { } - private void createGenerateTestsTask(ContractVerifierExtension extension, - TaskProvider copyContracts, TaskProvider initContracts) { + private void createGenerateTestsTask(ContractVerifierExtension extension, TaskProvider copyContracts) { TaskProvider task = project.tasks.register(GenerateServerTestsTask.TASK_NAME, GenerateServerTestsTask) task.configure { it.description = "Generate server tests from the contracts" it.group = GROUP_NAME it.enabled = !project.gradle.startParameter.excludedTaskNames.contains("test") - it.config = GenerateServerTestsTask.fromExtension(extension, initContracts, copyContracts) + it.config = GenerateServerTestsTask.fromExtension(extension, copyContracts) it.dependsOn copyContracts } @@ -133,7 +131,7 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin { task.configure { it.description = "The generated stubs get committed to the SCM repo and pushed to origin" it.group = GROUP_NAME - it.config = PublishStubsToScmTask.fromExtension(extension) + it.config = PublishStubsToScmTask.fromExtension(extension, project.objects) it.dependsOn generateClientStubs } @@ -242,25 +240,12 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin { return task } - private TaskProvider createAndConfigureCopyContractsTask(TaskProvider initContractsTask, - ContractVerifierExtension extension) { + private TaskProvider createAndConfigureCopyContractsTask(ContractVerifierExtension extension) { TaskProvider task = project.tasks.register(ContractsCopyTask.TASK_NAME, ContractsCopyTask) task.configure { it.description = "Copies contracts to the output folder" it.group = GROUP_NAME - it.config = ContractsCopyTask.fromExtension(extension, initContractsTask, buildRootPath(), project) - - it.dependsOn initContractsTask - } - return task - } - - private TaskProvider createAndConfigureContractsInitTask(ContractVerifierExtension extension) { - TaskProvider task = project.tasks.register(InitContractsTask.TASK_NAME, InitContractsTask) - task.configure { - it.description = "Initialises contracts either downloading or using default provided folder" - it.group = GROUP_NAME - it.config = InitContractsTask.fromExtension(extension, project) + it.config = ContractsCopyTask.fromExtension(extension, buildRootPath(), project) } return task } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy index 32b3897e30..58064d307d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy @@ -70,16 +70,6 @@ class ContractVerifierSpec extends Specification { project.tasks.verifierStubsJar.getDependsOn().contains(project.tasks.named("copyContracts")) } - def "should create initContracts task"() { - expect: - project.tasks.named("initContracts") != null - } - - def "should configure initContracts task as a dependency of the copyContracts task"() { - expect: - project.tasks.copyContracts.getDependsOn().contains(project.tasks.named("initContracts")) - } - /** * project.evaluate() is used here in order to trigger the evaluation lifecycle of a project. * This method is currently exposed via the internal API and is subject to change, however, Gradle diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy new file mode 100644 index 0000000000..aadb5eb074 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy @@ -0,0 +1,86 @@ +package org.springframework.cloud.contract.verifier.plugin + +import org.gradle.api.internal.provider.DefaultPropertyState +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.springframework.cloud.contract.stubrunner.StubConfiguration +import spock.lang.Specification + +/** + * @author Marcin Grzejszczak + * @author Anatoliy Balakirev + */ +class GradleContractsDownloaderHelperSpec extends Specification { + + ObjectFactory objectFactory = Mock(ObjectFactory) + + def setup() { + // Is there any better way to say that I need a new object on each interaction with mock? + objectFactory.property(String) >>> [prop(String), prop(String), prop(String), prop(String), prop(String)] + } + + def "should parse dependency via string notation"() { + given: + String stringNotation = "com.example:foo:1.0.0:stubs" + ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) + dep.stringNotation.set(stringNotation) + when: + StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) + then: + stubConfig.groupId == "com.example" + stubConfig.artifactId == "foo" + stubConfig.version == "1.0.0" + stubConfig.classifier == "stubs" + } + + def "should parse dependency via direct setting"() { + given: + ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) + dep.groupId.set("com.example") + dep.artifactId.set("foo") + dep.version.set("1.0.0") + dep.classifier.set("stubs") + when: + StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) + then: + stubConfig.groupId == "com.example" + stubConfig.artifactId == "foo" + stubConfig.version == "1.0.0" + stubConfig.classifier == "stubs" + } + + def "should parse dependency via string notation with methods"() { + given: + String stringNotation = "com.example:foo:1.0.0:stubs" + ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) + dep.stringNotation.set(stringNotation) + when: + StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) + then: + stubConfig.groupId == "com.example" + stubConfig.artifactId == "foo" + stubConfig.version == "1.0.0" + stubConfig.classifier == "stubs" + } + + def "should parse dependency via direct setting with methods"() { + given: + ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) + dep.groupId.set("com.example") + dep.artifactId.set("foo") + dep.version.set("1.0.0") + dep.classifier.set("stubs") + when: + StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) + then: + stubConfig.groupId == "com.example" + stubConfig.artifactId == "foo" + stubConfig.version == "1.0.0" + stubConfig.classifier == "stubs" + } + + // Have to use this internal property impl here. Is there some better way? + static Property prop(Class aClass) { + return new DefaultPropertyState(aClass) + } +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderSpec.groovy deleted file mode 100644 index 983b9f1d76..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderSpec.groovy +++ /dev/null @@ -1,257 +0,0 @@ -package org.springframework.cloud.contract.verifier.plugin - -import org.gradle.api.Project -import org.gradle.api.internal.provider.DefaultPropertyState -import org.gradle.api.logging.Logger -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Property -import org.springframework.cloud.contract.stubrunner.AetherStubDownloader -import org.springframework.cloud.contract.stubrunner.ContractDownloader -import org.springframework.cloud.contract.stubrunner.StubConfiguration -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties -import spock.lang.Specification - -/** - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - */ -class GradleContractsDownloaderSpec extends Specification { - - Project project = Stub(Project) - Logger logger = Stub(Logger) - ObjectFactory objectFactory = Mock(ObjectFactory) - - def setup() { - // Is there any better way to say that I need a new object on each interaction with mock? - objectFactory.property(String) >>> [prop(String), prop(String), prop(String), prop(String), prop(String), prop(String), prop(String), prop(String), prop(String)] - objectFactory.property(Integer) >> prop(Integer) - objectFactory.property(Boolean) >> prop(Boolean) - } - - def "should parse dependency via string notation"() { - given: - String stringNotation = "com.example:foo:1.0.0:stubs" - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.stringNotation.set(stringNotation) - when: - StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should parse dependency via direct setting"() { - given: - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.groupId.set("com.example") - dep.artifactId.set("foo") - dep.version.set("1.0.0") - dep.classifier.set("stubs") - when: - StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should parse dependency via string notation with methods"() { - given: - String stringNotation = "com.example:foo:1.0.0:stubs" - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.stringNotation.set(stringNotation) - when: - StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should parse dependency via direct setting with methods"() { - given: - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.groupId.set("com.example") - dep.artifactId.set("foo") - dep.version.set("1.0.0") - dep.classifier.set("stubs") - when: - StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should pick dependency from cache for a non snapshot contract dependency with new property"() { - given: - ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory) - contractDependency.groupId.set("com.example") - contractDependency.artifactId.set("foo") - contractDependency.version.set("1.0.0") - contractDependency.classifier.set("stubs") - ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory) - contractRepository.repositoryUrl.set("foo") - and: - final AetherStubDownloader downloader = Mock(AetherStubDownloader) - final ContractDownloader contractDownloader = Mock(ContractDownloader) - and: - GradleContractsDownloader gradleDownloader = stubbedContractDownloader(downloader, contractDownloader) - and: - StubConfiguration expectedStubConfig = new StubConfiguration("com.example:foo:1.0.0:stubs") - File expectedFileFromCache = new File("foo/bar") - GradleContractsDownloader.downloadedContract.put(expectedStubConfig, expectedFileFromCache) - when: - GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true) - then: - downloaded.downloadedContracts == expectedFileFromCache - } - - def "should not pick dependency from cache for a non snapshot contract dependency with cache switch off"() { - given: - ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory) - contractDependency.groupId.set("com.example") - contractDependency.artifactId.set("foo") - contractDependency.version.set("1.0.0") - contractDependency.classifier.set("stubs") - ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory) - contractRepository.repositoryUrl.set("foo") - contractRepository.cacheDownloadedContracts.set(false) - and: - final AetherStubDownloader downloader = Mock(AetherStubDownloader) - final ContractDownloader contractDownloader = Mock(ContractDownloader) - and: - GradleContractsDownloader gradleDownloader = stubbedContractDownloader(downloader, contractDownloader) - and: - StubConfiguration expectedStubConfig = new StubConfiguration("com.example:foo:1.0.0:stubs") - File expectedFileFromCache = new File("foo/bar") - GradleContractsDownloader.downloadedContract.put(expectedStubConfig, expectedFileFromCache) - and: - File expectedFileNotFromCache = new File("foo/bar/baz") - contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache - when: - GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true) - then: - downloaded.downloadedContracts == expectedFileNotFromCache - } - - def "should not pick dependency from cache for snapshot contract dependency"() { - given: - ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory) - contractDependency.groupId.set("com.example") - contractDependency.artifactId.set("foo") - contractDependency.version.set("1.0.0.BUILD-SNAPSHOT") - contractDependency.classifier.set("stubs") - ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory) - contractRepository.repositoryUrl.set("foo") - and: - final AetherStubDownloader downloader = Mock(AetherStubDownloader) - final ContractDownloader contractDownloader = Mock(ContractDownloader) - File expectedFileNotFromCache = new File("foo/bar/baz") - contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache - and: - GradleContractsDownloader gradleDownloader = stubbedContractDownloader(downloader, contractDownloader) - when: - GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true) - then: - downloaded.downloadedContracts == expectedFileNotFromCache - } - - private GradleContractsDownloader stubbedContractDownloader(downloader, contractDownloader) { - new GradleContractsDownloader(project, logger) { - @Override - protected AetherStubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository, - StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest, - Map contractsProperties, boolean failOnNoContracts) { - return downloader - } - - @Override - protected ContractDownloader contractDownloader(StubConfiguration configuration, - ContractVerifierExtension.ContractRepository contractRepository, - String contractsPath, StubRunnerProperties.StubsMode contractsMode, - boolean deleteStubsAfterTest, Map contractsProperties, - boolean failOnNoContracts) { - return contractDownloader - } - } - } - - def "should not start downloading"() { - given: - ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory) - ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory) - and: - final AetherStubDownloader downloader = Mock(AetherStubDownloader) - final ContractDownloader contractDownloader = Mock(ContractDownloader) - and: - GradleContractsDownloader gradleDownloader = stubbedContractDownloader(downloader, contractDownloader) - and: - StubConfiguration expectedStubConfig = new StubConfiguration("com.example:foo:1.0.0:stubs") - GradleContractsDownloader.downloadedContract.put(expectedStubConfig, new File("foo/bar")) - when: - GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.CLASSPATH, true, [:], true) - then: - downloaded == null - } - - def "should pass contract dependency properties as a parameter to the builder"() { - given: - ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory) - contractDependency.groupId.set("com.example") - contractDependency.artifactId.set("foo") - contractDependency.version.set("1.0.0.BUILD-SNAPSHOT") - contractDependency.classifier.set("stubs") - ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory) - contractRepository.repositoryUrl.set("foo") - contractRepository.username.set("foo1") - contractRepository.password.set("foo2") - contractRepository.proxyHost.set("foo3") - contractRepository.proxyPort.set(12) - and: - final AetherStubDownloader downloader = Mock(AetherStubDownloader) - final ContractDownloader contractDownloader = Mock(ContractDownloader) - File expectedFileNotFromCache = new File("foo/bar/baz") - contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache - and: - GradleContractsDownloader gradleDownloader = assertingContractDownloader(downloader, contractDownloader) - when: - gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.CLASSPATH, true, [:], true) - then: - noExceptionThrown() - } - - private GradleContractsDownloader assertingContractDownloader(downloader, contractDownloader) { - new GradleContractsDownloader(project, logger) { - @Override - protected AetherStubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository, - StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest, - Map contractsProperties, boolean failOnNoContracts) { - assert extension.contractRepository.username == "foo1" - assert extension.contractRepository.password == "foo2" - assert extension.contractRepository.proxyHost == "foo3" - assert extension.contractRepository.proxyPort == 12 - return downloader - } - - @Override - protected ContractDownloader contractDownloader(StubConfiguration configuration, - ContractVerifierExtension.ContractRepository contractRepository, - String contractsPath, StubRunnerProperties.StubsMode contractsMode, - boolean deleteStubsAfterTest, Map contractsProperties, - boolean failOnNoContracts) { - return contractDownloader - } - } - } - - // Have to use this internal property impl here. Is there some better way? - static Property prop(Class aClass) { - return new DefaultPropertyState(aClass) - } -}