Skip to content

Commit 9c25cc8

Browse files
authored
Add support for JUnit 5.12 (#367)
* Update dependency on JUnit 5 * Add junit-platform-launcher to runtime classpath * Fix usage of TestPlan API in instrumentation tests to match binary-incompatible change in 5.12 * Bump dependency versions to plugin+instrumentation libraries
1 parent 6fd63d9 commit 9c25cc8

File tree

12 files changed

+76
-22
lines changed

12 files changed

+76
-22
lines changed

build-logic/src/main/kotlin/Dependencies.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
object libs {
44
object versions {
55
const val kotlin = "1.9.25"
6-
const val junitJupiter = "5.11.4"
7-
const val junitVintage = "5.11.4"
8-
const val junitPlatform = "1.11.4"
6+
const val junitJupiter = "5.12.0"
7+
const val junitVintage = "5.12.0"
8+
const val junitPlatform = "1.12.0"
99

1010
const val composeBom = "2024.09.00"
1111
const val androidXTestAnnotation = "1.0.1"
@@ -55,6 +55,7 @@ object libs {
5555
const val junitJupiterEngine = "org.junit.jupiter:junit-jupiter-engine:${versions.junitJupiter}"
5656
const val junitVintageEngine = "org.junit.vintage:junit-vintage-engine:${versions.junitVintage}"
5757
const val junitPlatformCommons = "org.junit.platform:junit-platform-commons:${versions.junitPlatform}"
58+
const val junitPlatformLauncher = "org.junit.platform:junit-platform-launcher:${versions.junitPlatform}"
5859
const val junitPlatformRunner = "org.junit.platform:junit-platform-runner:${versions.junitPlatform}"
5960
const val apiguardianApi = "org.apiguardian:apiguardian-api:${versions.apiGuardian}"
6061

build-logic/src/main/kotlin/Deployment.kt

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.gradle.configurationcache.extensions.capitalized
1515
import org.gradle.jvm.tasks.Jar
1616
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
1717
import org.gradle.kotlin.dsl.withGroovyBuilder
18+
import org.gradle.plugins.signing.Sign
1819
import org.gradle.plugins.signing.SigningExtension
1920
import java.io.File
2021

@@ -112,6 +113,12 @@ fun Project.configureDeployment(deployConfig: Deployed) {
112113
signing {
113114
sign(publishing.publications)
114115
}
116+
117+
// Connect signing task to artifact-producing task
118+
// (build an AAR for Android modules, assemble a JAR for other modules)
119+
tasks.withType(Sign::class.java).configureEach {
120+
dependsOn(if (isAndroid) "bundleReleaseAar" else "assemble")
121+
}
115122
}
116123

117124
/* Private */

build-logic/src/main/kotlin/Environment.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ object Artifacts {
9191
platform = Java,
9292
groupId = "de.mannodermaus.gradle.plugins",
9393
artifactId = "android-junit5",
94-
currentVersion = "1.11.4.0-SNAPSHOT",
94+
currentVersion = "1.12.0.0-SNAPSHOT",
9595
latestStableVersion = "1.11.3.0",
9696
description = "Unit Testing with JUnit 5 for Android."
9797
)
@@ -101,7 +101,7 @@ object Artifacts {
101101
*/
102102
object Instrumentation {
103103
const val groupId = "de.mannodermaus.junit5"
104-
private const val currentVersion = "1.6.1-SNAPSHOT"
104+
private const val currentVersion = "1.7.0-SNAPSHOT"
105105
private const val latestStableVersion = "1.6.0"
106106

107107
val Core = Deployed(
@@ -156,6 +156,7 @@ class DeployedCredentials(private val project: Project) {
156156
//
157157
// * Local development:
158158
// Stored in local.properties file on the machine
159+
// (in the root folder of the project – the one containing "plugin/" and "instrumentation/")
159160
// * CI Server:
160161
// Stored in environment variables before launch
161162
val properties = Properties().apply {

build-logic/src/main/kotlin/Tasks.kt

+4
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ fun Copy.configureCreateVersionClassTask(
166166
// Find an appropriate version of the instrumentation library,
167167
// depending on the version of how the plugin is configured
168168
"INSTRUMENTATION_VERSION" to instrumentationVersion,
169+
170+
// JUnit 5.12+ requires the platform launcher on the runtime classpath;
171+
// to prevent issues with version mismatching, the plugin applies this for users
172+
"JUNIT_PLATFORM_LAUNCHER" to libs.junitPlatformLauncher
169173
)
170174
), ReplaceTokens::class.java
171175
)

instrumentation/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Change Log
33

44
## Unreleased
55

6+
- **This version requires (at least) android-junit5 1.12.0.0 and JUnit 5.12.0.**
7+
- Migrate to new TestPlan API in JUnit 5.12, which changed in a binary-incompatible fashion
8+
69
## 1.6.0 (2024-10-05)
710

811
- Use square brackets for parameterized tests to ensure that their logs show correctly in the IDE (#350)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package de.mannodermaus.junit5.internal.discovery
2+
3+
import androidx.annotation.RequiresApi
4+
import org.junit.platform.engine.ConfigurationParameters
5+
import org.junit.platform.engine.TestDescriptor
6+
import org.junit.platform.engine.reporting.OutputDirectoryProvider
7+
import org.junit.platform.launcher.TestPlan
8+
import java.io.File
9+
import java.util.Optional
10+
11+
/**
12+
* A JUnit TestPlan that does absolutely nothing.
13+
* Used by [de.mannodermaus.junit5.internal.runners.AndroidJUnit5] whenever a class
14+
* is not loadable through the JUnit Platform and should be discarded.
15+
*/
16+
@RequiresApi(26)
17+
internal object EmptyTestPlan : TestPlan(
18+
false,
19+
emptyConfigurationParameters,
20+
emptyOutputDirectoryProvider
21+
)
22+
23+
@RequiresApi(26)
24+
private val emptyConfigurationParameters = object : ConfigurationParameters {
25+
override fun get(key: String?) = Optional.empty<String>()
26+
override fun getBoolean(key: String?) = Optional.empty<Boolean>()
27+
override fun keySet() = emptySet<String>()
28+
29+
@Deprecated("Deprecated in Java", ReplaceWith("keySet().size"))
30+
override fun size() = 0
31+
}
32+
33+
@RequiresApi(26)
34+
private val emptyOutputDirectoryProvider = object : OutputDirectoryProvider {
35+
private val path = File.createTempFile("empty-output", ".nop").toPath()
36+
override fun getRootDirectory() = path
37+
override fun createOutputDirectory(testDescriptor: TestDescriptor?) = path
38+
}

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5.kt

+2-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package de.mannodermaus.junit5.internal.runners
22

33
import androidx.annotation.RequiresApi
44
import androidx.annotation.VisibleForTesting
5+
import de.mannodermaus.junit5.internal.discovery.EmptyTestPlan
56
import de.mannodermaus.junit5.internal.runners.notification.ParallelRunNotifier
67
import org.junit.platform.commons.JUnitException
78
import org.junit.platform.engine.ConfigurationParameters
@@ -25,19 +26,6 @@ internal class AndroidJUnit5(
2526
private val testClass: Class<*>,
2627
paramsSupplier: () -> AndroidJUnit5RunnerParams = AndroidJUnit5RunnerParams.Companion::create,
2728
) : Runner() {
28-
private companion object {
29-
private val emptyConfigurationParameters = object : ConfigurationParameters {
30-
override fun get(key: String?) = Optional.empty<String>()
31-
override fun getBoolean(key: String?) = Optional.empty<Boolean>()
32-
override fun keySet() = emptySet<String>()
33-
34-
@Deprecated("Deprecated in Java", ReplaceWith("keySet().size"))
35-
override fun size() = 0
36-
}
37-
38-
private val emptyTestPlan = TestPlan.from(emptyList(), emptyConfigurationParameters)
39-
}
40-
4129
private val launcher = LauncherFactory.create()
4230
private val testTree by lazy { generateTestTree(paramsSupplier()) }
4331

@@ -80,7 +68,7 @@ internal class AndroidJUnit5(
8068
// or anything else not present in the Android runtime).
8169
// Log those to console, but discard them from being considered at all
8270
e.printStackTrace()
83-
emptyTestPlan
71+
EmptyTestPlan
8472
}
8573

8674
return AndroidJUnitPlatformTestTree(

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTree.kt

+7-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ internal class AndroidJUnitPlatformTestTree(
137137

138138
return if (identifier.isTest || identifier.isDynamicTest) {
139139
Description.createTestDescription(
140-
/* className = */ testPlan.getParent(identifier)
140+
/* className = */
141+
testPlan.getParent(identifier)
141142
.map(nameExtractor)
142143
.orElse("<unrooted>"),
143144
/* name = */ name,
@@ -180,7 +181,11 @@ internal class AndroidJUnitPlatformTestTree(
180181
* Custom drop-in TestPlan for Android purposes.
181182
*/
182183
private class ModifiedTestPlan(val delegate: TestPlan) :
183-
TestPlan(delegate.containsTests(), delegate.configurationParameters) {
184+
TestPlan(
185+
/* containsTests = */ delegate.containsTests(),
186+
/* configurationParameters = */ delegate.configurationParameters,
187+
/* outputDirectoryProvider = */ delegate.outputDirectoryProvider
188+
) {
184189

185190
fun getRealParent(child: TestIdentifier?): Optional<TestIdentifier> {
186191
// Because the overridden "getParent()" from the superclass is modified,

plugin/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ Change Log
22
==========
33

44
## Unreleased
5+
- JUnit 5.12.0
6+
- Add dependency on JUnit Platform Launcher to runtime classpath, accommodating an upstream change
57

68
## 1.11.3.0 (2024-12-23)
79
- JUnit 5.11.3

plugin/android-junit5/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ dependencies {
114114
testImplementation(libs.junitJupiterApi)
115115
testImplementation(libs.junitJupiterParams)
116116
testRuntimeOnly(libs.junitJupiterEngine)
117+
testRuntimeOnly(libs.junitPlatformLauncher)
117118
}
118119

119120
project.configureDeployment(Artifacts.Plugin)

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/configureJUnit5.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,15 @@ private fun AndroidJUnitPlatformExtension.attachDependencies(
148148
includeRunner: Boolean,
149149
) {
150150
if (project.usesJUnitJupiterIn(configurationName)) {
151+
val runtimeOnlyConfigurationName = configurationName.replace("Implementation", "RuntimeOnly")
151152
val version = instrumentationTests.version.get()
152153

154+
project.dependencies.add(runtimeOnlyConfigurationName, Libraries.junitPlatformLauncher)
153155
project.dependencies.add(configurationName, "${Libraries.instrumentationCore}:$version")
154156

155157
if (includeRunner) {
156158
project.dependencies.add(
157-
configurationName.replace("Implementation", "RuntimeOnly"),
159+
runtimeOnlyConfigurationName,
158160
"${Libraries.instrumentationRunner}:$version",
159161
)
160162
}

plugin/android-junit5/src/main/templates/Libraries.kt

+2
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ internal object Libraries {
66
const val instrumentationCore = "@INSTRUMENTATION_GROUP@:@INSTRUMENTATION_CORE@"
77
const val instrumentationExtensions = "@INSTRUMENTATION_GROUP@:@INSTRUMENTATION_EXTENSIONS@"
88
const val instrumentationRunner = "@INSTRUMENTATION_GROUP@:@INSTRUMENTATION_RUNNER@"
9+
10+
const val junitPlatformLauncher = "@JUNIT_PLATFORM_LAUNCHER@"
911
}

0 commit comments

Comments
 (0)