Skip to content

Commit

Permalink
Added reports filtering
Browse files Browse the repository at this point in the history
Resolves #17
  • Loading branch information
shanshin committed Jan 14, 2022
1 parent de1452a commit be7cbbb
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 80 deletions.
39 changes: 37 additions & 2 deletions README.md
Expand Up @@ -204,11 +204,17 @@ the project in which the plugin is applied (usually this is the root project):
tasks.koverMergedHtmlReport {
isEnabled = true // false to disable report generation
htmlReportDir.set(layout.buildDirectory.dir("my-merged-report/html-result"))

includes = listOf("com.example.*") // inclusion rules for classes
excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
}

tasks.koverMergedXmlReport {
isEnabled = true // false to disable report generation
xmlReportFile.set(layout.buildDirectory.file("my-merged-report/result.xml"))

includes = listOf("com.example.*") // inclusion rules for classes
excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
}
```
</details>
Expand All @@ -220,11 +226,17 @@ tasks.koverMergedXmlReport {
tasks.koverMergedHtmlReport {
enabled = true // false to disable report generation
htmlReportDir.set(layout.buildDirectory.dir("my-merged-report/html-result"))
includes = ['com.example.*'] // inclusion rules for classes
excludes = ['com.example.subpackage.*'] // exclusion rules for classes
}
tasks.koverMergedXmlReport {
enabled = true // false to disable report generation
xmlReportFile.set(layout.buildDirectory.file("my-merged-report/result.xml"))
includes = ['com.example.*'] // inclusion rules for classes
excludes = ['com.example.subpackage.*'] // exclusion rules for classes
}
```
</details>
Expand All @@ -241,11 +253,17 @@ the corresponding tasks in this project:
tasks.koverHtmlReport {
isEnabled = true // false to disable report generation
htmlReportDir.set(layout.buildDirectory.dir("my-project-report/html-result"))

includes = listOf("com.example.*") // inclusion rules for classes
excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
}

tasks.koverXmlReport {
isEnabled = true // false to disable report generation
xmlReportFile.set(layout.buildDirectory.file("my-project-report/result.xml"))

includes = listOf("com.example.*") // inclusion rules for classes
excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
}
```
</details>
Expand All @@ -257,11 +275,16 @@ tasks.koverXmlReport {
tasks.koverHtmlReport {
enabled = true // false to disable report generation
htmlReportDir.set(layout.buildDirectory.dir("my-project-report/html-result"))
includes = ['com.example.*'] // inclusion rules for classes
excludes = ['com.example.subpackage.*'] // exclusion rules for classes
}
tasks.koverXmlReport {
enabled = true // false to disable report generation
xmlReportFile.set(layout.buildDirectory.file("my-project-report/result.xml"))
includes = ['com.example.*'] // inclusion rules for classes
excludes = ['com.example.subpackage.*'] // exclusion rules for classes
}
```
</details>
Expand Down Expand Up @@ -308,7 +331,7 @@ kover {
coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ) // change instrumentation agent and reporter
intellijEngineVersion.set("1.0.640") // change version of IntelliJ agent and reporter
jacocoEngineVersion.set("0.8.7") // change version of JaCoCo agent and reporter
generateReportOnCheck.set(true) // false to do not execute `koverMergedReport` task before `check` task
generateReportOnCheck = true // false to do not execute `koverMergedReport` task before `check` task
disabledProjects = setOf() // setOf("project-name") to disable coverage for project with name `project-name`
instrumentAndroidPackage = false // true to instrument packages `android.*` and `com.android.*`
runAllTestsForProjectTask = false // true to run all tests in all projects if `koverHtmlReport`, `koverXmlReport`, `koverReport`, `koverVerify` or `check` tasks executed on some project
Expand All @@ -325,7 +348,7 @@ kover {
coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ) // change instrumentation agent and reporter
intellijEngineVersion.set('1.0.640') // change version of IntelliJ agent and reporter
jacocoEngineVersion.set('0.8.7') // change version of JaCoCo agent and reporter
generateReportOnCheck.set(true) // false to do not execute `koverMergedReport` task before `check` task
generateReportOnCheck = true // false to do not execute `koverMergedReport` task before `check` task
disabledProjects = [] // ["project-name"] to disable coverage for project with name `project-name`
instrumentAndroidPackage = false // true to instrument packages `android.*` and `com.android.*`
runAllTestsForProjectTask = false // true to run all tests in all projects if `koverHtmlReport`, `koverXmlReport`, `koverReport`, `koverVerify` or `check` tasks executed on some project
Expand All @@ -349,6 +372,9 @@ is applied (usually this is the root project):

