diff --git a/build.gradle b/build.gradle index 753a86cfc35..29537dd77dc 100644 --- a/build.gradle +++ b/build.gradle @@ -157,7 +157,7 @@ ext { libs.log4j2Api, libs.log4j2Core ] - + } allprojects { @@ -484,11 +484,36 @@ subprojects { } } + // Workaround for Mockito Java Agent restrictions in Java 21+ + // Starting with Java 21, the JDK restricts libraries from attaching a Java agent + // to their own JVM. As a result, Mockito’s inline mock maker (mockito-core) + // fails without explicit instrumentation, and the JVM consistently emits warnings. + // See also: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#mockito-instrumentation + afterEvaluate { subproject -> + def hasMockitoCore = subproject.configurations.findAll { + it.canBeResolved + }.any { config -> + config.incoming.dependencies.any { dependency -> + "$dependency" == libs.mockitoCore + } + } + + if (hasMockitoCore) { + subproject.configurations { + mockitoAgent { + transitive = false + } + } + subproject.dependencies { + mockitoAgent libs.mockitoCore + } + } + } + // The suites are for running sets of tests in IDEs. // Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice. def testsToExclude = ['**/*Suite.class'] - // This task will copy JUnit XML files out of the sub-project's build directory and into // a top-level build/junit-xml directory. This is necessary to avoid reporting on tests which // were not run, but instead were restored via FROM-CACHE. See KAFKA-17479 for more details. @@ -518,6 +543,14 @@ subprojects { } test { + + doFirst { + def mockitoAgentConfig = configurations.findByName('mockitoAgent') + if (mockitoAgentConfig) { + jvmArgs("-javaagent:${mockitoAgentConfig.asPath}") + } + } + maxParallelForks = maxTestForks ignoreFailures = userIgnoreFailures @@ -551,7 +584,7 @@ subprojects { maxFailures = userMaxTestRetryFailures } } - + finalizedBy("copyTestXml") }