Skip to content

Commit 18244fe

Browse files
authored
Add Exclusion Rules for Jacoco Tasks (#44)
* Update to Gradle 4.4 * Use common exclusion rules for Jacoco & customizability * Added new DSL "junitPlatform.jacoco.excludedClasses" * Added new DSL "junitPlatform.jacoco.excludedSources" * Automatically add R.class to excluded classes
1 parent c896c48 commit 18244fe

File tree

8 files changed

+132
-24
lines changed

8 files changed

+132
-24
lines changed

android-junit5-tests/testAgp2x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP2FunctionalSpec.groovy

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ class AGP2FunctionalSpec extends BaseFunctionalSpec {
2121
def flavors = flavorNames.collect { "$it {}" }.join("\n")
2222

2323
return """
24-
productFlavors {
25-
$flavors
26-
}
27-
"""
24+
productFlavors {
25+
$flavors
26+
}
27+
"""
2828
}
2929
}

android-junit5-tests/testAgp3x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP3FunctionalSpec.groovy

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ class AGP3FunctionalSpec extends BaseFunctionalSpec {
2121
def flavors = flavorNames.collect { """$it { dimension "tier" }""" }.join("\n")
2222

2323
return """
24-
flavorDimensions "tier"
25-
productFlavors {
26-
$flavors
27-
}
28-
"""
24+
flavorDimensions "tier"
25+
productFlavors {
26+
$flavors
27+
}
28+
"""
2929
}
3030
}

android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,88 @@ abstract class BasePluginSpec extends Specification {
576576
assert !runRelease.classDirectories.asPath.contains("classes/test/")
577577
}
578578
579+
def "Jacoco Exclude Rules: Addition"() {
580+
when:
581+
Project project = factory.newProject(rootProject())
582+
.asAndroidApplication()
583+
.applyJunit5Plugin()
584+
.applyJacocoPlugin()
585+
.build()
586+
587+
// Create some fake class files to verify the Jacoco task's tree
588+
project.file("build/intermediates/classes/debug").mkdirs()
589+
project.file("build/intermediates/classes/debug/R.class").createNewFile()
590+
project.file("build/intermediates/classes/debug/FirstFile.class").createNewFile()
591+
project.file("build/intermediates/classes/debug/SecondFile.class").createNewFile()
592+
project.file("src/main/java").mkdirs()
593+
project.file("src/main/java/OkFile.java").createNewFile()
594+
project.file("src/main/java/AnnoyingFile.java").createNewFile()
595+
596+
project.junitPlatform {
597+
jacoco {
598+
// In addition to the default exclusion rules for R.class,
599+
// also exclude any class prefixed with "Second"
600+
excludedClasses += "Second*.class"
601+
excludedSources += "AnnoyingFile.java"
602+
}
603+
}
604+
605+
project.evaluate()
606+
607+
then:
608+
def jacocoTask = project.tasks.getByName("jacocoTestReportDebug") as AndroidJUnit5JacocoReport
609+
610+
def classFiles = jacocoTask.classDirectories.asFileTree.files
611+
classFiles.find { it.name == "R.class" } == null
612+
classFiles.find { it.name == "FirstFile.class" } != null
613+
classFiles.find { it.name == "SecondFile.class" } == null
614+
615+
def sourceFiles = jacocoTask.sourceDirectories.asFileTree.files
616+
sourceFiles.find { it.name == "OkFile.java" } != null
617+
sourceFiles.find { it.name == "AnnoyingFile.java" } == null
618+
}
619+
620+
def "Jacoco Exclude Rules: Replacement"() {
621+
when:
622+
Project project = factory.newProject(rootProject())
623+
.asAndroidApplication()
624+
.applyJunit5Plugin()
625+
.applyJacocoPlugin()
626+
.build()
627+
628+
// Create some fake class files to verify the Jacoco task's tree
629+
project.file("build/intermediates/classes/debug").mkdirs()
630+
project.file("build/intermediates/classes/debug/R.class").createNewFile()
631+
project.file("build/intermediates/classes/debug/FirstFile.class").createNewFile()
632+
project.file("build/intermediates/classes/debug/SecondFile.class").createNewFile()
633+
project.file("src/main/java").mkdirs()
634+
project.file("src/main/java/OkFile.java").createNewFile()
635+
project.file("src/main/java/AnnoyingFile.java").createNewFile()
636+
637+
project.junitPlatform {
638+
jacoco {
639+
// Replace the default exclusion rules
640+
// and only exclude any class prefixed with "Second"
641+
excludedClasses = ["Second*.class"]
642+
excludedSources = ["AnnoyingFile.java"]
643+
}
644+
}
645+
646+
project.evaluate()
647+
648+
then:
649+
def jacocoTask = project.tasks.getByName("jacocoTestReportDebug") as AndroidJUnit5JacocoReport
650+
651+
def files = jacocoTask.classDirectories.asFileTree.files
652+
files.find { it.name == "R.class" } != null
653+
files.find { it.name == "FirstFile.class" } != null
654+
files.find { it.name == "SecondFile.class" } == null
655+
656+
def sourceFiles = jacocoTask.sourceDirectories.asFileTree.files
657+
sourceFiles.find { it.name == "OkFile.java" } != null
658+
sourceFiles.find { it.name == "AnnoyingFile.java" } == null
659+
}
660+
579661
def "Library: Basic Integration"() {
580662
when:
581663
Project project = factory.newProject(rootProject())

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension {
206206
private final Report csv
207207
private final Report xml
208208

209+
@NonNull
210+
public List<String> excludedClasses = ["**/R.class", '**/R$*.class']
211+
212+
@NonNull
213+
public List<String> excludedSources = []
214+
209215
JacocoOptions(Project project) {
210216
this.project = project
211217
this.html = new Report()

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,19 @@ fun Project.withDependencies(defaults: Properties, config: (Versions) -> Any): A
206206
/* Types */
207207

208208
/**
209-
* Multi-language functional construct,
209+
* Multi-language functional construct with no parameters,
210210
* mapped to Groovy's dynamic Closures as well as Kotlin's invoke syntax.
211211
*
212212
* A [Callable0] can be invoked with the short-hand
213213
* function syntax from both Kotlin & Groovy:
214214
*
215215
* <code><pre>
216-
* val callable = Callable { 2 + 2 }
216+
* val callable = Callable0 { 2 + 2 }
217217
* val result = callable() // result == 4
218218
* </pre></code>
219219
*
220220
* <code><pre>
221-
* def callable = new Callable({ 2 + 2 })
221+
* def callable = new Callable0({ 2 + 2 })
222222
* def result = callable() // result == 4
223223
* </pre></code>
224224
*/
@@ -232,20 +232,20 @@ class Callable0<R>(private val body: () -> R) : Closure<R>(null) {
232232
}
233233

234234
/**
235-
* Multi-language functional construct,
235+
* Multi-language functional construct with 1 parameter,
236236
* mapped to Groovy's dynamic Closures as well as Kotlin's invoke syntax.
237237
*
238238
* A [Callable1] can be invoked with the short-hand
239239
* function syntax from both Kotlin & Groovy:
240240
*
241241
* <code><pre>
242-
* val callable = Callable { 2 + 2 }
243-
* val result = callable() // result == 4
242+
* val callable = Callable1 { 2 + it }
243+
* val result = callable(2) // result == 4
244244
* </pre></code>
245245
*
246246
* <code><pre>
247-
* def callable = new Callable({ 2 + 2 })
248-
* def result = callable() // result == 4
247+
* def callable = new Callable1({ input -> 2 + input })
248+
* def result = callable(2) // result == 4
249249
* </pre></code>
250250
*/
251251
@Suppress("unused")

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import de.mannodermaus.gradle.plugins.junit5.providers.DirectoryProvider
88
import de.mannodermaus.gradle.plugins.junit5.providers.mainClassDirectories
99
import de.mannodermaus.gradle.plugins.junit5.providers.mainSourceDirectories
1010
import org.gradle.api.Project
11+
import org.gradle.api.file.FileCollection
1112
import org.gradle.testing.jacoco.tasks.JacocoReport
13+
import java.io.File
1214

1315
private const val TASK_NAME_DEFAULT = "jacocoTestReport"
1416
private const val GROUP_REPORTING = "reporting"
@@ -51,12 +53,6 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
5153
reportTask.description = "Generates Jacoco coverage reports " +
5254
"for the ${variant.name.capitalize()} variant."
5355

54-
// Task-level Configuration
55-
val taskJacoco = testTask.jacoco
56-
reportTask.executionData = project.files(taskJacoco.destinationFile.path)
57-
reportTask.classDirectories = project.files(directoryProviders.mainClassDirectories())
58-
reportTask.sourceDirectories = project.files(directoryProviders.mainSourceDirectories())
59-
6056
// Apply JUnit 5 configuration parameters
6157
val junit5Jacoco = project.junit5.jacoco
6258
val allReports = listOf(
@@ -69,6 +65,17 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
6965
from.destination?.let { to.destination = it }
7066
}
7167

68+
// Task-level Configuration
69+
val taskJacoco = testTask.jacoco
70+
reportTask.executionData = project.files(taskJacoco.destinationFile.path)
71+
72+
// Apply exclusion rules to both class & source directories for Jacoco,
73+
// using the sum of all DirectoryProviders' outputs as a foundation:
74+
reportTask.classDirectories = directoryProviders.mainClassDirectories()
75+
.toFileCollectionExcluding(junit5Jacoco.excludedClasses)
76+
reportTask.sourceDirectories = directoryProviders.mainSourceDirectories()
77+
.toFileCollectionExcluding(junit5Jacoco.excludedSources)
78+
7279
project.logger.junit5Info(
7380
"Assembled Jacoco Code Coverage for JUnit 5 Task '${testTask.name}':")
7481
project.logger.junit5Info("|__ Execution Data: ${reportTask.executionData.asPath}")
@@ -81,5 +88,18 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
8188
group = GROUP_REPORTING)
8289
defaultJacocoTask.dependsOn(reportTask)
8390
}
91+
92+
/* Extension Functions */
93+
94+
/**
95+
* Joins the given collection of Files together, while
96+
* ignoring the provided patterns in the resulting FileCollection.
97+
*/
98+
private fun Iterable<File>.toFileCollectionExcluding(
99+
patterns: Iterable<String>): FileCollection = this
100+
// Convert each directory to a Gradle FileTree, excluding the specified patterns
101+
.map { project.fileTree(it).exclude(patterns) }
102+
// Convert the resulting list of FileTree objects into a single FileCollection
103+
.run { project.files(this) }
84104
}
85105
}

gradle/wrapper/gradle-wrapper.jar

-5 Bytes
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
33
zipStoreBase=GRADLE_USER_HOME
44
zipStorePath=wrapper/dists
5-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-rc-4-bin.zip
5+
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip

0 commit comments

Comments
 (0)