org.gradle.build.event.BuildEventsIntegrationTest.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 2019 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.build.event
import org.gradle.api.services.BuildServiceParameters
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.DefaultTestExecutionResult
import org.gradle.test.fixtures.file.TestFile
import org.gradle.test.precondition.Requires
import org.gradle.test.preconditions.IntegTestPreconditions
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskFailureResult
import org.gradle.tooling.events.task.TaskFinishEvent
import org.gradle.tooling.events.task.TaskSkippedResult
import org.gradle.tooling.events.task.TaskSuccessResult
import org.gradle.util.internal.TextUtil
import spock.lang.Issue
import static org.hamcrest.Matchers.containsString
class BuildEventsIntegrationTest extends AbstractIntegrationSpec {
def "listener can subscribe to task completion events"() {
loggingListener()
registeringPlugin()
buildFile << """
apply plugin: LoggingPlugin
task notUpToDate {
doFirst { println("not up-to-date") }
}
task upToDate {
def file = file("thing.txt")
outputs.file(file)
doFirst { file.text = "thing" }
}
task broken {
dependsOn notUpToDate, upToDate
doFirst {
throw new RuntimeException()
}
}
"""
when:
run("notUpToDate")
then:
output.count("EVENT:") == 1
outputContains("EVENT: finish :notUpToDate OK")
when:
run("notUpToDate")
then:
output.count("EVENT:") == 1
outputContains("EVENT: finish :notUpToDate OK")
when:
fails("broken")
then:
output.count("EVENT:") == 3
outputContains("EVENT: finish :notUpToDate OK")
outputContains("EVENT: finish :upToDate OK")
outputContains("EVENT: finish :broken FAILED")
when:
fails("broken")
then:
output.count("EVENT:") == 3
outputContains("EVENT: finish :notUpToDate OK")
outputContains("EVENT: finish :upToDate UP-TO-DATE")
outputContains("EVENT: finish :broken FAILED")
}
def "plugin applied to multiple projects can register a shared listener"() {
createDirs("a", "b")
settingsFile << """
include 'a', 'b'
"""
loggingListener()
registeringPlugin()
buildFile << """
subprojects {
apply plugin: LoggingPlugin
task thing { }
}
"""
when:
run("a:thing")
then:
output.count("EVENT:") == 1
outputContains("EVENT: finish :a:thing")
when:
run("a:thing")
then:
output.count("EVENT:") == 1
outputContains("EVENT: finish :a:thing")
when:
run("thing")
then:
output.count("EVENT:") == 2
outputContains("EVENT: finish :a:thing")
outputContains("EVENT: finish :b:thing")
when:
run("thing")
then:
output.count("EVENT:") == 2
outputContains("EVENT: finish :a:thing")
outputContains("EVENT: finish :b:thing")
}
def "listener receives task completion events from included builds"() {
settingsFile << """
includeBuild 'a'
includeBuild 'b'
"""
loggingListener()
registeringPlugin()
buildFile << """
apply plugin: LoggingPlugin
task thing {
dependsOn gradle.includedBuilds*.task(':thing')
}
"""
file("a/build.gradle") << """
task thing
"""
file("b/build.gradle") << """
task thing
"""
when:
run("thing")
then:
output.count("EVENT:") == 3
outputContains("EVENT: finish :thing")
outputContains("EVENT: finish :a:thing")
outputContains("EVENT: finish :b:thing")
when:
run("thing")
then:
output.count("EVENT:") == 3
outputContains("EVENT: finish :thing")
outputContains("EVENT: finish :a:thing")
outputContains("EVENT: finish :b:thing")
}
@Requires(value = IntegTestPreconditions.NotConfigCached, reason = "already covers CC")
def "listener is not discarded after configuration phase when used with configuration cache"() {
listenerReceivedConfigurationTimeData()
registeringPlugin()
buildFile << """
apply plugin: LoggingPlugin
def listener = gradle.sharedServices.registrations["listener"].service.get()
listener.configTime("data")
task thing {
doLast { }
}
"""
executer.beforeExecute {
withArgument("--configuration-cache")
}
when:
run("thing")
then:
output.count("service:") == 2
outputContains("service: finish :thing with data=data")
outputContains("service: closed with data=data")
when:
run("thing")
then:
output.count("service:") == 2
outputContains("service: finish :thing with data=null")
outputContains("service: closed with data=null")
}
def "listener registered from init script can receive task completion events from buildSrc and main build"() {
def initScript = initScriptFile
loggingListener(initScript)
initScript << """
if (gradle.parent == null) {
def listener = gradle.sharedServices.registerIfAbsent("listener", LoggingListener) { }
services.get(${BuildEventsListenerRegistry.name}).onTaskCompletion(listener)
}
"""
file("buildSrc/src/main/java/Thing.java") << """
class Thing { }
"""
buildFile << """
task thing {
}
"""
when:
executer.usingInitScript(initScript)
run("thing")
then:
output.count("EVENT:") == 6
outputContains("EVENT: finish :buildSrc:processResources SKIPPED")
outputContains("EVENT: finish :buildSrc:compileJava OK")
outputContains("EVENT: finish :thing UP-TO-DATE")
when:
executer.usingInitScript(initScript)
run("thing")
then:
outputContains("EVENT: finish :thing UP-TO-DATE")
}
def "reports failure to handle event and continues with task execution"() {
loggingListener()
brokenListener()
buildFile << """
import ${BuildEventsListenerRegistry.name}
def registry = project.services.get(BuildEventsListenerRegistry)
registry.onTaskCompletion(gradle.sharedServices.registerIfAbsent('broken', BrokenListener) { })
registry.onTaskCompletion(gradle.sharedServices.registerIfAbsent('listener', LoggingListener) { })
task a
task b { dependsOn a }
"""
when:
fails("b")
then:
// TODO - add some context to the failure
failure.assertHasDescription("broken")
output.count("BROKEN:") == 1
output.count("EVENT:") == 2
outputContains("EVENT: finish :a")
outputContains("EVENT: finish :b")
when:
fails("b")
then:
// TODO - add some context to the failure
failure.assertHasDescription("broken")
output.count("BROKEN:") == 1
output.count("EVENT:") == 2
outputContains("EVENT: finish :a")
outputContains("EVENT: finish :b")
}
def "reports failure to create listener and continues with task execution"() {
loggingListener()
cannotConstructListener()
buildFile << """
import ${BuildEventsListenerRegistry.name}
def registry = project.services.get(BuildEventsListenerRegistry)
registry.onTaskCompletion(gradle.sharedServices.registerIfAbsent('broken', BrokenListener) { })
registry.onTaskCompletion(gradle.sharedServices.registerIfAbsent('listener', LoggingListener) { })
task a
task b { dependsOn a }
"""
when:
fails("b")
then:
// TODO - add some context to the failure
failure.assertHasDescription("Failed to create service 'broken'.")
failure.assertHasCause("broken")
output.count("BROKEN:") == 1
output.count("EVENT:") == 2
outputContains("EVENT: finish :a")
outputContains("EVENT: finish :b")
when:
fails("b")
then:
// TODO - add some context to the failure
failure.assertHasDescription("Failed to create service 'broken'.")
failure.assertHasCause("broken")
output.count("BROKEN:") == 1
output.count("EVENT:") == 2
outputContains("EVENT: finish :a")
outputContains("EVENT: finish :b")
}
@Requires(value = IntegTestPreconditions.NotEmbeddedExecutor, reason = "Tries to resolve external (api) jars that are not available in the embedded environment")
@Issue("https://github.com/gradle/gradle/issues/16774")
def "can use plugin that registers build event listener with ProjectBuilder"() {
given:
file("build.gradle") << """
plugins { id 'groovy-gradle-plugin' }
repositories { mavenCentral() }
dependencies { testImplementation("junit:junit:4.13") }
"""
def plugin = file('src/main/groovy/my-plugin.gradle')
loggingListener(plugin)
plugin << """
def listener = project.gradle.sharedServices.registerIfAbsent("listener", LoggingListener) { }
gradle.services.get(${BuildEventsListenerRegistry.name}).onTaskCompletion(listener)
"""
file("src/test/groovy/my/MyTest.groovy") << """
package my
import org.gradle.testfixtures.*
import org.junit.Test
class MyTest {
@Test void test() {
def project = ProjectBuilder.builder().build()
project.plugins.apply("my-plugin")
}
}
"""
when:
run 'test'
then:
executedAndNotSkipped(':test')
// ensure the test has been executed
def result = new DefaultTestExecutionResult(testDirectory)
result.assertTestClassesExecuted('my.MyTest')
result.testClass('my.MyTest').assertTestCount(1, 0, 0)
}
@Requires(value = IntegTestPreconditions.NotEmbeddedExecutor, reason = "Cannot run TestKit in embedded mode")
def "can use plugin that registers build event listener with TestKit"() {
given:
def plugin = file('src/main/groovy/my-plugin.gradle')
loggingListener(plugin)
plugin << """
def listener = project.gradle.sharedServices.registerIfAbsent("listener", LoggingListener) { }
gradle.services.get(${BuildEventsListenerRegistry.name}).onTaskCompletion(listener)
println('listener registered')
"""
file("build.gradle") << """
plugins { id 'groovy-gradle-plugin' }
repositories { mavenCentral() }
dependencies { testImplementation("junit:junit:4.13") }
"""
def testProjectDir = file("testTmp").tap { it.mkdirs() }
file("src/test/groovy/my/MyTest.groovy") << """
package my
import org.gradle.testfixtures.*
import org.gradle.testkit.runner.GradleRunner
import org.junit.Test
class MyTest {
@Test void test() {
def projectDir = new File("${TextUtil.normaliseFileSeparators(testProjectDir.absolutePath)}")
new File(projectDir, 'settings.gradle').text = ""
new File(projectDir, 'build.gradle').text = '''
plugins { id 'my-plugin' }
'''
def runner = GradleRunner.create()
runner.forwardOutput()
runner.withPluginClasspath()
runner.withArguments("help")
runner.withProjectDir(projectDir)
runner.withDebug(true)
def result = runner.build()
}
}
"""
when:
run 'test'
then:
executedAndNotSkipped(':test')
// ensure the test has been executed
def result = new DefaultTestExecutionResult(testDirectory)
result.assertTestClassesExecuted('my.MyTest')
result.testClass('my.MyTest').assertTestCount(1, 0, 0)
result.testClass('my.MyTest').assertStdout(containsString("listener registered"))
}
void loggingListener(TestFile file = buildFile) {
file << """
import ${OperationCompletionListener.name}
import ${FinishEvent.name}
import ${TaskFinishEvent.name}
import ${TaskSuccessResult.name}
import ${TaskFailureResult.name}
import ${TaskSkippedResult.name}
import ${BuildServiceParameters.name}
abstract class LoggingListener implements OperationCompletionListener, BuildService {
@Override
void onFinish(FinishEvent event) {
if (event instanceof TaskFinishEvent) {
print("EVENT: finish \${event.descriptor.taskPath}")
if (event.result instanceof TaskSuccessResult) {
if (event.result.upToDate) {
println(" UP-TO-DATE")
} else {
println(" OK")
}
} else if (event.result instanceof TaskFailureResult) {
println(" FAILED")
} else if (event.result instanceof TaskSkippedResult) {
println(" SKIPPED")
} else {
throw new IllegalArgumentException()
}
} else {
throw new IllegalArgumentException()
}
}
}
"""
}
def cannotConstructListener() {
buildFile << """
import ${OperationCompletionListener.name}
import ${FinishEvent.name}
import ${TaskFinishEvent.name}
import ${TaskSuccessResult.name}
import ${TaskFailureResult.name}
import ${TaskSkippedResult.name}
import ${BuildServiceParameters.name}
abstract class BrokenListener implements OperationCompletionListener, BuildService {
BrokenListener() {
println("BROKEN: created")
throw new RuntimeException("broken")
}
@Override
void onFinish(FinishEvent event) {
println("BROKEN: received event")
}
}
"""
}
def brokenListener() {
buildFile << """
import ${OperationCompletionListener.name}
import ${FinishEvent.name}
import ${TaskFinishEvent.name}
import ${TaskSuccessResult.name}
import ${TaskFailureResult.name}
import ${TaskSkippedResult.name}
import ${BuildServiceParameters.name}
abstract class BrokenListener implements OperationCompletionListener, BuildService {
@Override
void onFinish(FinishEvent event) {
println("BROKEN: received event")
throw new RuntimeException("broken")
}
}
"""
}
void listenerReceivedConfigurationTimeData(TestFile file = buildFile) {
file << """
import ${OperationCompletionListener.name}
import ${FinishEvent.name}
import ${TaskFinishEvent.name}
import ${TaskSuccessResult.name}
import ${TaskFailureResult.name}
import ${TaskSkippedResult.name}
import ${BuildServiceParameters.name}
abstract class LoggingListener implements OperationCompletionListener, BuildService, Closeable {
String data
void configTime(String data) {
this.data = data
}
@Override
void onFinish(FinishEvent event) {
if (event instanceof TaskFinishEvent) {
println("service: finish \${event.descriptor.taskPath} with data=" + data)
} else {
throw new IllegalArgumentException()
}
}
@Override
void close() {
println("service: closed with data=" + data)
}
}
"""
}
def registeringPlugin() {
buildFile << """
import ${BuildEventsListenerRegistry.name}
abstract class LoggingPlugin implements Plugin {
@Inject
abstract BuildEventsListenerRegistry getListenerRegistry()
void apply(Project project) {
def listener = project.gradle.sharedServices.registerIfAbsent("listener", LoggingListener) { }
listenerRegistry.onTaskCompletion(listener)
}
}
"""
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy