
org.gradle.integtests.ParallelTaskExecutionIntegrationTest.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 2014 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.integtests
import org.gradle.execution.taskgraph.DefaultTaskExecutionPlan
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
import org.gradle.integtests.fixtures.executer.GradleHandle
import org.gradle.test.fixtures.ConcurrentTestUtil
import org.gradle.test.fixtures.server.http.BlockingHttpServer
import org.junit.Rule
import spock.lang.IgnoreIf
@IgnoreIf({ GradleContextualExecuter.parallel })
// no point, always runs in parallel
class ParallelTaskExecutionIntegrationTest extends AbstractIntegrationSpec {
@Rule
public final BlockingHttpServer blockingServer = new BlockingHttpServer()
def setup() {
executer.withArgument("-D${DefaultTaskExecutionPlan.INTRA_PROJECT_TOGGLE}=true")
blockingServer.start()
settingsFile << 'include "a", "b"'
buildFile << """
class SerialPing extends DefaultTask {
@TaskAction
void ping() {
URL url = new URL("http://localhost:${blockingServer.port}/" + path)
url.openConnection().getHeaderField('RESPONSE')
}
}
@ParallelizableTask
class Ping extends SerialPing {
}
@ParallelizableTask
class FailingPing extends DefaultTask {
@TaskAction
void ping() {
URL url = new URL("http://localhost:${blockingServer.port}/" + path)
url.openConnection().getHeaderField('RESPONSE')
throw new RuntimeException("task failure")
}
}
allprojects {
tasks.addRule("<>Ping") { String name ->
if (name.endsWith("Ping") && name.size() == 5) {
tasks.create(name, Ping)
}
}
tasks.addRule("<>FailingPing") { String name ->
if (name.endsWith("FailingPing")) {
tasks.create(name, FailingPing)
}
}
tasks.addRule("<>SerialPing") { String name ->
if (name.endsWith("SerialPing")) {
tasks.create(name, SerialPing)
}
}
}
"""
executer.withArgument('--info')
}
void withParallelThreads(int threadCount) {
executer.withArgument("--parallel")
executer.withArgument("--max-workers=$threadCount")
}
def "feature toggle is required for tasks to run in parallel"() {
given:
executer.withArguments() // clears what was set in setup
withParallelThreads(2)
expect:
blockingServer.expectSerialExecution(":aPing")
blockingServer.expectSerialExecution(":bPing")
run ":aPing", ":bPing"
}
def "info is logged when overlapping outputs prevent parallel execution"() {
given:
executer.withArgument("-i")
withParallelThreads(2)
and:
buildFile << """
aPing.outputs.file "dir"
bPing.outputs.file "dir/file"
"""
expect:
GradleHandle handle
def handleStarter = { handle = executer.withTasks(":aPing", ":bPing").start() }
blockingServer.expectConcurrentExecution([":aPing"]) {
ConcurrentTestUtil.poll(1) {
assert handle.standardOutput.contains("Cannot execute task :bPing in parallel with task :aPing due to overlapping output: ${file("dir")}")
}
}
blockingServer.expectSerialExecution(":bPing")
handleStarter.call()
handle.waitForFinish()
}
def "info is logged when task is prevented from executing in parallel due to custom actions"() {
given:
executer.withArgument("-i")
withParallelThreads(2)
and:
buildFile << """
bPing.doLast {}
"""
expect:
GradleHandle handle
def handleStarter = { handle = executer.withTasks(":aPing", ":bPing").start() }
blockingServer.expectConcurrentExecution([":aPing"]) {
ConcurrentTestUtil.poll(1) {
assert handle.standardOutput.contains("Unable to parallelize task :bPing due to presence of custom actions (e.g. doFirst()/doLast())")
}
}
blockingServer.expectSerialExecution(":bPing")
handleStarter.call()
handle.waitForFinish()
}
def "two independent parallelizable tasks execute in parallel"() {
given:
withParallelThreads(2)
expect:
blockingServer.expectConcurrentExecution(":aPing", ":bPing")
run ":aPing", ":bPing"
}
def "independent parallelizable tasks from multiple projects execute in parallel"() {
given:
withParallelThreads(3)
expect:
blockingServer.expectConcurrentExecution(":a:aPing", ":a:bPing", ":b:aPing")
run ":a:aPing", ":a:bPing", ":b:aPing"
}
def "two parallelizable tasks with should run after execute in parallel"() {
given:
withParallelThreads(2)
when:
buildFile << """
bPing.shouldRunAfter aPing
"""
then:
blockingServer.expectConcurrentExecution(":aPing", ":bPing")
run ":aPing", ":bPing"
}
def "two parallelizable tasks that are dependencies of another task are executed in parallel"() {
given:
withParallelThreads(2)
when:
buildFile << """
aPing.dependsOn bPing, cPing
"""
then:
blockingServer.expectConcurrentExecution(":bPing", ":cPing")
blockingServer.expectSerialExecution(":aPing")
run ":aPing"
}
def "task is not executed if one of its dependencies executed in parallel fails"() {
given:
withParallelThreads(2)
when:
buildFile << """
aPing.dependsOn bPing, cFailingPing
"""
then:
blockingServer.expectConcurrentExecution(":bPing", ":cFailingPing")
when:
fails ":aPing"
then:
notExecuted(":aPing")
}
def "the number of tasks executed in parallel is limited by the number of parallel threads"() {
given:
withParallelThreads(2)
expect:
blockingServer.expectConcurrentExecution(":aPing", ":bPing")
blockingServer.expectConcurrentExecution(":cPing", ":dPing")
run ":aPing", ":bPing", ":cPing", ":dPing"
}
def "non parallelizable tasks are not started if there are any parallelizable tasks running"() {
given:
withParallelThreads(3)
when:
buildFile << """
aPing.dependsOn bPing, cPing, dSerialPing
"""
then:
blockingServer.expectConcurrentExecution(":bPing", ":cPing")
blockingServer.expectConcurrentExecution(":dSerialPing")
blockingServer.expectSerialExecution(":aPing")
run ":aPing"
}
def "parallelizable tasks are not started if there are any non parallelizable tasks running"() {
given:
withParallelThreads(3)
when:
buildFile << """
aPing.dependsOn bSerialPing, cPing, dPing
"""
then:
blockingServer.expectConcurrentExecution(":bSerialPing")
blockingServer.expectConcurrentExecution(":cPing", ":dPing")
blockingServer.expectSerialExecution(":aPing")
run ":aPing"
}
def "parallelizable tasks are run in parallel even if there are some non parallelizable tasks running in different project"() {
given:
withParallelThreads(3)
expect:
blockingServer.expectConcurrentExecution(":a:aSerialPing", ":b:aPing", ":b:bPing")
run ":a:aSerialPing", ":b:aPing", ":b:bPing"
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy