[#464] Use Kover for multiplatform coverage

This commit is contained in:
Carter Jernigan 2022-06-02 13:00:32 -04:00 committed by Carter Jernigan
parent e6a9889976
commit 1ce826e453
13 changed files with 43 additions and 52 deletions

View File

@ -5,7 +5,7 @@ This code review checklist is intended to serve as a starting point for the auth
- [ ] Self-review: Did you review your own code in GitHub's web interface? _Code often looks different when reviewing the diff in a browser, making it easier to spot potential bugs._
- [ ] Automated tests: Did you add appropriate automated tests for any code changes?
- [ ] Manual tests: Did you update the [manual tests](../blob/main/docs/testing/manual_testing) as appropriate? _While we aim for automated testing of the application, some aspects require manual testing. If you had to manually test something during development of this pull request, write those steps down._
- [ ] Code coverage: Did you check the code coverage report for the automated tests? _While we are not looking for perfect coverage, the tool can point out potential cases that have been missed._ Code Coverage can be run with: `./gradlew connectedCheck -PIS_COVERAGE_ENABLED=true`
- [ ] Code coverage: Did you check the code coverage report for the automated tests? _While we are not looking for perfect coverage, the tool can point out potential cases that have been missed._ Code coverage can be generated with: `./gradlew check` for Kotlin modules and `./gradlew connectedCheck -PIS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED=true` for Android modules.
- [ ] Documentation: Did you update documentation as appropriate? (e.g [README.md](../blob/main/README.md), etc.)
- [ ] Run the app: Did you run the app and try the changes?
- [ ] Screenshots: Did you provide before and after UI screenshots in the description of this pull request? _This is only applicable for changes that modify the UI._
@ -18,6 +18,6 @@ This code review checklist is intended to serve as a starting point for the auth
- [ ] Ad hoc review: Did you perform an ad hoc review? _In addition to a first pass using the code review guidelines, do a second pass using your best judgement and experience which may identify additional questions or comments. Research shows that code review is most effective when done in multiple passes, where reviewers look for different things through each pass._
- [ ] Automated tests: Did you review the automated tests?
- [ ] Manual tests: Did you review the manual tests?
- [ ] How is Code Coverage affected by this PR? _We encourage you to compare coverage before and after changes and when possible, leave it in a better place._
- [ ] How is code coverage affected? _We encourage you to compare coverage before and after changes and when possible, leaving it in a better place._
- [ ] Documentation: Did you review Docs, [README.md](../blob/main/README.md), and [Architecture.md](../blob/main/docs/Architecture.md) as appropriate?
- [ ] Run the app: Did you run the app and try the changes? _While the CI server runs the app to look for build failures or crashes, humans running the app are more likely to notice unexpected log messages, UI inconsistencies, or bad output data. Perform this step last, after verifying the code changes are safe to run locally._

View File

@ -35,7 +35,7 @@ If you plan to fork the project to create a new app of your own, please make the
# Known Issues
1. During builds, a warning will be printed that says "Unable to detect AGP versions for included builds. All projects in the build should use the same AGP version." This can be safely ignored. The version under build-conventions is the same as the version used elsewhere in the application.
1. When the code coverage Gradle property `IS_COVERAGE_ENABLED` is enabled, the debug app APK cannot be run. The coverage flag should therefore only be set when running automated tests.
1. When the code coverage Gradle property `IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED` is enabled, the debug app APK cannot be run. The coverage flag should therefore only be set when running automated tests.
1. Test coverage for Compose code will be low, due to [known limitations](https://github.com/jacoco/jacoco/issues/1208) in the interaction between Compose and Jacoco.
1. Adding the `espresso-contrib` dependency will cause builds to fail, due to conflicting classes. This is a [known issue](https://github.com/zcash/zcash-android-wallet-sdk/issues/306) with the Zcash Android SDK.
1. Android Studio will warn about the Gradle checksum. This is a [known issue](https://github.com/gradle/gradle/issues/9361) and can be safely ignored.

View File

@ -66,7 +66,8 @@ fun com.android.build.gradle.BaseExtension.configureBaseExtension() {
buildTypes {
getByName("debug").apply {
isTestCoverageEnabled = project.property("IS_COVERAGE_ENABLED").toString().toBoolean()
isTestCoverageEnabled =
project.property("IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED").toString().toBoolean()
}
}

View File

@ -1,26 +0,0 @@
if (project.property("IS_COVERAGE_ENABLED").toString().toBoolean()) {
apply(plugin = "java-library")
apply(plugin = "jacoco")
configure<JacocoPluginExtension> {
toolVersion = project.property("JACOCO_VERSION").toString()
}
afterEvaluate {
tasks.withType<JacocoReport>().configureEach {
classDirectories.setFrom(
fileTree("${buildDir}/classes/kotlin/jvm/") {
exclude("**/*Test*.*", "**/*Fixture*.*")
}
)
sourceDirectories.setFrom(
// This could be better if it dynamically got the source directories, e.g. more along the lines of
// kotlin.sourceSets["commonMain"].kotlin.sourceDirectories,
// kotlin.sourceSets["jvmMain"].kotlin.sourceDirectories
listOf("src/commonMain/kotlin", "src/jvmMain/kotlin")
)
executionData.setFrom("${buildDir}/jacoco/jvmTest.exec")
}
}
}

View File

@ -9,6 +9,7 @@ plugins {
id("com.github.ben-manes.versions")
id("com.osacky.fulladle")
id("io.gitlab.arturbosch.detekt")
id("org.jetbrains.kotlinx.kover")
id("zcash.ktlint-conventions")
}
@ -105,6 +106,25 @@ fladle {
))
}
kover {
isDisabled = !project.property("IS_KOTLIN_TEST_COVERAGE_ENABLED").toString().toBoolean()
// Don't run on the Android projects, as they have coverage generated in a different way
// through Android's instrumented tests
disabledProjects = setOf(
"app",
"crash-android-lib",
"preference-impl-android-lib",
"sdk-ext-lib",
"sdk-ext-ui-lib",
"spackle-android-lib",
"test-lib",
"ui-design-lib",
"ui-integration-test-lib",
"ui-lib",
)
}
// All of this should be refactored to build-conventions
subprojects {
pluginManager.withPlugin("com.android.library") {

View File

@ -156,12 +156,14 @@ org.jetbrains.kotlin:kotlin-stdlib:1.5.31=classpath
org.jetbrains.kotlin:kotlin-tooling-metadata:1.6.21=classpath
org.jetbrains.kotlin:kotlin-util-io:1.6.21=classpath
org.jetbrains.kotlin:kotlin-util-klib:1.6.21=classpath
org.jetbrains.kotlinx.kover:org.jetbrains.kotlinx.kover.gradle.plugin:0.5.0=classpath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=classpath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1=classpath
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.1.0=classpath
org.jetbrains.kotlinx:kotlinx-serialization-core:1.1.0=classpath
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.1.0=classpath
org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0=classpath
org.jetbrains.kotlinx:kover:0.5.0=classpath
org.jetbrains:annotations:13.0=classpath
org.jetbrains:markdown-jvm:0.2.1=classpath
org.jetbrains:markdown:0.2.1=classpath

View File

@ -1,7 +1,6 @@
plugins {
kotlin("multiplatform")
id("zcash.kotlin-multiplatform-build-conventions")
id("zcash.kotlin-multiplatform-jacoco-conventions")
id("zcash.dependency-conventions")
id("zcash.android-build-conventions")
}

View File

@ -14,7 +14,7 @@ While this repository is for an Android application, efforts are made to give mu
* In UI state management code, Kotlin Flow is often preferred over Android LiveData and Compose State to grant future flexibility
* Saver is preferred over @Parcelize for objects in the SDK
Note: test coverage for multiplatform modules behaves differently than coverage for Android modules. Coverage is only generated for a JVM target, and requires running two tasks in sequence: `./gradlew check -PIS_COVERAGE_ENABLED=true; ./gradlew jacocoTestReport -PIS_COVERAGE_ENABLED=true`
Note: Test coverage for multiplatform modules behaves differently than coverage for Android modules. Coverage is enabled by default for the JVM target when running `./gradlew check`.
# App
The main entrypoints of the application are:

View File

@ -2,13 +2,9 @@
The app consists of different Gradle module types (e.g. Kotlin Multiplatform, Android). Generating coverage for these different module types requires different command line invocations.
## Kotlin Multiplatform
Kotlin Multiplatform does not support coverage for all platforms. Most of our code lives under commonMain, with a JVM target. This effectively allows generation of coverage reports with Jacoco.
Due to some quirks with the Jacoco integration, coverage must be generated in two Gradle invocations like this:
`./gradlew test -x connectedCheck -PIS_COVERAGE_ENABLED=true; ./gradlew jacocoTestReport -PIS_COVERAGE_ENABLED=true`
Kotlin Multiplatform does not support coverage for all platforms. Most of our code lives under commonMain, with a JVM target. This effectively allows generation of coverage reports with Jacoco. Coverage is enabled by default when running `./gradlew check`.
## Android
The Android Gradle plugin supports code coverage with Jacoco. This integration can sometimes be buggy. For that reason, coverage is disabled by default and can be enabled on a case-by-case basis, by passing `-PIS_COVERAGE_ENABLED=true` as a command line argument for Gradle builds. For example: `./gradlew :app:connectedCheck -PIS_COVERAGE_ENABLED=true`.
The Android Gradle plugin supports code coverage with Jacoco. This integration can sometimes be buggy. For that reason, coverage is disabled by default and can be enabled on a case-by-case basis, by passing `-PIS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED=true` as a command line argument for Gradle builds. For example: `./gradlew connectedCheck -PIS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED=true`.
When coverage is enabled, running instrumentation tests will automatically generate coverage reports stored under `build/reports/coverage`.
When coverage is enabled, running instrumentation tests will automatically generate coverage reports stored under `$module/build/reports/coverage`.

View File

@ -15,8 +15,12 @@ android.builder.sdkDownload=true
# Kotlin compiler warnings can be considered errors, failing the build.
ZCASH_IS_TREAT_WARNINGS_AS_ERRORS=true
# Optionally configure coverage for Kotlin modules (e.g. with Kover)
IS_KOTLIN_TEST_COVERAGE_ENABLED=true
# Optionally configure Android instumentation test coverage.
# The app module will crash at launch when coverage is enabled, so coverage is only enabled explicitly for tests.
IS_COVERAGE_ENABLED=false
IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED=false
# Optionally configure test orchestrator.
# It is disabled by default, because it causes tests to take about 2x longer to run.
@ -107,6 +111,7 @@ JACOCO_VERSION=0.8.8
KOTLIN_VERSION=1.6.21
KOTLINX_COROUTINES_VERSION=1.6.2
KOTLINX_DATETIME_VERSION=0.3.2
KOVER_VERSION=0.5.0
PLAY_CORE_VERSION=1.10.3
PLAY_CORE_KTX_VERSION=1.8.1
ZCASH_ANDROID_WALLET_PLUGINS_VERSION=1.0.0

View File

@ -1,7 +1,6 @@
plugins {
kotlin("multiplatform")
id("zcash.kotlin-multiplatform-build-conventions")
id("zcash.kotlin-multiplatform-jacoco-conventions")
id("zcash.dependency-conventions")
}

View File

@ -39,20 +39,16 @@ pluginManagement {
plugins {
val androidGradlePluginVersion = extra["ANDROID_GRADLE_PLUGIN_VERSION"].toString()
val emulatorWtfGradlePluginVersion = extra["EMULATOR_WTF_GRADLE_PLUGIN_VERSION"].toString()
val detektVersion = extra["DETEKT_VERSION"].toString()
val fulladleVersion = extra["FULLADLE_VERSION"].toString()
val gradleVersionsPluginVersion = extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()
val kotlinVersion = extra["KOTLIN_VERSION"].toString()
val playPublisherVersion = extra["PLAY_PUBLISHER_PLUGIN_VERSION"].toString()
id("com.android.application") version (androidGradlePluginVersion) apply (false)
id("com.android.library") version (androidGradlePluginVersion) apply (false)
id("com.github.ben-manes.versions") version (gradleVersionsPluginVersion) apply (false)
id("com.github.triplet.play") version (playPublisherVersion) apply (false)
id("com.osacky.fulladle") version (fulladleVersion) apply (false)
id("io.gitlab.arturbosch.detekt") version (detektVersion) apply (false)
id("wtf.emulator.gradle") version (emulatorWtfGradlePluginVersion) apply (false)
id("com.github.ben-manes.versions") version (extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()) apply (false)
id("com.github.triplet.play") version (extra["PLAY_PUBLISHER_PLUGIN_VERSION"].toString()) apply (false)
id("com.osacky.fulladle") version (extra["FULLADLE_VERSION"].toString()) apply (false)
id("io.gitlab.arturbosch.detekt") version (extra["DETEKT_VERSION"].toString()) apply (false)
id("org.jetbrains.kotlinx.kover") version (extra["KOVER_VERSION"].toString()) apply (false)
id("wtf.emulator.gradle") version (extra["EMULATOR_WTF_GRADLE_PLUGIN_VERSION"].toString()) apply (false)
kotlin("android") version (kotlinVersion) apply (false)
kotlin("jvm") version (kotlinVersion)
kotlin("multiplatform") version (kotlinVersion)

View File

@ -1,7 +1,6 @@
plugins {
kotlin("multiplatform")
id("zcash.kotlin-multiplatform-build-conventions")
id("zcash.kotlin-multiplatform-jacoco-conventions")
id("zcash.dependency-conventions")
}