org.gradle.execution.taskgraph.RuleTaskCreationIntegrationTest.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.execution.taskgraph
import org.gradle.api.reporting.model.ModelReportOutput
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
import org.gradle.model.internal.core.ModelPath
@UnsupportedWithConfigurationCache(because = "software model")
class RuleTaskCreationIntegrationTest extends AbstractIntegrationSpec implements WithRuleBasedTasks {
def setup() {
buildFile << ruleBasedTasks()
}
def "can use rule method to create tasks from model"() {
given:
buildFile << """
class MyModel {
List tasks = []
}
class MyPlugin extends RuleSource {
@Model
MyModel myModel() {
new MyModel()
}
@Mutate
void addTasks(ModelMap tasks, MyModel myModel) {
myModel.tasks.each { n ->
tasks.create(n) {
description = "task \$n"
}
}
}
}
apply type: MyPlugin
model {
myModel {
tasks << "a" << "b"
}
}
"""
when:
succeeds "tasks", "--all"
then:
output.contains "a - task a"
output.contains "b - task b"
}
def "can use rule DSL to create tasks"() {
given:
buildFile << """
model {
tasks {
a {
description = 'task a'
}
a(EchoTask)
b(EchoTask) {
description = 'task b'
}
c(Task) {
description = 'task c'
}
}
}
"""
when:
succeeds "tasks", "--all"
then:
output.contains "a - task a"
output.contains "b - task b"
output.contains "c - task c"
}
def "can use rule DSL to configure task using another task as input"() {
given:
buildFile << '''
model {
tasks {
a {
message = 'greetings from task a'
}
a(EchoTask)
b(EchoTask) {
def taskA = $.tasks.a
message = taskA.message + " via task b"
}
}
}
'''
when:
succeeds "b"
then:
output.contains "greetings from task a via task b"
}
def "can configure tasks using rule DSL"() {
given:
buildFile << """
class MyMessage {
String message
}
class MyPlugin extends RuleSource {
@Model
MyMessage myMessage() {
new MyMessage()
}
@Mutate
void addTasks(ModelMap tasks, MyMessage myMessage) {
['foo', 'bar'].each { n ->
tasks.create(n, EchoTask) {
message = "\${myMessage.message} \${name}: "
}
}
}
}
apply type: MyPlugin
model {
tasks.bar {
message += "bar message!"
}
tasks {
foo {
message += 'foo message!'
}
}
myMessage {
message = "task"
}
}
"""
when:
succeeds "foo", "bar"
then:
output.contains "foo: task foo: foo message!"
output.contains "bar: task bar: bar message!"
}
def "can configure tasks using rule methods taking some input"() {
given:
buildFile << """
class MyMessage {
String message
}
class MyPlugin extends RuleSource {
@Model
MyMessage myMessage() {
new MyMessage()
}
@Mutate
void customMessage(@Path('tasks.bar') EchoTask task) {
task.message += ' from'
}
@Defaults
void prepareMessage(@Path('tasks.bar') EchoTask task) {
task.message = "task bar: "
}
@Finalize
void tweakCustomMessage(@Path('tasks.bar') EchoTask task) {
task.message += " \$task.name"
}
@Mutate
void addTasks(ModelMap tasks, MyMessage myMessage) {
tasks.create('bar') {
message += myMessage.message
}
tasks.create('foo') {
message = 'foo'
}
}
}
apply type: MyPlugin
model {
myMessage {
message = "hi"
}
}
"""
when:
succeeds "foo", "bar"
then:
output.contains "foo: foo"
output.contains "bar: task bar: hi from bar"
}
def "can validate tasks using rule methods"() {
given:
buildFile << """
class MyPlugin extends RuleSource {
@Validate
void checkTask(@Path('tasks.bar') EchoTask task) {
throw new RuntimeException("task is invalid!")
}
@Mutate
void addTasks(ModelMap tasks) {
['foo', 'bar'].each { n ->
tasks.create(n, EchoTask)
}
}
}
apply type: MyPlugin
"""
when:
fails "bar"
then:
failure.assertHasDescription('Exception thrown while executing model rule: MyPlugin#checkTask')
failure.assertHasCause('task is invalid!')
}
def "can use ModelMap API from a method rule to apply rules to tasks"() {
given:
buildFile << """
class MyMessage {
String message
}
class MyPlugin extends RuleSource {
@Model
MyMessage myMessage() {
new MyMessage()
}
@Mutate
void addTasks(ModelMap tasks) {
['foo', 'bar'].each { n ->
tasks.create(n, EchoTask) {
message = "\$message \$name"
}
}
}
@Defaults
void applyMessages(ModelMap tasks, MyMessage myMessage) {
tasks.beforeEach {
message = myMessage.message
}
tasks.all {
message += " with"
}
tasks.afterEach {
message += " message!"
}
}
@Mutate
void cleanupMessages(ModelMap tasks) {
tasks.named('bar') {
message = "[\$message]"
}
}
}
apply type: MyPlugin
model {
myMessage {
message = "task"
}
}
"""
when:
succeeds "foo", "bar"
then:
output.contains "foo: task foo with message!"
output.contains "bar: [task bar] with message!"
}
def "can use rule DSL to apply rules to all tasks"() {
given:
buildFile << """
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
['foo', 'bar'].each { n ->
tasks.create(n, EchoTask) {
message = "\$message \$name"
}
}
}
}
apply type: MyPlugin
model {
tasks {
def messageTasks = withType(EchoTask)
messageTasks.beforeEach {
message = "task"
}
messageTasks.all {
message += " with"
}
messageTasks.afterEach {
message += " message"
}
}
}
"""
when:
succeeds "foo", "bar"
then:
output.contains "foo: task foo with message"
output.contains "bar: task bar with message"
}
def "can configure dependencies between tasks using task name"() {
given:
buildFile << """
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create("foo")
tasks.create("bar")
}
}
apply type: MyPlugin
model {
tasks.bar {
dependsOn "foo"
}
}
"""
when:
succeeds "bar"
then:
result.assertTasksExecuted(":foo", ":bar")
}
def "task instantiation and configuration is deferred until required"() {
given:
buildFile << """
class SomeTask extends DefaultTask {
SomeTask() { println "\$name created" }
}
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create("foo") {
println "\$name configured"
}
tasks.create("bar") {
println "\$name configured"
}
tasks.beforeEach {
println "\$name initialized"
}
println "tasks defined"
}
}
apply type: MyPlugin
"""
when:
succeeds "bar", "foo"
then:
output.contains """tasks defined
bar created
bar initialized
bar configured
foo created
foo initialized
foo configured
"""
}
def "two rules attempt to create task"() {
given:
buildFile << """
class MyModel {
List tasks = []
}
class MyPlugin extends RuleSource {
@Model
MyModel myModel() {
new MyModel()
}
@Mutate
void addTasks1(ModelMap tasks, MyModel myModel) {
myModel.tasks.each { n ->
tasks.create(n) {
description = "task \$n"
}
}
}
@Mutate
void addTasks2(ModelMap tasks, MyModel myModel) {
myModel.tasks.each { n ->
tasks.create(n) {
description = "task \$n"
}
}
}
}
apply type: MyPlugin
model {
myModel {
tasks << "a" << "b"
}
}
"""
when:
fails "tasks"
then:
failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks2(ModelMap, MyModel)")
failure.assertHasCause("Cannot create 'tasks.a' using creation rule 'MyPlugin#addTasks2(ModelMap, MyModel) > create(a)' as the rule 'MyPlugin#addTasks1(ModelMap, MyModel) > create(a)' is already registered to create this model element.")
}
def "cannot create tasks during config of task"() {
given:
buildFile << """
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create("foo") {
tasks.create("bar")
}
}
}
apply type: MyPlugin
"""
when:
fails "tasks"
then:
failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks(ModelMap) > create(foo)")
failure.assertHasCause("Attempt to modify a closed view of model element 'tasks' of type 'ModelMap' given to rule MyPlugin#addTasks(ModelMap)")
}
def "failure during task instantiation is reasonably reported"() {
given:
buildFile << """
class Faulty extends DefaultTask {
Faulty() {
throw new RuntimeException("!")
}
}
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create("foo", Faulty)
}
}
apply type: MyPlugin
"""
when:
fails "tasks"
then:
failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks(ModelMap) > create(foo)")
failure.assertHasCause("Could not create task of type 'Faulty'")
}
def "failure during task initial configuration is reasonably reported"() {
given:
buildFile << """
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create("foo") {
throw new RuntimeException("config failure")
}
}
}
apply type: MyPlugin
"""
when:
fails "tasks"
then:
failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks")
failure.assertHasCause("config failure")
}
def "failure during task configuration is reasonably reported"() {
given:
buildFile << """
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create("foo")
}
}
apply type: MyPlugin
model {
tasks.foo {
throw new RuntimeException("config failure")
}
}
"""
when:
fails "tasks"
then:
failure.assertHasCause("Exception thrown while executing model rule: tasks.foo { ... } @ build.gradle line 43, column 17")
failure.assertHasCause("config failure")
failure.assertHasLineNumber(44)
}
def "can create task with invalid model space name"() {
given:
def taskName = "-"
when:
buildFile << """
tasks.create('$taskName').doFirst {}
"""
run taskName
then:
executed ":$taskName"
when:
ModelPath.validateName(taskName)
then:
thrown(ModelPath.InvalidNameException)
}
def "tasks are visible to rules using their public type"(){
given:
buildFile << """
tasks.create(name: 'taskContainerTask', type: DefaultTask) { }
task standardTask
model {
tasks {
newModelTask(Task) {}
}
}
class MyPlugin extends RuleSource {
@Mutate
void addTasks(ModelMap tasks) {
tasks.create('modelMapTask') {}
}
}
apply type: MyPlugin
"""
when:
succeeds("model")
then:
def tasksNode = ModelReportOutput.from(output).modelNode.tasks
tasksNode.taskContainerTask.@type[0] == 'org.gradle.api.DefaultTask'
tasksNode.standardTask.@type[0] == 'org.gradle.api.DefaultTask'
tasksNode.newModelTask.@type[0] == 'org.gradle.api.Task'
tasksNode.modelMapTask.@type[0] == 'org.gradle.api.Task'
tasksNode.model.@type[0] == 'org.gradle.api.reporting.model.ModelReport' //Placeholder task
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy