Update kotlin to v0.8.0 (#2854)

* Update kotlin to v0.8.0

* Adapt our setup to `v0.8.0`'s changes

* Make sure verification tasks run on `check` tasks

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
renovate[bot] 2024-05-17 17:07:19 +02:00 committed by GitHub
parent cd03a83797
commit 46107a9cff
4 changed files with 197 additions and 149 deletions

View file

@ -33,7 +33,7 @@ jobs:
run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES
- name: 📈 Generate kover report and verify coverage - name: 📈 Generate kover report and verify coverage
run: ./gradlew :app:koverXmlReportGplayDebug :app:koverHtmlReportGplayDebug :app:koverVerifyGplayDebug $CI_GRADLE_ARG_PROPERTIES run: ./gradlew :app:koverXmlReportGplayDebug :app:koverHtmlReportGplayDebug :app:koverVerifyAll $CI_GRADLE_ARG_PROPERTIES
- name: ✅ Upload kover report - name: ✅ Upload kover report
if: always() if: always()

View file

@ -55,7 +55,7 @@ jobs:
run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES
- name: 📈Generate kover report and verify coverage - name: 📈Generate kover report and verify coverage
run: ./gradlew :app:koverXmlReportGplayDebug :app:koverHtmlReportGplayDebug :app:koverVerifyGplayDebug $CI_GRADLE_ARG_PROPERTIES run: ./gradlew :app:koverXmlReportGplayDebug :app:koverHtmlReportGplayDebug :app:koverVerifyAll $CI_GRADLE_ARG_PROPERTIES
- name: 🚫 Upload kover failed coverage reports - name: 🚫 Upload kover failed coverage reports
if: failure() if: failure()

View file

@ -57,7 +57,7 @@ autoservice = "1.1.1"
junit = "4.13.2" junit = "4.13.2"
androidx-test-ext-junit = "1.1.5" androidx-test-ext-junit = "1.1.5"
espresso-core = "3.5.1" espresso-core = "3.5.1"
kover = "0.7.6" kover = "0.8.0"
[libraries] [libraries]
# Project # Project

View file

@ -16,10 +16,24 @@
package extension package extension
import kotlinx.kover.gradle.plugin.dsl.KoverReportExtension import kotlinx.kover.gradle.plugin.dsl.AggregationType
import kotlinx.kover.gradle.plugin.dsl.CoverageUnit
import kotlinx.kover.gradle.plugin.dsl.GroupingEntityType
import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
import kotlinx.kover.gradle.plugin.dsl.KoverVariantCreateConfig
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.configurationcache.extensions.capitalized
import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.assign
enum class KoverVariant(val variantName: String) {
Presenters("presenters"),
States("states"),
Views("views"),
}
val koverVariants = KoverVariant.values().map { it.variantName }
val localAarProjects = listOf( val localAarProjects = listOf(
":libraries:rustsdk", ":libraries:rustsdk",
@ -44,160 +58,179 @@ val excludedKoverSubProjects = listOf(
":libraries:di", ":libraries:di",
) + localAarProjects ) + localAarProjects
private fun Project.koverReport(action: Action<KoverReportExtension>) { private fun Project.kover(action: Action<KoverProjectExtension>) {
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("koverReport", action) (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("kover", action)
} }
fun Project.setupKover() { fun Project.setupKover() {
// Create verify all task joining all existing verification tasks
task("koverVerifyAll") {
group = "verification"
description = "Verifies the code coverage of all subprojects."
val dependencies = listOf(":app:koverVerifyGplayDebug") + koverVariants.map { ":app:koverVerify${it.capitalized()}" }
dependsOn(dependencies)
}
// https://kotlin.github.io/kotlinx-kover/ // https://kotlin.github.io/kotlinx-kover/
// Run `./gradlew :app:koverHtmlReport` to get report at ./app/build/reports/kover // Run `./gradlew :app:koverHtmlReport` to get report at ./app/build/reports/kover
// Run `./gradlew :app:koverXmlReport` to get XML report // Run `./gradlew :app:koverXmlReport` to get XML report
koverReport { kover {
filters { reports {
excludes { filters {
classes( excludes {
// Exclude generated classes. classes(
"*_ModuleKt", // Exclude generated classes.
"anvil.hint.binding.io.element.*", "*_ModuleKt",
"anvil.hint.merge.*", "anvil.hint.binding.io.element.*",
"anvil.hint.multibinding.io.element.*", "anvil.hint.merge.*",
"anvil.module.*", "anvil.hint.multibinding.io.element.*",
"com.airbnb.android.showkase*", "anvil.module.*",
"io.element.android.libraries.designsystem.showkase.*", "com.airbnb.android.showkase*",
"io.element.android.x.di.DaggerAppComponent*", "io.element.android.libraries.designsystem.showkase.*",
"*_Factory", "io.element.android.x.di.DaggerAppComponent*",
"*_Factory_Impl", "*_Factory",
"*_Factory$*", "*_Factory_Impl",
"*_Module", "*_Factory$*",
"*_Module$*", "*_Module",
"*Module_Provides*", "*_Module$*",
"Dagger*Component*", "*Module_Provides*",
"*ComposableSingletons$*", "Dagger*Component*",
"*_AssistedFactory_Impl*", "*ComposableSingletons$*",
"*BuildConfig", "*_AssistedFactory_Impl*",
// Generated by Showkase "*BuildConfig",
"*Ioelementandroid*PreviewKt$*", // Generated by Showkase
"*Ioelementandroid*PreviewKt", "*Ioelementandroid*PreviewKt$*",
// Other "*Ioelementandroid*PreviewKt",
// We do not cover Nodes (normally covered by maestro, but code coverage is not computed with maestro) // Other
"*Node", // We do not cover Nodes (normally covered by maestro, but code coverage is not computed with maestro)
"*Node$*", "*Node",
"*Presenter\$present\$*", "*Node$*",
// Forked from compose "*Presenter\$present\$*",
"io.element.android.libraries.designsystem.theme.components.bottomsheet.*", // Forked from compose
// Test presenter "io.element.android.libraries.designsystem.theme.components.bottomsheet.*",
"io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter", // Test presenter
) "io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter",
annotatedBy( )
"androidx.compose.ui.tooling.preview.Preview", annotatedBy(
"io.element.android.libraries.architecture.coverage.ExcludeFromCoverage", "androidx.compose.ui.tooling.preview.Preview",
"io.element.android.libraries.designsystem.preview.PreviewsDayNight", "io.element.android.libraries.architecture.coverage.ExcludeFromCoverage",
"io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight", "io.element.android.libraries.designsystem.preview.PreviewsDayNight",
) "io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight",
)
}
} }
}
defaults { total {
// add reports of both 'debug' and 'release' Android build variants to default reports verify {
mergeWith("gplayDebug") onCheck = true
// General rule: minimum code coverage.
rule("Global minimum code coverage.") {
groupBy = GroupingEntityType.APPLICATION
bound {
minValue = 70
// Setting a max value, so that if coverage is bigger, it means that we have to change minValue.
// For instance if we have minValue = 20 and maxValue = 30, and current code coverage is now 31.32%, update
// minValue to 25 and maxValue to 35.
maxValue = 80
coverageUnits = CoverageUnit.INSTRUCTION
aggregationForGroup = AggregationType.COVERED_PERCENTAGE
}
}
}
}
variant(KoverVariant.Presenters.variantName) {
verify {
onCheck = true
// Rule to ensure that coverage of Presenters is sufficient.
rule("Check code coverage of presenters") {
groupBy = GroupingEntityType.CLASS
verify { bound {
onCheck = true minValue = 85
// General rule: minimum code coverage. coverageUnits = CoverageUnit.INSTRUCTION
rule("Global minimum code coverage.") { aggregationForGroup = AggregationType.COVERED_PERCENTAGE
isEnabled = true }
entity = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.APPLICATION
bound {
minValue = 70
// Setting a max value, so that if coverage is bigger, it means that we have to change minValue.
// For instance if we have minValue = 20 and maxValue = 30, and current code coverage is now 31.32%, update
// minValue to 25 and maxValue to 35.
maxValue = 80
metric = kotlinx.kover.gradle.plugin.dsl.MetricType.INSTRUCTION
aggregation = kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE
} }
} }
// Rule to ensure that coverage of Presenters is sufficient. filters {
rule("Check code coverage of presenters") { includes {
isEnabled = true classes(
entity = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.CLASS "*Presenter",
filters { )
includes {
classes(
"*Presenter",
)
}
excludes {
classes(
"*Fake*Presenter",
"io.element.android.appnav.loggedin.LoggedInPresenter$*",
// Some options can't be tested at the moment
"io.element.android.features.preferences.impl.developer.DeveloperSettingsPresenter$*",
"*Presenter\$present\$*",
)
}
} }
bound { excludes {
minValue = 85 classes(
metric = kotlinx.kover.gradle.plugin.dsl.MetricType.INSTRUCTION "*Fake*Presenter",
aggregation = kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE "io.element.android.appnav.loggedin.LoggedInPresenter$*",
// Some options can't be tested at the moment
"io.element.android.features.preferences.impl.developer.DeveloperSettingsPresenter$*",
"*Presenter\$present\$*",
)
} }
} }
// Rule to ensure that coverage of States is sufficient. }
rule("Check code coverage of states") { variant(KoverVariant.States.variantName) {
isEnabled = true verify {
entity = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.CLASS onCheck = true
filters { // Rule to ensure that coverage of States is sufficient.
includes { rule("Check code coverage of states") {
classes( groupBy = GroupingEntityType.CLASS
"^*State$", bound {
) minValue = 90
coverageUnits = CoverageUnit.INSTRUCTION
aggregationForGroup = AggregationType.COVERED_PERCENTAGE
} }
excludes {
classes(
"io.element.android.appnav.root.RootNavState*",
"io.element.android.libraries.matrix.api.timeline.item.event.OtherState$*",
"io.element.android.libraries.matrix.api.timeline.item.event.EventSendState$*",
"io.element.android.libraries.matrix.api.room.RoomMembershipState*",
"io.element.android.libraries.matrix.api.room.MatrixRoomMembersState*",
"io.element.android.libraries.push.impl.notifications.NotificationState*",
"io.element.android.features.messages.impl.media.local.pdf.PdfViewerState",
"io.element.android.features.messages.impl.media.local.LocalMediaViewState",
"io.element.android.features.location.impl.map.MapState*",
"io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState*",
"io.element.android.libraries.designsystem.swipe.SwipeableActionsState*",
"io.element.android.features.messages.impl.timeline.components.ExpandableState*",
"io.element.android.features.messages.impl.timeline.model.bubble.BubbleState*",
"io.element.android.libraries.maplibre.compose.CameraPositionState*",
"io.element.android.libraries.maplibre.compose.SaveableCameraPositionState",
"io.element.android.libraries.maplibre.compose.SymbolState*",
"io.element.android.features.ftue.api.state.*",
"io.element.android.features.ftue.impl.welcome.state.*",
)
}
}
bound {
minValue = 90
metric = kotlinx.kover.gradle.plugin.dsl.MetricType.INSTRUCTION
aggregation = kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE
} }
} }
// Rule to ensure that coverage of Views is sufficient (deactivated for now). filters {
rule("Check code coverage of views") { includes {
isEnabled = true classes(
entity = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.CLASS "^*State$",
filters { )
includes { }
classes( excludes {
"*ViewKt", classes(
) "io.element.android.appnav.root.RootNavState*",
"io.element.android.libraries.matrix.api.timeline.item.event.OtherState$*",
"io.element.android.libraries.matrix.api.timeline.item.event.EventSendState$*",
"io.element.android.libraries.matrix.api.room.RoomMembershipState*",
"io.element.android.libraries.matrix.api.room.MatrixRoomMembersState*",
"io.element.android.libraries.push.impl.notifications.NotificationState*",
"io.element.android.features.messages.impl.media.local.pdf.PdfViewerState",
"io.element.android.features.messages.impl.media.local.LocalMediaViewState",
"io.element.android.features.location.impl.map.MapState*",
"io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState*",
"io.element.android.libraries.designsystem.swipe.SwipeableActionsState*",
"io.element.android.features.messages.impl.timeline.components.ExpandableState*",
"io.element.android.features.messages.impl.timeline.model.bubble.BubbleState*",
"io.element.android.libraries.maplibre.compose.CameraPositionState*",
"io.element.android.libraries.maplibre.compose.SaveableCameraPositionState",
"io.element.android.libraries.maplibre.compose.SymbolState*",
"io.element.android.features.ftue.api.state.*",
"io.element.android.features.ftue.impl.welcome.state.*",
)
}
}
}
variant(KoverVariant.Views.variantName) {
verify {
onCheck = true
// Rule to ensure that coverage of Views is sufficient (deactivated for now).
rule("Check code coverage of views") {
groupBy = GroupingEntityType.CLASS
bound {
// TODO Update this value, for now there are too many missing tests.
minValue = 0
coverageUnits = CoverageUnit.INSTRUCTION
aggregationForGroup = AggregationType.COVERED_PERCENTAGE
} }
} }
bound { }
// TODO Update this value, for now there are too many missing tests. filters {
minValue = 0 includes {
metric = kotlinx.kover.gradle.plugin.dsl.MetricType.INSTRUCTION classes(
aggregation = kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE "*ViewKt",
)
} }
} }
} }
@ -205,22 +238,37 @@ fun Project.setupKover() {
} }
} }
fun Project.applyKoverPluginToAllSubProjects() = rootProject.allprojects { fun Project.applyKoverPluginToAllSubProjects() = rootProject.subprojects {
if (project.path !in localAarProjects) { if (project.path !in localAarProjects) {
apply(plugin = "org.jetbrains.kotlinx.kover") apply(plugin = "org.jetbrains.kotlinx.kover")
kover {
currentProject {
for (variant in koverVariants) {
createVariant(variant) {
defaultVariants()
}
}
}
}
} }
} }
fun KoverVariantCreateConfig.defaultVariants() {
addWithDependencies("gplayDebug", "debug", optional = true)
}
fun Project.koverSubprojects() = project.rootProject.subprojects
.filter {
it.project.projectDir.resolve("build.gradle.kts").exists()
}
.map { it.path }
.sorted()
.filter {
it !in excludedKoverSubProjects
}
fun Project.koverDependencies() { fun Project.koverDependencies() {
project.rootProject.subprojects project.koverSubprojects()
.filter {
it.project.projectDir.resolve("build.gradle.kts").exists()
}
.map { it.path }
.sorted()
.filter {
it !in excludedKoverSubProjects
}
.forEach { .forEach {
// println("Add $it to kover") // println("Add $it to kover")
dependencies.add("kover", project(it)) dependencies.add("kover", project(it))