Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(9)
lineCoverage.set(25)
branchCoverage = 0
lineCoverage = 0
}
4 changes: 2 additions & 2 deletions app-k9mail/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,6 @@ dependencyGuard {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(25)
branchCoverage = 0
lineCoverage = 24
}
4 changes: 2 additions & 2 deletions app-thunderbird/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,6 @@ tasks.register("printConfigurations") {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(25)
branchCoverage = 0
lineCoverage = 25
}
4 changes: 2 additions & 2 deletions app-ui-catalog/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(0)
branchCoverage = 0
lineCoverage = 0
}
4 changes: 2 additions & 2 deletions backend/api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(0)
branchCoverage = 0
lineCoverage = 0
}
4 changes: 2 additions & 2 deletions backend/demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ dependencies {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(0)
branchCoverage = 0
lineCoverage = 0
}

tasks.register<UpdateDemoMailbox>("updateDemoMailbox") {
Expand Down
4 changes: 2 additions & 2 deletions backend/imap/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(45)
lineCoverage.set(44)
branchCoverage = 45
lineCoverage = 42
}
4 changes: 2 additions & 2 deletions backend/jmap/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(33)
lineCoverage.set(42)
branchCoverage = 31
lineCoverage = 42
}
4 changes: 2 additions & 2 deletions backend/pop3/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(0)
branchCoverage = 0
lineCoverage = 0
}
4 changes: 2 additions & 2 deletions backend/testing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ dependencies {
}

codeCoverage {
branchCoverage.set(0)
lineCoverage.set(0)
branchCoverage = 0
lineCoverage = 0
}
4 changes: 2 additions & 2 deletions build-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ dependencies {
implementation(plugin(libs.plugins.spotless))
implementation(plugin(libs.plugins.kover))

// Make custom plugins in :plugins available to precompiled convention plugins by classpath
implementation(project(":plugins"))
// Make custom plugins in ":plugin" available to precompiled convention plugins by classpath
implementation(project(":plugin"))

implementation(libs.diff.utils)
compileOnly(libs.android.tools.common)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
org.gradle.parallel=true
kotlin.code.style=official
kotlin.incremental=true
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
75 changes: 75 additions & 0 deletions build-plugin/plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
`kotlin-dsl`
}

group = "net.thunderbird.gradle.plugin"

// Configure the build-logic plugins to target JDK 21 similar to the JDK used to build the project.
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_21
}
}

dependencies {
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))

compileOnly(plugin(libs.plugins.android.application))

compileOnly(plugin(libs.plugins.kotlin.multiplatform))
compileOnly(plugin(libs.plugins.kotlin.serialization))

implementation(plugin(libs.plugins.compose))

implementation(plugin(libs.plugins.jetbrains.compose))

implementation(plugin(libs.plugins.dependency.check))
implementation(plugin(libs.plugins.detekt))
implementation(plugin(libs.plugins.spotless))

compileOnly(plugin(libs.plugins.kover))
implementation(libs.diff.utils)
compileOnly(libs.kotlinx.datetime)
}

kotlin {
compilerOptions {
allWarningsAsErrors = true
}
}

tasks {
validatePlugins {
enableStricterValidation = true
failOnWarning = true
}
}

gradlePlugin {
plugins {
register("Badging") {
id = "net.thunderbird.gradle.plugin.app.badging"
implementationClass = "net.thunderbird.gradle.plugin.app.badging.BadgingPlugin"
}
register("Versioning") {
id = "net.thunderbird.gradle.plugin.app.versioning"
implementationClass = "net.thunderbird.gradle.plugin.app.versioning.VersioningPlugin"
}

register("QualityCodeCoverage") {
id = "net.thunderbird.gradle.plugin.quality.coverage"
implementationClass = "net.thunderbird.gradle.plugin.quality.coverage.CodeCoveragePlugin"
}
}
}

