How to configure JVM args for the new Gradle TestSuit? - gradle

I am trying the new TestSuit from Gradle. I have this in my project:
testing {
suites {
val integrationTest by registering(JvmTestSuite::class) {
useJUnitJupiter()
jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
jvmArgs("--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED")
}
}
But when I run ./gradlew integrationTest, I get:
/buildSrc/src/main/kotlin/quarkus-testing.quarkus-conventions.gradle.kts: (35, 13): Unresolved reference: jvmArgs
How to configure jvmArgs now?

I recently solved this by working down the hierarchy, from the JvmTestSuite object to the Test object which is the object that contains the jvmArgs reference.
testing {
suites {
val integrationTest by registering(JvmTestSuite::class) {
useJUnitJupiter()
// here: really calling JvmTestSuite#getTargets() then,
// DomainObjectCollection#all(Action)
targets.all {
// here: really calling JvmTestSuiteTarget#getTestTask() then,
// TaskProvider#configure(Action)
testTask.configure {
// now we're operating on a Test instance
jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
jvmArgs("--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED")
}
}
}
}
}

Related

InvalidMutabilityExceptionInvalidMutabilityException on common test with Ktor

I have a multiplatform library to do few APIs calls with Ktor (2.0.0-beta-1)
class DiscoveryServicesImpl(private val client: HttpClient) : DiscoveryServices {
override suspend fun getServers(domain: String): DAAServers {
return client.get(Servers.Domain(domain = domain)).body()
}
}
// Where client is created with specific engine (OkHttp for Android and Darwin for iOS)
This code works as expected (implemented in both Android and iOS apps)
To secure this code, I've added a unit test to verify that the url is well builded.
#Test
fun getServersSuccessful() {
runBlocking {
var _request: HttpRequestData? = null
//Given
val mockEngine = MockEngine {
_request = it
}
val discoveryApiMock = DiscoveryApi(mockEngine)
val service = DiscoveryServicesImpl(discoveryApiMock.client)
//When
val servers: DAAServers = service.getServers(domain)
//Then
assertEquals("https://****/$domain", _request!!.url.toString())
}
}
But when a run this test I got an error (for iOS test only) :
> Task :api:discovery:iosTest
***.discovery.DiscoveryServicesImplTest.getServersSuccessful FAILED
kotlin.native.concurrent.InvalidMutabilityException at /Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.native.internal.Ref#15cb7c8
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.native.internal.Ref#15cb7c8
at kotlin.Throwable#<init>(/Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24)
at kotlin.Exception#<init>(/Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23)
at kotlin.RuntimeException#<init>(/Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34)
at kotlin.native.concurrent.InvalidMutabilityException#<init>(/Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:24)
at <global>.ThrowInvalidMutabilityException(/Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:109)
at <global>.MutationCheck(Unknown Source)
at kotlin.native.internal.Ref#<set-element>(/Users/teamcity1/teamcity_work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Ref.kt:12)
It's happening only on the ios target
To fix this, I've added this code pretty much everywhere (commonMain, commonTest, iosMain, iosTest) but it doesn't change anything
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-native-mt") {
version {
strictly("1.6.0-native-mt")
}
}
Below, my gradles files :
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(Ktor.clientCore)
implementation(Ktor.clientResources)
implementation(Ktor.contentNegotiation)
implementation(Ktor.clientJson)
implementation(Ktor.clientLogging)
implementation(Ktor.clientAuth)
implementation(Koin.koinMultiplatform)
implementation(project(":serialization"))
implementation(project(":transverse:log"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-native-mt") {
version {
strictly("1.6.0-native-mt")
}
}
}
}
val commonTest by getting {
dependencies {
implementation(Ktor.clientMock)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0-native-mt") {
version {
strictly("1.6.0-native-mt")
}
}
}
}
val iosMain by getting {
dependencies {
implementation(Ktor.clientDarwin)
}
}
}
}
If you're using the latest version of ktor and coroutines, you'll probably want to use the new memory model, and just the 1.6.0 kotlinx.coroutines rather than the native-mt version.
https://blog.jetbrains.com/kotlin/2021/08/try-the-new-kotlin-native-memory-manager-development-preview/
https://github.com/touchlab/KaMPKit/blob/main/gradle.properties#L26
Most library dev from Jetbrains will likely focus on the new memory model from now on, so unless there are specific near term concerns on moving, I would do that.

Jacoco Kotlin Dsl Multimodule exclude

