diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ca127188..ab3f4aff 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -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._ \ No newline at end of file diff --git a/README.md b/README.md index 58c028d4..9da33602 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/build-convention/src/main/kotlin/zcash.android-build-conventions.gradle.kts b/build-convention/src/main/kotlin/zcash.android-build-conventions.gradle.kts index cb3928c9..eaa42d2f 100644 --- a/build-convention/src/main/kotlin/zcash.android-build-conventions.gradle.kts +++ b/build-convention/src/main/kotlin/zcash.android-build-conventions.gradle.kts @@ -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() } } diff --git a/build-convention/src/main/kotlin/zcash.kotlin-multiplatform-jacoco-conventions.gradle.kts b/build-convention/src/main/kotlin/zcash.kotlin-multiplatform-jacoco-conventions.gradle.kts deleted file mode 100644 index 76eb8702..00000000 --- a/build-convention/src/main/kotlin/zcash.kotlin-multiplatform-jacoco-conventions.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -if (project.property("IS_COVERAGE_ENABLED").toString().toBoolean()) { - apply(plugin = "java-library") - apply(plugin = "jacoco") - - configure { - toolVersion = project.property("JACOCO_VERSION").toString() - } - - afterEvaluate { - tasks.withType().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") - } - } -} diff --git a/build.gradle.kts b/build.gradle.kts index c7cc5a82..97a868ac 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") { diff --git a/buildscript-gradle.lockfile b/buildscript-gradle.lockfile index f45bcdbd..b5b7d062 100644 --- a/buildscript-gradle.lockfile +++ b/buildscript-gradle.lockfile @@ -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 diff --git a/crash-lib/build.gradle.kts b/crash-lib/build.gradle.kts index 4ef197f0..d337dac1 100644 --- a/crash-lib/build.gradle.kts +++ b/crash-lib/build.gradle.kts @@ -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") } diff --git a/docs/Architecture.md b/docs/Architecture.md index 65a4fa0c..97217e7e 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -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: diff --git a/docs/testing/local_coverage.md b/docs/testing/local_coverage.md index 53b59345..dd59163b 100644 --- a/docs/testing/local_coverage.md +++ b/docs/testing/local_coverage.md @@ -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`. \ No newline at end of file +When coverage is enabled, running instrumentation tests will automatically generate coverage reports stored under `$module/build/reports/coverage`. diff --git a/gradle.properties b/gradle.properties index 70ce4b47..36439c9a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/preference-api-lib/build.gradle.kts b/preference-api-lib/build.gradle.kts index 720e81f4..7e20dba2 100644 --- a/preference-api-lib/build.gradle.kts +++ b/preference-api-lib/build.gradle.kts @@ -1,7 +1,6 @@ plugins { kotlin("multiplatform") id("zcash.kotlin-multiplatform-build-conventions") - id("zcash.kotlin-multiplatform-jacoco-conventions") id("zcash.dependency-conventions") } diff --git a/settings.gradle.kts b/settings.gradle.kts index fa82b447..8bbf5575 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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) diff --git a/spackle-lib/build.gradle.kts b/spackle-lib/build.gradle.kts index 21ee1d3e..be071bda 100644 --- a/spackle-lib/build.gradle.kts +++ b/spackle-lib/build.gradle.kts @@ -1,7 +1,6 @@ plugins { kotlin("multiplatform") id("zcash.kotlin-multiplatform-build-conventions") - id("zcash.kotlin-multiplatform-jacoco-conventions") id("zcash.dependency-conventions") }