org.gradle.integtests.tooling.fixture.ProgressEvents.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 2015 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.tooling.fixture
import junit.framework.AssertionFailedError
import org.gradle.api.specs.Spec
import org.gradle.internal.os.OperatingSystem
import org.gradle.tooling.Failure
import org.gradle.tooling.events.FailureResult
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationDescriptor
import org.gradle.tooling.events.OperationResult
import org.gradle.tooling.events.ProgressEvent
import org.gradle.tooling.events.ProgressListener
import org.gradle.tooling.events.StartEvent
import org.gradle.tooling.events.SuccessResult
import org.gradle.tooling.events.configuration.ProjectConfigurationOperationDescriptor
import org.gradle.tooling.events.task.TaskOperationDescriptor
import org.gradle.tooling.events.test.TestOperationDescriptor
import org.gradle.tooling.events.transform.TransformOperationDescriptor
import org.gradle.tooling.events.work.WorkItemOperationDescriptor
import org.gradle.util.GradleVersion
class ProgressEvents implements ProgressListener {
private final List events = []
private boolean dirty
private final List operations = new ArrayList()
private static final boolean IS_WINDOWS_OS = OperatingSystem.current().isWindows()
boolean skipValidation
/**
* Creates a {@link ProgressEvents} implementation for the current tooling api client version.
*/
static ProgressEvents create() {
return GradleVersion.current().baseVersion < GradleVersion.version("3.5") ? new ProgressEvents() : new ProgressEventsWithStatus()
}
protected ProgressEvents() {
}
void clear() {
events.clear()
operations.clear()
dirty = false
}
/**
* Asserts that the events form zero or more well-formed trees of operations.
*/
void assertHasZeroOrMoreTrees() {
if (dirty) {
Set seen = []
Map running = [:]
for (ProgressEvent event : events) {
assert event.displayName == event.toString()
assert event.descriptor.displayName
assert event.descriptor.displayName == event.descriptor.toString()
assert event.descriptor.name
if (event instanceof StartEvent) {
def descriptor = event.descriptor
assert seen.add(descriptor)
assert !running.containsKey(descriptor)
running[descriptor] = event
// Display name should be mostly unique
if (!skipValidation && uniqueBuildOperation(descriptor)) {
if (descriptor.displayName in ['Configure settings', 'Configure build', 'Calculate task graph', 'Run tasks']
|| descriptor.displayName.contains('/maven-metadata.xml')
|| descriptor.displayName.startsWith('Apply plugin ')
|| descriptor.displayName.startsWith('Configure project ')
|| descriptor.displayName.startsWith('Cross-configure project ')
|| descriptor.displayName.startsWith('Resolve files of')
|| descriptor.displayName.startsWith('Executing ')
|| descriptor.displayName.startsWith('Execute container callback action')
|| descriptor.displayName.startsWith('Resolving ')
) {
// Ignore this for now
} else {
def duplicateName = operations.find({
it.descriptor.displayName == descriptor.displayName &&
it.parent.descriptor == descriptor.parent
})
if (duplicateName != null) {
// Same display name and same parent
throw new AssertionFailedError("Found duplicate operation '${duplicateName}' in events:\n${describeList(events)}")
}
}
}
// parent should also be running
assert descriptor.parent == null || running.containsKey(descriptor.parent)
def parent = descriptor.parent == null ? null : operations.find { it.descriptor == descriptor.parent }
Operation operation = newOperation(parent, descriptor)
operations.add(operation)
assert descriptor.displayName == descriptor.toString()
assert event.displayName == "${descriptor.displayName} started" as String
} else if (event instanceof FinishEvent) {
def descriptor = event.descriptor
def startEvent = running.remove(descriptor)
assert startEvent != null
// parent should still be running
assert descriptor.parent == null || running.containsKey(descriptor.parent)
def storedOperation = operations.find { it.descriptor == descriptor }
storedOperation.result = event.result
assert event.displayName.matches("\\Q${descriptor.displayName}\\E [\\w-]+")
// don't check event timestamp order on Windows OS
// timekeeping in CI environment on Windows is currently problematic
if (!IS_WINDOWS_OS) {
assert startEvent.eventTime <= event.eventTime
}
assert event.result.startTime == startEvent.eventTime
assert event.result.endTime == event.eventTime
} else {
def descriptor = event.descriptor
// operation should still be running
assert running.containsKey(descriptor) != null
def operation = operations.find { it.descriptor == event.descriptor }
otherEvent(event, operation)
}
}
assert running.size() == 0: "Not all operations completed: ${running.values()}, events: ${events}"
dirty = false
}
}
protected Operation newOperation(Operation parent, OperationDescriptor descriptor) {
new Operation(parent, descriptor)
}
protected void otherEvent(ProgressEvent event, Operation operation) {
throw new AssertionError("Unexpected type of progress event received: ${event.getClass()}")
}
// Ignore this check for TestOperationDescriptors as they are currently not unique when coming from different test tasks
// Ignore resolve artifact operations as they are not necessarily unique atm
boolean uniqueBuildOperation(OperationDescriptor operationDescriptor) {
return !(operationDescriptor instanceof TestOperationDescriptor) && !operationDescriptor.displayName.startsWith("Resolve artifact ")
}
/**
* Asserts that the events form exactly one well-formed trees of operations.
*/
void assertHasSingleTree() {
assertHasZeroOrMoreTrees()
assert !operations.empty
assert operations[0].descriptor.parent == null
operations.tail().each { assert it.descriptor.parent != null }
}
/**
* Asserts that the events form a typical tree of operations for a build.
*/
void assertIsABuild() {
assertHasSingleTree()
def root = operations[0]
assert root.buildOperation
assert root.descriptor.displayName == 'Run build'
}
boolean isEmpty() {
assertHasZeroOrMoreTrees()
return events.empty
}
/**
* Returns all events, in the order received.
*/
List getAll() {
assertHasZeroOrMoreTrees()
return events
}
/**
* Returns all operations, in the order started.
*/
List getOperations() {
assertHasZeroOrMoreTrees()
return operations
}
/**
* Returns all generic build operations, in the order started.
*/
List getBuildOperations() {
assertHasZeroOrMoreTrees()
return operations.findAll { it.buildOperation } as List
}
/**
* Returns all tests, in the order started.
*/
List getTests() {
assertHasZeroOrMoreTrees()
return operations.findAll { it.test } as List
}
/**
* Returns all tasks, in the order started.
*/
List getTasks() {
assertHasZeroOrMoreTrees()
return operations.findAll { it.task } as List
}
/**
* Returns all successful operations, in the order started.
*/
List getSuccessful() {
assertHasZeroOrMoreTrees()
return operations.findAll { it.successful } as List
}
/**
* Returns all failed operations, in the order started.
*/
List getFailed() {
assertHasZeroOrMoreTrees()
return operations.findAll { it.failed } as List
}
/**
* Returns the operation with the given display name. Fails when there is not exactly one such operation.
*
* @param displayNames candidate display names (may be different depending on the Gradle version under test)
*/
Operation operation(String... displayNames) {
assertHasZeroOrMoreTrees()
def operation = operations.find { it.descriptor.displayName in displayNames }
if (operation == null) {
throw new AssertionFailedError("No operation with display name '${displayNames[0]}' found in:\n${describeList(operations)}")
}
return operation
}
/**
* Returns the first operation with a display name matching the given regex. Fails when an operation is not found.
*
* @param regex candidate display names (may be different depending on the Gradle version under test)
*/
Operation operationMatches(String regex) {
assertHasZeroOrMoreTrees()
def operation = operations.find { it.descriptor.displayName.matches(regex) }
if (operation == null) {
throw new AssertionFailedError("No operation matching regex '${regex}' found in:\n${describeList(operations)}")
}
return operation
}
/**
* Returns the operation with the given display name. Fails when there is not exactly one such operation.
*
* @param displayNames candidate display names (may be different depending on the Gradle version under test)
*/
Operation operation(Operation parent, String... displayNames) {
assertHasZeroOrMoreTrees()
def operation = operations.find { it.parent == parent && it.descriptor.displayName in displayNames }
if (operation == null) {
throw new AssertionFailedError("No operation with display name '${displayNames[0]}' and parent '$parent' found in:\n${describeList(operations)}")
}
return operation
}
@Override
void statusChanged(ProgressEvent event) {
dirty = true
operations.clear()
events << event
}
static class Operation {
final OperationDescriptor descriptor
final Operation parent
final List children = []
OperationResult result
protected Operation(Operation parent, OperationDescriptor descriptor) {
this.descriptor = descriptor
this.parent = parent
if (parent != null) {
parent.children.add(this)
}
}
@Override
String toString() {
return descriptor.displayName
}
boolean isTest() {
return descriptor instanceof TestOperationDescriptor
}
boolean isTask() {
return descriptor instanceof TaskOperationDescriptor
}
boolean isWorkItem() {
try {
// the class is not present in pre 5.1 TAPI
return descriptor instanceof WorkItemOperationDescriptor
} catch (NoClassDefFoundError ignore) {
false
}
}
boolean isProjectConfiguration() {
try {
// the class is not present in pre 5.1 TAPI
return descriptor instanceof ProjectConfigurationOperationDescriptor
} catch (NoClassDefFoundError ignore) {
false
}
}
boolean isTransform() {
try {
// the class is not present in pre 5.1 TAPI
return descriptor instanceof TransformOperationDescriptor
} catch (NoClassDefFoundError ignore) {
false
}
}
boolean isBuildOperation() {
return !test && !task && !workItem && !projectConfiguration && !transform
}
boolean isSuccessful() {
return result instanceof SuccessResult
}
boolean isFailed() {
return result instanceof FailureResult
}
List getFailures() {
assert result instanceof FailureResult
return result.failures
}
Operation child(Spec displayNameSpec) {
def child = children.find { displayNameSpec.isSatisfiedBy(it.descriptor.displayName) }
if (child == null) {
throw new AssertionFailedError("No operation matching display name found in children of '$descriptor.displayName':\n${describeList(children)}")
}
return child
}
Operation child(String... displayNames) {
def child = children.find { it.descriptor.displayName in displayNames }
if (child == null) {
throw new AssertionFailedError("No operation with display name '${displayNames[0]}' found in children of '$descriptor.displayName':\n${describeList(children)}")
}
return child
}
/**
* Select child operations that have the given display name.
*
* @param displayName Operation display name
* @return the selected Operations, potentially empty
*/
List children(String displayName) {
return children.findAll { it.descriptor.displayName == displayName }
}
List descendants(Spec super Operation> filter) {
def found = [] as List
def recurse
recurse = { List children ->
children.each { child ->
if (filter.isSatisfiedBy(child)) {
found << child
}
recurse child.children
}
}
recurse children
found
}
Operation descendant(String displayName) {
def found = descendants { it.descriptor.displayName == displayName }
if (found.size() == 1) {
return found[0]
}
if (found.empty) {
throw new AssertionFailedError("No operation with display name '$displayName' found in descendants of '$descriptor.displayName':\n${describeOperationsTree(children)}")
}
throw new AssertionFailedError("More than one operation with display name '$displayName' found in descendants of '$descriptor.displayName':\n${describeOperationsTree(children)}")
}
}
private static String describeList(List/**/ haveDescriptor) {
return '\t' + haveDescriptor.collect { it.descriptor.displayName }.join('\n\t')
}
String describeOperationsTree() {
return describeOperationsTree(operations.findAll { !it.parent })
}
static String describeOperationsTree(List operations) {
def description = ''
def recurse
recurse = { List children, int level = 0 ->
children.each { child ->
description += "\t${' ' * level}${child.descriptor.displayName}\n"
recurse child.children, level + 1
}
}
recurse operations
return description
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy