From 633aebf3afb7618ef61e4fb670aa62bd0b8b1c2f Mon Sep 17 00:00:00 2001 From: Sergey Shanshin Date: Wed, 31 Aug 2022 14:41:46 +0300 Subject: [PATCH] Added JaCoCo reports filtering Fixes #220 --- .../functional/cases/ReportsFilteringTests.kt | 4 +-- .../kover/engines/commons/EngineManager.kt | 4 +-- .../kotlinx/kover/engines/commons/Reports.kt | 6 ++++ .../kover/engines/jacoco/JacocoReports.kt | 32 ++++++++++++++++--- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsFilteringTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsFilteringTests.kt index 8d5e4ce2..c7104086 100644 --- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsFilteringTests.kt +++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsFilteringTests.kt @@ -9,7 +9,7 @@ internal class ReportsFilteringTests : BaseGradleScriptTest() { @Test fun testExclude() { - val build = diverseBuild(languages = ALL_LANGUAGES) + val build = diverseBuild(languages = ALL_LANGUAGES, engines = ALL_ENGINES) build.addKoverRootProject { sourcesFrom("simple") @@ -32,7 +32,7 @@ internal class ReportsFilteringTests : BaseGradleScriptTest() { @Test fun testExcludeInclude() { - val build = diverseBuild(languages = ALL_LANGUAGES) + val build = diverseBuild(languages = ALL_LANGUAGES, engines = ALL_ENGINES) build.addKoverRootProject { sourcesFrom("simple") diff --git a/src/main/kotlin/kotlinx/kover/engines/commons/EngineManager.kt b/src/main/kotlin/kotlinx/kover/engines/commons/EngineManager.kt index 37506408..c3ff5558 100644 --- a/src/main/kotlin/kotlinx/kover/engines/commons/EngineManager.kt +++ b/src/main/kotlin/kotlinx/kover/engines/commons/EngineManager.kt @@ -43,7 +43,7 @@ internal object EngineManager { if (details.variant.vendor == CoverageEngineVendor.INTELLIJ) { task.intellijReport(exec, projectFiles, classFilter, xmlFile, htmlDir, details.classpath) } else { - task.jacocoReport(projectFiles, xmlFile, htmlDir, details.classpath) + task.jacocoReport(projectFiles, classFilter, xmlFile, htmlDir, details.classpath) } } @@ -58,7 +58,7 @@ internal object EngineManager { return if (details.variant.vendor == CoverageEngineVendor.INTELLIJ) { task.intellijVerification(exec, projectFiles, classFilter, rules, details.classpath) } else { - task.jacocoVerification(projectFiles, rules, details.classpath) + task.jacocoVerification(projectFiles, classFilter, rules, details.classpath) } } diff --git a/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt b/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt index 6da66ca6..72fca73c 100644 --- a/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt +++ b/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt @@ -5,6 +5,7 @@ package kotlinx.kover.engines.commons import kotlinx.kover.api.* +import java.io.File import java.math.BigDecimal @@ -26,6 +27,11 @@ internal class ReportVerificationBound( private val regexMetacharactersSet = "<([{\\^-=$!|]})+.>".toSet() +internal fun String.wildcardsToClassFileRegex(): String { + val filenameWithWildcards = "*" + File.separatorChar + this.replace('.', File.separatorChar) + ".class" + return filenameWithWildcards.wildcardsToRegex() +} + /** * Replaces characters `*` or `.` to `.*` and `.` regexp characters. */ diff --git a/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt b/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt index 86aed395..ce956014 100644 --- a/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt +++ b/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt @@ -6,8 +6,9 @@ package kotlinx.kover.engines.jacoco import groovy.lang.* import kotlinx.kover.api.* -import kotlinx.kover.engines.commons.ReportVerificationRule +import kotlinx.kover.engines.commons.* import kotlinx.kover.engines.commons.ONE_HUNDRED +import kotlinx.kover.engines.commons.ReportVerificationRule import kotlinx.kover.tasks.* import org.gradle.api.* import org.gradle.api.file.* @@ -18,11 +19,12 @@ import java.util.* internal fun Task.jacocoReport( projectFiles: Map, + filters: KoverClassFilter, xmlFile: File?, htmlDir: File?, classpath: FileCollection ) { - callJacocoAntReportTask(projectFiles, classpath) { + callJacocoAntReportTask(projectFiles, filters, classpath) { if (xmlFile != null) { xmlFile.parentFile.mkdirs() invokeMethod("xml", mapOf("destfile" to xmlFile)) @@ -37,10 +39,11 @@ internal fun Task.jacocoReport( internal fun Task.jacocoVerification( projectFiles: Map, + filters: KoverClassFilter, rules: List, classpath: FileCollection ): String? { - callJacocoAntReportTask(projectFiles, classpath) { + callJacocoAntReportTask(projectFiles, filters, classpath) { invokeWithBody("check", mapOf("failonviolation" to "false", "violationsproperty" to "jacocoErrors")) { rules.forEach { val entityType = when(it.target) { @@ -97,6 +100,7 @@ internal fun Task.jacocoVerification( private fun Task.callJacocoAntReportTask( projectFiles: Map, + filters: KoverClassFilter, classpath: FileCollection, block: GroovyObject.() -> Unit ) { @@ -119,6 +123,26 @@ private fun Task.callJacocoAntReportTask( outputs += pf.value.outputs } + + val filteredOutput = if (filters.excludes.isNotEmpty() || filters.includes.isNotEmpty()) { + val excludeRegexes = filters.excludes.map { Regex(it.wildcardsToClassFileRegex()) } + val includeRegexes = filters.includes.map { Regex(it.wildcardsToClassFileRegex()) } + val trees = outputs.map { + project.fileTree(it).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) } + } + } + project.files(trees) + } else { + project.files(outputs) + } + + builder.invokeWithBody("jacocoReport") { invokeWithBody("executiondata") { project.files(binaries).addToAntBuilder(this, "resources") @@ -128,7 +152,7 @@ private fun Task.callJacocoAntReportTask( project.files(sources).addToAntBuilder(this, "resources") } invokeWithBody("classfiles") { - project.files(outputs).addToAntBuilder(this, "resources") + filteredOutput.addToAntBuilder(this, "resources") } } block()