diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy new file mode 100644 index 00000000..af755580 --- /dev/null +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy @@ -0,0 +1,114 @@ +/* + * Copyright 2019 SpotBugs team + * + *

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 + * + *

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. + */ +package com.github.spotbugs.snom + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.util.GradleVersion +import spock.lang.Specification + +import java.nio.file.Files + +class CacheabilityFunctionalTest extends Specification { + + + /** + * Verifies the cacheability of {@link SpotBugsTask} by invoking the same code + * in two different, uniquely-named folders. + * + * Thanks to the presence of sysprop {@code org.gradle.caching.debug=true}, the + * build cache key for the task is echoed to stdout. + * + * If we compare these keys for both builds and they are equal, we can now trust the + * task is cacheable. + * + */ + def 'spotbugsMain task is cacheable'() { + given: + def buildDir1 = Files.createTempDirectory(null).toFile() + def buildDir2 = Files.createTempDirectory(null).toFile() + + def version = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version) + + initializeBuildFile(buildDir1) + initializeBuildFile(buildDir2) + + when: + BuildResult result1 = + GradleRunner.create() + .withProjectDir(buildDir1) + .withArguments(':spotbugsMain') + .withPluginClasspath() + .forwardOutput() + .withGradleVersion(version) + .build() + def hashKeyLine1 = getHashKeyLine(result1) + + then: + hashKeyLine1 + + when: + BuildResult result2 = + GradleRunner.create() + .withProjectDir(buildDir2) + .withArguments(':spotbugsMain') + .withPluginClasspath() + .forwardOutput() + .withGradleVersion(version) + .build() + def hashKeyLine2 = getHashKeyLine(result2) + + then: + hashKeyLine2 + hashKeyLine1 == hashKeyLine2 + } + + private static String getHashKeyLine(BuildResult result) { + return result.output.find('Build cache key for task \':spotbugsMain\' is .*') + } + + private static void initializeBuildFile(File buildDir) { + File buildFile = new File(buildDir, 'build.gradle') + File propertiesFile = new File(buildDir, 'gradle.properties') + + buildFile << ''' + |plugins { + | id 'java' + | id 'com.github.spotbugs' + |} + | + |version = 1.0 + | + |repositories { + | mavenCentral() + |} + |'''.stripMargin() + + File sourceDir = buildDir.toPath().resolve('src').resolve('main').resolve('java').toFile() + sourceDir.mkdirs() + File sourceFile = new File(sourceDir, 'Foo.java') + sourceFile << ''' + |public class Foo { + | public static void main(String... args) { + | System.out.println("Hello, SpotBugs!"); + | } + |} + |'''.stripMargin() + + propertiesFile << ''' + |org.gradle.caching = true + |org.gradle.caching.debug = true + |'''.stripMargin() + } +} diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy b/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy index 51fc3a29..8d831b51 100644 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy +++ b/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy @@ -35,6 +35,7 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; @@ -82,6 +83,8 @@ import javax.inject.Inject * *

See also SpotBugs Manual about configuration.

*/ + +@CacheableTask class SpotBugsTask extends DefaultTask implements VerificationTask { private static final String FEATURE_FLAG_WORKER_API = "com.github.spotbugs.snom.worker"; private final Logger log = LoggerFactory.getLogger(SpotBugsTask); @@ -174,8 +177,12 @@ class SpotBugsTask extends DefaultTask implements VerificationTask { /** * Property to specify the name of project. Some reporting formats use this property. * Default value is {@code "${project.name} (${task.name})"}. + *
+ * Note that this property, if treated as a task input, can break cacheability.
+ * As such, it has been marked {@link Internal} to exclude it from task up-to-date and + * cacheability checks. */ - @Input + @Internal @NonNull final Property projectName; /**