Clarifications about various test tasks
In addition this commit replaces eager tasks generation for running tests using different java versions with rule based task created on-demand.
MODIFY
CONTRIBUTING.md
CONTRIBUTING.md
Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality [open an issue](https://github.com/open-telemetry/opentelemetry-auto-instr-java/issues/new)
and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for review.
+### Testing
+#### Java versions
+Open Telemetry Auto Instrumentation's minimal supported version is java 7.
+All jar files that we produce, unless noted otherwise, have bytecode compatible with java 7 runtime.
+In addition to that we test our code with all later java versions as well: from 8 to 14.
+
+Some libraries that we auto-instrument may have higher minimal requirements.
+In this case we compile and test corresponding auto-instrumentation with higher java version as required by library.
+The resulting classes will have higher bytecode level,
+but as it matches library's java version, no runtime problem arise.
+
+#### Instrumentation tests
+Executing `./gradlew instrumentation:test` will run tests for all supported auto-instrumentations
+using that java version which runs the Gradle build itself.
+These tests usually use the minimal supported version of the instrumented library.
+
+In addition to that each instrumentation has a separate test set called `latestDepTest`.
+It was created by [Gradle test sets plugin](https://github.com/unbroken-dome/gradle-testsets-plugin).
+It uses the very same tests as before, but declares a dynamic dependency on the latest available version of this library.
+You can run them all by executing `./gradlew latestDepTest`.
+
+#### Executing tests with specific java version
+In order to run tests on a specific java version, just execute `./gradlew testJava7` (or `testJava11` or `latestDepTestJava14` etc).
+Then Gradle task rule will kick in and do the following:
+* check, if Gradle already runs on a java with required version
+* if not, look for an environment variable named `JAVA_N_HOME`, where `N` is the requested java version
+* if Gradle could not found requested java version, then build will fail
+* Gradle will now find all corresponding test tasks and configure them to use java executable of the requested version.
+
+This works both for tasks named `test` and `latestDepTest`.
+But currently does not work for other custom test tasks, such as those created by test sets plugin.
+
### Code Style
This project includes a `.editorconfig` file for basic editor settings. This file is supported by most common text editors.

MODIFY
gradle/java.gradle
gradle/java.gradle
@@ -10,6 +10,7 @@ apply from: "$rootDir/gradle/spotbugs.gradle"
def applyCodeCoverage = !(
project.path.startsWith(":smoke-tests") ||
+ //TODO why some tests fail on java 11 if jacoco is present?
project.path == ":opentelemetry-auto" ||
project.path == ":load-generator" ||
project.path.startsWith(":benchmark") ||
@@ -234,21 +235,22 @@ if (project.hasProperty("removeJarVersionNumbers") && removeJarVersionNumbers) {
}
}
-if (project.parent && project.parent.ext.has("javaExecutableVersionCache")) {
- project.ext.javaExecutableVersionCache = project.parent.ext.javaExecutableVersionCache
-} else {
- project.ext.javaExecutableVersionCache = [:]
-}
-JavaVersion getJavaExecutableVersion(String path) {
- def cache = project.ext.javaExecutableVersionCache
+if (!rootProject.ext.has("javaExecutableVersionCache")) {
+ rootProject.ext.javaExecutableVersionCache = [:]
+}
- if (cache.containsKey(path)) {
- return cache.get(path)
+/**
+ * Returns version of java from a given java home.
+ */
+JavaVersion getJavaHomeVersion(String javaHome) {
+ def cache = rootProject.ext.javaExecutableVersionCache
+ if (cache.containsKey(javaHome)) {
+ return cache.get(javaHome)
}
new ByteArrayOutputStream().withStream { stream ->
exec {
- commandLine = [path, "-version"]
+ commandLine = [toExecutable(javaHome), "-version"]
errorOutput = stream
}
def output = stream.toString()
@@ -257,18 +259,14 @@ JavaVersion getJavaExecutableVersion(String path) {
def matcher = line =~ /^(?:java|openjdk) version "([^"]+)"/
if (matcher) {
def version = JavaVersion.toVersion(matcher.group(1))
- cache.put(path, version)
+ cache.put(javaHome, version)
return version
}
}
// Getting here means we didn't find a line matching the version pattern.
- throw new GradleScriptException("Cannot determine java version. Executable: ${path}, output: ${output}", null)
- }
+ throw new GradleScriptException("Cannot determine java version. Executable: ${javaHome}, output: ${output}", null)
}
-
-ext {
- getJavaExecutableVersion = this.&getJavaExecutableVersion
}
def isJavaVersionAllowed(JavaVersion version) {
@@ -281,77 +279,67 @@ def isJavaVersionAllowed(JavaVersion version) {
return true
}
-def isJdkForced(String javaName) {
- return (project.hasProperty('forceJdk') && project.getProperty('forceJdk').contains(javaName))
+/**
+ * For a given java home return the location of java executable
+ */
+static String toExecutable(String javaHome) {
+ return Objects.requireNonNull(javaHome) + "/bin/java"
}
-// Disable default test tasks if current JVM doesn't match version requirements
-tasks.withType(Test).configureEach {
- if (name.endsWith("Generated")) {
- return
+/**
+ * Returns java home for a given version or {@code null} if not found
+ */
+String findJavaHome(JavaVersion version) {
+ def javaHome = System.getenv("JAVA_${version.majorVersion}_HOME")
+ if (javaHome == null) {
+ return null
}
-
- // Always run all tests that are runnable on JVM used for compilation
- onlyIf { isJavaVersionAllowed(JavaVersion.current()) }
+ def foundVersion = getJavaHomeVersion(javaHome)
+ return version == foundVersion ? javaHome : null
}
-// Generate tests tasks for all provided JVMs
-for (def env : System.getenv().entrySet()) {
- def matcher = env.key =~ /JAVA_([^_]+)_HOME/
- if (!matcher) {
- continue
+ext {
+ findJavaHome = this.&findJavaHome
}
- def javaName = matcher.group(1)
- def javaHome = env.value
- def javaPath = "$javaHome/bin/java"
- def javaVersion = getJavaExecutableVersion(javaPath)
- // This is slightly complicated because we need to dereference symlinks to make sure
- // we are considering same JVM implementation
- def currentJavaHome = new File(System.getProperty("java.home")).toPath().toRealPath()
- if (currentJavaHome.endsWith("jre")) {
- currentJavaHome = currentJavaHome.parent
- }
- if (currentJavaHome == new File(javaHome).toPath().toRealPath()) {
- // Skip JVM implementation we are running gradle on
- continue
- }
+def addTestRule(String testTaskName) {
+ def prefix = testTaskName + "Java"
+ tasks.addRule("Pattern: $prefix<Version>: Runs tests using given java version") { String taskName ->
+ if (taskName.startsWith(prefix)) {
+ def requestedJavaVersion = JavaVersion.toVersion(taskName - prefix)
+ def gradleJavaVersion = JavaVersion.current()
- def parentTask = task "testJava${javaName}"() {
- group = 'Verification'
- description = "Run tests for Java ${javaName}"
- }
- tasks.check.dependsOn parentTask
+ if (gradleJavaVersion != requestedJavaVersion) {
+ def javaHomeForTests = findJavaHome(requestedJavaVersion)
+ if (javaHomeForTests != null) {
tasks.withType(Test).all {
- //if (name.endsWith("Generated")) {
- if (!name.equals("test")) {
- // The way we're copying the test doesn't currently work with "test-sets" generated tests.
+ // minJavaVersionForTests property, which specifies java version requirements for tests,
+ // currently does not work with custom source sets created by org.unbroken-dome.test-sets plugin.
+ // Thus we are forced to ignore all of them
+ // TODO solve this problem. We want to run custom test sets with all java versions as well
+ if (name != testTaskName) {
return
}
-
- def clonedTask = it
- def newTask = task "${clonedTask.name}Java${javaName}Generated"(type: clonedTask.class) {
- description "Runs $clonedTask.name under java ${javaName}"
- executable = javaPath
-
- if (javaName == "7") {
+ executable = toExecutable(javaHomeForTests)
+ enabled = isJavaVersionAllowed(requestedJavaVersion)
+ if (requestedJavaVersion.isJava7()) {
// Disable JIT for this method. Sometimes Java7 JVM crashes trying to compile it.
jvmArgs '-XX:CompileCommand=exclude,net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor::onParameterizedType'
}
-
- onlyIf { isJavaVersionAllowed(javaVersion) || isJdkForced(javaName) }
- if (applyCodeCoverage) {
- jacoco {
- // Disable jacoco for additional JVM tests to speed things up a bit
- enabled = false
}
+ } else {
+ throw new BuildCancelledException("Requested java version $requestedJavaVersion not found")
}
}
- parentTask.dependsOn newTask
+ task(taskName, dependsOn: testTaskName)
}
}
+}
+
+addTestRule("test")
+addTestRule("latestDepTest")
tasks.withType(Test).configureEach {
if (project.findProperty("enableJunitPlatform") == true) {
@@ -362,8 +350,9 @@ tasks.withType(Test).configureEach {
// This value is quite big because with lower values (3 mins) we were experiencing large number of false positives
timeout = Duration.ofMinutes(15)
+ // Disable all tests if current JVM doesn't match version requirements
// Disable all tests if skipTests property was specified
- onlyIf { !project.rootProject.hasProperty("skipTests") }
+ enabled = isJavaVersionAllowed(JavaVersion.current()) && !project.rootProject.hasProperty("skipTests")
}
plugins.withType(BasePlugin) {

MODIFY
smoke-tests/java9-modules/java9-modules.gradle
smoke-tests/java9-modules/java9-modules.gradle
@@ -12,60 +12,22 @@ jar {
}
}
-// If the current JDK version (the one running gradle) is < 9, we need to find a version >= 9
-// to compile this project. java.gradle creates a map of java executables
-// called "javaExecutableVersionCache" pulled from the environment.
-// This loops over the cache to find a usable jdk.
-// Since this project is the only one that requires a version above Java 8
-// it's special cased here instead of putting a generic version matcher in java.gradle
-if (JavaVersion.VERSION_1_9.compareTo(JavaVersion.current()) > 0) {
- def targetJavaHome
-
- // Find a compatible version in the cache
- ext.javaExecutableVersionCache.find { key, value ->
- if (JavaVersion.VERSION_1_9.compareTo(value) <= 0) {
- // JAVA_HOME/bin/java -> JAVA_HOME
- targetJavaHome = file(key).parentFile.parentFile
- return true
- }
-
- return false
- }
-
- if (targetJavaHome != null) {
- // if we found a compatible jdk, compile the src/main/java9 folder with it
compileMain_java9Java {
options.fork = true
- options.forkOptions.javaHome = targetJavaHome
+ options.forkOptions.javaHome = file(findJavaHome(JavaVersion.VERSION_1_9))
+
options.compilerArgs = ['--module-path', classpath.asPath]
options.sourcepath = files(sourceSets.main_java9.java.srcDirs)
}
- } else {
- compileMain_java9Java {
- enabled = false
- }
- }
-}
-// java.gradle generates a test task per jdk and assigns the test task its own java executable
-// For each Test task, this loop creates a jlink image using the test's executable
-// At the end, we have 1 jlink image per JVM: each one used by a testXXXGenerated task
-tasks.withType(Test).each {
- def javaExecutable = it.executable
- def javaVersion = getJavaExecutableVersion(javaExecutable)
-
- // Only Java 9 and above have jlink
- if (JavaVersion.VERSION_1_9.compareTo(javaVersion) > 0) {
- return
- }
-
- // JAVA_HOME/bin/java -> JAVA_HOME
- def specificJDKHome = file(javaExecutable).parentFile.parent
+//This test will be automatically disabled by `java.gradle` if tried to run on java earlier than 9
+test {
+ doFirst {
+ def specificJDKHome = file(executable).parentFile.parent
def jlinkExecutable = specificJDKHome + "/bin/jlink"
def jdkModulesPath = specificJDKHome + "/jmods"
def generatedImageDir = "${buildDir}/${it.name}image"
- it.doFirst {
delete generatedImageDir
// Run the jlink command to create the image
@@ -74,10 +36,10 @@ tasks.withType(Test).each {
'--add-modules', 'java.instrument,io.opentelemetry.smoketest.moduleapp',
"--module-path", "${jdkModulesPath}:" + jar.archiveFile.get().toString(), "--output", generatedImageDir
}
- }
- it.jvmArgs "-Dio.opentelemetry.smoketest.module.image=${generatedImageDir}"
- it.dependsOn jar
+ jvmArgs "-Dio.opentelemetry.smoketest.module.image=${generatedImageDir}"
+ }
+ dependsOn jar
}
dependencies {

MODIFY
trace-java.gradle
trace-java.gradle
@@ -39,8 +39,6 @@ repositories {
description = 'trace-java'
-tasks.register("latestDepTest")
-
wrapper {
distributionType = Wrapper.DistributionType.ALL
}

Clarifications about various test tasks (#358)

In addition this commit replaces eager tasks generation for running tests using different java versions with rule based task created on-demand.