question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Java 8 CI to prevent regressions

See original GitHub issue

Expected Behavior

Java 8 is tested on CI on every PR, so that breakages like #898 cannot happen.

Current Behavior

Java 11 - 18 on CI.

Fix

You can keep building on whatever Java you want, but in Gradle, specify on what JVM the tests are executed on. Gradle Test tasks fork a separate process by default, so there’s no hit here. It should be a simple case of setting the Java launcher for the tests:

tasks.withType<Test>().configureEach {
  it.javaLauncher.set(javaToolchains.launcherFor {
      languageVersion = JavaLanguageVersion.of("8")
  })
}

Going further

You can remove all the builds from CI and just use 1 build that tests all the Javas at once… 🤯: https://jakewharton.com/build-on-latest-java-test-through-lowest-java/ Jake delivers once again! 😃

Failure Logs

I can see (by running the build on Java 8) that AGP is preventing you from that, but that needn’t be the case.

* Where:
Build file 'P:\projects\contrib\github-mockk\modules\mockk-agent-android\build.gradle.kts' line: 4

* What went wrong:
An exception occurred applying plugin request [id: 'buildsrc.convention.android-library']
> Failed to apply plugin 'com.android.internal.library'.
   > Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
     Your current JDK is located in  P:\tools\lang\java-1.8.0_201-x64-jdk\jre
     You can try some of the following options:
       - changing the IDE settings.
       - changing the JAVA_HOME environment variable.
       - changing `org.gradle.java.home` in `gradle.properties`.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:11 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
3flexcommented, Aug 31, 2022

I guess this leaves us with two options:

  • Not testing Java 8 in CI
  • Not testing Android in CI

Another option - use something like @EnabledForJreRange(min = JAVA_11) on the Android tests. I didn’t check which test runner you’re using (this is for JUnit 5 - but should be possible on others), but this allows full range of Java versions to be used in CI while skipping Android tests on incompatible versions.

1reaction
aSemycommented, Sep 11, 2022

You can remove all the builds from CI and just use 1 build that tests all the Javas at once… 🤯: https://jakewharton.com/build-on-latest-java-test-through-lowest-java/ Jake delivers once again! 😃

I’ve tried implementing this approach. It wasn’t easy because the Kotlin JVM test tasks are named dynamically, and they’re created lazily.

Unfortunately, once set up, all the tests pass - including the sealed-class tests that are supposed to be broken 😦

Here’s the updated Toolchain convention, but because it doesn’t work, I don’t want to commit it.

It provides these tasks:

  • lifecycle tasks that will run all JDK tests, whether Kotlin or Java
    • checkJdk11
    • checkJdk17
    • checkJdk18
    • checkJdk8
    • checkJdks - runs all checkJdk* tasks
  • Kotlin JVM JDK tests
    • jvmTestJdk11
    • jvmTestJdk17
    • jvmTestJdk18
    • jvmTestJdk8
  • Java JDK tests
    • testJdk11
    • testJdk17
    • testJdk18
    • testJdk8
// buildSrc/src/main/kotlin/buildsrc/convention/toolchain-jvm.gradle.kts

package buildsrc.convention

import buildsrc.config.Deps
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain


description = "Set JavaToolchain for compiling main and test code"


tasks.withType<JavaCompile>().configureEach {
    options.encoding = "UTF-8"
    sourceCompatibility = Deps.Versions.jvmTarget.toString()
    targetCompatibility = Deps.Versions.jvmTarget.toString()
}

tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions {
        jvmTarget = Deps.Versions.jvmTarget.toString()
    }
}

// Retrieve the JavaToolchainService extension
val javaToolchains: JavaToolchainService = extensions.getByType()

// Add this marker property to each JDK specific task, to differentiate them from the regular test task it is based on
val testTaskJdkMarkerName = "test jdk task"

val testJdkTargets = listOf(8, 11, 17, 18)

val javaToolchainMainVersion: Provider<JavaLanguageVersion> =
    providers.gradleProperty("javaToolchainMainVersion")
        .map { JavaLanguageVersion.of(it) }


tasks.withType<JavaCompile>().configureEach {
    javaCompiler.set(
        javaToolchains.compilerFor { languageVersion.set(javaToolchainMainVersion) }
    )
}

tasks.withType<UsesKotlinJavaToolchain>().configureEach {
    kotlinJavaToolchain.toolchain.use(
        javaToolchains.launcherFor { languageVersion.set(javaToolchainMainVersion) }
    )
}

val checkJdks by tasks.registering {
    group = LifecycleBasePlugin.VERIFICATION_GROUP
    description = "Run all JDK tests"
}

// create Lifecycle tasks for each JDK version
testJdkTargets.forEach { testJavaVersion ->
    val checkJdkVersion = tasks.register(checkJdkTaskName(testJavaVersion)) {
        group = LifecycleBasePlugin.VERIFICATION_GROUP
        description = "Run all JDK $testJavaVersion tests"
    }
    checkJdks.configure { dependsOn(checkJdkVersion) }
}


// for each Test task, create JDK tests
// must use afterEvaluate because the Kotlin plugin adds jvmTest lazily
afterEvaluate {
    tasks.withType<Test>()
        .matching {
            !it.extensions.extraProperties.has(testTaskJdkMarkerName)
        }
        .forEach {
            registerTestJdkTasks(it)
        }
}


fun registerTestJdkTasks(baseTask: Test) {
    testJdkTargets.forEach { testJavaVersion ->
        logger.info("registering test jdk $testJavaVersion task ${baseTask.name} ${baseTask::class}")

        val testJdkVersion = tasks.register<Test>("${baseTask.name}Jdk$testJavaVersion") {
            javaLauncher.set(
                javaToolchains.launcherFor {
                    languageVersion.set(JavaLanguageVersion.of(testJavaVersion))
                }
            )

            description = "Runs the '${baseTask.name}' suite on JDK $testJavaVersion"
            group = LifecycleBasePlugin.VERIFICATION_GROUP

            // marker property to stop recursion
            extensions.extraProperties.set(testTaskJdkMarkerName, testJavaVersion)

            // Copy inputs from base Test task
            classpath = baseTask.classpath
            testClassesDirs = baseTask.testClassesDirs

            useJUnitPlatform()
        }
        tasks.named(checkJdkTaskName(testJavaVersion)).configure { dependsOn(testJdkVersion) }
    }
}


fun checkJdkTaskName(testJavaVersion: Int): String = "checkJdk$testJavaVersion"
Read more comments on GitHub >

github_iconTop Results From Across the Web

Regression Testing: Tools and Techniques - Java Code Geeks
Regression testing ensures that the changes have not introduced new bugs in the existing features which were working fine before. Sometimes ...
Read more >
Accelerate your Java pipeline with AI-assisted coding - Diffblue
Unit testing has an important role to play, accelerating cycle times by detecting regressions at the earliest possible stage. Since testing ...
Read more >
Continuous Performance Regression Testing for CI/CD
Benchmarking allows developers to see the performance impact on their code. In a CI/CD pipeline, it prevents catastrophic performance drops.
Read more >
Understanding and Improving Regression Test Selection in ...
Regression test selection (RTS) can reduce the costs of regression testing. ... with all three RTS tools (e.g., GIB requires Java 8), resulting....
Read more >
What is Regression Testing? Test Cases (Example) - Guru99
Regression Testing means to confirm that a recent program or code change has not adversely affected existing features. In this tutorial ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found