Skip to content

Commit 7633d64

Browse files
authored
Improved configuration options for test execution and the coverage report (#15)
- Deprecated `testTypes` and `skipTestExecution` in the plugin configuration. - Added `executeTests`, `executeAndroidTests` and `executeUnitTests` to the plugin configuration options, these provide better control compared to the old and now deprecated `skipTestExecution`. These new options are useful when running tests remotely. - Added `includeAndroidTestResults` and `includeUnitTestResults` plugin configuration options. These options control whether the final report takes into the account the results of certain tests. If disabled these options will also implicitly disable executing the tests (and thus override `execute[Android|Unit]Tests`). - Rewrote the tests that are executed by the integration test, also gave the tests better names. - Update Jacoco version used by the integration test to 8.5.
1 parent b5f230a commit 7633d64

File tree

13 files changed

+326
-126
lines changed

13 files changed

+326
-126
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ local.properties
1313
*.exec
1414
repo/
1515
build/
16+
*/out/*/classes/*

plugin/src/main/kotlin/org/neotech/plugin/rootcoverage/RootCoveragePlugin.kt

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.android.build.gradle.AppExtension
44
import com.android.build.gradle.LibraryExtension
55
import com.android.build.gradle.api.BaseVariant
66
import com.android.build.gradle.api.SourceKind
7-
import com.android.builder.model.TestVariantBuildOutput
87
import org.gradle.api.DomainObjectSet
98
import org.gradle.api.GradleException
109
import org.gradle.api.Plugin
@@ -18,54 +17,55 @@ class RootCoveragePlugin : Plugin<Project> {
1817

1918
override fun apply(project: Project) {
2019
if (project.rootProject !== project) {
21-
throw GradleException("The RootCoveragePlugin can not be applied to project '${project.name}' because it is not the root project. Build file: ${project.buildFile}")
20+
throw GradleException("The RootCoveragePlugin cannot be applied to project '${project.name}' because it is not the root project. Build file: ${project.buildFile}")
2221
}
2322
rootProjectExtension = project.extensions.create("rootCoverage", RootCoveragePluginExtension::class.java)
2423

2524
if (project.plugins.withType(JacocoPlugin::class.java).isEmpty()) {
26-
project.logger.warn("Warning: Jacoco plugin was not found for project: '${project.name}', it has been applied automatically but you should do this manually. Build file: ${project.buildFile}")
25+
project.logger.warn("Warning: Jacoco plugin was not found for project: '${project.name}', it has been applied automatically, but you should do this manually. Build file: ${project.buildFile}")
2726
project.plugins.apply(JacocoPlugin::class.java)
2827
}
2928

3029
project.afterEvaluate { createCoverageTaskForRoot(it) }
3130
}
3231

3332
private fun getFileFilterPatterns(): List<String> = listOf(
34-
"**/AutoValue_*.*", // Filter to remove generated files from: https://github.com/google/auto
35-
//"**/*JavascriptBridge.class",
36-
37-
// Android Databinding
38-
"**/*databinding",
39-
"**/*binders",
40-
"**/*layouts",
41-
"**/BR.class", // Filter to remove generated databinding files
42-
43-
// Core Android generated class filters
44-
"**/R.class",
45-
"**/R$*.class",
46-
"**/Manifest*.*",
47-
"**/BuildConfig.class",
48-
"android/**/*.*",
49-
50-
"**/*\$ViewBinder*.*",
51-
"**/*\$ViewInjector*.*",
52-
"**/Lambda$*.class",
53-
"**/Lambda.class",
54-
"**/*Lambda.class",
55-
"**/*Lambda*.class",
56-
"**/*\$InjectAdapter.class",
57-
"**/*\$ModuleAdapter.class",
58-
"**/*\$ViewInjector*.class") + rootProjectExtension.excludes
33+
"**/AutoValue_*.*", // Filter to remove generated files from: https://github.com/google/auto
34+
//"**/*JavascriptBridge.class",
35+
36+
// Android Databinding
37+
"**/*databinding",
38+
"**/*binders",
39+
"**/*layouts",
40+
"**/BR.class", // Filter to remove generated databinding files
41+
42+
// Core Android generated class filters
43+
"**/R.class",
44+
"**/R$*.class",
45+
"**/Manifest*.*",
46+
"**/BuildConfig.class",
47+
"android/**/*.*",
48+
49+
"**/*\$ViewBinder*.*",
50+
"**/*\$ViewInjector*.*",
51+
"**/Lambda$*.class",
52+
"**/Lambda.class",
53+
"**/*Lambda.class",
54+
"**/*Lambda*.class",
55+
"**/*\$InjectAdapter.class",
56+
"**/*\$ModuleAdapter.class",
57+
"**/*\$ViewInjector*.class"
58+
) + rootProjectExtension.excludes
5959

