I'll explain my use case first and then the generalized description of what I want to do.
The default $gradle test
will run the test
task and run all unit tests. I would like to add a browseTest
task which will, if specified on the command line, open the test report in the default browser. I already have the code to open the test result, but I need to figure out how to execute it. Here is how I think it should work:
Command | Tests up-to-date | Run tests? | Test result? | Open test report? |
---|---|---|---|---|
test | no | yes | success/failure | no |
test | yes | no | success/failure | no |
browseTest | no | yes | success/failure | yes |
browseTest | yes | no | success/failure | yes |
I want to be able to have one task (i.e. browseTest
) add another task (e.g. task
) to the task graph without depending on the other task succeeding or failing. If I use dependsOn
then the first take failing prevents the second task from executing. Using mustRunAfter
specifies an ordering but doesn't add the task to the task graph (and thus will not be executed).
If there was something akin to the following, I believe it would get me what I want:
task("browseTest") {
addsToTaskGraph("test")
mustRunAfter("test")
doLast {
// Open test results in browser
}
}
I answered my own question again. I was able to successfully create a runsAfter
extension function. I would love to be able to clean up the logic for triggering the callback/hook, but overall this is very user-friendly and not overly ineffecient.
val browseTest = task("browseTest") {
runsAfter(tasks.test.name) {
// perform "callback"
val file = project.file("build/reports/tests/test/index.html")
browse(file.absolutePath)
}
}
fun Task.runsAfter(vararg paths: String, action: () -> Unit) {
val parent = this
val helper = task("${name}__runsAfter") {
doLast {
if (gradle.taskGraph.hasTask(parent)) {
action()
}
}
}
paths.forEach {
dependsOn(it)
tasks[it].finalizedBy(helper)
}
}
I was able to get this working, although I don't really like the result. If anyone has any other ideas, please let me know.
val browseTest = task("browseTest") {
dependsOn("test")
val parent = this
val helper = task("browseTestHelper") {
doLast {
if (gradle.taskGraph.hasTask(parent)) {
// Open test results in browser
val file = project.file("build/reports/tests/test/index.html")
browse(file.absolutePath)
}
}
}
tasks.test {
finalizedBy(helper)
}
}
There are a few pieces here:
browseTest
task depends on test
, which means that it will force test
to run if it is specified.browseTest
task doesn't actually do anything.browseTestHelper
task is created.test
task is configured to be "finalized" by the browseTestHelper
.browseTestHelper
runs, it checks to see if the original browseTest
task is in the task graph. If not, it doesn't do anything.
browseTestHelper
task is created and finalizes test
even if the browseTest
task is not invoked.browseTest
is in the task graph, it performs its actions.There are a few ways this could improve:
val parent = this
line?It would be great to do something like this:
val browseTest = task("browseTest") {
runsAfter("test") {
val file = project.file("build/reports/tests/test/index.html")
browse(file.absolutePath)
}
}