forked from BottleRocketStudios/Android-Maps-Demo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjacocoModule.gradle
183 lines (162 loc) · 8.72 KB
/
jacocoModule.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
apply plugin: 'jacoco'
/** Inspiration from:
*
* * https://proandroiddev.com/unified-code-coverage-for-android-revisited-44789c9b722f
*/
def verboseLogging = false
jacoco {
toolVersion = "0.8.7"
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*'] // https://stackoverflow.com/a/68139455/201939
}
/**
* # Setup:
* Apply to each module's build.gradle.kts
* Ex: apply(from = "jacocoModule.gradle")
*
* # To run jacoco reports:
*
* 1a. Execute the following gradle task in the Android Studio Gradle Tool Window:
* :app -> reporting -> testVARIANTUnitTestCoverage
* Ex: :app -> reporting -> testInternalDebugUnitTestCoverage
* OR 1b. Execute the following in Terminal from project root dir:
* ./gradlew testVARIANTUnitTestCoverage
* Ex: ./gradlew testInternalDebugUnitTestCoverage
*
*
* # To view jacoco reports:
*
* 1a. Upon successful completion of the testFLAVORUnitTestCoverage (ex: testInternalDebugUnitTestCoverage) task, open the report located in:
* Ex :app - PROJECT_ROOT_DIR/app/build/reports/jacoco/testInternalDebugUnitTestCoverage/html/index.html
* Ex :data - PROJECT_ROOT_DIR/data/build/reports/jacoco/testInternalDebugUnitTestCoverage/html/index.html
* OR 1b. Execute the `Open Jacoco Report` project shell script to open both reports in the default browser on your machine.
*
*
* # To run the verification to fail builds when the threshold is not met:
*
* 1a. Execute the following gradle task in the Android Studio Gradle Tool Window:
* :app -> reporting -> testFLAVORUnitTestCoverageVerification
* Ex: :app -> reporting -> testInternalDebugUnitTestCoverageVerification
* OR 1b. Execute the following in Terminal from project root dir:
* ./gradlew testFLAVORUnitTestCoverageVerification
* Ex: ./gradlew testInternalDebugUnitTestCoverageVerification
* 2a. On success, the task executes successfully with no output
* 2b. On failure to meet verification threshold, the task fails with an error message similar to:
* Execution failed for task ':data:testInternalDebugUnitTestCoverageVerification'.
* > Rule violated for bundle data: instructions covered ratio is 0.4, but expected minimum is 0.8
*/
project.afterEvaluate {
// Grab all build types and product flavors
def buildTypes = android.buildTypes.collect { type ->
type.name
}
def productFlavors = android.productFlavors.collect { flavor ->
flavor.name
}
def threshold = project.hasProperty('jacocoCoverageThreshold')
? project.jacocoCoverageThreshold
: project.jacocoCoverageThresholdDefault // value from jacocoConfig.gradle
if (verboseLogging) println("[jacocoSetup.gradle] threshold overrides default?=${project.hasProperty('jacocoCoverageThreshold')}, threshold=${threshold}")
// When no product flavors defined, use empty
if (!productFlavors) productFlavors.add('')
productFlavors.each { productFlavorName ->
buildTypes.each { buildTypeName ->
def sourceName, sourcePath, sourcePathKotlin
if (!productFlavorName) {
sourceName = sourcePath = sourcePathKotlin = "${buildTypeName}"
} else {
sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
sourcePath = "${productFlavorName}/${buildTypeName}"
sourcePathKotlin = "${productFlavorName}${buildTypeName.capitalize()}"
}
if (verboseLogging) {
println("[jacocoSetup.gradle] productFlavorName=${productFlavorName}")
println("[jacocoSetup.gradle] buildTypeName=${buildTypeName}")
println("[jacocoSetup.gradle] sourceName=${sourceName}")
println("[jacocoSetup.gradle] sourcePath=${sourcePath}")
println("[jacocoSetup.gradle] sourcePathKotlin=${sourcePathKotlin}")
}
def testTaskName = "test${sourceName.capitalize()}UnitTest"
def coverageReportTaskName = "${testTaskName}Coverage"
def coverageVerificationTaskName = "${testTaskName}CoverageVerification"
// //// CONFIGURATION START
// TODO: TEMPLATE - Update this exclusion list to include any project dependencies that auto-generate code (ex: Dagger, etc)
def excludesArray = [
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Application.*', // filter custom Application class
'**/*Activity.*', // filter Activities
'**/*BindingAdapter*.*', // filter all BindingAdapter classes
'**/*StartupInitializer*.*', // filter all StartupInitializer classes
'**/*BuildConfigProvider*.*', // filter BuildConfigProvider class
'**/*KeyboardUtils*.*', // filter KeyboardUtils class
'**/*DevOptionsViewModel*.*', // filter dev/qa only ViewModel
'**/*Module*.*', // filter koin app/data module classes
'**/*Args*.*', // filtering Navigation Component generated classes
'**/*Directions*.*', // filtering Navigation Component generated classes
'**/*$Lambda$*.*', // Jacoco can not handle several "$" in class name.
'**/*JsonAdapter.*', // Moshi auto-generated code
'**/*GlideRequest.*', // Glide auto-generated code
'**/*GlideRequests.*', // Glide auto-generated code
'**/*GlideOptions.*', // Glide auto-generated code
'**/*GlideApp.*', // Glide auto-generated code
'**/*GeneratedRequestManagerFactory.*', // Glide auto-generated code
'**/*GeneratedAppGlideModuleImpl.*', // Glide auto-generated code
'**/*$*$*.*' // Anonymous classes generated by kotlin
]
def debugTree = fileTree(
dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
excludes: excludesArray
)
def kotlinDebugTree = fileTree(
dir: "${project.buildDir}/tmp/kotlin-classes/${sourcePathKotlin}",
excludes: excludesArray
)
def coverageSourceDirs = [
"src/main/java",
"src/$sourceName/java",
"src/$productFlavorName/java",
"src/$buildTypeName/java"
]
def executionDataFilePath = "${project.buildDir}/jacoco/${testTaskName}.exec"
// //// CONFIGURATION END
if (verboseLogging) println("[jacocoSetup.gradle] Creating jacoco tasks named '${coverageReportTaskName}' and '${coverageVerificationTaskName}'")
// Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
// See https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoReport.html
task "${coverageReportTaskName}"(type: JacocoReport, dependsOn: "$testTaskName") {
group = "Reporting"
description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."
reports {
xml.required.set(true)
html.required.set(true)
}
classDirectories.from = files([debugTree], [kotlinDebugTree])
additionalSourceDirs.from = files(coverageSourceDirs)
sourceDirectories.from = files(coverageSourceDirs)
executionData.from = files("${executionDataFilePath}")
}
// Create coverage verification task of form 'testFlavorTypeCoverageVerification' depending on 'testFlavorTypeUnitTest'
// See https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.html
task "${coverageVerificationTaskName}"(type: JacocoCoverageVerification, dependsOn: "$testTaskName") {
group = "Reporting"
description = "Verify Jacoco coverage thresholds are met on the ${sourceName.capitalize()} build, failing the build when not."
violationRules {
failOnViolation = true
rule {
limit {
minimum = threshold
}
}
}
classDirectories.from = files([debugTree], [kotlinDebugTree])
additionalSourceDirs.from = files(coverageSourceDirs)
sourceDirectories.from = files(coverageSourceDirs)
executionData.from = files("${executionDataFilePath}")
}
}
}
}