Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to specify inclusion filter for source sets #726

Merged
merged 6 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions kover-gradle-plugin/api/kover-gradle-plugin.api
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantCrea
public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantSources {
public abstract fun getExcludeJava ()Lorg/gradle/api/provider/Property;
public abstract fun getExcludedSourceSets ()Lorg/gradle/api/provider/SetProperty;
public abstract fun getIncludedSourceSets ()Lorg/gradle/api/provider/SetProperty;
}

public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerificationRulesConfig {
Expand Down
31 changes: 29 additions & 2 deletions kover-gradle-plugin/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1122,8 +1122,6 @@ By specifying an empty filter `filters { }`, you can completely disable report f

It is possible to exclude from all reports the code declared in certain source sets.

As a side effect, the generation of Kover reports ceases to depend on the compilation tasks of these source sets.

```kotlin
kover {
currentProject {
Expand All @@ -1134,6 +1132,35 @@ kover {
}
```

In addition, it is possible to exclude classes from all source sets that are not specified.

```kotlin
kover {
currentProject {
sources {
includedSourceSets.addAll("main")
}
}
}
```

This way, only classes from the `main` source set will be included in the report, but not from others.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this includedSourceSets then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least excludeEverySourceSetExcept

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to get away from naming included. On the other hand, such things are now called included in the entire DSL.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let it be included for now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid we can move from this naming only for all functions at once, when we'll migrate whole Kover plugin

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I took this into account


These filters can be combined, the `excludedSourceSets` filter has priority.
```kotlin
kover {
currentProject {
sources {
includedSourceSets.addAll("main", "extra")
excludedSourceSets.addAll("main")
}
}
}
```
In this case, only classes from the `extra` source set will be included in the report.

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.

### Exclusion of test tasks
If some task does not test the coverage of application classes,
or it is necessary to exclude its coverage from reports, then specify this task in the excluded list.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.kover.gradle.plugin.test.functional.cases

import kotlinx.kover.gradle.plugin.test.functional.framework.checker.createCheckerContext
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.buildFromTemplate
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.runWithParams
import org.junit.jupiter.api.Test
import kotlin.test.assertTrue

internal class SourceSetsTests {

@Test
fun testExclude() {
val template = buildFromTemplate("sourcesets-multi")

template.kover {
currentProject {
sources.excludedSourceSets.addAll("main", "foo")
}
}

val gradleBuild = template.generate()
val buildResult = gradleBuild.runWithParams(":koverXmlReport")

gradleBuild.createCheckerContext(buildResult).xmlReport {
assertTrue(buildResult.isSuccessful, "Build should be successful")
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
}
}

@Test
fun testInclude() {
val template = buildFromTemplate("sourcesets-multi")

template.kover {
currentProject {
sources.includedSourceSets.addAll("extra")
}
}

val gradleBuild = template.generate()
val buildResult = gradleBuild.runWithParams(":koverXmlReport")

gradleBuild.createCheckerContext(buildResult).xmlReport {
assertTrue(buildResult.isSuccessful, "Build should be successful")
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
}
}

@Test
fun testInclude2() {
val template = buildFromTemplate("sourcesets-multi")

template.kover {
currentProject {
sources.includedSourceSets.addAll("extra", "foo")
}
}

val gradleBuild = template.generate()
val buildResult = gradleBuild.runWithParams(":koverXmlReport")

gradleBuild.createCheckerContext(buildResult).xmlReport {
assertTrue(buildResult.isSuccessful, "Build should be successful")
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertPresent()
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
}
}

@Test
fun testMixed() {
val template = buildFromTemplate("sourcesets-multi")

template.kover {
currentProject {
sources {
includedSourceSets.addAll("extra", "foo")
excludedSourceSets.add("foo")
}
}
}

val gradleBuild = template.generate()
val buildResult = gradleBuild.runWithParams(":koverXmlReport")

gradleBuild.createCheckerContext(buildResult).xmlReport {
assertTrue(buildResult.isSuccessful, "Build should be successful")
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertPresent()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add classCounter("kotlinx.kover.examples.sourcesets.TestClasses") to this test and all above

}
}

@Test
fun testTestSourceSet() {
val template = buildFromTemplate("sourcesets-multi")

template.kover {
currentProject {
sources {
includedSourceSets.addAll("test")
}
}
}

val gradleBuild = template.generate()
val buildResult = gradleBuild.runWithParams(":koverXmlReport")

gradleBuild.createCheckerContext(buildResult).xmlReport {
assertTrue(buildResult.isSuccessful, "Build should be successful")
classCounter("kotlinx.kover.examples.sourcesets.MainClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.FooClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.ExtraClass").assertAbsent()
classCounter("kotlinx.kover.examples.sourcesets.TestClasses").assertPresent()
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ private class CounterImpl(
assertNull(values, "Counter '$symbol' with type '$type' isn't absent")
}

override fun assertPresent() {
assertNotNull(values, "Counter for '$symbol' with type '$type' isn't present in report")
}

override fun assertFullyMissed() {
assertNotNull(values, "Counter '$symbol' with type '$type' isn't fully missed because it absent")
assertTrue(values.missed > 0, "Counter '$symbol' with type '$type' isn't fully missed")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ internal interface ProjectAnalysisData {
internal interface Counter {
fun assertAbsent()

fun assertPresent()

fun assertFullyMissed()

fun assertCovered()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

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

import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
import kotlinx.kover.gradle.plugin.test.functional.framework.common.BuildSlice
import kotlinx.kover.gradle.plugin.test.functional.framework.common.ScriptLanguage
import kotlinx.kover.gradle.plugin.test.functional.framework.common.isDebugEnabled
import kotlinx.kover.gradle.plugin.test.functional.framework.common.logInfo
import kotlinx.kover.gradle.plugin.test.functional.framework.common.uri
import kotlinx.kover.gradle.plugin.test.functional.framework.configurator.ProjectScope
import kotlinx.kover.gradle.plugin.test.functional.framework.mirroring.printGradleDsl
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.*
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.buildSrc
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.patchSettingsFile
Expand All @@ -31,6 +36,8 @@ internal interface BuildSource {

fun from(rootProjectDir: File)

fun kover(config: KoverProjectExtension.(ProjectScope) -> Unit)

fun generate(): GradleBuild
}

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

private var copy: Boolean = false

private val koverBlocks: MutableList<(ScriptLanguage, String) -> String> = mutableListOf()

override var buildName: String = "default"

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

override fun kover(config: KoverProjectExtension.(ProjectScope) -> Unit) {
koverBlocks += { language, gradle ->
printGradleDsl<KoverProjectExtension, ProjectScope>(language, gradle, "kover", config)
}
}

override fun generate(): GradleBuild {
val actualDir = dir ?: throw Exception("No source was specified for the build")
val targetDir = if (copy) {
Expand All @@ -88,6 +103,9 @@ private class BuildSourceImpl(val snapshotRepos: List<String>, val koverVersion:
val buildSrcScript = targetDir.buildSrc?.build
buildSrcScript?.patchKoverDependency(koverVersion)

val buildScript = targetDir.build
buildScript.addKoverBlocks(koverBlocks)

return GradleBuildImpl(targetDir, copy, buildName, buildType)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,17 @@ internal fun File.patchKoverDependency(koverVersion: String) {
}
}
}

internal fun File.addKoverBlocks(koverBlocks: MutableList<(ScriptLanguage, String) -> String>) {
if (koverBlocks.isEmpty()) return

val language = if (name.endsWith(".kts")) ScriptLanguage.KTS else ScriptLanguage.GROOVY

val builder = StringBuilder()
koverBlocks.forEach { block ->
builder.appendLine()
builder.append(block(language, "8.12"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 8.12 a Gradle version? Shouldn't it be some constant?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, any version of Gradle with support for assign operator redefinition is needed here.

builder.appendLine()
}
appendText(builder.toString())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
kotlin("jvm") version "2.1.0"
id("org.jetbrains.kotlinx.kover") version "0.7.0"
}

sourceSets.create("extra")
sourceSets.create("foo")

dependencies {
testImplementation(kotlin("test"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
rootProject.name = "example-source-multi"

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kotlinx.kover.examples.sourcesets

class ExtraClass {
fun function() {
println("Example")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kotlinx.kover.examples.sourcesets

class FooClass {
fun function() {
println("Example")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kotlinx.kover.examples.sourcesets

class MainClass {
fun function() {
println("Example")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kotlinx.kover.examples.sourcesets

import kotlin.test.Test

class TestClasses {
@Test
fun test() {
// no-tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private fun KoverVariantSources.wrap(project: Project): KoverMergingVariantSourc
return object : KoverMergingVariantSources {
override val excludeJava: Property<Boolean> = this@wrap.excludeJava
override val excludedSourceSets: SetProperty<String> = this@wrap.excludedSourceSets
override val includedSourceSets: SetProperty<String> = this@wrap.includedSourceSets
override val project: Project = project
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kotlinx.kover.gradle.plugin.commons.VariantOriginAttr
import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantConfigImpl
import kotlinx.kover.gradle.plugin.appliers.origin.JvmVariantOrigin
import kotlinx.kover.gradle.plugin.commons.JVM_VARIANT_NAME
import kotlinx.kover.gradle.plugin.dsl.KoverVariantSources
import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl
import kotlinx.kover.gradle.plugin.tools.CoverageTool
import org.gradle.api.Project
Expand Down Expand Up @@ -48,9 +49,25 @@ internal class JvmVariantArtifacts(
}

fromOrigin(variantOrigin) { compilationName ->
compilationName !in variantConfig.sources.excludedSourceSets.get()
&& compilationName != "test"
compilationIsExcluded(compilationName, variantConfig.sources)
}
}

private fun compilationIsExcluded(compilationName: String, variant: KoverVariantSources): Boolean {
if (compilationName in variant.excludedSourceSets.get()) {
return false
}

val included = variant.includedSourceSets.get()

if (included.isEmpty() && compilationName == "test") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are tests excluded by default? Is it mentioned somewhere in the documentation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, test source set excluded by default.

return false
}
if (included.isNotEmpty() && compilationName !in included) {
return false
}

return true
}

}
Loading