Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
914c780
chore: Starts extracting testJvm configuration to another script plugin
bric3 Oct 24, 2025
770111c
chore: Moved test jvm constraint logic to convention plugin
bric3 Oct 24, 2025
c6cda9f
chore: Moved additional test suite constraints to new test task exten…
bric3 Oct 24, 2025
dfefaea
chore: Configure test jvm constraint on test task, added project wide…
bric3 Oct 29, 2025
9f643d4
chore: configure source set via extension method
bric3 Oct 29, 2025
594cbb1
chore: Use source set configuration
bric3 Oct 29, 2025
dcce4f2
chore: Renames jvmConstraints to testJvmConstraints
bric3 Oct 30, 2025
3ef9429
fix: profiling-controller-oracle tests could never run, introduce an …
bric3 Oct 30, 2025
39c00ee
refactor: Simplify test jvm enablement code
bric3 Oct 30, 2025
975044a
chore: Handle test sources compilation override
bric3 Oct 30, 2025
deaebf1
chore: testJvm is optional
bric3 Oct 30, 2025
4a2aa18
refactor: small rename job
bric3 Oct 30, 2025
1e2e37b
refactor: Convert remaining usage of min/max jdk properties to our ex…
bric3 Oct 30, 2025
f5a892a
refactor: Renames min|maxJavaVersionForTests to min|maxJavaVersion
bric3 Oct 31, 2025
79cf261
chore: Make spotless happy
bric3 Oct 31, 2025
04bd398
fix: Apply test sources compiler settings via common compilerConfigur…
bric3 Oct 31, 2025
5a502dc
fix: Make vertx compile test with Java 11
bric3 Oct 31, 2025
590f959
fix: PR comments
bric3 Nov 10, 2025
b2345ac
Merge remote-tracking branch 'origin/master' into bdu/from-extra-prop…
bric3 Nov 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions buildSrc/src/main/kotlin/datadog.test-jvm-contraints.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import datadog.gradle.plugin.testJvmConstraints.ProvideJvmArgsOnJvmLauncherVersion
import datadog.gradle.plugin.testJvmConstraints.TestJvmConstraintsExtension
import datadog.gradle.plugin.testJvmConstraints.TestJvmConstraintsExtension.Companion.TEST_JVM_CONSTRAINTS
import datadog.gradle.plugin.testJvmConstraints.TestJvmSpec
import datadog.gradle.plugin.testJvmConstraints.isJavaVersionAllowed
import datadog.gradle.plugin.testJvmConstraints.isTestJvmAllowed

plugins {
java
}

val projectExtension = extensions.create<TestJvmConstraintsExtension>(TEST_JVM_CONSTRAINTS)

val testJvmSpec = TestJvmSpec(project)

tasks.withType<Test>().configureEach {
if (extensions.findByName(TEST_JVM_CONSTRAINTS) != null) {
return@configureEach
}

inputs.property("testJvm", testJvmSpec.testJvmProperty).optional(true)

val taskExtension = project.objects.newInstance<TestJvmConstraintsExtension>().also {
configureConventions(it, projectExtension)
}

inputs.property("$TEST_JVM_CONSTRAINTS.allowReflectiveAccessToJdk", taskExtension.allowReflectiveAccessToJdk).optional(true)
inputs.property("$TEST_JVM_CONSTRAINTS.excludeJdk", taskExtension.excludeJdk)
inputs.property("$TEST_JVM_CONSTRAINTS.includeJdk", taskExtension.includeJdk)
inputs.property("$TEST_JVM_CONSTRAINTS.forceJdk", taskExtension.forceJdk)
inputs.property("$TEST_JVM_CONSTRAINTS.minJavaVersion", taskExtension.minJavaVersion).optional(true)
inputs.property("$TEST_JVM_CONSTRAINTS.maxJavaVersion", taskExtension.maxJavaVersion).optional(true)

extensions.add(TEST_JVM_CONSTRAINTS, taskExtension)

configureTestJvm(taskExtension)
}