private fun plugin(provider: Provider<PluginDependency>) = with(provider.get()) {
"$pluginId:$pluginId.gradle.plugin:$version"
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ interface CodeCoverageExtension {
* Minimum required line coverage in percent (0-100).
*/
val lineCoverage: Property<Int>

}

internal fun CodeCoverageExtension.initialize() {
disabled.convention(false)
disabled.convention(true)
branchCoverage.convention(DEFAULT_MIN_BRANCH_COVERAGE)
lineCoverage.convention(DEFAULT_MIN_LINE_COVERAGE)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,77 @@ import kotlinx.kover.gradle.plugin.KoverGradlePlugin
import kotlinx.kover.gradle.plugin.dsl.AggregationType
import kotlinx.kover.gradle.plugin.dsl.CoverageUnit
import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
import kotlinx.kover.gradle.plugin.dsl.KoverReportFiltersConfig
import kotlinx.kover.gradle.plugin.dsl.KoverVerificationRulesConfig
import net.thunderbird.gradle.plugin.quality.coverage.filter.androidFilter
import net.thunderbird.gradle.plugin.quality.coverage.filter.commonFilter
import net.thunderbird.gradle.plugin.quality.coverage.filter.composeFilter
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.withType
import org.gradle.process.JavaForkOptions

/**
* A Gradle plugin that configures code coverage using the Kover plugin.
*
* It sets up default coverage thresholds and allows for customization via the [CodeCoverageExtension].
*
* The plugin can be globally disabled using a Gradle property or environment variable:
* - Gradle property: `-PcodeCoverageDisabled=true`
* - Environment variable: `CODE_COVERAGE_DISABLED=true`
* The plugin is disabled by default. It can be globally enabled using a Gradle property or environment variable:
* - Gradle property: `-PcodeCoverageDisabled=false`
* - Environment variable: `CODE_COVERAGE_DISABLED=false`
*
* Example usage in a build script:
* Example usage in a build script to enable it:
*
* ```kotlin
* plugins {
* id("net.thunderbird.gradle.plugin.quality.coverage")
* }
*
* codeCoverage {
* disabled.set(false) // Enable or disable coverage
* lineCoverage.set(80) // Set line coverage threshold
* branchCoverage.set(70) // Set branch coverage threshold
* disabled.set(false) // Enable coverage
* lineCoverage = 80 // Set line coverage threshold
* branchCoverage = 70 // Set branch coverage threshold
* }
*/
class CodeCoveragePlugin: Plugin<Project> {
class CodeCoveragePlugin : Plugin<Project> {

override fun apply(target: Project) {
val extension = target.extensions.create<CodeCoverageExtension>("codeCoverage")

val gradleProperty = target.providers.gradleProperty("codeCoverageDisabled").map { it.toBoolean() }
val environmentProperty = target.providers.environmentVariable("CODE_COVERAGE_DISABLED")
.map { it.equals("true", ignoreCase = true) }
val disabledProvider = environmentProperty.orElse(gradleProperty).orElse(false)
val disabledProvider = environmentProperty.orElse(gradleProperty).orElse(true)

extension.disabled.convention(disabledProvider)

extension.initialize()
extension.finalizeValueOnRead()

target.pluginManager.apply(KoverGradlePlugin::class)
target.configureKover(extension)
with(target) {
with(pluginManager) {
apply(KoverGradlePlugin::class)
}

// Ensure forked JVMs (tests + kover verify daemons) get a larger CodeCache
configureCodeCacheForForkedJvms(
reservedCodeCacheSize = "256m",
initialCodeCacheSize = "128m",
)

// Defer configuration until after all build scripts had a chance
// to configure the `codeCoverage { ... }` extension.
afterEvaluate {
configureKover(
coverageExtension = extension,
isRoot = this == rootProject,
)
}
}
}

private fun Project.configureKover(coverageExtension: CodeCoverageExtension) {
private fun Project.configureKover(coverageExtension: CodeCoverageExtension, isRoot: Boolean) {
extensions.configure<KoverProjectExtension>("kover") {
if (coverageExtension.disabled.get()) {
disable()
Expand All @@ -64,6 +83,24 @@ class CodeCoveragePlugin: Plugin<Project> {
// See https://www.jacoco.org/jacoco/
useJacoco("0.8.14")

if (isRoot) {
merge {
allProjects()
}
}

currentProject {
sources {
excludedSourceSets.addAll(
"androidMainResourceCollectors",
"commonMainResourceAccessors",
"commonMainResourceCollectors",
"commonResClass",
"jvmMainResourceCollectors",
)
}
}

reports {
total {
filters {
Expand Down Expand Up @@ -100,4 +137,30 @@ class CodeCoveragePlugin: Plugin<Project> {
}
}
}

/**
* Ensure forked JVMs (tests + kover verify daemons) get a larger CodeCache.
*/
private fun Project.configureCodeCacheForForkedJvms(
reservedCodeCacheSize: String,
initialCodeCacheSize: String,
) {
val args = listOf(
"-XX:ReservedCodeCacheSize=$reservedCodeCacheSize",
"-XX:InitialCodeCacheSize=$initialCodeCacheSize",
)

// Tests are the most common forked JVM used under koverVerify
tasks.withType<Test>().configureEach {
// Avoid overwriting if someone else already added jvmArgs
jvmArgs = jvmArgs + args
}

// Kover-related tasks that fork JVMs (varies by Kover version)
tasks.matching { it.name.contains("kover", ignoreCase = true) }.configureEach {
(this as? JavaForkOptions)?.let { fork ->
fork.jvmArgs((fork.jvmArgs ?: emptyList()) + args)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import kotlinx.kover.gradle.plugin.dsl.KoverReportFiltersConfig

internal fun KoverReportFiltersConfig.composeFilter() {
excludes {
// Exclude Compose Multiplatform generated resource packages and runtime resource wrappers
// so that auto-generated resource accessors don't affect coverage numbers.
classes(
// Compose Resources
"*.Res",
"*.ActualResourceCollectorsKt"
)

annotatedBy(
"androidx.compose.ui.tooling.preview.Preview",
"androidx.compose.ui.tooling.preview.PreviewLightDark",
Expand Down
35 changes: 0 additions & 35 deletions build-plugin/plugins/build.gradle.kts

This file was deleted.

Loading
Loading