Skip to content

Update to JUnit 5.13 and refactor minimum version detection scheme #375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 3 additions & 5 deletions build-logic/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
object libs {
object versions {
const val kotlin = "2.1.21"
const val junitJupiter = "5.13.0-RC1"
const val junitVintage = "5.13.0-RC1"
const val junitPlatform = "1.13.0-RC1"
const val junitJupiter = "5.13.0"
const val junitVintage = "5.13.0"
const val junitPlatform = "1.13.0"

const val composeBom = "2025.03.00"
const val androidXMultidex = "2.0.1"
Expand All @@ -19,7 +19,6 @@ object libs {
const val coroutines = "1.10.2"
const val dokka = "2.0.0"
const val espresso = "3.6.1"
const val javaSemver = "0.10.2"
const val junit4 = "4.13.2"
const val konfToml = "1.1.2"
const val kotlinxBinaryCompatibilityValidator = "0.17.0"
Expand Down Expand Up @@ -51,7 +50,6 @@ object libs {

const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
const val kotlinCoroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"
const val javaSemver = "com.github.zafarkhaja:java-semver:${versions.javaSemver}"

const val junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}"
const val junitJupiterParams = "org.junit.jupiter:junit-jupiter-params:${versions.junitJupiter}"
Expand Down
4 changes: 2 additions & 2 deletions build-logic/src/main/kotlin/Environment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ enum class SupportedAgp(
AGP_8_7("8.7.3", gradle = "8.9"),
AGP_8_8("8.8.2", gradle = "8.10.2"),
AGP_8_9("8.9.3", gradle = "8.11.1"),
AGP_8_10("8.10.0", gradle = "8.11.1"),
AGP_8_10("8.10.1", gradle = "8.11.1"),
AGP_8_11("8.11.0-alpha10", gradle = "8.13"),
AGP_8_12("8.12.0-alpha01", gradle = "8.13")
AGP_8_12("8.12.0-alpha03", gradle = "8.13")
;

companion object {
Expand Down
1 change: 0 additions & 1 deletion plugin/android-junit5/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ dependencies {

implementation(gradleApi())
implementation(libs.kotlinStdLib)
implementation(libs.javaSemver)
implementation(libs.junitPlatformCommons)

testImplementation(gradleTestKit())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package de.mannodermaus.gradle.plugins.junit5

import com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
import de.mannodermaus.gradle.plugins.junit5.dsl.AndroidJUnitPlatformExtension.Companion.createJUnit5Extension
import de.mannodermaus.gradle.plugins.junit5.internal.config.MIN_REQUIRED_AGP_VERSION
import de.mannodermaus.gradle.plugins.junit5.internal.config.MIN_REQUIRED_GRADLE_VERSION
import de.mannodermaus.gradle.plugins.junit5.internal.config.PluginConfig
import de.mannodermaus.gradle.plugins.junit5.internal.configureJUnit5
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.whenAndroidPluginAdded
import de.mannodermaus.gradle.plugins.junit5.internal.utils.requireGradle
import de.mannodermaus.gradle.plugins.junit5.internal.utils.requireVersion
import de.mannodermaus.gradle.plugins.junit5.internal.utils.requireAgp
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.util.GradleVersion

/**
* Android JUnit Platform plugin for Gradle.
Expand All @@ -19,21 +19,23 @@ import org.gradle.api.Project
public class AndroidJUnitPlatformPlugin : Plugin<Project> {

override fun apply(project: Project) {
requireGradle(MIN_REQUIRED_GRADLE_VERSION) {
"android-junit5 plugin requires Gradle $MIN_REQUIRED_GRADLE_VERSION or later"
}

requireVersion(
actual = ANDROID_GRADLE_PLUGIN_VERSION,
required = MIN_REQUIRED_AGP_VERSION
requireGradle(
actual = GradleVersion.current(),
required = MIN_REQUIRED_GRADLE_VERSION
) {
"android-junit5 plugin requires Android Gradle Plugin $MIN_REQUIRED_AGP_VERSION or later"
"android-junit5 plugin requires Gradle $MIN_REQUIRED_GRADLE_VERSION or later"
}

project.whenAndroidPluginAdded { plugin ->
val extension = project.createJUnit5Extension()
val config = PluginConfig.find(project, plugin)
if (config != null) {
PluginConfig.find(project, plugin)?.let { config ->
requireAgp(
actual = config.currentAgpVersion,
required = MIN_REQUIRED_AGP_VERSION
) {
"android-junit5 plugin requires Android Gradle Plugin $MIN_REQUIRED_AGP_VERSION or later"
}

val extension = project.createJUnit5Extension()
configureJUnit5(project, config, extension)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

package de.mannodermaus.gradle.plugins.junit5.internal.config

internal const val MIN_REQUIRED_GRADLE_VERSION = "8.2" // When updating this, check buildSrc/Tasks.kt and update it there, too
internal const val MIN_REQUIRED_AGP_VERSION = "8.2.0"
import com.android.build.api.AndroidPluginVersion
import org.gradle.util.GradleVersion

// When updating this, check buildSrc/Tasks.kt and update it there, too
internal val MIN_REQUIRED_GRADLE_VERSION = GradleVersion.version("8.2")
internal val MIN_REQUIRED_AGP_VERSION = AndroidPluginVersion(8, 2)

internal const val EXTENSION_NAME = "junitPlatform"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ private constructor(
val hasJacocoPlugin get() = project.plugins.hasPlugin("jacoco")
private val hasKotlinPlugin get() = project.plugins.findPlugin("kotlin-android") != null

val currentAgpVersion get() = componentsExtension.pluginVersion

fun finalizeDsl(block: (CommonExtension<*, *, *, *, *>) -> Unit) {
componentsExtension.finalizeDsl(block)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.mannodermaus.gradle.plugins.junit5.internal.utils

import com.github.zafarkhaja.semver.Version
import com.android.build.api.AndroidPluginVersion
import org.gradle.api.GradleException
import org.gradle.util.GradleVersion

Expand All @@ -9,16 +9,14 @@ internal fun excludedPackagingOptions() = listOf(
"/META-INF/LICENSE-notice.md"
)

internal fun requireGradle(version: String, message: () -> String) {
require(GradleVersion.current() >= GradleVersion.version(version)) {
internal fun requireGradle(actual: GradleVersion, required: GradleVersion, message: () -> String) {
require(actual >= required) {
throw GradleException(message())
}
}

internal fun requireVersion(actual: String, required: String, message: () -> String) {
val actualVersion = Version.parse(actual)
val requiredVersion = Version.parse(required)
require(actualVersion.isHigherThanOrEquivalentTo(requiredVersion)) {
internal fun requireAgp(actual: AndroidPluginVersion, required: AndroidPluginVersion, message: () -> String) {
require(actual >= required) {
throw GradleException(message())
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package de.mannodermaus.gradle.plugins.junit5

import com.android.build.api.AndroidPluginVersion
import com.google.common.truth.Truth.assertThat
import de.mannodermaus.gradle.plugins.junit5.internal.config.MIN_REQUIRED_AGP_VERSION
import de.mannodermaus.gradle.plugins.junit5.internal.utils.requireVersion
import de.mannodermaus.gradle.plugins.junit5.internal.utils.requireAgp
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource

Expand All @@ -14,39 +15,71 @@ import org.junit.jupiter.params.provider.CsvSource
class VersionCheckerTests {

@CsvSource(
"7.0.0-alpha01, false",
"7.0.0-alpha01, false",
"7.0.0-beta01, false",
"7.0.0-rc01, false",
"7.0.0, false",
"8.0.0-beta01, false",
"8.0.0, false",
"8.0.1, false",
"8.0.1-alpha01, false",
"8.1.0, false",
"8.1.0-beta01, false",
"8.2.0, true",
"8.3.0-rc01, true",
"8.4.0-alpha05, true",
"8.10.1-alpha01, true",
"8.10.0, true",
"8.11.0-beta01, true",
"8.11.0, true",
"7.0.0-alpha1, false",
"7.0.0-alpha1, false",
"7.0.0-beta1, false",
"7.0.0-rc1, false",
"7.0.0, false",
"8.0.0-beta1, false",
"8.0.0, false",
"8.0.1, false",
"8.0.1-alpha1, false",
"8.1.0, false",
"8.1.0-beta1, false",
"8.2.0, true",
"8.3.0-rc1, true",
"8.4.0-alpha5, true",
"8.10.1-alpha11, true",
"8.10.0, true",
"8.11.0-beta1, true",
"8.11.0, true",
)
@ParameterizedTest
fun `check AGP compatibility`(version: String, compatible: Boolean) {
assertThat(versionCompatible(version)).isEqualTo(compatible)
val pluginVersion = version.toAndroidPluginVersion()
assertThat(versionCompatible(pluginVersion)).isEqualTo(compatible)
}

private fun versionCompatible(version: String): Boolean {
private fun versionCompatible(version: AndroidPluginVersion): Boolean {
return try {
requireVersion(
actual = version,
required = MIN_REQUIRED_AGP_VERSION,
message = { "" })
requireAgp(
actual = version,
required = MIN_REQUIRED_AGP_VERSION,
message = { "" }
)
true
} catch (error: Throwable) {
} catch (_: Throwable) {
false
}
}

private fun String.toAndroidPluginVersion(): AndroidPluginVersion {
// Split into stable and optional preview parts
val firstSplit = split('-')

// Split first part further into major, minor, patch
val stableComponents = firstSplit[0].split('.')

var version = AndroidPluginVersion(
major = stableComponents[0].toInt(),
minor = stableComponents[1].toInt(),
micro = stableComponents.getOrNull(2)?.toInt() ?: 0
)

// Attach preview part
val preview = firstSplit.getOrNull(1)

version = when {
preview == null -> version
preview.startsWith("alpha") -> version.alpha(preview.substringAfter("alpha").toInt())
preview.startsWith("beta") -> version.beta(preview.substringAfter("beta").toInt())
preview.startsWith("rc") -> version.rc(preview.substringAfter("rc").toInt())
else -> version
}

// Validate correctness
assertThat(version.toString()).endsWith(this)

return version
}
}