Skip to content

Commit

Permalink
Added the ability to disable the plugin for the specified modules
Browse files Browse the repository at this point in the history
  • Loading branch information
shanshin committed Dec 22, 2021
1 parent c9be72a commit 920c13d
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 70 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ kover {
intellijEngineVersion.set("1.0.639") // 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 `koverReport` task before `check` task
disabledModules = setOf() // setOf("module-name") to disable coverage for module with name `module-name`
}
```
</details>
Expand All @@ -317,6 +318,7 @@ kover {
intellijEngineVersion.set('1.0.639') // 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 `koverReport` task before `check` task
disabledModules = [] // ["module-name"] to disable coverage for module with name `module-name`
}
```
</details>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,39 @@ internal class MultiModulesTest : BaseGradleScriptTest() {
}
}
}

@Test
fun testDisableModule() {
builder("Testing the generation of module reports")
.types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
.engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
.sources("multimodule-user")
.submodule(SUBMODULE_NAME) {
sources("multimodule-common")
}
.dependency(
"implementation(project(\":$SUBMODULE_NAME\"))",
"implementation project(':$SUBMODULE_NAME')"
)
.configKover { disabledModules += SUBMODULE_NAME }
.build()
.run("build", "koverModuleReport") {
checkDefaultBinaryReport()
checkDefaultReports()
checkDefaultModuleReports()
xml(defaultXmlReport()) {
assertCounterFullyCovered(classCounter("org.jetbrains.UserClass"))

// classes from disabled module should not be included in the aggregated report
assertCounterAbsent(classCounter("org.jetbrains.CommonClass"))
assertCounterAbsent(classCounter("org.jetbrains.CommonInternalClass"))
}

submodule(SUBMODULE_NAME) {
checkDefaultBinaryReport(false)
checkDefaultReports(false)
checkDefaultModuleReports(false)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ internal data class KoverRootConfig(
var disabled: Boolean? = null,
var intellijVersion: String? = null,
var jacocoVersion: String? = null,
var generateReportOnCheck: Boolean? = null
var generateReportOnCheck: Boolean? = null,
val disabledModules: MutableSet<String> = mutableSetOf()
) {
val isDefault =
disabled == null && intellijVersion == null && jacocoVersion == null && generateReportOnCheck == null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ private fun ProjectBuilderState.buildRootExtension(slice: ProjectSlice): String
}
}

if (koverConfig.disabledModules.isNotEmpty()) {
val prefix = if (slice.language == GradleScriptLanguage.KOTLIN) "setOf(" else "["
val postfix = if (slice.language == GradleScriptLanguage.KOTLIN) ")" else "]"
val value = koverConfig.disabledModules.joinToString(prefix = prefix, postfix = postfix) { "\"$it\"" }
builder.appendLine(" disabledModules = $value")
}

builder.appendLine("}")

return builder.toString()
Expand Down
48 changes: 27 additions & 21 deletions src/main/kotlin/kotlinx/kover/KoverPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,21 @@ class KoverPlugin : Plugin<Project> {
providers: ProjectProviders,
block: (T) -> Unit
): T {
return tasks.create(taskName, type.java) {
it.group = VERIFICATION_GROUP
return tasks.create(taskName, type.java) { task ->
task.group = VERIFICATION_GROUP

providers.modules.forEach { (moduleName, m) ->
it.binaryReportFiles.put(moduleName, NestedFiles(it.project.objects, m.reports))
it.smapFiles.put(moduleName, NestedFiles(it.project.objects, m.smap))
it.srcDirs.put(moduleName, NestedFiles(it.project.objects, m.sources))
it.outputDirs.put(moduleName, NestedFiles(it.project.objects, m.output))
task.binaryReportFiles.put(moduleName, NestedFiles(task.project.objects, m.reports))
task.smapFiles.put(moduleName, NestedFiles(task.project.objects, m.smap))
task.srcDirs.put(moduleName, NestedFiles(task.project.objects, m.sources))
task.outputDirs.put(moduleName, NestedFiles(task.project.objects, m.output))
}

it.coverageEngine.set(providers.engine)
it.classpath.set(providers.classpath)
it.dependsOn(providers.allModules.tests)
task.coverageEngine.set(providers.engine)
task.classpath.set(providers.classpath)
task.dependsOn(providers.allModules.tests)

block(it)
block(task)
}
}

Expand Down Expand Up @@ -215,20 +215,23 @@ class KoverPlugin : Plugin<Project> {
moduleProviders: ModuleProviders,
block: (T) -> Unit
): T {
return tasks.create(taskName, type.java) {
it.group = VERIFICATION_GROUP
return tasks.create(taskName, type.java) { task ->
task.group = VERIFICATION_GROUP

it.coverageEngine.set(providers.engine)
it.classpath.set(providers.classpath)
it.srcDirs.set(moduleProviders.sources)
it.outputDirs.set(moduleProviders.output)
task.coverageEngine.set(providers.engine)
task.classpath.set(providers.classpath)
task.srcDirs.set(moduleProviders.sources)
task.outputDirs.set(moduleProviders.output)

// it is necessary to read all binary reports because module's classes can be invoked in another module
it.binaryReportFiles.set(providers.allModules.reports)
it.smapFiles.set(providers.allModules.smap)
it.dependsOn(providers.allModules.tests)
task.binaryReportFiles.set(providers.allModules.reports)
task.smapFiles.set(providers.allModules.smap)
task.dependsOn(providers.allModules.tests)

task.onlyIf { !moduleProviders.disabled.get() }
task.onlyIf { !task.binaryReportFiles.get().isEmpty }

block(it)
block(task)
}
}

Expand Down Expand Up @@ -299,7 +302,10 @@ private class CoverageArgumentProvider(
val koverExtensionValue = koverExtension.get()
val taskExtensionValue = taskExtension.get()

if (!taskExtensionValue.isEnabled || !koverExtensionValue.isEnabled) {
if (!taskExtensionValue.isEnabled
|| !koverExtensionValue.isEnabled
|| koverExtensionValue.disabledModules.contains(task.project.name)
) {
return mutableListOf()
}

Expand Down
70 changes: 57 additions & 13 deletions src/main/kotlin/kotlinx/kover/Providers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ internal fun Project.createProviders(agents: Map<CoverageEngine, CoverageAgent>)

allprojects {
modules[it.name] = ModuleProviders(
it.provider { it.files(it.binaryReports()) },
it.provider { it.files(it.smapFiles()) },
it.provider { it.testTasks() },
it.provider { it.collectDirs().first },
it.provider { it.collectDirs().second }
it.provider { it.files(it.binaryReports(this)) },
it.provider { it.files(it.smapFiles(this)) },
it.provider { it.testTasks(this) },
it.provider { it.collectDirs(this).first },
it.provider { it.collectDirs(this).second },
it.provider { it.isDisabled(this) }
)
}

Expand All @@ -41,31 +42,45 @@ internal fun Project.createProviders(agents: Map<CoverageEngine, CoverageAgent>)
// all sources and all outputs providers are unused, so NOW it can return empty file collection
val emptyProvider: Provider<FileCollection> = provider { files() }
val allModulesProviders =
ModuleProviders(allReportsProvider, allSmapProvider, allTestsProvider, emptyProvider, emptyProvider)
ModuleProviders(
allReportsProvider,
allSmapProvider,
allTestsProvider,
emptyProvider,
emptyProvider,
provider { false })

return ProjectProviders(modules, allModulesProviders, engineProvider, classpathProvider, extensionProvider)
}


internal fun Project.allTestTasks(): List<Test> {
return allprojects.flatMap { it.testTasks() }
return allprojects.flatMap { it.testTasks(this) }
}

internal fun Project.allBinaryReports(): List<File> {
return allprojects.flatMap { it.binaryReports() }
return allprojects.flatMap { it.binaryReports(this) }
}

internal fun Project.allSmapFiles(): List<File> {
return allprojects.flatMap { it.smapFiles() }
return allprojects.flatMap { it.smapFiles(this) }
}


internal fun Project.testTasks(): List<Test> {
internal fun Project.testTasks(rootProject: Project): List<Test> {
if (isDisabled(rootProject)) {
return emptyList()
}

return tasks.withType(Test::class.java)
.filter { t -> t.extensions.getByType(KoverTaskExtension::class.java).isEnabled }
}

internal fun Project.binaryReports(): List<File> {
internal fun Project.binaryReports(root: Project): List<File> {
if (isDisabled(root)) {
return emptyList()
}

return tasks.withType(Test::class.java).asSequence()
.map { t -> t.extensions.getByType(KoverTaskExtension::class.java) }
// process binary report only from tasks with enabled cover
Expand All @@ -76,7 +91,11 @@ internal fun Project.binaryReports(): List<File> {
.toList()
}

internal fun Project.smapFiles(): List<File> {
internal fun Project.smapFiles(root: Project): List<File> {
if (isDisabled(root)) {
return emptyList()
}

return tasks.withType(Test::class.java).asSequence()
.map { t -> t.extensions.getByType(KoverTaskExtension::class.java) }
.filter { e -> e.isEnabled }
Expand All @@ -91,6 +110,30 @@ internal fun Project.smapFiles(): List<File> {
.toList()
}

private fun Project.collectDirs(root: Project): Pair<FileCollection, FileCollection> {
if (isDisabled(root)) {
return files() to files()
}

val srcDirs = HashMap<String, File>()
val outDirs = HashMap<String, File>()

createAdapters().forEach {
val dirs = it.findDirs(this)
srcDirs += dirs.sources.asSequence().map { f -> f.canonicalPath to f }
outDirs += dirs.output.asSequence().map { f -> f.canonicalPath to f }
}

val src = srcDirs.asSequence().map { it.value }.filter { it.exists() && it.isDirectory }.toList()
val out = outDirs.asSequence().map { it.value }.filter { it.exists() && it.isDirectory }.toList()

return files(src) to files(out)
}

private fun Project.isDisabled(root: Project): Boolean {
return root.extensions.getByType(KoverExtension::class.java).disabledModules.contains(name)
}


internal class ProjectProviders(
val modules: Map<String, ModuleProviders>,
Expand All @@ -106,6 +149,7 @@ internal class ModuleProviders(
val smap: Provider<FileCollection>,
val tests: Provider<List<Test>>,
val sources: Provider<FileCollection>,
val output: Provider<FileCollection>
val output: Provider<FileCollection>,
val disabled: Provider<Boolean>
)

35 changes: 0 additions & 35 deletions src/main/kotlin/kotlinx/kover/adapters/PluginAdapter.kt

This file was deleted.

19 changes: 19 additions & 0 deletions src/main/kotlin/kotlinx/kover/adapters/PluginAdaptersFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.kover.adapters

import kotlinx.kover.adapters.api.*
import org.gradle.api.*
import org.gradle.api.file.*
import java.io.*

internal fun createAdapters(): List<CompilationPluginAdapter> {
return listOf(
OldJavaPluginAdapter(),
KotlinMultiplatformPluginAdapter(),
AndroidPluginAdapter(),
KotlinAndroidPluginAdapter()
)
}
6 changes: 6 additions & 0 deletions src/main/kotlin/kotlinx/kover/api/KoverExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ open class KoverExtension(objects: ObjectFactory) {
*/
@get:Input
val generateReportOnCheck: Property<Boolean> = objects.property(Boolean::class.java)

/**
* Specifies the modules to be disabled from instrumentation and reportings.
*/
@get:Input
var disabledModules: Set<String> = emptySet()
}

public enum class CoverageEngine {
Expand Down

0 comments on commit 920c13d

Please sign in to comment.