Skip to content

Commit 1739cc5

Browse files
authored
Implemented Kover Maven Plugin
Resolves #51 Co-authored-by: Leonid Startsev <[email protected]> PR #654
1 parent 54637f7 commit 1739cc5

File tree

113 files changed

+5655
-69
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+5655
-69
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Kover is a set of solutions for collecting test coverage of Kotlin code compiled
88

99
Kover Toolset:
1010
- [Kover Gradle Plugin](#kover-gradle-plugin)
11+
- [Kover Maven Plugin](#kover-maven-plugin)
1112
- [Kover CLI](#kover-cli)
1213
- [Kover offline instrumentation](#kover-offline-instrumentation)
1314
- [Kover JVM agent](#kover-jvm-agent)
@@ -119,6 +120,21 @@ It is in its infancy, it is recommended to use it only for test or pet projects.
119120

120121
Refer to the [documentation](https://kotlin.github.io/kotlinx-kover/gradle-plugin/aggregated.html) for details.
121122

123+
## Kover Maven Plugin
124+
The Kover Maven Plugin can be applied by specifying build plugin
125+
```xml
126+
<plugin>
127+
<groupId>org.jetbrains.kotlinx</groupId>
128+
<artifactId>kover-maven-plugin</artifactId>
129+
<version>0.8.2</version>
130+
</plugin>
131+
```
132+
133+
The list of Kover goals is specified in [this document section](https://kotlin.github.io/kotlinx-kover/maven-plugin#goals).
134+
135+
For full information about latest stable release of Kover Gradle Plugin, please refer to the [documentation](https://kotlin.github.io/kotlinx-kover/maven-plugin).
136+
137+
122138
## Kover CLI
123139
Standalone JVM application used for offline instrumentation and generation of human-readable reports.
124140

build-logic/src/main/kotlin/kotlinx/kover/conventions/kover-docs-conventions.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension.callDokkaHtml.convention(false)
2626

2727
tasks.register("releaseDocs") {
2828
dependsOn(
29-
tasks.matching { extension.callDokkaHtml.get() && it.name == "dokkaHtml" }
29+
tasks.named { it == "dokkaHtml" }.matching { extension.callDokkaHtml.get() }
3030
)
3131

3232
doLast {

gradle/libs.versions.toml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
[versions]
22

3-
intellij-coverage = "1.0.760"
3+
intellij-coverage = "1.0.761"
44
junit = "5.9.0"
55
kotlinx-bcv = "0.13.2"
66
kotlinx-dokka = "1.8.10"
77
args4j = "2.33"
88
gradle-plugin-publish = "1.2.1"
9+
maven-plugin-development = "0.4.3"
10+
maven-embedder = "3.9.8"
11+
maven-api = "3.0"
12+
maven-resolver = "1.9.21"
13+
maven-slf4j = "1.7.36"
914

1015
[libraries]
1116

@@ -20,9 +25,26 @@ junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref
2025

2126
args4j = { module = "args4j:args4j", version.ref = "args4j" }
2227

23-
gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin" }
28+
gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin" }
29+
30+
maven-embedder = { module = "org.apache.maven:maven-embedder", version.ref = "maven-embedder" }
31+
maven-compat = { module = "org.apache.maven:maven-compat", version.ref = "maven-embedder" }
32+
maven-slf4j-provider = { module = "org.apache.maven:maven-slf4j-provider", version.ref = "maven-embedder" }
33+
34+
maven-plugin-annotations = { module = "org.apache.maven.plugin-tools:maven-plugin-annotations", version.ref = "maven-api" }
35+
maven-core = { module = "org.apache.maven:maven-core", version.ref = "maven-api" }
36+
maven-reporting-api = { module = "org.apache.maven.reporting:maven-reporting-api", version.ref = "maven-api" }
37+
38+
maven-resolver-basic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "maven-resolver" }
39+
maven-resolver-file = { module = "org.apache.maven.resolver:maven-resolver-transport-file", version.ref = "maven-resolver" }
40+
maven-resolver-http = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "maven-resolver" }
41+
42+
maven-slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "maven-slf4j" }
43+
2444

2545
[plugins]
26-
gradle-pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" }
46+
gradle-pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" }
2747
kotlinx-binaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlinx-bcv" }
2848
kotlinx-dokka = { id = "org.jetbrains.dokka", version.ref = "kotlinx-dokka" }
49+
mavenPluginDevelopment = { id = "de.benediktritter.maven-plugin-development", version.ref = "maven-plugin-development" }
50+

kover-cli/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ plugins {
2424
id("kover-release-conventions")
2525
}
2626

27-
extensions.configure<Kover_publishing_conventions_gradle.KoverPublicationExtension> {
27+
koverPublication {
2828
description.set("Command Line Interface for Kotlin Coverage Toolchain")
2929
}
3030

@@ -65,7 +65,7 @@ repositories {
6565
mavenCentral()
6666
}
6767

68-
extensions.configure<Kover_docs_conventions_gradle.KoverDocsExtension> {
68+
koverDocs {
6969
docsDirectory.set("cli")
7070
description.set("Kover Command Line Interface")
7171
}

kover-features-jvm/api/kover-features-jvm.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public final class kotlinx/kover/features/jvm/KoverLegacyFeatures {
106106
public final fun generateXmlReport (Ljava/io/File;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Lkotlinx/kover/features/jvm/ClassFilters;)V
107107
public final fun instrument (Ljava/io/File;Ljava/util/List;Lkotlinx/kover/features/jvm/ClassFilters;Z)V
108108
public final fun verify (Ljava/util/List;Ljava/io/File;Lkotlinx/kover/features/jvm/ClassFilters;Ljava/util/List;Ljava/util/List;)Ljava/util/List;
109+
public final fun violationMessage (Ljava/util/List;)Ljava/lang/String;
109110
}
110111

111112
public abstract interface class kotlinx/kover/features/jvm/OfflineInstrumenter {

kover-features-jvm/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ plugins {
2525
id("kover-release-conventions")
2626
}
2727

28-
extensions.configure<Kover_publishing_conventions_gradle.KoverPublicationExtension> {
28+
koverPublication {
2929
description.set("Implementation of calling the main features of Kover programmatically")
3030
}
3131

kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverLegacyFeatures.kt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,30 @@ public object KoverLegacyFeatures {
183183

184184
return result
185185
}
186+
187+
public fun violationMessage(violations: List<RuleViolations>): String {
188+
if (violations.isEmpty()) {
189+
return ""
190+
}
191+
val messageBuilder = StringBuilder()
192+
193+
violations.forEach { rule ->
194+
val namedRule = if (rule.rule.name.isNotEmpty()) "Rule '${rule.rule.name}'" else "Rule"
195+
196+
if (rule.violations.size == 1) {
197+
messageBuilder.appendLine("$namedRule violated: ${rule.violations[0].format(rule)}")
198+
} else {
199+
messageBuilder.appendLine("$namedRule violated:")
200+
201+
rule.violations.forEach { bound ->
202+
messageBuilder.append(" ")
203+
messageBuilder.appendLine(bound.format(rule))
204+
}
205+
}
206+
}
207+
208+
return messageBuilder.toString()
209+
}
186210
}
187211

188212
/**
@@ -251,3 +275,30 @@ public data class ClassFilters(
251275
*/
252276
public val excludeInheritedFrom: Set<String>
253277
)
278+
279+
private fun BoundViolation.format(rule: RuleViolations): String {
280+
val directionText = if (isMax) "maximum" else "minimum"
281+
282+
val metricText = when (bound.coverageUnits) {
283+
CoverageUnit.LINE -> "lines"
284+
CoverageUnit.INSTRUCTION -> "instructions"
285+
CoverageUnit.BRANCH -> "branches"
286+
}
287+
288+
val valueTypeText = when (bound.aggregationForGroup) {
289+
AggregationType.COVERED_COUNT -> "covered count"
290+
AggregationType.MISSED_COUNT -> "missed count"
291+
AggregationType.COVERED_PERCENTAGE -> "covered percentage"
292+
AggregationType.MISSED_PERCENTAGE -> "missed percentage"
293+
}
294+
295+
val entityText = when (rule.rule.groupBy) {
296+
GroupingBy.APPLICATION -> ""
297+
GroupingBy.CLASS -> " for class '$entityName'"
298+
GroupingBy.PACKAGE -> " for package '$entityName'"
299+
}
300+
301+
val expectedValue = if (isMax) bound.maxValue else bound.minValue
302+
303+
return "$metricText $valueTypeText$entityText is $value, but expected $directionText is $expectedValue"
304+
}

kover-gradle-plugin/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,13 @@ tasks.dokkaHtml {
189189
}
190190
}
191191

192-
extensions.configure<Kover_docs_conventions_gradle.KoverDocsExtension> {
192+
koverDocs {
193193
docsDirectory.set("gradle-plugin")
194194
description.set("Kover Gradle Plugin")
195195
callDokkaHtml.set(true)
196196
}
197197

198-
extensions.configure<Kover_publishing_conventions_gradle.KoverPublicationExtension> {
198+
koverPublication {
199199
description.set("Kover Gradle Plugin - Kotlin code coverage")
200200
//`java-gradle-plugin` plugin already creates publication with name `pluginMaven`
201201
addPublication.set(false)

kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverDoVerifyTask.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
package kotlinx.kover.gradle.plugin.tasks.reports
66

7+
import kotlinx.kover.features.jvm.KoverLegacyFeatures
78
import kotlinx.kover.gradle.plugin.commons.VerificationRule
8-
import kotlinx.kover.gradle.plugin.tools.generateErrorMessage
99
import org.gradle.api.file.RegularFileProperty
1010
import org.gradle.api.provider.ListProperty
1111
import org.gradle.api.tasks.*
@@ -24,7 +24,7 @@ internal abstract class KoverDoVerifyTask @Inject constructor(@get:Internal over
2424
val enabledRules = rules.get().filter { it.isEnabled }
2525
val violations = tool.get().verify(enabledRules, context())
2626

27-
val errorMessage = generateErrorMessage(violations)
27+
val errorMessage = KoverLegacyFeatures.violationMessage(violations)
2828
resultFile.get().asFile.writeText(errorMessage)
2929
}
3030

kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/Verification.kt

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -41,57 +41,3 @@ internal data class CoverageRequest(
4141
val header: String?,
4242
val lineFormat: String,
4343
): Serializable
44-
45-
internal fun generateErrorMessage(violations: List<RuleViolations>): String {
46-
if (violations.isEmpty()) {
47-
return ""
48-
}
49-
val messageBuilder = StringBuilder()
50-
51-
violations.forEach { rule ->
52-
val namedRule = if (rule.rule.name.isNotEmpty()) "Rule '${rule.rule.name}'" else "Rule"
53-
54-
if (rule.violations.size == 1) {
55-
messageBuilder.appendLine("$namedRule violated: ${rule.violations[0].format(rule)}")
56-
} else {
57-
messageBuilder.appendLine("$namedRule violated:")
58-
59-
rule.violations.forEach { bound ->
60-
messageBuilder.append(" ")
61-
messageBuilder.appendLine(bound.format(rule))
62-
}
63-
}
64-
}
65-
66-
return messageBuilder.toString()
67-
}
68-
69-
private fun BoundViolation.format(rule: RuleViolations): String {
70-
val directionText = if (isMax) "maximum" else "minimum"
71-
72-
val metricText = when (bound.coverageUnits) {
73-
FeatureCoverageUnit.LINE -> "lines"
74-
FeatureCoverageUnit.INSTRUCTION -> "instructions"
75-
FeatureCoverageUnit.BRANCH -> "branches"
76-
}
77-
78-
val valueTypeText = when (bound.aggregationForGroup) {
79-
FeatureAggregationType.COVERED_COUNT -> "covered count"
80-
FeatureAggregationType.MISSED_COUNT -> "missed count"
81-
FeatureAggregationType.COVERED_PERCENTAGE -> "covered percentage"
82-
FeatureAggregationType.MISSED_PERCENTAGE -> "missed percentage"
83-
}
84-
85-
val entityText = when (rule.rule.groupBy) {
86-
GroupingBy.APPLICATION -> ""
87-
GroupingBy.CLASS -> " for class '$entityName'"
88-
GroupingBy.PACKAGE -> " for package '$entityName'"
89-
}
90-
91-
val expectedValue = if (isMax) bound.maxValue else bound.minValue
92-
93-
return "$metricText $valueTypeText$entityText is $value, but expected $directionText is $expectedValue"
94-
}
95-
96-
private typealias FeatureCoverageUnit = kotlinx.kover.features.jvm.CoverageUnit
97-
private typealias FeatureAggregationType = kotlinx.kover.features.jvm.AggregationType

kover-jvm-agent/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ plugins {
2222
id("kover-release-conventions")
2323
}
2424

25-
extensions.configure<Kover_publishing_conventions_gradle.KoverPublicationExtension> {
25+
koverPublication {
2626
description.set("Kover JVM instrumentation agent")
2727
}
2828

29-
extensions.configure<Kover_docs_conventions_gradle.KoverDocsExtension> {
29+
koverDocs {
3030
docsDirectory.set("jvm-agent")
3131
description.set("Kover JVM instrumentation agent")
3232
}

kover-maven-plugin/build.gradle.kts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
2+
3+
/*
4+
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
5+
*/
6+
7+
plugins {
8+
kotlin("jvm")
9+
id("kover-publishing-conventions")
10+
id("kover-docs-conventions")
11+
id("kover-release-conventions")
12+
13+
alias(libs.plugins.mavenPluginDevelopment)
14+
}
15+
16+
17+
repositories {
18+
mavenCentral()
19+
}
20+
21+
sourceSets {
22+
create("functionalTest")
23+
}
24+
25+
// name of configuration for functionalTest source set with implementation dependencies
26+
val functionalTestImplementation = "functionalTestImplementation"
27+
28+
29+
dependencies {
30+
implementation(project(":kover-features-jvm"))
31+
implementation(project(":kover-jvm-agent"))
32+
33+
snapshotRelease(project(":kover-features-jvm"))
34+
snapshotRelease(project(":kover-jvm-agent"))
35+
36+
compileOnly(libs.maven.plugin.annotations)
37+
compileOnly(libs.maven.core)
38+
implementation(libs.maven.reporting.api)
39+
40+
functionalTestImplementation(libs.maven.embedder)
41+
functionalTestImplementation(libs.maven.compat)
42+
43+
44+
functionalTestImplementation(libs.maven.resolver.basic)
45+
functionalTestImplementation(libs.maven.resolver.file)
46+
functionalTestImplementation(libs.maven.resolver.http)
47+
functionalTestImplementation(libs.maven.slf4j.api)
48+
functionalTestImplementation(libs.maven.slf4j.provider)
49+
50+
51+
functionalTestImplementation(kotlin("test"))
52+
functionalTestImplementation(libs.junit.jupiter)
53+
functionalTestImplementation(libs.junit.params)
54+
}
55+
56+
mavenPlugin {
57+
goalPrefix = "kover"
58+
}
59+
60+
kotlin {
61+
jvmToolchain {
62+
languageVersion.set(JavaLanguageVersion.of(8))
63+
}
64+
}
65+
66+
val functionalTest by tasks.registering(Test::class) {
67+
group = LifecycleBasePlugin.VERIFICATION_GROUP
68+
testClassesDirs = sourceSets["functionalTest"].output.classesDirs
69+
classpath = sourceSets["functionalTest"].runtimeClasspath
70+
71+
// use JUnit 5
72+
useJUnitPlatform()
73+
74+
dependsOn(tasks.collectRepository)
75+
76+
val localRepository = layout.buildDirectory.dir("maven-collected")
77+
doFirst {
78+
systemProperties["kotlinVersion"] = embeddedKotlinVersion
79+
systemProperties["koverVersion"] = version
80+
81+
val dir = localRepository.get().asFile
82+
dir.deleteRecursively()
83+
dir.mkdirs()
84+
85+
tasks.collectRepository.get().repositories.forEach { repository ->
86+
repository.copyRecursively(dir)
87+
}
88+
89+
systemProperties["snapshotRepository"] = dir.absolutePath
90+
}
91+
}
92+
93+
tasks.check {
94+
dependsOn(functionalTest)
95+
}
96+
97+
koverDocs {
98+
docsDirectory.set("maven-plugin")
99+
description.set("Kover Maven Plugin")
100+
callDokkaHtml.set(false)
101+
}

0 commit comments

Comments
 (0)