Skip to content

Commit 1ba9a9c

Browse files
authored
Address binary-incompatible API change between Gradle 7&8 for output location of Jacoco reports (#304)
* Add test project to expose NoSuchMethodError with Jacoco on AGP 7.4 and Gradle 7.5 * Use fallback to access Jacoco report output location from older Gradle versions There is a binary incompatibility between Gradle 7 and 8 that changed the return type of `outputLocation`. Catch an error with the new method and fall back to a safe alternative (see #302)
1 parent 56dcdf0 commit 1ba9a9c

File tree

8 files changed

+54
-12
lines changed

8 files changed

+54
-12
lines changed

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ package de.mannodermaus.gradle.plugins.junit5.internal.extensions
22

33
import org.gradle.api.file.FileSystemLocationProperty
44
import org.gradle.api.reporting.ConfigurableReport
5+
import java.lang.reflect.Method
56

6-
internal val ConfigurableReport.outputLocationFile
7-
get() = outputLocation as? FileSystemLocationProperty<*>
7+
internal val ConfigurableReport.outputLocationFile: FileSystemLocationProperty<*>
8+
get() = try {
9+
outputLocation as FileSystemLocationProperty<*>
10+
} catch (e: NoSuchMethodError) {
11+
// Observed before Gradle 8.x
12+
getOutputLocationMethod.invoke(this) as FileSystemLocationProperty<*>
13+
}
14+
15+
private val ConfigurableReport.getOutputLocationMethod: Method
16+
get() = javaClass.declaredMethods.first { method ->
17+
method.name == "getOutputLocation" && method.returnType == FileSystemLocationProperty::class.java
18+
}

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5JacocoReport.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() {
9898

9999
allReports.forEach { (from, to) ->
100100
to.required.set(from.enabled)
101-
from.destination?.let { to.outputLocationFile?.set(it) }
101+
from.destination?.let { to.outputLocationFile.set(it) }
102102
}
103103

104104
// Task-level Configuration

plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/FunctionalTests.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ class FunctionalTests {
6767
val project = projectCreator.createProject(spec, agp)
6868

6969
// Execute the tests of the virtual project with Gradle
70-
val result = runGradle(agp)
70+
val taskName = spec.task ?: "test"
71+
val result = runGradle(agp, taskName)
7172
.withProjectDir(project)
7273
.build()
7374

7475
// Check that the task execution was successful in general
75-
when (val outcome = result.task(":test")?.outcome) {
76+
when (val outcome = result.task(":$taskName")?.outcome) {
7677
TaskOutcome.UP_TO_DATE -> {
7778
// Nothing to do, a previous build already checked this
7879
println("Test task up-to-date; skipping assertions.")
@@ -122,14 +123,14 @@ class FunctionalTests {
122123
}
123124
}
124125

125-
private fun runGradle(agpVersion: TestedAgp) =
126+
private fun runGradle(agpVersion: TestedAgp, task: String) =
126127
GradleRunner.create()
127128
.apply {
128129
if (agpVersion.requiresGradle != null) {
129130
withGradleVersion(agpVersion.requiresGradle)
130131
}
131132
}
132-
.withArguments("test", "--stacktrace")
133+
.withArguments(task, "--stacktrace")
133134
.withPrunedPluginClasspath(agpVersion)
134135

135136
// Helper DSL to assert AGP-specific results of the virtual Gradle executions.

plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/FunctionalTestProjectCreator.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class FunctionalTestProjectCreator(
7474
replacements["AGP_VERSION"] = agp.version
7575
replacements["USE_KOTLIN"] = spec.useKotlin
7676
replacements["USE_FLAVORS"] = spec.useFlavors
77+
replacements["USE_JACOCO"] = spec.useJacoco
7778
replacements["USE_CUSTOM_BUILD_TYPE"] = spec.useCustomBuildType
7879
replacements["RETURN_DEFAULT_VALUES"] = spec.returnDefaultValues
7980
replacements["INCLUDE_ANDROID_RESOURCES"] = spec.includeAndroidResources
@@ -115,8 +116,10 @@ class FunctionalTestProjectCreator(
115116
val srcFolder: File,
116117
config: Config
117118
) {
119+
val task = config[TomlSpec.Settings.task]
118120
val minAgpVersion = config[TomlSpec.Settings.minAgpVersion]
119121
val useKotlin = config[TomlSpec.Settings.useKotlin]
122+
val useJacoco = config[TomlSpec.Settings.useJacoco]
120123
val useFlavors = config[TomlSpec.Settings.useFlavors]
121124
val useCustomBuildType = config[TomlSpec.Settings.useCustomBuildType]
122125
val returnDefaultValues = config[TomlSpec.Settings.returnDefaultValues]
@@ -151,12 +154,14 @@ class FunctionalTestProjectCreator(
151154

152155
// Structure of the virtual project config file, used only internally
153156
private object TomlSpec : ConfigSpec(prefix = "") {
154-
val expectations by required<List<ExpectedTests>>()
157+
val expectations by optional<List<ExpectedTests>>(default = emptyList())
155158

156159
object Settings : ConfigSpec() {
160+
val task by optional<String?>(default = null)
157161
val minAgpVersion by optional<String?>(default = null)
158162
val useFlavors by optional(default = false)
159163
val useKotlin by optional(default = false)
164+
val useJacoco by optional(default = false)
160165
val useCustomBuildType by optional<String?>(default = null)
161166
val returnDefaultValues by optional(default = false)
162167
val includeAndroidResources by optional(default = false)

plugin/android-junit5/src/test/resources/test-projects/build.gradle.kts.template

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ plugins {
2929
id("org.jetbrains.kotlin.android")
3030
{% endif %}
3131

32+
{% if USE_JACOCO %}
33+
jacoco
34+
{% endif %}
35+
3236
id("de.mannodermaus.android-junit5")
3337
}
3438

@@ -116,11 +120,20 @@ android {
116120
}
117121
}
118122

119-
{% if INCLUDE_ANDROID_RESOURCES %}
120-
junitPlatform {
123+
junitPlatform {
124+
{% if INCLUDE_ANDROID_RESOURCES %}
121125
instrumentationTests.integrityCheckEnabled = false
122-
}
123-
{% endif %}
126+
{% endif %}
127+
128+
{% if USE_JACOCO %}
129+
jacocoOptions {
130+
html {
131+
enabled = true
132+
destination = layout.buildDirectory.file("reports/jacoco/${name}.html").get().asFile
133+
}
134+
}
135+
{% endif %}
136+
}
124137

125138
{% for type in DISABLE_TESTS_FOR_BUILD_TYPES %}
126139
androidComponents {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[settings]
2+
useJacoco = true
3+
minAgpVersion = "7.4"
4+
task = "tasks"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<manifest package="de.mannodermaus.app"></manifest>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package de.mannodermaus.app;
2+
3+
public class Adder {
4+
public int add(int a, int b) {
5+
return a + b;
6+
}
7+
}

0 commit comments

Comments
 (0)