Skip to content

Commit

Permalink
Fixed package exclusion in reports for JaCoCo
Browse files Browse the repository at this point in the history
Class filtering in JaCoCo took place by file name. Because previously the absolute path to the class file was taken, the filter worked on any occurrence of the specified string, even if it was no match starting from the root package.

Now the search takes place relative to the classes root directory, which allows you to change the regular expression of the search, and remove arbitrary characters at the beginning in it.

Fixes #543
PR #546
  • Loading branch information
shanshin committed Feb 16, 2024
1 parent 5380bc9 commit 896699e
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 16 deletions.
Expand Up @@ -147,4 +147,33 @@ internal class ReportsFilteringTests {
}
}


/**
* Check that when excluding packages, the excluding occurs starting from the root package,
* and there is no search for any middle occurrence of the specified string.
*
* See https://github.com/Kotlin/kotlinx-kover/issues/543
*/
@SlicedGeneratedTest(allTools = true)
fun BuildConfigurator.testPackageInTheMiddle() {
addProjectWithKover {
sourcesFrom("different-packages")

koverReport {
filters {
excludes {
packages("foo")
}
}

}
}
run("koverXmlReport") {
xmlReport {
classCounter("foo.bar.FooClass").assertAbsent()
classCounter("org.jetbrains.foo.ExampleClass").assertCovered()
}
}
}

}
@@ -0,0 +1,8 @@
package foo.bar

class FooClass {
fun function() {
println("Hello")
}

}
@@ -0,0 +1,11 @@
package org.jetbrains.foo

class ExampleClass {
fun used(value: Int): Int {
return value + 1
}

fun unused(value: Long): Long {
return value - 1
}
}
@@ -0,0 +1,14 @@
package org.jetbrains.serialuser

import org.jetbrains.foo.ExampleClass
import foo.bar.FooClass
import kotlin.test.Test

class TestClass {
@Test
fun simpleTest() {
ExampleClass().used(-20)
FooClass().function()
}

}
Expand Up @@ -7,7 +7,8 @@ package kotlinx.kover.gradle.plugin.tools.jacoco
import groovy.lang.Closure
import groovy.lang.GroovyObject
import kotlinx.kover.gradle.plugin.commons.ReportContext
import kotlinx.kover.gradle.plugin.util.wildcardsToClassFileRegex
import kotlinx.kover.gradle.plugin.util.wildcardsToRegex
import java.io.File


internal fun ReportContext.callAntReport(
Expand All @@ -28,14 +29,18 @@ internal fun ReportContext.callAntReport(
val filteredOutput = if (filters.excludesClasses.isNotEmpty() || filters.includesClasses.isNotEmpty()) {
val excludeRegexes = filters.excludesClasses.map { Regex(it.wildcardsToClassFileRegex()) }
val includeRegexes = filters.includesClasses.map { Regex(it.wildcardsToClassFileRegex()) }
services.objects.fileCollection().from(files.outputs).asFileTree.filter { file ->
// the `canonicalPath` is used because a `File.separatorChar` was used to construct the class-file regex
val path = file.canonicalPath
// if the inclusion rules are declared, then the file must fit at least one of them
(includeRegexes.isEmpty() || includeRegexes.any { regex -> path.matches(regex) })
// if the exclusion rules are declared, then the file should not fit any of them
&& excludeRegexes.none { regex -> path.matches(regex) }

val outputCollections = files.outputs.map { output ->
services.objects.fileCollection().from(output).asFileTree.filter { file ->
// the `canonicalPath` is used because a `File.separatorChar` was used to construct the class-file regex
val path = file.toRelativeString(output)
// if the inclusion rules are declared, then the file must fit at least one of them
(includeRegexes.isEmpty() || includeRegexes.any { regex -> path.matches(regex) })
// if the exclusion rules are declared, then the file should not fit any of them
&& excludeRegexes.none { regex -> path.matches(regex) }
}
}
services.objects.fileCollection().from(outputCollections)
} else {
services.objects.fileCollection().from(files.outputs)
}
Expand Down Expand Up @@ -77,3 +82,12 @@ internal inline fun GroovyObject.invokeWithBody(
)
)
}

/**
* Replaces characters `.` to `|` or `\` and added `.class` as postfix and `.* /` or `.*\` as prefix.
*/
private fun String.wildcardsToClassFileRegex(): String {
val filenameWithWildcards = this.replace('.', File.separatorChar) + ".class"
return filenameWithWildcards.wildcardsToRegex()
}

Expand Up @@ -31,14 +31,6 @@ internal inline fun <T : Any> Boolean.ifFalse(block: () -> T): T? {
}
}

/**
* Replaces characters `.` to `|` or `\` and added `.class` as postfix and `.* /` or `.*\` as prefix.
*/
internal fun String.wildcardsToClassFileRegex(): String {
val filenameWithWildcards = "*" + File.separatorChar + this.replace('.', File.separatorChar) + ".class"
return filenameWithWildcards.wildcardsToRegex()
}

/**
* Replaces characters `*` or `.` to `.*` and `.` regexp characters.
*/
Expand Down

0 comments on commit 896699e

Please sign in to comment.