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.
This commit is contained in:
parent
fbbccd31a6
commit
6e1c0436da
|
@ -3,6 +3,38 @@
|
|||
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.
|
||||
|
|
|
@ -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 = [:]
|
||||
|
||||
if (!rootProject.ext.has("javaExecutableVersionCache")) {
|
||||
rootProject.ext.javaExecutableVersionCache = [:]
|
||||
}
|
||||
|
||||
JavaVersion getJavaExecutableVersion(String path) {
|
||||
def cache = project.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,20 +259,16 @@ 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) {
|
||||
if (project.hasProperty('minJavaVersionForTests') && project.getProperty('minJavaVersionForTests').compareTo(version) > 0) {
|
||||
return false
|
||||
|
@ -281,78 +279,68 @@ 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
|
||||
}
|
||||
def javaName = matcher.group(1)
|
||||
def javaHome = env.value
|
||||
def javaPath = "$javaHome/bin/java"
|
||||
def javaVersion = getJavaExecutableVersion(javaPath)
|
||||
ext {
|
||||
findJavaHome = this.&findJavaHome
|
||||
}
|
||||
|
||||
// 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.
|
||||
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") {
|
||||
// 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
|
||||
tasks.withType(Test).all {
|
||||
// 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
|
||||
}
|
||||
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'
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
useJUnitPlatform()
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
compileMain_java9Java {
|
||||
options.fork = true
|
||||
options.forkOptions.javaHome = file(findJavaHome(JavaVersion.VERSION_1_9))
|
||||
|
||||
// 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.compilerArgs = ['--module-path', classpath.asPath]
|
||||
options.sourcepath = files(sourceSets.main_java9.java.srcDirs)
|
||||
}
|
||||
} else {
|
||||
compileMain_java9Java {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
options.compilerArgs = ['--module-path', classpath.asPath]
|
||||
options.sourcepath = files(sourceSets.main_java9.java.srcDirs)
|
||||
}
|
||||
|
||||
// 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)
|
||||
//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"
|
||||
|
||||
// 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
|
||||
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 {
|
||||
|
|
|
@ -39,8 +39,6 @@ repositories {
|
|||
|
||||
description = 'trace-java'
|
||||
|
||||
tasks.register("latestDepTest")
|
||||
|
||||
wrapper {
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue