org.gradle.api.tasks.AbstractIncrementalTasksIntegrationTest.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2013 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
*
* 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 org.gradle.api.tasks
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.internal.execution.history.changes.ChangeTypeInternal
abstract class AbstractIncrementalTasksIntegrationTest extends AbstractIntegrationSpec {
abstract String getTaskAction()
abstract ChangeTypeInternal getRebuildChangeType();
abstract String getPrimaryInputAnnotation();
def setup() {
setupTaskSources()
buildFile << buildFileBase
buildFile << """
task incremental(type: IncrementalTask) {
inputDir = project.mkdir('inputs')
outputDir = project.mkdir('outputs')
prop = 'foo'
}
"""
file('inputs/file0.txt') << "inputFile0"
file('inputs/file1.txt') << "inputFile1"
file('inputs/file2.txt') << "inputFile2"
file('outputs/file1.txt') << "outputFile1"
file('outputs/file2.txt') << "outputFile2"
}
void setupTaskSources(String inputDirAnnotation = primaryInputAnnotation) {
file("buildSrc/src/main/groovy/BaseIncrementalTask.groovy").text = """
import org.gradle.api.*
import org.gradle.api.file.*
import org.gradle.api.plugins.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.*
import org.gradle.work.*
abstract class BaseIncrementalTask extends DefaultTask {
${inputDirAnnotation}
@InputDirectory
abstract DirectoryProperty getInputDir()
@Optional
@OutputFile
abstract RegularFileProperty getOutputFile()
@TaskAction
$taskAction
def touchOutputs() {
}
def createOutputsNonIncremental() {
}
@Internal
def addedFiles = []
@Internal
def modifiedFiles = []
@Internal
def removedFiles = []
@Internal
def incrementalExecution
}
"""
file("buildSrc/src/main/groovy/IncrementalTask.groovy").text = """
import org.gradle.api.*
import org.gradle.api.plugins.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.*
abstract class IncrementalTask extends BaseIncrementalTask {
@Input
def String prop
@OutputDirectory
def File outputDir
@Override
def createOutputsNonIncremental() {
new File(outputDir, 'file1.txt').text = 'outputFile1'
new File(outputDir, 'file2.txt').text = 'outputFile2'
}
@Override
def touchOutputs() {
outputDir.eachFile {
it << "more content"
}
}
}
"""
}
private static String getBuildFileBase() {
"""
ext {
incrementalExecution = true
added = []
modified = []
removed = []
}
task incrementalCheck(dependsOn: "incremental") {
def ext = project.ext
doLast {
assert incremental.incrementalExecution == ext.incrementalExecution
assert incremental.addedFiles.collect({ it.name }).sort() == ext.added
assert incremental.modifiedFiles.collect({ it.name }).sort() == ext.modified
assert incremental.removedFiles.collect({ it.name }).sort() == ext.removed
}
}
"""
}
def "incremental task is informed that all input files are 'out-of-date' when run for the first time"() {
expect:
executesNonIncrementally()
}
def "incremental task is skipped when run with no changes since last execution"() {
given:
previousExecution()
when:
run "incremental"
then:
skipped(":incremental")
}
def "incremental task is informed of 'out-of-date' files when input file modified"() {
given:
previousExecution()
when:
file('inputs/file1.txt') << "changed content"
then:
executesIncrementally(modified: ['file1.txt'])
}
def "incremental task is informed of 'out-of-date' files when input file added"() {
given:
previousExecution()
when:
file('inputs/file3.txt') << "file3 content"
then:
executesIncrementally(added: ['file3.txt'])
}
def "incremental task is informed of 'out-of-date' files when input file removed"() {
given:
previousExecution()
when:
file('inputs/file2.txt').delete()
then:
executesIncrementally(removed: ['file2.txt'])
}
def "incremental task is informed of 'out-of-date' files when all input files removed"() {
given:
previousExecution()
when:
file('inputs/file0.txt').delete()
file('inputs/file1.txt').delete()
file('inputs/file2.txt').delete()
then:
executesIncrementally(removed: ['file0.txt', 'file1.txt', 'file2.txt'])
}
def "incremental task is informed of 'out-of-date' files with added, removed and modified files"() {
given:
previousExecution()
when:
file('inputs/file1.txt') << "changed content"
file('inputs/file2.txt').delete()
file('inputs/file3.txt') << "new file 3"
file('inputs/file4.txt') << "new file 4"
then:
executesIncrementally(
modified: ['file1.txt'],
removed: ['file2.txt'],
added: ['file3.txt', 'file4.txt']
)
}
def "incremental task is informed of 'out-of-date' files when task has no declared outputs or properties"() {
given:
buildFile.text = buildFileBase
buildFile << """
task incremental(type: BaseIncrementalTask) {
inputDir = project.mkdir('inputs')
}
"""
and:
previousExecution()
when:
file('inputs/file3.txt') << "file3 content"
then:
executesIncrementally(added: ['file3.txt'])
}
def "incremental task is informed that all input files are 'out-of-date' when input property has changed"() {
given:
previousExecution()
when:
buildFile << "incremental.prop = 'changed'"
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when input file property has been removed"() {
given:
buildFile << """
if (file('new-input.txt').exists()) {
incremental.inputs.file('new-input.txt')
}
"""
def toBeRemovedInputFile = file('new-input.txt')
toBeRemovedInputFile.text = "to be removed input file"
previousExecution()
when:
toBeRemovedInputFile.delete()
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when task class has changed"() {
given:
previousExecution()
when:
buildFile.text = buildFileBase
buildFile << """
abstract class IncrementalTask2 extends BaseIncrementalTask {}
task incremental(type: IncrementalTask2) {
inputDir = project.mkdir('inputs')
}
"""
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when output directory is changed"() {
given:
previousExecution()
when:
buildFile << "incremental.outputDir = project.mkdir('new-outputs')"
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when output file has changed"() {
given:
previousExecution()
when:
file("outputs/file1.txt") << "further change"
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when output file has been removed"() {
given:
previousExecution()
when:
file("outputs/file1.txt").delete()
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when all output files have been removed"() {
given:
previousExecution()
when:
file("outputs").deleteDir()
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when Task.upToDate() is false"() {
given:
previousExecution()
when:
buildFile << "incremental.outputs.upToDateWhen { false }"
then:
executesNonIncrementally()
}
def "incremental task is informed that all input files are 'out-of-date' when gradle is executed with --rerun-tasks"() {
given:
previousExecution()
when:
executer.withArgument("--rerun-tasks")
then:
executesNonIncrementally()
}
def "incremental task is informed of 'out-of-date' files since previous successful execution"() {
given:
previousExecution()
and:
file('inputs/file1.txt') << "changed content"
when:
failedExecution()
then:
executesIncrementally(modified: ['file1.txt'])
}
/*
7. Sad-day cases
- Incremental task has input files declared
- Incremental task action throws exception
- Incremental task action processes outOfDate files multiple times
- Attempt to process removed files without first processing outOfDate files
*/
def previousExecution() {
run "incremental"
}
def failedExecution() {
executer.withArgument("-DforceFail=yep")
assert fails("incremental")
executer.withArguments()
}
def executesIncrementally(Map changes) {
executesIncrementalTask(incremental: true, *:changes)
}
def executesNonIncrementally(List rebuiltFiles = preexistingInputs) {
executesIncrementalTask(
incremental: false,
(rebuildChangeType.name().toLowerCase(Locale.US)): rebuiltFiles
)
}
List preexistingInputs = ['file0.txt', 'file1.txt', 'file2.txt', 'inputs']
def executesIncrementalTask(Map options) {
boolean incremental = options.incremental != false
List added = options.added ?: []
List modified = options.modified ?: []
List removed = options.removed ?: []
buildFile << """
ext.added = ${added.collect { "'${it}'"}}
ext.modified = ${modified.collect { "'${it}'"}}
ext.removed = ${removed.collect { "'${it}'"}}
ext.incrementalExecution = ${incremental}
"""
succeeds("incrementalCheck")
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy