mirror of
https://github.com/ProtonMail/protoncore_android.git
synced 2026-06-14 09:54:49 +00:00
build(coverage): Add plugins for global coverage and common coverage configuration.
This commit is contained in:
committed by
Neil Marietta
parent
e730fe635a
commit
97384ce56a
@@ -34,6 +34,7 @@ plugins {
|
||||
id("publish-core-libraries")
|
||||
id("me.proton.core.gradle-plugins.tests")
|
||||
id("me.proton.core.gradle-plugins.jacoco")
|
||||
id("me.proton.core.gradle-plugins.coverage-config")
|
||||
alias(libs.plugins.benManes.versions.gradle)
|
||||
alias(libs.plugins.kotlin.binaryCompatibilityValidator)
|
||||
alias(libs.plugins.kotlin.gradle)
|
||||
@@ -56,6 +57,17 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
protonCoverage {
|
||||
excludes.add {
|
||||
classes(
|
||||
"me.proton.core.accountmanager.data.db.AccountManagerDatabaseMigrations*",
|
||||
"*Database_Impl*",
|
||||
"*Database\$Companion*", // DB migrations
|
||||
"*LogTag"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun isNonStable(version: String): Boolean {
|
||||
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
|
||||
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
plugins {
|
||||
id("me.proton.core.gradle-plugins.global-coverage")
|
||||
}
|
||||
|
||||
publishOption.shouldBePublishedAsLib = false
|
||||
+17
-1
@@ -35,10 +35,26 @@ Use internally in core project to orchestrate dependencies and apply android/kot
|
||||
- Plugin id: `me.proton.core.gradle-plugins.coverage`
|
||||
- Published on MavenCentral.
|
||||
|
||||
Apply the plugin to each module for which the code coverage is needed.
|
||||
Apply the plugin to each (non-root) project for which the code coverage is needed.
|
||||
Additional settings can be configured via `protonCoverage` extension,
|
||||
e.g. setting custom minimum coverage levels, excluding additional files.
|
||||
|
||||
## Global coverage plugin
|
||||
- Plugin id: `me.proton.core.gradle-plugins.global-coverage`
|
||||
- Published on MavenCentral.
|
||||
|
||||
The plugin is intended to be applied to a separate project, that's only used to
|
||||
generate a global coverage report or perform coverage percentage verification.
|
||||
|
||||
## Common coverage config plugin
|
||||
- Plugin id: `me.proton.core.gradle-plugins.coverage-config`
|
||||
- Published on MavenCentral.
|
||||
|
||||
Apply the plugin on the root project.
|
||||
Use the `protonCoverage` extension to configure common coverage settings.
|
||||
Those settings will be picked up the all the submodules that
|
||||
use the Coverage plugin (`me.proton.core.gradle-plugins.coverage`).
|
||||
|
||||
## Detekt plugin
|
||||
- Plugin id: `me.proton.core.gradle-plugins.detekt`
|
||||
- Published on MavenCentral.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and ProtonCore.
|
||||
* Copyright (c) 2023 Proton AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -44,6 +44,18 @@ gradlePlugin {
|
||||
description = "Plugin to generate coverage reports compatible with GitLab"
|
||||
implementationClass = "me.proton.core.gradle.plugins.coverage.ProtonCoveragePlugin"
|
||||
}
|
||||
create("globalCoveragePlugin") {
|
||||
id = "me.proton.core.gradle-plugins.global-coverage"
|
||||
displayName = "Proton global coverage plugin"
|
||||
description = "Plugin to generate global coverage reports compatible with GitLab"
|
||||
implementationClass = "me.proton.core.gradle.plugins.coverage.ProtonGlobalCoveragePlugin"
|
||||
}
|
||||
create("coverageCommonConfigPlugin") {
|
||||
id = "me.proton.core.gradle-plugins.coverage-config"
|
||||
displayName = "Proton coverage config plugin"
|
||||
description = "Plugin to configure common coverage settings"
|
||||
implementationClass = "me.proton.core.gradle.plugins.coverage.ProtonCoverageCommonConfigPlugin"
|
||||
}
|
||||
}
|
||||
|
||||
testSourceSets(functionalTest)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.proton.core.gradle.plugins.coverage
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
private val koverTaskNameRegex = Regex("(.*:)?kover.*")
|
||||
private val tasksTaskNameRegex = Regex("(.*:)?tasks")
|
||||
|
||||
/** Optional optimization.
|
||||
* Avoid further configuration if we're not trying to run a `kover` task,
|
||||
* or display the project's tasks.
|
||||
* Example:
|
||||
* > ./gradlew koverHtmlReport # <- The kover plugin will be configured
|
||||
* > ./gradlew tasks # <- The kover plugin will be configured
|
||||
* > ./gradlew someOtherTask # <- The kover plugin will NOT be configured
|
||||
*/
|
||||
internal fun Project.shouldSkipPluginApplication(): Boolean =
|
||||
!gradle.startParameter.taskNames.any {
|
||||
it.matches(koverTaskNameRegex) || it.matches(tasksTaskNameRegex)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.gradle.plugins.coverage
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.create
|
||||
|
||||
/**
|
||||
* The plugin should be applied on a root project.
|
||||
* It can be used to provide a common configuration for code coverage.
|
||||
* The configuration will be picked up by the submodules which use
|
||||
* the [ProtonCoveragePlugin].
|
||||
*/
|
||||
public class ProtonCoverageCommonConfigPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
if (target != target.rootProject) error("${this::class.simpleName} should be applied on the root project.")
|
||||
val ext = target.extensions.create<ProtonCoverageExtension>("protonCoverage")
|
||||
|
||||
target.afterEvaluate {
|
||||
ext.finalizeValuesOnRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -19,6 +19,7 @@
|
||||
package me.proton.core.gradle.plugins.coverage
|
||||
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilter
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
|
||||
@@ -67,6 +68,27 @@ public interface ProtonCoverageExtension {
|
||||
public val minLineCoveragePercentage: Property<Int>
|
||||
}
|
||||
|
||||
internal fun ProtonCoverageExtension.applyConventionsFrom(project: Project) {
|
||||
project.extensions.findByType(ProtonCoverageExtension::class.java)?.let { otherExt ->
|
||||
require(otherExt != this) { "Cannot apply the convention from the same object." }
|
||||
applyConventionsFrom(otherExt)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ProtonCoverageExtension.applyConventionsFrom(other: ProtonCoverageExtension) {
|
||||
other.androidBuildVariant.orNull?.let { androidBuildVariant.convention(it) }
|
||||
other.disabled.orNull?.let { disabled.convention(it) }
|
||||
other.enableAllRules.orNull?.let { enableAllRules.convention(it) }
|
||||
other.enableAndroidRules.orNull?.let { enableAndroidRules.convention(it) }
|
||||
other.enableDaggerRules.orNull?.let { enableDaggerRules.convention(it) }
|
||||
other.enableKotlinParcelizeRules.orNull?.let { enableKotlinParcelizeRules.convention(it) }
|
||||
other.enableKotlinSerializationRules.orNull?.let { enableKotlinSerializationRules.convention(it) }
|
||||
other.enableRoomDbRules.orNull?.let { enableRoomDbRules.convention(it) }
|
||||
other.excludes.orNull?.let { excludes.convention(it) }
|
||||
other.minBranchCoveragePercentage.orNull?.let { minBranchCoveragePercentage.convention(it) }
|
||||
other.minLineCoveragePercentage.orNull?.let { minLineCoveragePercentage.convention(it) }
|
||||
}
|
||||
|
||||
internal fun ProtonCoverageExtension.applyGeneralConventions() {
|
||||
disabled.convention(false)
|
||||
excludes.convention(emptyList())
|
||||
|
||||
+19
-72
@@ -25,6 +25,11 @@ import kotlinx.kover.gradle.plugin.dsl.KoverReportExtension
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilters
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverVerifyReportConfig
|
||||
import kotlinx.kover.gradle.plugin.dsl.MetricType
|
||||
import me.proton.core.gradle.plugins.coverage.rules.androidRules
|
||||
import me.proton.core.gradle.plugins.coverage.rules.daggerRules
|
||||
import me.proton.core.gradle.plugins.coverage.rules.kotlinParcelizeRules
|
||||
import me.proton.core.gradle.plugins.coverage.rules.kotlinSerializationRules
|
||||
import me.proton.core.gradle.plugins.coverage.rules.roomDbRules
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
@@ -38,11 +43,14 @@ private const val DEFAULT_XML_REPORT_FILE = "reports/kover/report.xml"
|
||||
|
||||
internal const val PROTON_COVERAGE_EXT: String = "protonCoverage"
|
||||
|
||||
/**
|
||||
* The plugin can be applied on a non-root project.
|
||||
* It can be used to generate coverage HTML/XML report, and verify minimum coverage percentages.
|
||||
*/
|
||||
public class ProtonCoveragePlugin : Plugin<Project> {
|
||||
private val koverTaskNameRegex = Regex("(.*:)?kover.*")
|
||||
private val tasksTaskNameRegex = Regex("(.*:)?tasks")
|
||||
|
||||
override fun apply(target: Project) {
|
||||
if (target == target.rootProject) error("${this::class.simpleName} should not be applied on the root project.")
|
||||
|
||||
val ext = target.extensions.create<ProtonCoverageExtension>(PROTON_COVERAGE_EXT)
|
||||
|
||||
if (target.shouldSkipPluginApplication()) {
|
||||
@@ -50,6 +58,7 @@ public class ProtonCoveragePlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
ext.applyGeneralConventions()
|
||||
ext.applyConventionsFrom(target.rootProject)
|
||||
ext.finalizeValuesOnRead()
|
||||
|
||||
target.afterEvaluate {
|
||||
@@ -59,31 +68,11 @@ public class ProtonCoveragePlugin : Plugin<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Optional optimization.
|
||||
* Avoid further configuration if we're not trying to run a `kover` task,
|
||||
* or display the project's tasks.
|
||||
* Example:
|
||||
* > ./gradlew koverHtmlReport # <- The kover plugin will be configured
|
||||
* > ./gradlew tasks # <- The kover plugin will be configured
|
||||
* > ./gradlew someOtherTask # <- The kover plugin will NOT be configured
|
||||
*/
|
||||
private fun Project.shouldSkipPluginApplication(): Boolean =
|
||||
!gradle.startParameter.taskNames.any {
|
||||
it.matches(koverTaskNameRegex) || it.matches(tasksTaskNameRegex)
|
||||
}
|
||||
|
||||
private fun Project.onAfterEvaluate(ext: ProtonCoverageExtension) {
|
||||
extensions.configure<KoverProjectExtension> {
|
||||
useKoverTool()
|
||||
}
|
||||
|
||||
if (ext.enableAllRules.orNull == true) {
|
||||
ext.enableAndroidRules.convention(true)
|
||||
ext.enableDaggerRules.convention(true)
|
||||
ext.enableKotlinParcelizeRules.convention(true)
|
||||
ext.enableKotlinSerializationRules.convention(true)
|
||||
ext.enableRoomDbRules.convention(true)
|
||||
}
|
||||
if (hasAndroidPlugin()) {
|
||||
ext.androidBuildVariant.convention(DEFAULT_ANDROID_BUILD_VARIANT)
|
||||
ext.enableAndroidRules.convention(true)
|
||||
@@ -98,6 +87,13 @@ public class ProtonCoveragePlugin : Plugin<Project> {
|
||||
if (plugins.hasPlugin(PluginIds.kotlinSerialization)) {
|
||||
ext.enableKotlinSerializationRules.convention(true)
|
||||
}
|
||||
if (ext.enableAllRules.orNull == true) {
|
||||
ext.enableAndroidRules.convention(true)
|
||||
ext.enableDaggerRules.convention(true)
|
||||
ext.enableKotlinParcelizeRules.convention(true)
|
||||
ext.enableKotlinSerializationRules.convention(true)
|
||||
ext.enableRoomDbRules.convention(true)
|
||||
}
|
||||
|
||||
configureKoverExtension(ext)
|
||||
}
|
||||
@@ -152,53 +148,4 @@ public class ProtonCoveragePlugin : Plugin<Project> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KoverReportFilters.androidRules() {
|
||||
excludes {
|
||||
classes(
|
||||
"*Activity",
|
||||
"*Fragment",
|
||||
"*.BuildConfig",
|
||||
"*.R",
|
||||
"*.R$*"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KoverReportFilters.daggerRules() {
|
||||
excludes {
|
||||
annotatedBy(
|
||||
"dagger.internal.DaggerGenerated",
|
||||
"dagger.Module",
|
||||
"javax.annotation.processing.Generated"
|
||||
)
|
||||
packages("hilt_aggregated_deps")
|
||||
}
|
||||
}
|
||||
|
||||
private fun KoverReportFilters.kotlinSerializationRules() {
|
||||
excludes {
|
||||
annotatedBy(
|
||||
"kotlinx.serialization.SerialName",
|
||||
"kotlinx.serialization.Serializable"
|
||||
)
|
||||
classes("*\$\$serializer")
|
||||
}
|
||||
}
|
||||
|
||||
private fun KoverReportFilters.kotlinParcelizeRules() {
|
||||
excludes {
|
||||
annotatedBy("kotlinx.parcelize.Parcelize")
|
||||
}
|
||||
}
|
||||
|
||||
private fun KoverReportFilters.roomDbRules() {
|
||||
excludes {
|
||||
annotatedBy(
|
||||
"androidx.room.Dao",
|
||||
"androidx.room.Entity"
|
||||
)
|
||||
classes("*Database\$Companion*") // For DB migrations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package me.proton.core.gradle.plugins.coverage
|
||||
|
||||
import kotlinx.kover.gradle.plugin.KoverGradlePlugin
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverNames
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
/**
|
||||
* The plugin can be applied on a separate project.
|
||||
* It can generate a combined coverage report from all subprojects
|
||||
* which use the [ProtonCoveragePlugin].
|
||||
*/
|
||||
public class ProtonGlobalCoveragePlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
if (!target.shouldSkipPluginApplication()) {
|
||||
configureGlobalCoverageReports(target)
|
||||
}
|
||||
|
||||
target.plugins.apply(ProtonCoveragePlugin::class.java)
|
||||
}
|
||||
|
||||
private fun configureGlobalCoverageReports(target: Project) {
|
||||
val rootProject = target.rootProject
|
||||
|
||||
rootProject.subprojects {
|
||||
if (project == target) return@subprojects
|
||||
target.evaluationDependsOn(project.path)
|
||||
}
|
||||
|
||||
target.afterEvaluate {
|
||||
target.plugins.apply("org.gradle.java-library")
|
||||
target.plugins.apply("org.jetbrains.kotlin.jvm")
|
||||
target.plugins.apply(KoverGradlePlugin::class.java)
|
||||
|
||||
target.extensions.configure<ProtonCoverageExtension> {
|
||||
enableAllRules.set(true)
|
||||
}
|
||||
|
||||
rootProject.subprojects {
|
||||
if (project == target) return@subprojects
|
||||
|
||||
if (project.plugins.hasPlugin(ProtonCoveragePlugin::class.java) &&
|
||||
project.extensions.findByType(ProtonCoverageExtension::class.java)?.disabled?.get() != true
|
||||
) {
|
||||
target.dependencies.add(KoverNames.DEPENDENCY_CONFIGURATION_NAME, project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package me.proton.core.gradle.plugins.coverage.rules
|
||||
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilters
|
||||
|
||||
internal fun KoverReportFilters.androidRules() {
|
||||
excludes {
|
||||
annotatedBy("androidx.compose.runtime.Composable")
|
||||
classes(
|
||||
"*Activity",
|
||||
"*Activity\$*",
|
||||
"*Binding",
|
||||
"*.BuildConfig",
|
||||
"*ComposableSingletons\$*",
|
||||
"*Fragment",
|
||||
"*Fragment\$*",
|
||||
"*.R",
|
||||
"*.R$*"
|
||||
)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package me.proton.core.gradle.plugins.coverage.rules
|
||||
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilters
|
||||
|
||||
internal fun KoverReportFilters.daggerRules() {
|
||||
excludes {
|
||||
annotatedBy(
|
||||
"dagger.hilt.android.AndroidEntryPoint",
|
||||
"dagger.internal.DaggerGenerated",
|
||||
"dagger.Binds",
|
||||
"dagger.Module",
|
||||
"dagger.Provides",
|
||||
"javax.annotation.processing.Generated"
|
||||
)
|
||||
classes(
|
||||
"*Hilt_*",
|
||||
"*_HiltModules_*",
|
||||
"*_Provide*Factory",
|
||||
"*_Provide*Factory\$*",
|
||||
"*_Factory"
|
||||
)
|
||||
packages("hilt_aggregated_deps")
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package me.proton.core.gradle.plugins.coverage.rules
|
||||
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilters
|
||||
|
||||
internal fun KoverReportFilters.kotlinParcelizeRules() {
|
||||
excludes {
|
||||
annotatedBy("kotlinx.parcelize.Parcelize")
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package me.proton.core.gradle.plugins.coverage.rules
|
||||
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilters
|
||||
|
||||
internal fun KoverReportFilters.kotlinSerializationRules() {
|
||||
excludes {
|
||||
annotatedBy(
|
||||
"kotlinx.serialization.SerialName",
|
||||
"kotlinx.serialization.Serializable"
|
||||
)
|
||||
classes("*\$\$serializer")
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package me.proton.core.gradle.plugins.coverage.rules
|
||||
|
||||
import kotlinx.kover.gradle.plugin.dsl.KoverReportFilters
|
||||
|
||||
internal fun KoverReportFilters.roomDbRules() {
|
||||
excludes {
|
||||
annotatedBy(
|
||||
"androidx.room.Dao",
|
||||
"androidx.room.Database",
|
||||
"androidx.room.Entity",
|
||||
"javax.annotation.processing.Generated"
|
||||
)
|
||||
classes(
|
||||
"*Dao_Impl",
|
||||
"*Dao_Impl\$*"
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user