/**
* Provide arguments if condition is met.
*/
fun Test.conditionalJvmArgs(
applyFromVersion: JavaVersion,
jvmArgsToApply: List<String>,
additionalCondition: Provider<Boolean> = project.providers.provider { true }
) {
jvmArgumentProviders.add(
ProvideJvmArgsOnJvmLauncherVersion(
this,
applyFromVersion,
jvmArgsToApply,
additionalCondition
)
)
}

/**
* Configure the jvm launcher of the test task and ensure the test task
* can be run with the test task launcher.
*/
private fun Test.configureTestJvm(extension: TestJvmConstraintsExtension) {
if (testJvmSpec.javaTestLauncher.isPresent) {
javaLauncher = testJvmSpec.javaTestLauncher
onlyIf("Allowed or forced JDK") {
extension.isTestJvmAllowed(testJvmSpec)
}
} else {
onlyIf("Is current Daemon JVM allowed") {
extension.isJavaVersionAllowed(JavaVersion.current())
}
}

// temporary workaround when using Java16+: some tests require reflective access to java.lang/java.util
conditionalJvmArgs(
JavaVersion.VERSION_16,
listOf(
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED"
),
extension.allowReflectiveAccessToJdk
)
}

// Jacoco plugin is not applied on every project
pluginManager.withPlugin("org.gradle.jacoco") {
tasks.withType<Test>().configureEach {
// Disable jacoco for additional 'testJvm' tests to speed things up a bit
if (testJvmSpec.javaTestLauncher.isPresent) {
extensions.configure<JacocoTaskExtension> {
isEnabled = false
}
}
}
}