I am trying to exclude some files from the merged jacoco report. I am using:
(root gradle)
tasks.register<JacocoReport>("codeCoverageReport") {
subprojects {
val subProject = this
subProject.plugins.withType<JacocoPlugin>().configureEach {
subProject.tasks.matching { it.extensions.findByType<JacocoTaskExtension>() != null }.configureEach {
val testTask = this
sourceSets(subProject.sourceSets.main.get())
executionData(testTask)
}
subProject.tasks.matching { it.extensions.findByType<JacocoTaskExtension>() != null }.forEach {
rootProject.tasks["codeCoverageReport"].dependsOn(it)
}
}
}
reports {
xml.isEnabled = false
html.isEnabled = true
csv.isEnabled = false
}
}
And for the every module exclusion jacoco report (e.g. for common module):
tasks.withType<JacocoReport> {
classDirectories.setFrom(
sourceSets.main.get().output.asFileTree.matching {
exclude(JacocoExcludes.commonModule)
}
)
}
For each module this is working but when trying to interact with root gradle task either the gradle sync fails or it only add the files from the last module. Any help ?
Thanks
I had the same problem and used the following code :
tasks.jacocoTestReport {
// tests are required to run before generating the report
dependsOn(tasks.test)
// print the report url for easier access
doLast {
println("file://${project.rootDir}/build/reports/jacoco/test/html/index.html")
}
classDirectories.setFrom(
files(classDirectories.files.map {
fileTree(it) {
exclude("**/generated/**", "**/other-excluded/**")
}
})
)
}
as suggested here : https://github.com/gradle/kotlin-dsl-samples/issues/1176#issuecomment-610643709

Idea Gradle integration add classes as a test source tree, but don't include in test sourceSet

