is there a way to get `GradleRunner` run code to b...
# community-support
c
is there a way to get
GradleRunner
run code to be reported in coverage? or is there a way to make a
ProjectBuilder
project execute a task?
👍 1
v
Why do you give thumbs-down @ephemient? It is not exactly trivial to get it set up right, and I have to check the exact steps before I write them here, but getting JaCoCo coverage from TestKit test is possible.
e
fell asleep on my phone
😄 2
v
Sooo, • I have a dependency bucket configuration named
jacocoAgentJarDependency
• To that I add
org.jacoco:org.jacoco.agent:runtime
as dependency • I have a resolvable configuration named
jacocoAgentJar
that extends from the other • For the test task that runs these functional tests ◦ I get
val jacocoDestfile = the<JacocoTaskExtension>().destinationFile!!
◦ To prevent the strings becoming build cache inputs I do not use
systemProperty
, but a
jvmArgumentProvider
to set ▪︎
"-DjacocoAgentJar=${jacocoAgentJar.singleFile.absolutePath}"
▪︎
"-DjacocoDestfile=${jacocoDestfile.absolutePath}"
◦ As part of some quirk-work-arounds I also add
Copy code
doLast {
    // wait for the read lock on the file, otherwise the daemon might still
    // have the write lock and Gradle complains that it cannot snapshot
    // the file as output of this task and as input of the report task
    FileChannel.open(jacocoDestfile.toPath(), READ).use {
        it.lock(0, Long.MAX_VALUE, true).release()
    }
}
• In my base test class from which all functional tests inherit ◦ I get ▪︎
def jacocoAgentJar = System.getProperty('jacocoAgentJar').replace($/\/$, $/\\/$)
▪︎
def jacocoDestfile = System.getProperty('jacocoDestfile').replace($/\/$, $/\\/$)
◦ write to the
gradle.properties
of the test project ▪︎
org.gradle.jvmargs = -javaagent:$jacocoAgentJar=destfile=$jacocoDestfile,append=true,dumponexit=false,jmx=true
◦ configure the
GradleRunner
with
Copy code
.withPluginClasspath()
.with { it.withPluginClasspath(it.pluginClasspath + new File(jacocoAgentJar)) }
◦ and finally write to the
settings.gradle.kts
of the test project
Copy code
import java.lang.management.ManagementFactory
import javax.management.JMX.newMBeanProxy
import javax.management.ObjectName
import org.jacoco.agent.rt.IAgent

abstract class JacocoDumper : BuildService<BuildServiceParameters.None>, AutoCloseable {
    override fun close() {
        val mBeanServer = ManagementFactory.getPlatformMBeanServer()
        val jacoco = ObjectName.getInstance("org.jacoco:type=Runtime")
        if (mBeanServer.isRegistered(jacoco)) {
            newMBeanProxy(mBeanServer, jacoco, IAgent::class.java).dump(true)
        }
    }
}
val jacocoDumper = gradle.sharedServices.registerIfAbsent("jacocoDumper", JacocoDumper::class) {}
jacocoDumper.get()
gradle.allprojects {
    tasks.configureEach {
        usesService(jacocoDumper)
    }
}
All this together turns out to work as expected for me so far. I told you it is not trivial to get it set up right. 🙂
c
why is this so complicated?
v
That's a very complicated explanation and I probably forgot some of the details. 😄 I needed quite some time to fiddle this together. But it has to do with the JaCoCo agent having to be in the process executing the tests and by default only dumping the result once that process quits, the daemon only quitting asynchronously so might still write the result file while Gradle thinks the tests are finished and tries to snapshot that output file which does not properly work as it is locked by the daemon, and even if it worked because not being on Windows might create the report on not yet completely written result files and so on.
If you execute all those tests in-process, it might be possible easier, but then you can for example not test different Gradle versions or Java version and so on, but only within the Gradle version executing the test.
c
rephrase: why hasn't gradle made this easy
that's a rhetorical question
v
🤷‍♂️