6060
private fun getBuildVariantFor(project: Project): String =
61-
rootProjectExtension.buildVariantOverrides[project.path] ?: rootProjectExtension.buildVariant
61+
rootProjectExtension.buildVariantOverrides[project.path] ?: rootProjectExtension.buildVariant
6262

6363
private fun getExecutionDataFilePatterns(): List<String> {
6464
val list = mutableListOf<String>()
65-
if (rootProjectExtension.testTypes.contains(TestVariantBuildOutput.TestType.UNIT)) {
65+
if (rootProjectExtension.includeUnitTestResults()) {
6666
list.add("jacoco/test*UnitTest.exec")
6767
}
68-
if (rootProjectExtension.testTypes.contains(TestVariantBuildOutput.TestType.ANDROID_TEST)) {
68+
if (rootProjectExtension.includeAndroidTestResults()) {
6969
// Android Build Tools Plugin 3.2
7070
list.add("outputs/code-coverage/connected/*coverage.ec")
7171

@@ -141,9 +141,6 @@ class RootCoveragePlugin : Plugin<Project> {
141141
val buildVariant = getBuildVariantFor(subProject)
142142
when (extension) {
143143
is LibraryExtension -> {
144-
145-
//assertVariantExists(extension.libraryVariants, buildVariant, subProject)
146-
147144
extension.libraryVariants.all { variant ->
148145

149146
if (variant.buildType.isTestCoverageEnabled && variant.name.capitalize() == buildVariant.capitalize()) {
@@ -157,9 +154,6 @@ class RootCoveragePlugin : Plugin<Project> {
157154
}
158155
}
159156
is AppExtension -> {
160-
161-
//assertVariantExists(extension.libraryVariants, buildVariant, subProject)
162-
163157
extension.applicationVariants.all { variant ->
164158
if (variant.buildType.isTestCoverageEnabled && variant.name.capitalize() == buildVariant.capitalize()) {
165159
if (subProject.plugins.withType(JacocoPlugin::class.java).isEmpty()) {
@@ -175,22 +169,18 @@ class RootCoveragePlugin : Plugin<Project> {
175169
}
176170

177171
private fun createTask(project: Project, variant: BaseVariant): RootCoverageModuleTask {
178-
//println("Create code coverage report for variant ${project.name} ${variant.name}")
179-
180172
val name = variant.name.capitalize()
181173

182174
val codeCoverageReportTask = project.tasks.register("codeCoverageReport$name", RootCoverageModuleTask::class.java)
183175
codeCoverageReportTask.configure { task ->
184176
task.group = null // null makes sure the group does not show in the gradle-view in Android Studio/Intellij
185177
task.description = "Generate unified Jacoco code codecoverage report"
186178

187-
if (!rootProjectExtension.skipTestExecution) {
188-
if (rootProjectExtension.testTypes.contains(TestVariantBuildOutput.TestType.UNIT)) {
189-
task.dependsOn("test${name}UnitTest")
190-
}
191-
if (rootProjectExtension.testTypes.contains(TestVariantBuildOutput.TestType.ANDROID_TEST)) {
192-
task.dependsOn("connected${name}AndroidTest")
193-
}
179+
if (rootProjectExtension.shouldExecuteUnitTests()) {
180+
task.dependsOn("test${name}UnitTest")
181+
}
182+
if (rootProjectExtension.shouldExecuteAndroidTests()) {
183+
task.dependsOn("connected${name}AndroidTest")
194184
}
195185

196186
// Collect the class files based on the Java Compiler output
Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,123 @@
11
package org.neotech.plugin.rootcoverage
22

33
import com.android.builder.model.TestVariantBuildOutput
4+
import org.gradle.api.Project
5+
import org.slf4j.LoggerFactory
46

57
open class RootCoveragePluginExtension {
68

79
var generateCsv: Boolean = false
810
var generateHtml: Boolean = true
911
var generateXml: Boolean = false
12+
1013
var buildVariant: String = "debug"
1114
var buildVariantOverrides: Map<String, String> = mutableMapOf()
1215
var excludes: List<String> = mutableListOf()
13-
var skipTestExecution: Boolean = false
14-
var testTypes: List<TestVariantBuildOutput.TestType> = mutableListOf(TestVariantBuildOutput.TestType.ANDROID_TEST, TestVariantBuildOutput.TestType.UNIT)
16+
17+
/**
18+
* Same as executeTests inverted.
19+
*
20+
* @see executeTests
21+
*/
22+
@Deprecated("Please use `executeTests` instead.")
23+
var skipTestExecution: Boolean
24+
set(value) {
25+
executeTests = !value
26+
}
27+
get() = !executeTests
28+
29+
/**
30+
* Same as executeTests except that this only disables/enables the instrumented Android tests.
31+
*
32+
* Default: true
33+
*
34+
* @see executeTests
35+
*/
36+
var executeAndroidTests: Boolean = true
37+
38+
/**
39+
* Same as executeTests except that this only disables/enables the unit tests.
40+
*
41+
* Default: true
42+
*
43+
* @see executeTests
44+
*/
45+
var executeUnitTests: Boolean = true
46+
47+
/**
48+
* When disabled the Android-Root-Coverage-Plugin will skip the execution of all tests (unit and instrumented Android tests). This can
49+
* be useful when you run the tests manually or remote (Firebase Test Lab). When using this setting make sure you fetch the
50+
* `build/outputs` and `build/jacoco/` folders the remote (or any other place) and put them into the local build so that this plugin can
51+
* use them.
52+
*
53+
* Default: true
54+
*
55+
* Note: if false this will override any value in `executeAndroidTests` and `executeUnitTests`.
56+
*
57+
* @see executeAndroidTests
58+
* @see executeUnitTests
59+
*/
60+
var executeTests: Boolean = true
61+
62+
/**
63+
* Whether to include results from instrumented Android tests into the final coverage report. If disabled this also causes the plugin to
64+
* not automatically execute instrumented Android tests (if not already disabled by either `executeTests` or `executeAndroidTests`).
65+
*
66+
* Default: true
67+
*
68+
* @see includeUnitTestResults
69+
*/
70+
var includeAndroidTestResults = true
71+
72+
/**
73+
* Whether to include results from unit tests into the final coverage report. If disabled this also causes the plugin to not
74+
* automatically execute unit tests (if not already disabled by either `executeTests` or `executeUnitTests`).
75+
*
76+
* Default: true
77+
*
78+
* @see includeAndroidTestResults
79+
*/
80+
var includeUnitTestResults = true
81+
82+
@Deprecated("This setting has been replaced with `includeUnitTestResults` and `includeAndroidTestResults`.")
83+
var testTypes: List<TestVariantBuildOutput.TestType> =
84+
mutableListOf(TestVariantBuildOutput.TestType.ANDROID_TEST, TestVariantBuildOutput.TestType.UNIT)
85+
86+
@Suppress("DEPRECATION")
87+
internal fun shouldExecuteAndroidTests() = executeTests && executeAndroidTests && includeAndroidTestResults()
88+
89+
@Suppress("DEPRECATION")
90+
internal fun shouldExecuteUnitTests() = executeTests && executeUnitTests && includeUnitTestResults()
91+
92+
@Suppress("DEPRECATION")
93+
internal fun includeAndroidTestResults(): Boolean {
94+
return if (!testTypes.contains(TestVariantBuildOutput.TestType.ANDROID_TEST)) {
95+
if (includeAndroidTestResults) {
96+
LoggerFactory.getLogger(Project::class.java).warn(
97+
"Warning: Inconsistent settings, `testTypes` does not include TestType.ANDROID_TEST but " +
98+
"`includeAndroidTestResults` is true. The setting `includeAndroidTestResults` will be ignored, to fix this " +
99+
"issue please do not use the deprecated `testTypes` setting!"
100+
)
101+
}
102+
false
103+
} else {
104+
includeAndroidTestResults
105+
}
106+
}
107+
108+
@Suppress("DEPRECATION")
109+
internal fun includeUnitTestResults(): Boolean {
110+
return if (!testTypes.contains(TestVariantBuildOutput.TestType.UNIT)) {
111+
if (includeUnitTestResults) {
112+
LoggerFactory.getLogger(Project::class.java).warn(
113+
"Warning: Inconsistent settings, `testTypes` does not include TestType.UNIT but " +
114+
"`includeUnitTestResults` is true. The setting `includeUnitTestResults` will be ignored, to fix this issue " +
115+
"please do not use the deprecated `testTypes` setting!"
116+
)
117+
}
118+
false
119+
} else {
120+
includeUnitTestResults
121+
}
122+
}
15123
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package org.neotech.plugin.rootcoverage
2+
3+
import com.android.builder.model.TestVariantBuildOutput
4+
import org.junit.Test
5+
import kotlin.test.assertEquals
6+
7+
class RootCoveragePluginExtensionTest {
8+
9+
@Test
10+
fun `default setting`() {
11+
val config = RootCoveragePluginExtension()
12+
assertEquals(true, config.includeUnitTestResults())
13+
assertEquals(true, config.includeAndroidTestResults())
14+
assertEquals(true, config.shouldExecuteUnitTests())
15+
assertEquals(true, config.shouldExecuteAndroidTests())
16+
}
17+
18+
@Test
19+
fun `non default testTypes overrules include(Unit|Android)TestResults`() {
20+
// testTypes overrules includeAndroidTestResults & includeUnitTestResults (when testTypes is not default)
21+
val config = RootCoveragePluginExtension().apply {
22+
includeAndroidTestResults = true
23+
includeUnitTestResults = true
24+
testTypes = listOf()
25+
}
26+
assertEquals(false, config.includeUnitTestResults())
27+
assertEquals(false, config.includeAndroidTestResults())
28+
}
29+
30+
@Test
31+
fun `default testTypes does not overrule include(Unit|Android)TestResults`() {
32+
// when testTypes is default includeAndroidTestResults & includeUnitTestResults overrule testTypes
33+
val config = RootCoveragePluginExtension().apply {
34+
includeAndroidTestResults = false
35+
includeUnitTestResults = false
36+
testTypes = listOf(TestVariantBuildOutput.TestType.UNIT, TestVariantBuildOutput.TestType.ANDROID_TEST)
37+
}
38+
assertEquals(false, config.includeUnitTestResults())
39+
assertEquals(false, config.includeAndroidTestResults())
40+
}
41+
42+
@Test
43+
fun `shouldExecute(Unit|Android)Tests() returns false when include(Unit|Android)TestResults() returns false`() {
44+
// When test results are not included into the final report (`include*TestResults`), running the tests does not make sense, therefor
45+
// make sure `skip*TestExecution` returns false when this is the case.
46+
val config = RootCoveragePluginExtension().apply {
47+
includeAndroidTestResults = false
48+
includeUnitTestResults = false
49+
testTypes = listOf(TestVariantBuildOutput.TestType.UNIT, TestVariantBuildOutput.TestType.ANDROID_TEST)
50+
}
51+
assertEquals(false, config.includeUnitTestResults())
52+
assertEquals(false, config.includeAndroidTestResults())
53+
54+
assertEquals(false, config.shouldExecuteUnitTests())
55+
assertEquals(false, config.shouldExecuteAndroidTests())
56+
}
57+
58+
@Test
59+
fun `executeTests=false overrules execute(Unit|Android)Tests`() {
60+
val config = RootCoveragePluginExtension().apply {
61+
executeTests = false
62+
}
63+
64+
config.apply {
65+
executeAndroidTests = true
66+
executeUnitTests = true
67+
}
68+
assertEquals(false, config.shouldExecuteUnitTests())
69+
assertEquals(false, config.shouldExecuteAndroidTests())
70+
71+
config.apply {
72+
executeAndroidTests = false
73+
executeUnitTests = false
74+
}
75+
assertEquals(false, config.shouldExecuteUnitTests())
76+
assertEquals(false, config.shouldExecuteAndroidTests())
77+
}
78+
79+
@Test
80+
fun `executeTests=true does not overrule execute(Unit|AndroidInstrumented)Tests`() {
81+
val config = RootCoveragePluginExtension().apply {
82+
executeTests = true
83+
}
84+
85+
config.apply {
86+
executeAndroidTests = false
87+
executeUnitTests = false
88+
}
89+
assertEquals(false, config.shouldExecuteUnitTests())
90+
assertEquals(false, config.shouldExecuteAndroidTests())
91+
92+
config.apply {
93+
executeAndroidTests = true
94+
executeUnitTests = true
95+
}
96+
assertEquals(true, config.shouldExecuteUnitTests())
97+
assertEquals(true, config.shouldExecuteAndroidTests())
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.neotech.app.multimoduleapplication;
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4;
4+
5+
import org.junit.Test;
6+
import org.junit.runner.RunWith;
7+
import org.neotech.library.android.LibraryAndroidJava;
8+
import org.neotech.library.android.LibraryAndroidKotlin;
9+
10+
@RunWith(AndroidJUnit4.class)
11+
public class TouchLibraryCodeFromJavaAndroidTest {
12+
13+
@Test
14+
public void touchJavaCode() {
15+
LibraryAndroidJava.getInstance().touchedByAndroidTestInConsumer();
16+
}
17+
18+
@Test
19+
public void touchKotlinCode() {
20+
LibraryAndroidKotlin.touchedByAndroidTestInConsumer();
21+
}
22+
}

0 commit comments

Comments
 (0)