I would like to mark the classes of my systest sourceSet as unit test classes. I tried to mark them with the following code:
sourceSets {
main {
groovy {
srcDirs = [
'src/main/masks'
}
resources {
srcDirs += 'src/main/journaltemplates'
}
}
/* This brings up systest in the test resources */
test.java.srcDir 'src/systest/java'
test.resources.srcDir 'src/systest/resources'
systest {
java {
srcDirs = ['src/systest/java']
}
resources {
srcDirs = ['src/systest/resources']
}
}
}
With this solution the sourceset got marked as unit test class, but was additionally added to the test sourceSet which is not desired. I want to keep the classes in the systest sourceSet and specify that the systest sourceSet, is a unit test sourceSet. I want the same behaviour for the systest sourceSet as for the test sourceSet, but they should be distinct sourceSets.
The second solution i tried was using the idea plugin for gradle and modify the module setting, as seen in this SO post:
idea {
module {
testSourceDirs += file('src/systest')
}
}
The problem with this solution is that the systest sources are added to the test sourceSet too.
Hopefully this is clear enough, otherwise please comment. Thank you.
There is a dedicated Gradle feature called Declarative Test Suite that supports this case:
testing {
suites {
val test by getting(JvmTestSuite::class) {
useJUnitJupiter()
}
register("integrationTest", JvmTestSuite::class) {
dependencies {
implementation(project())
}
targets {
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
More:
https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests
Please try this configuration:
apply plugin: "idea"
sourceSets {
systest {
java {
compileClasspath = test.output + main.output
runtimeClasspath = output + compileClasspath
}
}
}
idea {
module {
testSourceDirs = sourceSets.systest.allSource.srcDirs
}
}

How to re-run only failed JUnit test classes using Gradle?

Inspired by this neat TestNG task, and this SO question I thought I'd whip up something quick for re-running of only failed JUnit tests from Gradle.
But after searching around for awhile, I couldn't find anything analogous which was quite as convenient.
I came up with the following, which seems to work pretty well and adds a <testTaskName>Rerun task for each task of type Test in my project.
import static groovy.io.FileType.FILES
import java.nio.file.Files
import java.nio.file.Paths
// And add a task for each test task to rerun just the failing tests
subprojects {
afterEvaluate { subproject ->
// Need to store tasks in static temp collection, else new tasks will be picked up by live collection leading to StackOverflow
def testTasks = subproject.tasks.withType(Test)
testTasks.each { testTask ->
task "${testTask.name}Rerun"(type: Test) {
group = 'Verification'
description = "Re-run ONLY the failing tests from the previous run of ${testTask.name}."
// Depend on anything the existing test task depended on
dependsOn testTask.dependsOn
// Copy runtime setup from existing test task
testClassesDirs = testTask.testClassesDirs
classpath = testTask.classpath
// Check the output directory for failing tests
File textXMLDir = subproject.file(testTask.reports.junitXml.destination)
logger.info("Scanning: $textXMLDir for failed tests.")
// Find all failed classes
Set<String> allFailedClasses = [] as Set
if (textXMLDir.exists()) {
textXMLDir.eachFileRecurse(FILES) { f ->
// See: http://marxsoftware.blogspot.com/2015/02/determining-file-types-in-java.html
String fileType
try {
fileType = Files.probeContentType(f.toPath())
} catch (IOException e) {
logger.debug("Exception when probing content type of: $f.")
logger.debug(e)
// Couldn't determine this to be an XML file. That's fine, skip this one.
return
}
logger.debug("Filetype of: $f is $fileType.")
if (['text/xml', 'application/xml'].contains(fileType)) {
logger.debug("Found testsuite file: $f.")
def testSuite = new XmlSlurper().parse(f)
def failedTestCases = testSuite.testcase.findAll { testCase ->
testCase.children().find { it.name() == 'failure' }
}
if (!failedTestCases.isEmpty()) {
logger.info("Found failures in file: $f.")
failedTestCases.each { failedTestCase ->
def className = failedTestCase['#classname']
logger.info("Failure: $className")
allFailedClasses << className.toString()
}
}
}
}
}
if (!allFailedClasses.isEmpty()) {
// Re-run all tests in any class with any failures
allFailedClasses.each { c ->
def testPath = c.replaceAll('\\.', '/') + '.class'
include testPath
}
doFirst {
logger.warn('Re-running the following tests:')
allFailedClasses.each { c ->
logger.warn(c)
}
}
}
outputs.upToDateWhen { false } // Always attempt to re-run failing tests
// Only re-run if there were any failing tests, else just print warning
onlyIf {
def shouldRun = !allFailedClasses.isEmpty()
if (!shouldRun) {
logger.warn("No failed tests found for previous run of task: ${subproject.path}:${testTask.name}.")
}
return shouldRun
}
}
}
}
}
Is there any easier way to do this from Gradle? Is there any way to get JUnit to output a consolidated list of failures somehow so I don't have to slurp the XML reports?
I'm using JUnit 4.12 and Gradle 4.5.
Here is one way to do it. The full file will be listed at the end, and is available here.
Part one is to write a small file (called failures) for every failed test:
test {
// `failures` is defined elsewhere, see below
afterTest { desc, result ->
if ("FAILURE" == result.resultType as String) {
failures.withWriterAppend {
it.write("${desc.className},${desc.name}\n")
}
}
}
}
In part two, we use a test filter (doc here) to restrict the tests to any that are present in the failures file:
def failures = new File("${projectDir}/failures.log")
def failedTests = []
if (failures.exists()) {
failures.eachLine { line ->
def tokens = line.split(",")
failedTests << tokens[0]
}
}
failures.delete()
test {
filter {
failedTests.each {
includeTestsMatching "${it}"
}
}
// ...
}
The full file is:
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
testCompile('junit:junit:4.12')
}
def failures = new File("${projectDir}/failures.log")
def failedTests = []
if (failures.exists()) {
failures.eachLine { line ->
def tokens = line.split(",")
failedTests << tokens[0]
}
}
failures.delete()
test {
filter {
failedTests.each {
includeTestsMatching "${it}"
}
}
afterTest { desc, result ->
if ("FAILURE" == result.resultType as String) {
failures.withWriterAppend {
it.write("${desc.className},${desc.name}\n")
}
}
}
}
The Test Retry Gradle plugin is designed to do exactly this. It will rerun each failed test a certain number of times, with the option of failing the build if too many failures have occurred overall.
plugins {
id 'org.gradle.test-retry' version '1.2.1'
}
test {
retry {
maxRetries = 3
maxFailures = 20 // Optional attribute
}
}

How to configure the processResources task in a Gradle Kotlin build

I have the following in a groovy-based build script. How do I do the same in a kotlin-based script?
processResources {
filesMatching('application.properties'){
expand(project.properties)
}
}
Why not to just use "withType" ?
I just mean (IMHO)
tasks {
withType<ProcessResources> {
..
}
looks much better than
tasks {
"processResources"(ProcessResources::class) {
..
}
So,
tasks.withType<ProcessResources> {
//from("${project.projectDir}src/main/resources")
//into("${project.buildDir}/whatever/")
filesMatching("*.cfg") {
expand(project.properties)
}
}
EDIT:
With newer release you can just do:
tasks.processResources {}
or
tasks { processResources {} }
generated accessors are "lazy" so it has all the benefits and no downsides.
I think task should look like:
Edit: According this comment in gradle/kotlin-dsl repository. Task configuration should work this way:
import org.gradle.language.jvm.tasks.ProcessResources
apply {
plugin("java")
}
(tasks.getByName("processResources") as ProcessResources).apply {
filesMatching("application.properties") {
expand(project.properties)
}
}
Which is pretty ugly. So i suggest following utility function for this purpose, until one upstream done:
configure<ProcessResources>("processResources") {
filesMatching("application.properties") {
expand(project.properties)
}
}
inline fun <reified C> Project.configure(name: String, configuration: C.() -> Unit) {
(this.tasks.getByName(name) as C).configuration()
}
With updates to the APIs in newer release of the Kotlin DSL and Gradle, you can do something like:
import org.gradle.language.jvm.tasks.ProcessResources
plugins {
java
}
tasks {
"processResources"(ProcessResources::class) {
filesMatching("application.properties") {
expand(project.properties)
}
}
}
And also:
val processResources by tasks.getting(ProcessResources::class) {
filesMatching("application.properties") {
expand(project.properties)
}
}

Resources