org.gradle.internal.progress.DefaultBuildOperationExecutorTest.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.internal.progress
import org.gradle.internal.concurrent.ExecutorFactory
import org.gradle.internal.concurrent.GradleThread
import org.gradle.internal.concurrent.ParallelismConfigurationManagerFixture
import org.gradle.internal.operations.BuildOperationContext
import org.gradle.internal.operations.BuildOperationDescriptor
import org.gradle.internal.operations.BuildOperationListener
import org.gradle.internal.operations.BuildOperationQueueFactory
import org.gradle.internal.operations.BuildOperationRef
import org.gradle.internal.operations.CallableBuildOperation
import org.gradle.internal.operations.CurrentBuildOperationRef
import org.gradle.internal.operations.DefaultBuildOperationExecutor
import org.gradle.internal.operations.DefaultBuildOperationIdFactory
import org.gradle.internal.operations.ExecutingBuildOperation
import org.gradle.internal.operations.OperationFinishEvent
import org.gradle.internal.operations.OperationStartEvent
import org.gradle.internal.operations.RunnableBuildOperation
import org.gradle.internal.time.Clock
import org.gradle.test.fixtures.concurrent.ConcurrentSpec
import static org.gradle.internal.operations.BuildOperationDescriptor.displayName
class DefaultBuildOperationExecutorTest extends ConcurrentSpec {
def listener = Mock(BuildOperationListener)
def timeProvider = Mock(Clock)
def progressLoggerFactory = Spy(NoOpProgressLoggerFactory)
def operationExecutor = new DefaultBuildOperationExecutor(listener, timeProvider, progressLoggerFactory, Mock(BuildOperationQueueFactory), Mock(ExecutorFactory), new ParallelismConfigurationManagerFixture(true, 1), new DefaultBuildOperationIdFactory())
def setup() {
CurrentBuildOperationRef.instance().clear()
}
def "fires events when wrap-around operation starts and finishes successfully"() {
setup:
GradleThread.setManaged()
and:
def buildOperation = Mock(CallableBuildOperation)
def progressLogger = Spy(NoOpProgressLoggerFactory.Logger)
def details = Mock(Object)
def operationDetailsBuilder = displayName("").name("").progressDisplayName("").details(details)
def id
when:
def result = operationExecutor.call(buildOperation)
then:
result == "result"
then:
1 * buildOperation.description() >> operationDetailsBuilder
1 * timeProvider.currentTime >> 123L
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
id = operation.id
assert operation.id != null
assert operation.parentId == null
assert operation.name == ""
assert operation.displayName == ""
assert operation.details == details
assert start.startTime == 123L
}
then:
1 * progressLoggerFactory.newOperation(_ as Class, _ as BuildOperationDescriptor) >> progressLogger
1 * progressLogger.start("", "")
then:
1 * buildOperation.call(_) >> "result"
then:
1 * progressLogger.completed(null, false)
then:
1 * timeProvider.currentTime >> 124L
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.parentId == null
assert operation.id == id
assert operation.name == ""
assert operation.displayName == ""
assert operation.details == details
assert opResult.startTime == 123L
assert opResult.endTime == 124L
assert opResult.failure == null
}
cleanup:
GradleThread.setUnmanaged()
}
def "fires events when non-wrap-around operation starts and finishes successfully"() {
setup:
GradleThread.setManaged()
and:
def progressLogger = Spy(NoOpProgressLoggerFactory.Logger)
def details = Mock(Object)
def operationDetailsBuilder = displayName("").name("").progressDisplayName("").details(details)
def id
when:
def handle = operationExecutor.start(operationDetailsBuilder)
then:
1 * timeProvider.currentTime >> 123L
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
id = operation.id
assert operation.id != null
assert operation.parentId == null
assert operation.name == ""
assert operation.displayName == ""
assert operation.details == details
assert start.startTime == 123L
}
then:
1 * progressLoggerFactory.newOperation(_ as Class, _ as BuildOperationDescriptor) >> progressLogger
1 * progressLogger.start("", "")
when:
handle.setResult("result")
then:
1 * progressLogger.completed(null, false)
then:
1 * timeProvider.currentTime >> 124L
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.parentId == null
assert operation.id == id
assert operation.name == ""
assert operation.displayName == ""
assert operation.details == details
assert opResult.startTime == 123L
assert opResult.endTime == 124L
assert opResult.failure == null
}
cleanup:
GradleThread.setUnmanaged()
}
def "fires events when wrap-around operation starts and fails"() {
setup:
GradleThread.setManaged()
and:
def buildOperation = Mock(RunnableBuildOperation)
def operationDescriptionBuilder = displayName("").progressDisplayName("")
def failure = new RuntimeException()
def progressLogger = Spy(NoOpProgressLoggerFactory.Logger)
def id
when:
operationExecutor.run(buildOperation)
then:
def e = thrown(RuntimeException)
e == failure
then:
1 * buildOperation.description() >> operationDescriptionBuilder
1 * timeProvider.currentTime >> 123L
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
id = operation.id
assert operation.parentId == null
assert operation.displayName == ""
assert start.startTime == 123L
}
then:
1 * progressLoggerFactory.newOperation(_ as Class, _ as BuildOperationDescriptor) >> progressLogger
1 * progressLogger.start("", "")
then:
1 * buildOperation.run(_) >> { throw failure }
then:
1 * progressLogger.completed(null, true)
then:
1 * timeProvider.currentTime >> 124L
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == id
assert opResult.startTime == 123L
assert opResult.endTime == 124L
assert opResult.failure == failure
}
cleanup:
GradleThread.setUnmanaged()
}
def "fires events when non-wrap-around operation starts and fails"() {
setup:
GradleThread.setManaged()
and:
def operationDescriptionBuilder = displayName("").progressDisplayName("")
def failure = new RuntimeException()
def progressLogger = Spy(NoOpProgressLoggerFactory.Logger)
def id
when:
def handle = operationExecutor.start(operationDescriptionBuilder)
then:
1 * timeProvider.currentTime >> 123L
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
id = operation.id
assert operation.parentId == null
assert operation.displayName == ""
assert start.startTime == 123L
}
then:
1 * progressLoggerFactory.newOperation(_ as Class, _ as BuildOperationDescriptor) >> progressLogger
1 * progressLogger.start("", "")
when:
handle.failed(failure)
then:
1 * progressLogger.completed(null, true)
then:
1 * timeProvider.currentTime >> 124L
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == id
assert opResult.startTime == 123L
assert opResult.endTime == 124L
assert opResult.failure == failure
}
cleanup:
GradleThread.setUnmanaged()
}
def "action can mark operation as failed without throwing an exception"() {
setup:
GradleThread.setManaged()
and:
def buildOperation = Spy(TestRunnableBuildOperation)
def failure = new RuntimeException()
when:
operationExecutor.run(buildOperation)
then:
1 * progressLoggerFactory.newOperation(_ as Class, _ as BuildOperationDescriptor) >> Spy(NoOpProgressLoggerFactory.Logger)
1 * buildOperation.run(_) >> { BuildOperationContext context -> context.failed(failure) }
then:
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert opResult.failure == failure
}
cleanup:
GradleThread.setUnmanaged()
}
def "action can provide operation result"() {
setup:
GradleThread.setManaged()
and:
def buildOperation = Spy(TestRunnableBuildOperation)
def result = "SomeResult"
when:
operationExecutor.run(buildOperation)
then:
1 * buildOperation.run(_) >> { BuildOperationContext context -> context.result = result }
then:
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert opResult.result == result
}
cleanup:
GradleThread.setUnmanaged()
}
def "multiple threads can run independent operations concurrently"() {
def id1
def id2
when:
async {
start {
GradleThread.setManaged()
operationExecutor.run(runnableBuildOperation("") {
instant.action1Started
thread.blockUntil.action2Started
})
}
thread.blockUntil.action1Started
GradleThread.setManaged()
operationExecutor.run(runnableBuildOperation("") {
instant.action2Started
thread.blockUntil.action1Finished
})
}
then:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
id1 = operation.id
assert operation.id != null
assert operation.parentId == null
assert operation.displayName == ""
}
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
id2 = operation.id
assert operation.id != null
assert operation.parentId == null
assert operation.displayName == ""
}
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == id1
assert opResult.failure == null
instant.action1Finished
}
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == id2
assert opResult.failure == null
}
}
def "multiple threads can run child operations concurrently"() {
setup:
GradleThread.setManaged()
and:
BuildOperationRef parent
def id1
def id2
when:
operationExecutor.run(runnableBuildOperation("") {
parent = operationExecutor.currentOperation
async {
start {
operationExecutor.run(new RunnableBuildOperation() {
void run(BuildOperationContext context) {
instant.action1Started
thread.blockUntil.action2Started
}
BuildOperationDescriptor.Builder description() {
displayName("").parent(parent)
}
})
}
start {
thread.blockUntil.action1Started
operationExecutor.run(new RunnableBuildOperation() {
void run(BuildOperationContext context) {
instant.action2Started
thread.blockUntil.action1Finished
}
BuildOperationDescriptor.Builder description() {
displayName("").parent(parent)
}
})
}
}
})
then:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
assert operation.parentId == null
assert operation.displayName == ""
}
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
id1 = operation.id
assert operation.id != null
assert operation.parentId == parent.id
assert operation.displayName == ""
}
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
id2 = operation.id
assert operation.id != null
assert operation.parentId == parent.id
assert operation.displayName == ""
}
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == id1
assert opResult.failure == null
instant.action1Finished
}
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == id2
assert opResult.failure == null
}
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == parent.id
assert opResult.failure == null
}
cleanup:
GradleThread.setUnmanaged()
}
def "cannot start child operation when parent has completed"() {
BuildOperationRef parent = null
given:
operationExecutor.run(runnableBuildOperation("parent") {
parent = operationExecutor.currentOperation
})
when:
operationExecutor.run(new RunnableBuildOperation() {
void run(BuildOperationContext context) {}
BuildOperationDescriptor.Builder description() {
displayName("child").parent(parent)
}
})
then:
def e = thrown(IllegalStateException)
e.message == 'Cannot start operation (child) as parent operation (parent) has already completed.'
}
def "child fails when parent completes while child is still running"() {
when:
async {
operationExecutor.run(runnableBuildOperation("parent") {
def operation = operationExecutor.currentOperation
start {
operationExecutor.run(new RunnableBuildOperation() {
void run(BuildOperationContext context) {
instant.childStarted
thread.blockUntil.parentCompleted
}
BuildOperationDescriptor.Builder description() {
displayName("child").parent(operation)
}
})
}
thread.blockUntil.childStarted
})
instant.parentCompleted
}
then:
def e = thrown(IllegalStateException)
e.message == 'Parent operation (parent) completed before this operation (child).'
}
def "fails when non-wrap-around operation finishes after parent"() {
ExecutingBuildOperation operation
operationExecutor.run(runnableBuildOperation("parent") {
operation = operationExecutor.start(displayName("child"))
})
when:
operation.result = "result"
then:
def e = thrown(IllegalStateException)
e.message == 'Parent operation (parent) completed before this operation (child).'
}
def "can query operation id from inside operation"() {
given:
GradleThread.setManaged()
def parentOperation = Spy(TestRunnableBuildOperation)
def childOperation = Spy(TestRunnableBuildOperation)
def id
when:
operationExecutor.run(parentOperation)
then:
1 * parentOperation.run(_) >> {
assert operationExecutor.currentOperation.id != null
assert operationExecutor.currentOperation.parentId == null
id = operationExecutor.currentOperation.id
operationExecutor.run(childOperation)
}
1 * childOperation.run(_) >> {
assert operationExecutor.currentOperation.id != null
assert operationExecutor.currentOperation.id != id
assert operationExecutor.currentOperation.parentId == id
}
cleanup:
GradleThread.setUnmanaged()
}
def "cannot query operation id when no operation running on current managed thread"() {
when:
async {
start {
operationExecutor.run(runnableBuildOperation("operation") {
instant.operationRunning
thread.blockUntil.queried
})
}
thread.blockUntil.operationRunning
GradleThread.setManaged()
try {
operationExecutor.currentOperation.id
} finally {
instant.queried
GradleThread.setUnmanaged()
}
}
then:
IllegalStateException e = thrown()
e.message == "No operation is currently running."
}
def "cannot query current operation id when no operation running on current unmanaged thread"() {
when:
BuildOperationDescriptor op
async {
assert !GradleThread.managed
op = operationExecutor.currentOperation.id
}
then:
def ex = thrown(IllegalStateException)
ex.message == 'No operation is currently running.'
}
def "can nest operations on unmanaged threads"() {
when:
async {
assert !GradleThread.managed
operationExecutor.run(new RunnableBuildOperation() {
void run(BuildOperationContext outerContext) {
assert operationExecutor.currentOperation.id != null
assert operationExecutor.currentOperation.parentId.id < 0
operationExecutor.run(new RunnableBuildOperation() {
void run(BuildOperationContext innerContext) {}
BuildOperationDescriptor.Builder description() { displayName('inner') }
})
}
BuildOperationDescriptor.Builder description() { displayName('outer') }
})
}
then:
noExceptionThrown()
}
def "attaches parent id when operation is nested inside another"() {
setup:
GradleThread.setManaged()
and:
def operation1 = Spy(TestRunnableBuildOperation)
def operation2 = Spy(TestRunnableBuildOperation)
def operation3 = Spy(TestRunnableBuildOperation)
def parentId
def child1Id
def child2Id
when:
operationExecutor.run(operation1)
then:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
parentId = operation.id
assert operation.parentId == null
}
1 * operation1.run(_) >> {
operationExecutor.run(operation2)
}
and:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
child1Id = operation.id
assert operation.parentId == parentId
}
1 * operation2.run(_) >> {
operationExecutor.run(operation3)
}
and:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
child2Id = operation.id
assert operation.parentId == child1Id
}
1 * operation3.run(_) >> {}
and:
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == child2Id
}
and:
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == child1Id
}
and:
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == parentId
}
cleanup:
GradleThread.setUnmanaged()
}
def "attaches correct parent id when multiple threads run nested operations"() {
def parent1Id
def parent2Id
def child1Id
def child2Id
when:
async {
start {
GradleThread.setManaged()
operationExecutor.run(runnableBuildOperation("") {
operationExecutor.run(runnableBuildOperation("") {
instant.child1Started
thread.blockUntil.child2Started
})
})
}
start {
GradleThread.setManaged()
operationExecutor.run(runnableBuildOperation("") {
operationExecutor.run(runnableBuildOperation("") {
instant.child2Started
thread.blockUntil.child1Started
})
})
}
}
then:
1 * listener.started({ it.displayName == "" }, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
parent1Id = operation.id
assert operation.parentId == null
}
1 * listener.started({ it.displayName == "" }, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
parent2Id = operation.id
assert operation.parentId == null
}
1 * listener.started({ it.displayName == "" }, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
child1Id = operation.id
assert operation.parentId == parent1Id
}
1 * listener.started({ it.displayName == "" }, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
assert operation.id != null
child2Id = operation.id
assert operation.parentId == parent2Id
}
and:
1 * listener.finished({ it.id == child1Id }, _)
1 * listener.finished({ it.id == child2Id }, _)
1 * listener.finished({ it.id == parent1Id }, _)
1 * listener.finished({ it.id == parent2Id }, _)
}
def "attaches parent id when sibling operation fails"() {
setup:
GradleThread.setManaged()
and:
def operation1 = Spy(TestRunnableBuildOperation)
def operation2 = Spy(TestRunnableBuildOperation)
def operation3 = Spy(TestRunnableBuildOperation)
def parentId
def child1Id
def child2Id
when:
operationExecutor.run(operation1)
then:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
parentId = operation.id
assert operation.parentId == null
}
1 * operation1.run(_) >> {
try {
operationExecutor.run(operation2)
} catch (RuntimeException) {
// Ignore
}
operationExecutor.run(operation3)
}
and:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
child1Id = operation.id
assert operation.parentId == parentId
}
1 * operation2.run(_) >> { throw new RuntimeException() }
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == child1Id
}
and:
1 * listener.started(_, _) >> { BuildOperationDescriptor operation, OperationStartEvent start ->
child2Id = operation.id
assert operation.parentId == parentId
}
1 * operation3.run(_) >> {}
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == child2Id
}
and:
1 * listener.finished(_, _) >> { BuildOperationDescriptor operation, OperationFinishEvent opResult ->
assert operation.id == parentId
}
cleanup:
GradleThread.setUnmanaged()
}
def runnableBuildOperation(String name, Closure cl) {
new RunnableBuildOperation() {
void run(BuildOperationContext context) {
cl.run()
}
BuildOperationDescriptor.Builder description() {
return displayName(name)
}
}
}
static class TestRunnableBuildOperation implements RunnableBuildOperation {
BuildOperationDescriptor.Builder description() { displayName("test") }
String toString() { getClass().simpleName }
void run(BuildOperationContext buildOperationContext) {}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy