Skip to content

Commit 3f6745d

Browse files
authored
Added ability to specify inclusion filter for source sets
Resolves #714 PR #726
1 parent 8e47a67 commit 3f6745d

File tree

17 files changed

+292
-6
lines changed

17 files changed

+292
-6
lines changed

Diff for: kover-gradle-plugin/api/kover-gradle-plugin.api

+1
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantCrea
295295
public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantSources {
296296
public abstract fun getExcludeJava ()Lorg/gradle/api/provider/Property;
297297
public abstract fun getExcludedSourceSets ()Lorg/gradle/api/provider/SetProperty;
298+
public abstract fun getIncludedSourceSets ()Lorg/gradle/api/provider/SetProperty;
298299
}
299300

300301
public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerificationRulesConfig {

Diff for: kover-gradle-plugin/docs/index.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -1120,9 +1120,9 @@ By specifying an empty filter `filters { }`, you can completely disable report f
11201120

11211121
### Exclusion of JVM source sets
11221122

1123-
It is possible to exclude from all reports the code declared in certain source sets.
1123+
Code declarations from `test` source set are excluded by default.
11241124

1125-
As a side effect, the generation of Kover reports ceases to depend on the compilation tasks of these source sets.
1125+
It is possible to exclude from all reports the code declared in certain source sets.
11261126

11271127
```kotlin
11281128
kover {
@@ -1134,6 +1134,35 @@ kover {
11341134
}
11351135
```
11361136

1137+
In addition, it is possible to exclude classes from all source sets that are not specified.
1138+
1139+
```kotlin
1140+
kover {
1141+
currentProject {
1142+
sources {
1143+
includedSourceSets.addAll("main")
1144+
}
1145+
}
1146+
}
1147+
```
1148+
1149+
This way, only classes from the `main` source set will be included in the report, but not from others.
1150+
1151+
These filters can be combined, the `excludedSourceSets` filter has priority.
1152+
```kotlin
1153+
kover {
1154+
currentProject {
1155+
sources {
1156+
includedSourceSets.addAll("main", "extra")
1157+
excludedSourceSets.addAll("main")
1158+
}
1159+
}
1160+
}
1161+
```
1162+
In this case, only classes from the `extra` source set will be included in the report.
1163+
1164+
As a side effect of the source set exclusion, the generation of Kover reports ceases to depend on the compilation tasks of excluded source sets.
1165+
11371166
### Exclusion of test tasks
11381167
If some task does not test the coverage of application classes,
11391168
or it is necessary to exclude its coverage from reports, then specify this task in the excluded list.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.kover.gradle.plugin.test.functional.cases
6+
7+
import kotlinx.kover.gradle.plugin.test.functional.framework.checker.createCheckerContext
8+
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.buildFromTemplate
9+
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.runWithParams
10+
import org.junit.jupiter.api.Test
11+
import kotlin.test.assertTrue
12+
13+
internal class SourceSetsTests {
14+
15+
@Test
16+
fun testExclude() {
17+
val template = buildFromTemplate("sourcesets-multi")
18+
19+
template.kover {
20+
currentProject {
21+
sources.excludedSourceSets.addAll("main", "foo")
22+
}
23+
}
24+
25+
val gradleBuild = template.generate()
26+
val buildResult = gradleBuild.runWithParams(":koverXmlReport")
27+
28+
gradleBuild.createCheckerContext(buildResult).xmlReport {
29+
assertTrue(buildResult.isSuccessful, "Build should be successful")
30+
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
31+
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
32+
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
33+
classCounter("kotlinx.kover.examples.sourcesets.TestClasses").assertAbsent()
34+
}
35+
}
36+
37+
@Test
38+
fun testInclude() {
39+
val template = buildFromTemplate("sourcesets-multi")
40+
41+
template.kover {
42+
currentProject {
43+
sources.includedSourceSets.addAll("extra")
44+
}
45+
}
46+
47+
val gradleBuild = template.generate()
48+
val buildResult = gradleBuild.runWithParams(":koverXmlReport")
49+
50+
gradleBuild.createCheckerContext(buildResult).xmlReport {
51+
assertTrue(buildResult.isSuccessful, "Build should be successful")
52+
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
53+
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
54+
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
55+
classCounter("kotlinx.kover.examples.sourcesets.TestClasses").assertAbsent()
56+
}
57+
}
58+
59+
@Test
60+
fun testInclude2() {
61+
val template = buildFromTemplate("sourcesets-multi")
62+
63+
template.kover {
64+
currentProject {
65+
sources.includedSourceSets.addAll("extra", "foo")
66+
}
67+
}
68+
69+
val gradleBuild = template.generate()
70+
val buildResult = gradleBuild.runWithParams(":koverXmlReport")
71+
72+
gradleBuild.createCheckerContext(buildResult).xmlReport {
73+
assertTrue(buildResult.isSuccessful, "Build should be successful")
74+
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
75+
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertPresent()
76+
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
77+
classCounter("kotlinx.kover.examples.sourcesets.TestClasses").assertAbsent()
78+
}
79+
}
80+
81+
@Test
82+
fun testMixed() {
83+
val template = buildFromTemplate("sourcesets-multi")
84+
85+
template.kover {
86+
currentProject {
87+
sources {
88+
includedSourceSets.addAll("extra", "foo")
89+
excludedSourceSets.add("foo")
90+
}
91+
}
92+
}
93+
94+
val gradleBuild = template.generate()
95+
val buildResult = gradleBuild.runWithParams(":koverXmlReport")
96+
97+
gradleBuild.createCheckerContext(buildResult).xmlReport {
98+
assertTrue(buildResult.isSuccessful, "Build should be successful")
99+
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
100+
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
101+
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
102+
classCounter("kotlinx.kover.examples.sourcesets.TestClasses").assertAbsent()
103+
}
104+
}
105+
106+
@Test
107+
fun testTestSourceSet() {
108+
val template = buildFromTemplate("sourcesets-multi")
109+
110+
template.kover {
111+
currentProject {
112+
sources {
113+
includedSourceSets.addAll("test")
114+
}
115+
}
116+
}
117+
118+
val gradleBuild = template.generate()
119+
val buildResult = gradleBuild.runWithParams(":koverXmlReport")
120+
121+
gradleBuild.createCheckerContext(buildResult).xmlReport {
122+
assertTrue(buildResult.isSuccessful, "Build should be successful")
123+
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
124+
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
125+
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertAbsent()
126+
classCounter("kotlinx.kover.examples.sourcesets.TestClasses").assertPresent()
127+
}
128+
}
129+
130+
131+
}

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/Checker.kt

+4
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ private class CounterImpl(
460460
assertNull(values, "Counter '$symbol' with type '$type' isn't absent")
461461
}
462462

463+
override fun assertPresent() {
464+
assertNotNull(values, "Counter for '$symbol' with type '$type' isn't present in report")
465+
}
466+
463467
override fun assertFullyMissed() {
464468
assertNotNull(values, "Counter '$symbol' with type '$type' isn't fully missed because it absent")
465469
assertTrue(values.missed > 0, "Counter '$symbol' with type '$type' isn't fully missed")

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/CheckerTypes.kt

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ internal interface ProjectAnalysisData {
7373
internal interface Counter {
7474
fun assertAbsent()
7575

76+
fun assertPresent()
77+
7678
fun assertFullyMissed()
7779

7880
fun assertCovered()

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/runner/BuildsRunner.kt

+18
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44

55
package kotlinx.kover.gradle.plugin.test.functional.framework.runner
66

7+
import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
8+
import kotlinx.kover.gradle.plugin.test.functional.framework.common.BuildSlice
9+
import kotlinx.kover.gradle.plugin.test.functional.framework.common.ScriptLanguage
710
import kotlinx.kover.gradle.plugin.test.functional.framework.common.isDebugEnabled
811
import kotlinx.kover.gradle.plugin.test.functional.framework.common.logInfo
912
import kotlinx.kover.gradle.plugin.test.functional.framework.common.uri
13+
import kotlinx.kover.gradle.plugin.test.functional.framework.configurator.ProjectScope
14+
import kotlinx.kover.gradle.plugin.test.functional.framework.mirroring.printGradleDsl
1015
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.*
1116
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.buildSrc
1217
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.patchSettingsFile
@@ -31,6 +36,8 @@ internal interface BuildSource {
3136

3237
fun from(rootProjectDir: File)
3338

39+
fun kover(config: KoverProjectExtension.(ProjectScope) -> Unit)
40+
3441
fun generate(): GradleBuild
3542
}
3643

@@ -54,6 +61,8 @@ private class BuildSourceImpl(val snapshotRepos: List<String>, val koverVersion:
5461

5562
private var copy: Boolean = false
5663

64+
private val koverBlocks: MutableList<(ScriptLanguage, String) -> String> = mutableListOf()
65+
5766
override var buildName: String = "default"
5867

5968
override var buildType: String = "default"
@@ -70,6 +79,12 @@ private class BuildSourceImpl(val snapshotRepos: List<String>, val koverVersion:
7079
copy = false
7180
}
7281

82+
override fun kover(config: KoverProjectExtension.(ProjectScope) -> Unit) {
83+
koverBlocks += { language, gradle ->
84+
printGradleDsl<KoverProjectExtension, ProjectScope>(language, gradle, "kover", config)
85+
}
86+
}
87+
7388
override fun generate(): GradleBuild {
7489
val actualDir = dir ?: throw Exception("No source was specified for the build")
7590
val targetDir = if (copy) {
@@ -88,6 +103,9 @@ private class BuildSourceImpl(val snapshotRepos: List<String>, val koverVersion:
88103
val buildSrcScript = targetDir.buildSrc?.build
89104
buildSrcScript?.patchKoverDependency(koverVersion)
90105

106+
val buildScript = targetDir.build
107+
buildScript.addKoverBlocks(koverBlocks)
108+
91109
return GradleBuildImpl(targetDir, copy, buildName, buildType)
92110
}
93111
}

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Commons.kt

+15
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import java.lang.reflect.Method
2020

2121
private const val DIR_PARAM = "build-directory"
2222
private const val CHECKER_PARAM = "checker-context"
23+
private const val GRADLE_WITH_ASSIGN_OPERATOR_VERSION = "8.12"
2324

2425
internal class RunCommand(val buildSource: BuildSource, val gradleArgs: List<String>)
2526

@@ -183,3 +184,17 @@ internal fun File.patchKoverDependency(koverVersion: String) {
183184
}
184185
}
185186
}
187+
188+
internal fun File.addKoverBlocks(koverBlocks: MutableList<(ScriptLanguage, String) -> String>) {
189+
if (koverBlocks.isEmpty()) return
190+
191+
val language = if (name.endsWith(".kts")) ScriptLanguage.KTS else ScriptLanguage.GROOVY
192+
193+
val builder = StringBuilder()
194+
koverBlocks.forEach { block ->
195+
builder.appendLine()
196+
builder.append(block(language, GRADLE_WITH_ASSIGN_OPERATOR_VERSION))
197+
builder.appendLine()
198+
}
199+
appendText(builder.toString())
200+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
plugins {
2+
kotlin("jvm") version "2.1.0"
3+
id("org.jetbrains.kotlinx.kover") version "0.7.0"
4+
}
5+
6+
sourceSets.create("extra")
7+
sourceSets.create("foo")
8+
9+
dependencies {
10+
testImplementation(kotlin("test"))
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pluginManagement {
2+
repositories {
3+
gradlePluginPortal()
4+
mavenCentral()
5+
}
6+
}
7+
rootProject.name = "example-source-multi"
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package kotlinx.kover.examples.sourcesets
2+
3+
class ExtraClass {
4+
fun function() {
5+
println("Example")
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package kotlinx.kover.examples.sourcesets
2+
3+
class FooClass {
4+
fun function() {
5+
println("Example")
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package kotlinx.kover.examples.sourcesets
2+
3+
class MainClass {
4+
fun function() {
5+
println("Example")
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package kotlinx.kover.examples.sourcesets
2+
3+
import kotlin.test.Test
4+
5+
class TestClasses {
6+
@Test
7+
fun test() {
8+
// no-tests
9+
}
10+
}

Diff for: kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/KoverMerge.kt

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ private fun KoverVariantSources.wrap(project: Project): KoverMergingVariantSourc
9797
return object : KoverMergingVariantSources {
9898
override val excludeJava: Property<Boolean> = this@wrap.excludeJava
9999
override val excludedSourceSets: SetProperty<String> = this@wrap.excludedSourceSets
100+
override val includedSourceSets: SetProperty<String> = this@wrap.includedSourceSets
100101
override val project: Project = project
101102
}
102103
}

Diff for: kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/JvmVariantArtifacts.kt

+19-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import kotlinx.kover.gradle.plugin.commons.VariantOriginAttr
1010
import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantConfigImpl
1111
import kotlinx.kover.gradle.plugin.appliers.origin.JvmVariantOrigin
1212
import kotlinx.kover.gradle.plugin.commons.JVM_VARIANT_NAME
13+
import kotlinx.kover.gradle.plugin.dsl.KoverVariantSources
1314
import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl
1415
import kotlinx.kover.gradle.plugin.tools.CoverageTool
1516
import org.gradle.api.Project
@@ -48,9 +49,25 @@ internal class JvmVariantArtifacts(
4849
}
4950

5051
fromOrigin(variantOrigin) { compilationName ->
51-
compilationName !in variantConfig.sources.excludedSourceSets.get()
52-
&& compilationName != "test"
52+
!compilationIsExcluded(compilationName, variantConfig.sources)
5353
}
5454
}
5555

56+
private fun compilationIsExcluded(compilationName: String, variant: KoverVariantSources): Boolean {
57+
if (compilationName in variant.excludedSourceSets.get()) {
58+
return true
59+
}
60+
61+
val included = variant.includedSourceSets.get()
62+
63+
if (included.isEmpty() && compilationName == "test") {
64+
return true
65+
}
66+
if (included.isNotEmpty() && compilationName !in included) {
67+
return true
68+
}
69+
70+
return false
71+
}
72+
5673
}

0 commit comments

Comments
 (0)