Why do my Gradle tests run repeatedly? - gradle

I have a pretty standard Gradle build that's building a Java project.
When I run it for the first time, it compiles everything and runs the tests. When I run it a second time without changing any files, it runs the tests again.
According to this thread, Gradle is supposed to be lazy by defaut and not bother running tests if nothing has changed. Has the default behaviour here been changed?
EDIT:
If I run gradle test repeatedly, the tests only run the first time and are subsequently skipped. However, if I run gradle build repeatedly, the tests get re-run every time, even though all other tasks are marked as up-to-date.

the gradle uptodate check logs on info level why a task is not considered to be up-to-date. please rerun the "gradle build -i" to run with info logging at check the logging output.
cheers,
René

OK, so I got the answer thanks to Rene prompting me to look at the '-i' output...
I actually have 2 test tasks: the 'test' one from the Java plugin, and my own 'integrationTest' one. I didn't mention this in the question because I didn't think it was relevant.
It turns out that these tasks are writing their output (reports, etc.) to the same directory, so Gradle's task-based input and output tracking was thinking that something had changed, and re-running the tests.
So the next question (which I will ask separately) becomes: how do I cleanly (and with minimal Groovy/Gradle code) completely separate two instances of the test task.

You need to create test tasks in your build.gradle and then call those specific tasks to run a specific set of tests. Here is an example that will filter out classes so that they don't get run twice (such as when running a suite and then re-running its child classes independently):
tasks.withType(Test) {
jvmArgs '-Xms128m', '-Xmx1024m', '-XX:MaxPermSize=128m'
maxParallelForks = 4 // this runs tests parallel if more than one class
testLogging {
exceptionFormat "full"
events "started", "passed", "skipped", "failed", "standardOut", "standardError"
displayGranularity = 0
}
}
task runAllTests(type: Test) {
include '**/AllTests.class'
testReportDir = file("${reporting.baseDir}/AllTests")
testResultsDir = file("${buildDir}/test-results/AllTests")
}
task runSkipSuite(type: Test) {
include '**/Test*.class'
testReportDir = file("${reporting.baseDir}/Tests")
testResultsDir = file("${buildDir}/test-results/Tests")
}
Also, concerning your build question. The "build" task includes a clean step which is cleaning tests from your build directory. Otherwise the execution thinks the tests have already been ran.

Related

Execute more than one command in a task without breaking incremental build

We use gradle incremental builds and want to run two commands (ideally from one task). One of the solutions here worked getting the two commands running... however it breaks incremental builds.. It looks something like:
task myTask() {
inputs.files(inputFiles)
project.exec {
workingDir web
commandLine('mycmd')
}
project.exec {
workingDir web
commandLine('mysecond-cmd')
}
}
if running a single command and incremental builds is working, the task looked similar to this, the thing that seems to make the difference is the workingDir:
task myTask(type: Exec) {
workingDir myDir // this seems to trigger/enable continuos compilation
commandLine ('myCmd')
}
the best alternative so far is create 3 tasks, one for each of the cmdline tasks I want to run and a third one to group them, which seems dirty.
The question is: Is there a way to run two or more commands in one task with incremental builds still working?
I'll try to answer the question from the comments: how can I signal from a task that has no output files that the build should watch certain files. Unfortunately, this is hard to answer without knowing the exact use case.
To start with, Gradle requires some form of declared output that it can check to see whether a task has run or whether it needs to run. Consider that the task may have failed during the previous run, but the input files haven't changed since then. Should Gradle run the task?
If you have a task that doesn't have any outputs, that means you need to think about why that task should run or not in any given build execution. What's it doing if it's not creating or modifying files or interacting with another part of the system?
Assuming that incremental build is the right solution — it may not be — Gradle does allow you to handle unusual scenarios via TaskOutputs.upToDateWhen(Action). Here's an example that has a straightforward task (gen) that generates a file that acts as an input for a task (runIt) with no outputs:
task gen {
ext.outputDir = "$buildDir/stuff"
outputs.dir outputDir
doLast {
new File(outputDir, "test.txt").text = "Hurray!\n"
}
}
task runIt {
inputs.dir gen
outputs.upToDateWhen { !gen.didWork }
doLast {
println "Running it"
}
}
This specifies that runIt will only run if the input files have changed or the task gen actually did something. In this example, those two scenarios equate to the same thing. If gen runs, then the input files for runIt have changed.
The key thing to understand is that the condition you provide to upToDateWhen() is evaluated during the execution phase of the build, not the configuration phase.
Hope that helps, and if you need any clarification, let me know.

Gradle task dependency order

