Skip to content

Commit fc4a125

Browse files
author
Marcel Schnelle
authored
Introduce jacoco-centric functional tests & safely access its members for Gradle 4+ (#144)
1 parent 1ae256b commit fc4a125

File tree

8 files changed

+146
-27
lines changed

8 files changed

+146
-27
lines changed

android-junit5-tests/build.gradle.kts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ configurations {
4848
description = "Local dependencies used for compiling & running " +
4949
"tests source code in Gradle functional tests against AGP 3.4.X"
5050
}
51+
52+
create("functionalTestJacocoAgent") {
53+
description = "Local dependencies used for using Jacoco Agent during Gradle functional tests"
54+
}
55+
56+
create("functionalTestJacocoAnt") {
57+
description = "Local dependencies used for using Jacoco Ant during Gradle functional tests"
58+
}
5159
}
5260

5361
val processTestResources = tasks.getByName("processTestResources") as Copy
@@ -102,6 +110,12 @@ dependencies {
102110
functionalTest(Libs.junit_jupiter_api)
103111
functionalTest(Libs.junit_jupiter_engine)
104112

113+
val functionalTestJacocoAgent by configurations
114+
functionalTestJacocoAgent(Libs.org_jacoco_agent)
115+
116+
val functionalTestJacocoAnt by configurations
117+
functionalTestJacocoAnt(Libs.org_jacoco_ant)
118+
105119
val functionalTestAgp32X by configurations
106120
functionalTestAgp32X("com.android.tools.build:gradle:3.2.1")
107121

@@ -120,7 +134,13 @@ tasks.create("writePluginClasspath", WriteClasspathResource::class) {
120134
}
121135

122136
// Create a classpath-generating task for all functional test configurations
123-
listOf("functionalTest", "functionalTestAgp32X", "functionalTestAgp33X", "functionalTestAgp34X").forEach { config ->
137+
listOf(
138+
"functionalTest",
139+
"functionalTestJacocoAnt",
140+
"functionalTestJacocoAgent",
141+
"functionalTestAgp32X",
142+
"functionalTestAgp33X",
143+
"functionalTestAgp34X").forEach { config ->
124144
tasks.create("write${config.capitalize()}CompileClasspath", WriteClasspathResource::class) {
125145
inputFiles = configurations[config]
126146
outputDir = File("$buildDir/resources/test")

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import de.mannodermaus.gradle.plugins.junit5.util.FileLanguage2.Java
77
import de.mannodermaus.gradle.plugins.junit5.util.FileLanguage2.Kotlin
88
import org.gradle.testkit.runner.BuildResult
99
import org.gradle.testkit.runner.GradleRunner
10+
import org.gradle.testkit.runner.TaskOutcome
1011
import org.junit.jupiter.api.DynamicTest.dynamicTest
1112
import org.junit.jupiter.api.Test
1213
import org.junit.jupiter.api.TestFactory
@@ -52,6 +53,10 @@ class FunctionalTests {
5253

5354
// Classpath of the main "functionalTest" configuration
5455
private lateinit var functionalTestFiles: List<File>
56+
// Classpath of the main "functionalTestJacocoAnt" configuration
57+
private lateinit var functionalTestJacocoAntFiles: List<File>
58+
// Classpath of the main "functionalTestJacocoAgent" configuration
59+
private lateinit var functionalTestJacocoAgentFiles: List<File>
5560
// Classpath of the specialized "functionalTestAgpX" configuration
5661
private lateinit var functionalTestSpecialFiles: List<File>
5762

@@ -67,6 +72,10 @@ class FunctionalTests {
6772
this.pluginFiles = loadClassPathManifestResource("plugin-classpath.txt")
6873
this.functionalTestFiles = loadClassPathManifestResource(
6974
"functionalTest-compile-classpath.txt")
75+
this.functionalTestJacocoAntFiles = loadClassPathManifestResource(
76+
"functionalTestJacocoAnt-compile-classpath.txt")
77+
this.functionalTestJacocoAgentFiles = loadClassPathManifestResource(
78+
"functionalTestJacocoAgent-compile-classpath.txt")
7079
this.functionalTestSpecialFiles = loadClassPathManifestResource(
7180
"$configName-compile-classpath.txt")
7281

@@ -349,6 +358,33 @@ class FunctionalTests {
349358
}
350359
}
351360

361+
@TestFactory
362+
fun `Configures Jacoco correctly`(@TempDir testProjectDir: Path) =
363+
SUPPORTED_VARIATIONS
364+
.map { variation ->
365+
val (agpVersion, configName, gradleVersion) = variation
366+
dynamicTest("Using AGP $agpVersion & Gradle $gradleVersion tests") {
367+
val folder = testProjectDir.newFile("$gradleVersion-$agpVersion")
368+
setupNewProject(folder, configName)
369+
given {
370+
plugins {
371+
android()
372+
jacoco()
373+
junit5()
374+
}
375+
testSources(Java) {
376+
test()
377+
}
378+
}
379+
380+
runGradle(version = gradleVersion, tasks = "jacocoTestReportDebug") { result ->
381+
listOf(
382+
{ assertThat(result).executedTaskWithOutcome(":testDebugUnitTest", TaskOutcome.SUCCESS) }
383+
)
384+
}
385+
}
386+
}
387+
352388
/* Private */
353389

354390
private fun loadClassPathManifestResource(name: String): List<File> {
@@ -371,7 +407,7 @@ class FunctionalTests {
371407
val buildResult = GradleRunner.create()
372408
.withProjectDir(testProjectDir)
373409
.withPluginClasspath(pluginFiles)
374-
.withArguments(tasks)
410+
.withArguments(tasks, "--stacktrace")
375411
.apply {
376412
if (version != null) {
377413
withGradleVersion(version)
@@ -502,6 +538,17 @@ class FunctionalTests {
502538
}
503539
""")
504540
}
541+
542+
fun jacoco() {
543+
buildFile.appendText("""
544+
apply plugin: "jacoco"
545+
546+
dependencies {
547+
jacocoAnt files(${functionalTestJacocoAntFiles.splitClasspath()})
548+
jacocoAgent files(${functionalTestJacocoAgentFiles.splitClasspath()})
549+
}
550+
""".trimIndent())
551+
}
505552
}
506553

507554
inner class TestSources(private val language: FileLanguage2) {

android-junit5-tests/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestExtensions.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,13 @@ fun assertThat(buildResult: BuildResult) = AssertBuildResult(buildResult)
5656
class AssertBuildResult(buildResult: BuildResult) : AbstractAssert<AssertBuildResult, BuildResult>(
5757
buildResult, AssertBuildResult::class.java) {
5858
fun executedTaskSuccessfully(name: String) {
59+
executedTaskWithOutcome(name, TaskOutcome.SUCCESS)
60+
}
61+
62+
fun executedTaskWithOutcome(name: String, outcome: TaskOutcome) {
5963
val task = actual.task(name) ?: throw AssertionError("didn't execute task $name")
6064
org.assertj.core.api.Assertions.assertThat(task.outcome).isEqualTo(TaskOutcome.SUCCESS)
65+
org.assertj.core.api.Assertions.assertThat(task.outcome).isEqualTo(outcome)
6166
}
6267

6368
fun hasOutputContaining(substring: String, times: Int = 1) {

android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/GroovyInterop.groovy

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,52 @@ class GroovyInterop {
207207
report.sourceDirectories = project.files(paths)
208208
}
209209
}
210+
211+
/**
212+
* Obtains the execution data of a Jacoco task.
213+
*
214+
* @because Gradle 5 changed the return type of this field to expose the ConfigurableFileCollection directly
215+
* @param report Jacoco report task to operate on
216+
* @return The execution data of the task
217+
*/
218+
static ConfigurableFileCollection jacocoReportBase_getExecutionData(JacocoReportBase report) {
219+
def val = report.executionData
220+
if (val instanceof ConfigurableFileCollection) {
221+
return val
222+
} else {
223+
return (ConfigurableFileCollection) val
224+
}
225+
}
226+
227+
/**
228+
* Obtains the source directories of a Jacoco task.
229+
*
230+
* @because Gradle 5 changed the return type of this field to expose the ConfigurableFileCollection directly
231+
* @param report Jacoco report task to operate on
232+
* @return The source directories of the task
233+
*/
234+
static ConfigurableFileCollection jacocoReportBase_getSourceDirectories(JacocoReportBase report) {
235+
def val = report.sourceDirectories
236+
if (val instanceof ConfigurableFileCollection) {
237+
return val
238+
} else {
239+
return (ConfigurableFileCollection) val
240+
}
241+
}
242+
243+
/**
244+
* Obtains the class directories of a Jacoco task.
245+
*
246+
* @because Gradle 5 changed the return type of this field to expose the ConfigurableFileCollection directly
247+
* @param report Jacoco report task to operate on
248+
* @return The class directories of the task
249+
*/
250+
static ConfigurableFileCollection jacocoReportBase_getClassDirectories(JacocoReportBase report) {
251+
def val = report.classDirectories
252+
if (val instanceof ConfigurableFileCollection) {
253+
return val
254+
} else {
255+
return (ConfigurableFileCollection) val
256+
}
257+
}
210258
}

android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Interop.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ fun JacocoReportBase.safeSourceDirectoriesSetFrom(project: Project, vararg paths
4646
GroovyInterop.jacocoReportBase_sourceDirectories_setFrom(this, project, paths)
4747
}
4848

49+
val JacocoReportBase.safeGetExecutionData get() =
50+
GroovyInterop.jacocoReportBase_getExecutionData(this)
51+
52+
val JacocoReportBase.safeGetSourceDirectories get() =
53+
GroovyInterop.jacocoReportBase_getSourceDirectories(this)
54+
55+
val JacocoReportBase.safeGetClassDirectories get() =
56+
GroovyInterop.jacocoReportBase_getClassDirectories(this)
57+
4958
/* Types */
5059

5160
class VariantTypeCompat {

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

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import de.mannodermaus.gradle.plugins.junit5.providers.DirectoryProvider
1010
import de.mannodermaus.gradle.plugins.junit5.providers.mainClassDirectories
1111
import de.mannodermaus.gradle.plugins.junit5.providers.mainSourceDirectories
1212
import org.gradle.api.Project
13-
import org.gradle.api.file.ConfigurableFileCollection
1413
import org.gradle.api.file.FileCollection
1514
import org.gradle.api.tasks.testing.Test
1615
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
@@ -39,26 +38,6 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
3938
}
4039
}
4140

42-
/*
43-
* Gradle 5.0 changed the return type of these methods from FileCollection to ConfigurableFileCollection.
44-
* By explicitly re-declaring them here with the old return type, the binary incompatibility to Gradle 4.x is bridged.
45-
*/
46-
47-
@Suppress("RedundantOverride", "USELESS_CAST")
48-
override fun getExecutionData(): ConfigurableFileCollection? {
49-
return super.getExecutionData() as? ConfigurableFileCollection
50-
}
51-
52-
@Suppress("RedundantOverride", "USELESS_CAST")
53-
override fun getClassDirectories(): ConfigurableFileCollection? {
54-
return super.getClassDirectories() as? ConfigurableFileCollection
55-
}
56-
57-
@Suppress("RedundantOverride", "USELESS_CAST")
58-
override fun getSourceDirectories(): ConfigurableFileCollection? {
59-
return super.getSourceDirectories() as? ConfigurableFileCollection
60-
}
61-
6241
/**
6342
* Configuration closure for an Android JUnit5 Jacoco Report task.
6443
*/
@@ -109,9 +88,9 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
10988

11089
project.logger.junit5Info(
11190
"Assembled Jacoco Code Coverage for JUnit 5 Task '${testTask.name}':")
112-
project.logger.junit5Info("|__ Execution Data: ${reportTask.executionData?.asPath}")
113-
project.logger.junit5Info("|__ Source Dirs: ${reportTask.sourceDirectories?.asPath}")
114-
project.logger.junit5Info("|__ Class Dirs: ${reportTask.classDirectories?.asPath}")
91+
project.logger.junit5Info("|__ Execution Data: ${reportTask.safeGetExecutionData?.asPath}")
92+
project.logger.junit5Info("|__ Source Dirs: ${reportTask.safeGetSourceDirectories?.asPath}")
93+
project.logger.junit5Info("|__ Class Dirs: ${reportTask.safeGetClassDirectories?.asPath}")
11594

11695
// Hook into the main Jacoco task
11796
val defaultJacocoTask = project.tasks.maybeCreate(

buildSrc/src/main/kotlin/Libs.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ object Libs {
7070

7171
const val android_junit5: String = "de.mannodermaus.gradle.plugins:android-junit5:" +
7272
Versions.android_junit5
73+
/**
74+
* https://mvnrepository.com/artifact/org.jacoco/org.jacoco.agent */
75+
const val org_jacoco_agent: String = "org.jacoco:org.jacoco.agent:" + Versions.org_jacoco_agent
76+
77+
/**
78+
* https://mvnrepository.com/artifact/org.jacoco/org.jacoco.ant */
79+
const val org_jacoco_ant: String = "org.jacoco:org.jacoco.ant:" + Versions.org_jacoco_ant
80+
7381

7482
const val android_instrumentation_test_runner: String =
7583
"de.mannodermaus.junit5:android-instrumentation-test-runner:" +

buildSrc/src/main/kotlin/Versions.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ object Versions {
4747

4848
const val org_jetbrains_kotlin: String = "1.3.11"
4949

50-
const val org_jetbrains_spek: String = "1.2.1"
50+
const val org_jetbrains_spek: String = "1.2.1"
51+
52+
const val org_jacoco_agent: String = "0.8.2"
53+
const val org_jacoco_ant: String = "0.8.2"
5154

5255
const val junit_pioneer: String = "0.2.2" // available: "0.3.0"
5356

0 commit comments

Comments
 (0)