From 997a217fcf5098a7bdd65e4068e972818464414b Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Wed, 13 Apr 2022 00:31:08 +0300 Subject: [PATCH] Implemented the use of the full path to the project for the exclusion Fixes #151 --- README.md | 4 +- gradle.properties | 2 +- .../functional/cases/MultiProjectTests.kt | 29 ++++++++++ src/main/kotlin/kotlinx/kover/KoverPlugin.kt | 54 ++++++++++++++----- src/main/kotlin/kotlinx/kover/Providers.kt | 4 +- .../kotlinx/kover/api/KoverExtension.kt | 6 +++ .../kotlinx/kover/engines/commons/Reports.kt | 2 + .../kover/engines/intellij/IntellijReports.kt | 4 +- .../kover/engines/jacoco/JacocoReports.kt | 8 +-- .../kover/tasks/KoverCollectingTask.kt | 12 +++-- .../kotlinx/kover/tasks/KoverHtmlReport.kt | 4 +- .../kotlinx/kover/tasks/KoverMergedTask.kt | 10 ++-- 12 files changed, 107 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index b77424f1..fa703a4d 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ kover { intellijEngineVersion.set("1.0.656") // change version of IntelliJ agent and reporter jacocoEngineVersion.set("0.8.7") // change version of JaCoCo agent and reporter 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` + disabledProjects = setOf() // setOf("project-name") or setOf(":project-name") to disable coverage for project with path `:project-name` (`:` for the root project) 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 } @@ -349,7 +349,7 @@ kover { intellijEngineVersion.set('1.0.656') // change version of IntelliJ agent and reporter jacocoEngineVersion.set('0.8.7') // change version of JaCoCo agent and reporter generateReportOnCheck = true // false to do not execute `koverMergedReport` task before `check` task - disabledProjects = [] // ["project-name"] to disable coverage for project with name `project-name` + disabledProjects = [] // ["project-name"] or [":project-name"] to disable coverage for project with path `:project-name` (`:` for the root project) 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 } diff --git a/gradle.properties b/gradle.properties index fe4cefa3..172f7ae6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=0.5.0 +version=0.5.1-SNAPSHOT group=org.jetbrains.kotlinx kotlin.code.style=official diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/MultiProjectTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/MultiProjectTests.kt index 953ad8f0..6348c0ea 100644 --- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/MultiProjectTests.kt +++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/MultiProjectTests.kt @@ -145,4 +145,33 @@ internal class MultiProjectTests : BaseGradleScriptTest() { } } } + + @Test + fun testDisableSubprojectByPath() { + builder("Testing disabling one of subproject by path") + .sources("multiproject-user") + .subproject(subprojectName) { + sources("multiproject-common") + } + .configKover { disabledProjects += ":$subprojectName" } + .build() + .run("build", "koverReport") { + checkDefaultBinaryReport() + checkDefaultMergedReports() + checkDefaultReports() + xml(defaultMergedXmlReport()) { + classCounter("org.jetbrains.UserClass").assertFullyCovered() + + // classes from disabled project should not be included in the merged report + classCounter("org.jetbrains.CommonClass").assertAbsent() + classCounter("org.jetbrains.CommonInternalClass").assertAbsent() + } + + subproject(subprojectName) { + checkDefaultBinaryReport(false) + checkDefaultMergedReports(false) + checkDefaultReports(false) + } + } + } } diff --git a/src/main/kotlin/kotlinx/kover/KoverPlugin.kt b/src/main/kotlin/kotlinx/kover/KoverPlugin.kt index f747533e..e8f930ed 100644 --- a/src/main/kotlin/kotlinx/kover/KoverPlugin.kt +++ b/src/main/kotlin/kotlinx/kover/KoverPlugin.kt @@ -55,7 +55,8 @@ class KoverPlugin : Plugin { private fun Project.applyToProject(providers: BuildProviders, agents: Map) { val projectProviders = - providers.projects[name] ?: throw GradleException("Kover: Providers for project '$name' was not found") + providers.projects[path] + ?: throw GradleException("Kover: Providers for project '$name' ('$path') was not found") val xmlReportTask = createKoverProjectTask( XML_REPORT_TASK_NAME, @@ -165,10 +166,10 @@ class KoverPlugin : Plugin { task.group = VERIFICATION_GROUP - providers.projects.forEach { (projectName, m) -> - task.binaryReportFiles.put(projectName, NestedFiles(task.project.objects, m.reports)) - task.srcDirs.put(projectName, NestedFiles(task.project.objects, m.sources)) - task.outputDirs.put(projectName, NestedFiles(task.project.objects, m.output)) + providers.projects.forEach { (projectPath, m) -> + task.binaryReportFiles.put(projectPath, NestedFiles(task.project.objects, m.reports)) + task.srcDirs.put(projectPath, NestedFiles(task.project.objects, m.sources)) + task.outputDirs.put(projectPath, NestedFiles(task.project.objects, m.output)) } task.coverageEngine.set(providers.engine) @@ -199,8 +200,8 @@ class KoverPlugin : Plugin { task.mustRunAfter(xmlReportTask) task.mustRunAfter(htmlReportTask) - task.xmlFiles[proj.name] = xmlReportTask.xmlReportFile - task.htmlDirs[proj.name] = htmlReportTask.htmlReportDir + task.xmlFiles[proj.path] = xmlReportTask.xmlReportFile + task.htmlDirs[proj.path] = htmlReportTask.htmlReportDir } } } @@ -244,6 +245,9 @@ class KoverPlugin : Plugin { extension.coverageEngine.set(CoverageEngine.INTELLIJ) extension.intellijEngineVersion.set(defaultIntellijVersion.toString()) extension.jacocoEngineVersion.set(defaultJacocoVersion) + + afterEvaluate(CollectDisabledProjectsPathsAction(extension)) + return extension } @@ -279,7 +283,7 @@ class KoverPlugin : Plugin { } val targetErrorProvider = project.layout.buildDirectory.file("kover/errors/$name.log").map { it.asFile } - doFirst(BinaryReportCleanupAction(project.name, providers.koverExtension, taskExtension)) + doFirst(BinaryReportCleanupAction(project.path, providers.koverExtension, taskExtension)) doLast(MoveIntellijErrorLogAction(sourceErrorProvider, targetErrorProvider)) } } @@ -289,7 +293,7 @@ class KoverPlugin : Plugin { For this reason, before starting the tests, it is necessary to clear the file from the results of previous runs. */ private class BinaryReportCleanupAction( - private val projectName: String, + private val projectPath: String, private val koverExtensionProvider: Provider, private val taskExtension: KoverTaskExtension ) : Action { @@ -302,7 +306,7 @@ private class BinaryReportCleanupAction( if (!taskExtension.isDisabled && !koverExtension.isDisabled - && !koverExtension.disabledProjects.contains(projectName) + && !koverExtension.disabledProjectsPaths.contains(projectPath) && koverExtension.coverageEngine.get() == CoverageEngine.INTELLIJ ) { // IntelliJ engine expected empty file for parallel test execution. @@ -312,6 +316,32 @@ private class BinaryReportCleanupAction( } } +private class CollectDisabledProjectsPathsAction( + private val koverExtension: KoverExtension, +) : Action { + override fun execute(project: Project) { + val allProjects = project.allprojects + val paths = allProjects.associate { it.name to mutableListOf() } + allProjects.forEach { paths.getValue(it.name) += it.path } + + val result: MutableSet = mutableSetOf() + + koverExtension.disabledProjects.map { + if (it.startsWith(':')) { + result += it + } else { + val projectPaths = paths[it] ?: return@map + if (projectPaths.size > 1) { + throw GradleException("Cover configuring error: ambiguous name of the excluded project '$it': suitable projects with paths $projectPaths") + } + result += projectPaths + } + } + + koverExtension.disabledProjectsPaths = result + } +} + private class MoveIntellijErrorLogAction( private val sourceFile: Provider, private val targetFile: Provider @@ -333,7 +363,7 @@ private class CoverageArgumentProvider( @get:Input val excludeAndroidPackage: Provider ) : CommandLineArgumentProvider, Named { - private val projectName: String = task.project.name + private val projectPath: String = task.project.path @Internal override fun getName(): String { @@ -345,7 +375,7 @@ private class CoverageArgumentProvider( if (taskExtension.isDisabled || koverExtensionValue.isDisabled - || koverExtensionValue.disabledProjects.contains(projectName) + || koverExtensionValue.disabledProjectsPaths.contains(projectPath) ) { return mutableListOf() } diff --git a/src/main/kotlin/kotlinx/kover/Providers.kt b/src/main/kotlin/kotlinx/kover/Providers.kt index fa574542..6b33a3b7 100644 --- a/src/main/kotlin/kotlinx/kover/Providers.kt +++ b/src/main/kotlin/kotlinx/kover/Providers.kt @@ -15,7 +15,7 @@ internal fun Project.createProviders(agents: Map) val projects: MutableMap = mutableMapOf() allprojects { - projects[it.name] = ProjectProviders( + projects[it.path] = ProjectProviders( it.provider { it.files(if (runAllTests()) allBinaryReports() else it.binaryReports(this)) }, it.provider { if (runAllTests()) allTestTasks() else it.testTasks(this) }, it.provider { it.collectDirs(this).first }, @@ -108,7 +108,7 @@ private fun Project.collectDirs(root: Project): Pair = emptySet() } public enum class CoverageEngine { diff --git a/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt b/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt index b7d5e672..ee7d7749 100644 --- a/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt +++ b/src/main/kotlin/kotlinx/kover/engines/commons/Reports.kt @@ -30,3 +30,5 @@ internal fun String.wildcardsToRegex(): String { return builder.toString() } + +internal val ONE_HUNDRED = 100.toBigDecimal() diff --git a/src/main/kotlin/kotlinx/kover/engines/intellij/IntellijReports.kt b/src/main/kotlin/kotlinx/kover/engines/intellij/IntellijReports.kt index 20460d16..dd0cb5ce 100644 --- a/src/main/kotlin/kotlinx/kover/engines/intellij/IntellijReports.kt +++ b/src/main/kotlin/kotlinx/kover/engines/intellij/IntellijReports.kt @@ -189,9 +189,9 @@ private fun Task.checkRule(counters: Map, rule: Veri val ruleName = if (rule.name != null) "'${rule.name}' " else "" return if (boundsViolations.size > 1) { - "Rule ${ruleName}violated for '${project.name}':" + boundsViolations.joinToString("\n ", "\n ") + "Rule ${ruleName}violated for '${project.path}':" + boundsViolations.joinToString("\n ", "\n ") } else if (boundsViolations.size == 1) { - "Rule ${ruleName}violated for '${project.name}': ${boundsViolations[0]}" + "Rule ${ruleName}violated for '${project.path}': ${boundsViolations[0]}" } else { null } diff --git a/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt b/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt index 625e5700..9cdb07ee 100644 --- a/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt +++ b/src/main/kotlin/kotlinx/kover/engines/jacoco/JacocoReports.kt @@ -6,6 +6,8 @@ package kotlinx.kover.engines.jacoco import groovy.lang.* import kotlinx.kover.api.* +import kotlinx.kover.engines.commons.* +import kotlinx.kover.engines.commons.ONE_HUNDRED import kotlinx.kover.engines.commons.Report import org.gradle.api.* import org.gradle.api.file.* @@ -40,7 +42,7 @@ private fun Task.callJacocoAntReportTask( invokeWithBody("executiondata") { project.files(report.files).addToAntBuilder(this, "resources") } - invokeWithBody("structure", mapOf("name" to project.name)) { + invokeWithBody("structure", mapOf("name" to project.path)) { invokeWithBody("sourcefiles") { project.files(sources).addToAntBuilder(this, "resources") } @@ -93,8 +95,8 @@ internal fun Task.jacocoVerification( } VerificationValueType.COVERED_LINES_PERCENTAGE -> { limitArgs["value"] = "COVEREDRATIO" - min = min?.divide(BigDecimal(100)) - max = max?.divide(BigDecimal(100)) + min = min?.divide(ONE_HUNDRED) + max = max?.divide(ONE_HUNDRED) } } diff --git a/src/main/kotlin/kotlinx/kover/tasks/KoverCollectingTask.kt b/src/main/kotlin/kotlinx/kover/tasks/KoverCollectingTask.kt index 1ce9364a..b8cd2fab 100644 --- a/src/main/kotlin/kotlinx/kover/tasks/KoverCollectingTask.kt +++ b/src/main/kotlin/kotlinx/kover/tasks/KoverCollectingTask.kt @@ -24,20 +24,26 @@ open class KoverCollectingTask : DefaultTask() { it.into(outputDir) xmlFiles.forEach { (p, f) -> it.from(f) { c -> - c.rename { "$p.xml" } + c.rename { "${p.pathAsFilename()}.xml" } } } } htmlDirs.forEach { (p, d) -> + val name = p.pathAsFilename() + // delete directory for HTML reports so that the old reports do not overlap with the new ones - project.delete(outputDir.dir("html/$p")) + project.delete(outputDir.dir("html/$name")) project.copy { it.from(d) - it.into(outputDir.dir("html/$p")) + it.into(outputDir.dir("html/$name")) } } } + + private fun String.pathAsFilename(): String { + return if (this == ":") "_root_" else replace(':', '_') + } } diff --git a/src/main/kotlin/kotlinx/kover/tasks/KoverHtmlReport.kt b/src/main/kotlin/kotlinx/kover/tasks/KoverHtmlReport.kt index aa6da0b7..f1055831 100644 --- a/src/main/kotlin/kotlinx/kover/tasks/KoverHtmlReport.kt +++ b/src/main/kotlin/kotlinx/kover/tasks/KoverHtmlReport.kt @@ -44,7 +44,7 @@ open class KoverMergedHtmlReportTask : KoverMergedTask() { @CacheableTask open class KoverHtmlReportTask : KoverProjectTask() { - private val projectName = project.name + private val projectPath = project.path /** * Specifies directory path of generated HTML report. @@ -72,6 +72,6 @@ open class KoverHtmlReportTask : KoverProjectTask() { classpath.get(), ) } - logger.lifecycle("Kover: HTML report for '$projectName' file://${htmlDirFile.canonicalPath}/index.html") + logger.lifecycle("Kover: HTML report for '$projectPath' file://${htmlDirFile.canonicalPath}/index.html") } } diff --git a/src/main/kotlin/kotlinx/kover/tasks/KoverMergedTask.kt b/src/main/kotlin/kotlinx/kover/tasks/KoverMergedTask.kt index 95089692..29753a0a 100644 --- a/src/main/kotlin/kotlinx/kover/tasks/KoverMergedTask.kt +++ b/src/main/kotlin/kotlinx/kover/tasks/KoverMergedTask.kt @@ -71,16 +71,16 @@ open class KoverMergedTask : DefaultTask() { val sourcesMap = srcDirs.get() val outputsMap = outputDirs.get() - val projectsNames = sourcesMap.keys + val projectsPaths = sourcesMap.keys val reportFiles: MutableList = mutableListOf() val projects: MutableList = mutableListOf() - projectsNames.map { projectName -> - reportFiles += binariesMap.getValue(projectName).files.get() + projectsPaths.map { projectPath -> + reportFiles += binariesMap.getValue(projectPath).files.get() projects += ProjectInfo( - sources = sourcesMap.getValue(projectName).files.get(), - outputs = outputsMap.getValue(projectName).files.get() + sources = sourcesMap.getValue(projectPath).files.get(), + outputs = outputsMap.getValue(projectPath).files.get() ) }