/**
* Configures the convention, this tells Gradle where to look for values.
*
* Currently, the extension is still configured to look at project's _extra_ properties.
*/
private fun Test.configureConventions(
taskExtension: TestJvmConstraintsExtension,
projectExtension: TestJvmConstraintsExtension
) {
taskExtension.minJavaVersion.convention(projectExtension.minJavaVersion
.orElse(providers.provider { project.findProperty("${name}MinJavaVersionForTests") as? JavaVersion })
.orElse(providers.provider { project.findProperty("minJavaVersion") as? JavaVersion })
)
taskExtension.maxJavaVersion.convention(projectExtension.maxJavaVersion
.orElse(providers.provider { project.findProperty("${name}MaxJavaVersionForTests") as? JavaVersion })
.orElse(providers.provider { project.findProperty("maxJavaVersion") as? JavaVersion })
)
taskExtension.forceJdk.convention(projectExtension.forceJdk
.orElse(providers.provider {
@Suppress("UNCHECKED_CAST")
project.findProperty("forceJdk") as? List<String> ?: emptyList()
})
)
taskExtension.excludeJdk.convention(projectExtension.excludeJdk
.orElse(providers.provider {
@Suppress("UNCHECKED_CAST")
project.findProperty("excludeJdk") as? List<String> ?: emptyList()
})
)
taskExtension.allowReflectiveAccessToJdk.convention(projectExtension.allowReflectiveAccessToJdk
.orElse(providers.provider { project.findProperty("allowReflectiveAccessToJdk") as? Boolean })
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package datadog.gradle.plugin.testJvmConstraints

import org.gradle.api.JavaVersion
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.testing.Test
import org.gradle.process.CommandLineArgumentProvider

class ProvideJvmArgsOnJvmLauncherVersion(
@get:Internal
val test: Test,

@get:Input
val applyFromVersion: JavaVersion,

@get:Input
val jvmArgsToApply: List<String>,

@get:Input
@get:Optional
val additionalCondition: Provider<Boolean>
) : CommandLineArgumentProvider {

override fun asArguments(): Iterable<String> {
val launcherVersion = test.javaLauncher
.map { JavaVersion.toVersion(it.metadata.languageVersion.asInt()) }
.orElse(JavaVersion.current())
.get()

return if (launcherVersion.isCompatibleWith(applyFromVersion) && additionalCondition.getOrElse(true)) {
jvmArgsToApply
} else {
emptyList()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package datadog.gradle.plugin.testJvmConstraints

import org.gradle.api.JavaVersion
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property

interface TestJvmConstraintsExtension {
/**
* Sets an explicit minimum bound to allowed JDK version
*/
val minJavaVersion: Property<JavaVersion>

/**
* Sets an explicit maximum bound to allowed JDK version
*/
val maxJavaVersion: Property<JavaVersion>

/**
* List of allowed JDK names (passed through the `testJvm` property).
*/
val forceJdk: ListProperty<String>

/**
* List of included JDK names (passed through the `testJvm` property).
*/
val includeJdk: ListProperty<String>

/**
* List of excluded JDK names (passed through the `testJvm` property).
*/
val excludeJdk: ListProperty<String>

/**
* Indicate if the test JVM allows reflective access to JDK
* `java.base/java.lang` and `java.base/java.util` modules by
* openning them.
*/
val allowReflectiveAccessToJdk: Property<Boolean>

companion object {
const val TEST_JVM_CONSTRAINTS = "testJvmConstraints"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package datadog.gradle.plugin.testJvmConstraints

import org.gradle.api.JavaVersion
import org.gradle.api.logging.Logging

private val logger = Logging.getLogger("TestJvmConstraintsUtils")

internal fun TestJvmConstraintsExtension.isJavaVersionAllowed(version: JavaVersion): Boolean {
return withinAllowedRange(version)
}

internal fun TestJvmConstraintsExtension.isTestJvmAllowed(testJvmSpec: TestJvmSpec): Boolean {
val testJvmName = testJvmSpec.normalizedTestJvm.get()

val included = includeJdk.get()
if (included.isNotEmpty() && included.none { it.equals(testJvmName, ignoreCase = true) }) {
return false
}

val excluded = excludeJdk.get()
if (excluded.isNotEmpty() && excluded.any { it.equals(testJvmName, ignoreCase = true) }) {
return false
}

val launcherVersion = JavaVersion.toVersion(testJvmSpec.javaTestLauncher.get().metadata.languageVersion.asInt())
if (!withinAllowedRange(launcherVersion) && forceJdk.get().none { it.equals(testJvmName, ignoreCase = true) }) {
return false
}

return true
}

private fun TestJvmConstraintsExtension.withinAllowedRange(currentJvmVersion: JavaVersion): Boolean {
val definedMin = minJavaVersion.isPresent
val definedMax = maxJavaVersion.isPresent

if (definedMin && (minJavaVersion.get()) > currentJvmVersion) {
logger.info("isWithinAllowedRange returns false b/o minProp=${minJavaVersion.get()} is defined and greater than version=$currentJvmVersion")
return false
}

if (definedMax && (maxJavaVersion.get()) < currentJvmVersion) {
logger.info("isWithinAllowedRange returns false b/o maxProp=${maxJavaVersion.get()} is defined and lower than version=$currentJvmVersion")
return false
}

return true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package datadog.gradle.plugin.testJvmConstraints

import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.jvm.toolchain.JavaToolchainService
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths


class TestJvmSpec(val project: Project) {
companion object {
const val TEST_JVM = "testJvm"
}

private val currentJavaHomePath = project.providers.systemProperty("java.home").map { it.normalizeToJDKJavaHome() }

val testJvmProperty = project.providers.gradleProperty(TEST_JVM)

val normalizedTestJvm = testJvmProperty.map { testJvm ->
if (testJvm.isBlank()) {
throw GradleException("testJvm property is blank")
}

// "stable" is calculated as the largest X found in JAVA_X_HOME
if (testJvm == "stable") {
val javaVersions = project.providers.environmentVariablesPrefixedBy("JAVA_").map { javaHomes ->
javaHomes
.filter { it.key.matches(Regex("^JAVA_[0-9]+_HOME$")) }
.map { Regex("^JAVA_(\\d+)_HOME$").find(it.key)!!.groupValues[1].toInt() }
}.get()

if (javaVersions.isEmpty()) {
throw GradleException("No valid JAVA_X_HOME environment variables found.")
}

javaVersions.max().toString()
} else {
testJvm
}
}.map { project.logger.info("normalized testJvm: $it"); it }

val testJvmHomePath = normalizedTestJvm.map {
if (Files.exists(Paths.get(it))) {
it.normalizeToJDKJavaHome()
} else {
val matcher = Regex("([a-zA-Z]*)([0-9]+)").find(it)
if (matcher == null) {
throw GradleException("Unable to find launcher for Java '$it'. It needs to match '([a-zA-Z]*)([0-9]+)'.")
}
val testJvmEnv = "JAVA_${it}_HOME"
val testJvmHome = project.providers.environmentVariable(testJvmEnv).orNull
if (testJvmHome == null) {
throw GradleException("Unable to find launcher for Java '$it'. Have you set '$testJvmEnv'?")
}

testJvmHome.normalizeToJDKJavaHome()
}
}.map { project.logger.info("testJvm home path: $it"); it }

val javaTestLauncher = project.providers.zip(testJvmHomePath, normalizedTestJvm) { testJvmHome, testJvm ->
// Only change test JVM if it's not the one we are running the gradle build with
if (currentJavaHomePath.get() == testJvmHome) {
project.providers.provider<JavaLauncher?> { null }
} else {
// This is using internal APIs
val jvmSpec = org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec(
project.serviceOf<org.gradle.api.internal.provider.PropertyFactory>(),
project.file(testJvmHome)
)

// The provider always says that a value is present so we need to wrap it for proper error messages
project.javaToolchains.launcherFor(jvmSpec).orElse(project.providers.provider {
throw GradleException("Unable to find launcher for Java $testJvm. Does '$testJvmHome' point to a JDK?")
})
}
}.flatMap { it }.map { project.logger.info("testJvm launcher: ${it.executablePath}"); it }

private fun String.normalizeToJDKJavaHome(): Path {
val javaHome = project.file(this).toPath().toRealPath()
return if (javaHome.endsWith("jre")) javaHome.parent else javaHome
}

private val Project.javaToolchains: JavaToolchainService get() =
extensions.getByName("javaToolchains") as JavaToolchainService
}
2 changes: 1 addition & 1 deletion dd-java-agent/agent-bootstrap/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jmh {
}

tasks.withType(Test).configureEach {
configureJvmArgs(
conditionalJvmArgs(
it,
JavaVersion.VERSION_16,
['--add-opens', 'java.base/java.net=ALL-UNNAMED'] // for HostNameResolverForkedTest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
// Set properties before any plugins get loaded
ext {
minJavaVersionForTests = JavaVersion.VERSION_11
// By default tests with be compiled for `minJavaVersionForTests` version,
// but in this case we would like to avoid this since we would like to run with ZULU8
skipSettingTestJavaVersion = true
excludeJdk = ['SEMERU11', 'SEMERU17']
}

apply from: "$rootDir/gradle/java.gradle"
apply plugin: 'idea'

tracerJava {
addSourceSetFor(JavaVersion.VERSION_11) {
// By default tests with be compiled for `minJavaVersion` version,
// but in this case we would like to avoid this since we would like to run with ZULU8
applyForTestSources = false
}
}

testJvmConstraints {
minJavaVersion = JavaVersion.VERSION_11
excludeJdk = ['SEMERU11', 'SEMERU17']
}

minimumBranchCoverage = 0.5
minimumInstructionCoverage = 0.7

Expand Down
Loading