```kotlin
tasks.koverMergedVerify {
includes = listOf("com.example.*") // inclusion rules for classes
excludes = listOf("com.example.subpackage.*") // exclusion rules for classes

rule {
name = "Minimum number of lines covered"
bound {
Expand Down Expand Up @@ -380,6 +406,9 @@ tasks.koverMergedVerify {

```groovy
tasks.koverMergedVerify {
includes = ['com.example.*'] // inclusion rules for classes
excludes = ['com.example.subpackage.*'] // exclusion rules for classes
rule {
name = "Minimum number of lines covered"
bound {
Expand Down Expand Up @@ -413,6 +442,9 @@ To add rules for code coverage checks for the code of one specific project, you

```kotlin
tasks.koverVerify {
includes = listOf("com.example.*") // inclusion rules for classes
excludes = listOf("com.example.subpackage.*") // exclusion rules for classes

rule {
name = "Minimal line coverage rate in percent"
bound {
Expand All @@ -428,6 +460,9 @@ tasks.koverVerify {

```groovy
tasks.koverVerify {
includes = ['com.example.*'] // inclusion rules for classes
excludes = ['com.example.subpackage.*'] // exclusion rules for classes
rule {
name = "Minimal line coverage rate in percent"
bound {
Expand Down
@@ -0,0 +1,62 @@
package kotlinx.kover.test.functional.cases

import kotlinx.kover.test.functional.cases.utils.*
import kotlinx.kover.test.functional.core.*
import kotlinx.kover.test.functional.core.BaseGradleScriptTest
import kotlin.test.*

internal class ReportsFilteringTests : BaseGradleScriptTest() {

@Test
fun testExclude() {
builder("Test exclusion of classes from XML report")
.languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
.sources("simple")
.config(
"""
tasks.koverMergedXmlReport {
excludes = listOf("org.jetbrains.*Exa?ple*")
}""".trimIndent(),
"""
tasks.koverMergedXmlReport {
excludes = ['org.jetbrains.*Exa?ple*']
}""".trimIndent()
)
.build()
.run("build") {
xml(defaultXmlReport()) {
assertCounterAbsent(classCounter("org.jetbrains.ExampleClass"))
assertCounterCovered(classCounter("org.jetbrains.SecondClass"))
}
}
}

@Test
fun testExcludeInclude() {
builder("Test inclusion and exclusion of classes in XML report")
.languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
.sources("simple")
.config(
"""
tasks.koverMergedXmlReport {
includes = listOf("org.jetbrains.*Cla?s")
excludes = listOf("org.jetbrains.*Exa?ple*")
}""".trimIndent(),

"""
tasks.koverMergedXmlReport {
includes = ['org.jetbrains.*Cla?s']
excludes = ['org.jetbrains.*Exa?ple*']
}""".trimIndent()
)
.build()
.run("build") {
xml(defaultXmlReport()) {
assertCounterAbsent(classCounter("org.jetbrains.ExampleClass"))
assertCounterAbsent(classCounter("org.jetbrains.Unused"))
assertCounterFullyCovered(classCounter("org.jetbrains.SecondClass"))
}
}
}

}
Expand Up @@ -117,7 +117,7 @@ private open class ProjectBuilderImpl<B : ProjectBuilder<B>>(val projectState: P
}

override fun config(kotlin: String, groovy: String): B {
projectState.testScripts += GradleScript(kotlin, groovy)
projectState.scripts += GradleScript(kotlin, groovy)
return this as B
}

Expand Down
5 changes: 0 additions & 5 deletions src/main/kotlin/kotlinx/kover/KoverPlugin.kt
Expand Up @@ -33,7 +33,6 @@ import org.gradle.api.provider.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.testing.*
import org.gradle.process.*
import java.io.*
import kotlin.reflect.*

class KoverPlugin : Plugin<Project> {
Expand Down Expand Up @@ -86,8 +85,6 @@ class KoverPlugin : Plugin<Project> {
projectProviders
) {
it.onlyIf { t -> (t as KoverVerificationTask).rules.isNotEmpty() }
// kover takes counter values from XML file. Remove after reporter upgrade
it.mustRunAfter(xmlReportTask)
it.description = "Verifies code coverage metrics of one project based on specified rules."
}

Expand Down Expand Up @@ -141,8 +138,6 @@ class KoverPlugin : Plugin<Project> {
providers
) {
it.onlyIf { t -> (t as KoverMergedVerificationTask).rules.isNotEmpty() }
// kover takes counter values from XML file. Remove after reporter upgrade
it.mustRunAfter(xmlReportTask)
it.description = "Verifies code coverage metrics of all projects based on specified rules."
}

Expand Down
8 changes: 6 additions & 2 deletions src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt
Expand Up @@ -29,7 +29,9 @@ open class KoverTaskExtension(objects: ObjectFactory) {
public val binaryReportFile: Property<File> = objects.property(File::class.java)

/**
* Specifies class inclusion rules coverage engine. Exclusion rules have priority over inclusion ones.
* Specifies class instrumentation inclusion rules.
* Only the specified classes may be instrumented, for the remaining classes there will be zero coverage.
* Exclusion rules have priority over inclusion ones.
*
* Inclusion rules are represented as a set of fully-qualified names of the classes being instrumented.
* It's possible to use `*` and `?` wildcards.
Expand All @@ -38,7 +40,9 @@ open class KoverTaskExtension(objects: ObjectFactory) {
public var includes: List<String> = emptyList()

/**
* Specifies class exclusion rules for coverage engine. Exclusion rules have priority over inclusion ones.
* Specifies class instrumentation exclusion rules.
* The specified classes will not be instrumented and there will be zero coverage for them.
* Exclusion rules have priority over inclusion ones.
*
* Exclusion rules are represented as a set of fully-qualified names of the classes being instrumented.
* It's possible to use `*` and `?` wildcards.
Expand Down
21 changes: 21 additions & 0 deletions src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt
Expand Up @@ -4,3 +4,24 @@ import java.io.*

internal class Report(val files: List<File>, val projects: List<ProjectInfo>)
internal class ProjectInfo(val sources: Iterable<File>, val outputs: Iterable<File>)

private val regexMetacharactersSet = "<([{\\^-=$!|]})+.>".toSet()

/**
* Replaces characters `*` or `.` to `.*` and `.` regexp characters.
*/
internal fun String.wildcardsToRegex(): String {
// in most cases, the characters `*` or `.` will be present therefore, we increase the capacity in advance
val builder = StringBuilder(length * 2)

forEach { char ->
when (char) {
in regexMetacharactersSet -> builder.append('\\').append(char)
'*' -> builder.append('.').append("*")
'?' -> builder.append('.')
else -> builder.append(char)
}
}

return builder.toString()
}
23 changes: 3 additions & 20 deletions src/main/kotlin/kotlinx/kover/engines/intellij/IntellijAgent.kt
Expand Up @@ -5,6 +5,7 @@
package kotlinx.kover.engines.intellij

import kotlinx.kover.api.*
import kotlinx.kover.engines.commons.*
import kotlinx.kover.engines.commons.CoverageAgent
import org.gradle.api.*
import org.gradle.api.artifacts.*
Expand Down Expand Up @@ -51,38 +52,20 @@ private class IntellijAgent(private val config: Configuration): CoverageAgent {
pw.appendLine(appendToDataFile.toString())
pw.appendLine(samplingMode.toString())
extension.includes.forEach { i ->
pw.appendLine(i.replaceWildcards())
pw.appendLine(i.wildcardsToRegex())
}

if (extension.excludes.isNotEmpty()) {
pw.appendLine("-exclude")
}

extension.excludes.forEach { e ->
pw.appendLine(e.replaceWildcards())
pw.appendLine(e.wildcardsToRegex())
}
}
}

private fun String.replaceWildcards(): String {
// in most cases, the characters `*` or `.` will be present therefore, we increase the capacity in advance
val builder = StringBuilder(length * 2)

forEach { char ->
when (char) {
in regexMetacharactersSet -> builder.append('\\').append(char)
'*' -> builder.append('.').append("*")
'?' -> builder.append('.')
else -> builder.append(char)
}
}

return builder.toString()
}
}

private val regexMetacharactersSet = "<([{\\^-=$!|]})+.>".toSet()

private fun Project.createIntellijConfig(koverExtension: KoverExtension): Configuration {
val config = project.configurations.create("IntellijKoverConfig")
config.isVisible = false
Expand Down

0 comments on commit be7cbbb

Please sign in to comment.