I have a problem with a custom gradle task : i would like to copy my android jar library and rename it after that it as executed a 'clean build'
Here is how i defined it :
task('CreateJar', type: Copy, dependsOn: [':mylibmodule:clean', ':mylibmodule:build']){
doLast {
from('build/intermediates/bundles/release/')
into('libs')
include('classes.jar')
rename('classes.jar', 'MyLib.jar')
}
}
The problem is that in the gradle log results, the 'clean' is done after the 'build' task, so that the lib is never copied to the destination folder :
...
:mylibmodule:testReleaseUnitTest
:mylibmodule:test
:mylibmodule:check
:mylibmodule:build
:mylibmodule:clean
:mylibmodule:CreateJar NO-SOURCE
I have also tried to change the order of tasks in the 'dependsOn:[]', but it does not change anything... Does anyone has any idea of where is my mistake ?
Thanks in advance
The dependsOn list does not impose any ordering guarantees. Usually what is listed first is executed first if there are not other relations that actually do impose ordering guarantees.
(One example is if clean depends on build, then it doesn't matter how you define it in that dependsOn attribute, becuase build will always be run before clean. That this is not the case is clear to me, thus in parentheses, just to clarify what I mean.)
To determine why finally build is run before clean I cannot say without seeing the complete build script. From what you posted it is not determinable.
Maybe what you are after is clean.shouldRunAfter build or clean.mustRunAfter build which define an ordering constraint without adding a dependency. So you can run each task alone, but if both are run, then their order is defined as you specified it. The difference between those two is only relevant if parallelizing task execution, then should run after means they could run in parallel iirc, must run after does not allow that.

Gradle Run Task Even If Build Fails

So I'm trying to print some stuff to the console in Gradle even if the build fails. How can I do this?
I've found build.finalizedBy(taskName) but that only runs if the build finishes normally.
You can let any build continue on task failures by using the --continue parameter on Gradle invocation. If you do not want to type this parameter all the time you can use the following code in your settings.gradle:
startParameter.continueOnFailure = true
Please note that other tasks may fail due to an earlier task that failed. Using this option, tasks connected via finalizedBy will be executed, but you should only use this option if the tasks are related, even for non-failure cases.
Of course, you can also use lifecycle listeners of the Gradle object or its TaskExecutionGraph. You can use
afterTask
a full TaskExecutionListener implementation
buildFinished (for the whole build)
You can use either gradle.buildFinished or a finalizer task (as you mentioned).
In your example, build.finalizedBy(taskName) will only execute taskName if the build task executes. If the build fails before the build task executes, taskName won't be executed.
e.g., this prints a message based on the result of the build:
gradle.buildFinished { result ->
if (result.failure) {
logger.lifecycle("build failed")
} else {
logger.lifecycle("build successful")
}
}

How do I intercept failures of a Gradle task?

'I'm writing build script using net.foragerr.jmeter plug-in, version 1.0.2-2.13.
What my task does is mostly 1) runs plug-in JMeter task and 2) collects application log.
task perfTest(dependsOn: ['jmClean', 'jmRun'],
description:'Runs (cleanly) performance tests on a deployed application and collects the app log if available. ' +
'Use --no-daemon to see progress. Use --info to see all JMeter command-line arguments.') << {
if (logDir.isDirectory()) {
copy {
from "${logDir}"
into "${buildDir}/jmeter-report"
include 'iRePORT.log'
}
}
}
Now, I need to collect the log even if jmRun fails. When I try to implement a solution for this, I am really stuck:
I can't modify jmRun task, because it's a plug-in task
I can't use try/finally and execute another task directly because Gradle is designed not to support this, e.g. see How do I wrap a gradle task in another task?
I can't use --continue flag gradle.startParameter.continueOnFailure = true because it would not continue execution of a dependent task
The only possible workaround that I could think of is to separate log collection, always include it on a command line, e.g. perfTest collectLogs and set gradle.startParameter.continueOnFailure = true in perfTest.
This is far from ideal.
Are there any better solutions? Shouldn't there be a way for Gradle to support scenarios like this?
You could try creating a task like this
task collectPerfTestLogs(type: Copy) {
from "${logDir}"
into "${buildDir}/jmeter-report"
include 'iRePORT.log'
}
and then have perfTest.finalizedBy(collectPerfTestLogs).
To quote Gradle:
Finalizer tasks will be executed even if the finalized task fails.
See https://docs.gradle.org/current/userguide/more_about_tasks.html

How to prevent gradle build from executing test task

I know that I can use the -x test option to prevent the test task from getting called. I also have something like this in my gradle script to prevent tests from being executed in certain cases:
plugins.withType(JavaPlugin).whenPluginAdded {
test {
doFirst {
if (env.equals('prod')) {
throw new StopExecutionException("DON'T RUN TESTS IN PROD!!!!")
}
}
}
}
but is there a way to configure the java plugin to removed the dependency between build -> test?
build depends on test via check. You probably don't want to remove the dependency on check as it may do other things, so you could try:
check.dependsOn.remove(test)
Do you mind if I ask why you want to do this?
You can skip tasks via the command line with the -x option:
./gradlew assembleDebug -x taskToSkip
I don't know if it is possible to remove such a dependency.
You can however skip the execution of tasks, eg: skipping all test tasks (in production) goes like this.
tasks.withType(Test).each { task ->
task.enabled = !env.equals('prod')
}

Resources