diff --git a/README.md b/README.md
index c53c9c04..e33c20c2 100644
--- a/README.md
+++ b/README.md
@@ -12,17 +12,16 @@ Minimal supported `Gradle` version: `6.6`.
## Table of contents
- [Features](#features)
- [Quickstart](#quickstart)
- - [Apply plugin to a single-project build](#apply-plugin-to-a-single-project-build)
+ - [Apply plugin to a project](#apply-plugin)
- [Applying plugins with the plugins DSL](#applying-plugins-with-the-plugins-dsl)
- [Legacy Plugin Application: applying plugins with the buildscript block](#legacy-plugin-application-applying-plugins-with-the-buildscript-block)
- - [Apply plugin to a multi-project build](#apply-plugin-to-a-multi-project-build)
+ - [Merged reports](#merged-reports)
- [Configuration](#configuration)
- - [Configuring JVM test task](#configuring-jvm-test-task)
+ - [Configuring project](#configuring-project)
- [Configuring merged reports](#configuring-merged-reports)
- - [Configuring project reports](#configuring-project-reports)
- - [Configuring entire plugin](#configuring-entire-plugin)
-- [Verification](#verification)
-- [Tasks](#tasks)
+ - [Configuring JVM test task](#configuring-jvm-test-task)
+ - [Specifying Coverage Engine](#specifying-coverage-engine)
+- [Tasks](#kover-default-tasks)
- [Implicit plugin dependencies](#implicit-plugin-dependencies)
## Features
@@ -34,7 +33,7 @@ Minimal supported `Gradle` version: `6.6`.
* Customizable filters for instrumented classes
## Quickstart
-### Apply plugin to a single-project build
+### Apply plugin
#### Applying plugins with the plugins DSL
In top-level build file:
@@ -43,7 +42,7 @@ In top-level build file:
```kotlin
plugins {
- id("org.jetbrains.kotlinx.kover") version "0.5.0"
+ id("org.jetbrains.kotlinx.kover") version "0.6.0-RC"
}
```
@@ -53,7 +52,7 @@ plugins {
```groovy
plugins {
- id 'org.jetbrains.kotlinx.kover' version '0.5.0'
+ id 'org.jetbrains.kotlinx.kover' version '0.6.0-RC'
}
```
@@ -71,7 +70,7 @@ buildscript {
}
dependencies {
- classpath("org.jetbrains.kotlinx:kover:0.5.0")
+ classpath("org.jetbrains.kotlinx:kover:0.6.0-RC")
}
}
@@ -88,7 +87,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'org.jetbrains.kotlinx:kover:0.5.0'
+ classpath 'org.jetbrains.kotlinx:kover:0.6.0-RC'
}
}
@@ -96,9 +95,14 @@ apply plugin: 'kover'
```
-### Apply plugin to a multi-project build
-To apply the plugin to all Gradle projects, you only need to apply the plugin to the top-level build file as shown [above](#apply-plugin-to-a-single-project-build).
-Applying the plugin to subprojects if you have already applied it to the root project will cause configuration errors.
+### Merged reports
+Merged reports are reports that combine statistics of code coverage by test tasks from several projects.
+
+At the same time, for each project, its configuration of instrumentation and special filters (non-class filters) is applied.
+
+In all projects used for merged reports, a Kover plugin must be applied, as well as all Coverage Engines must match.
+
+See how to enable merge reports in [this section](#configuring-merged-reports).
## Configuration
@@ -108,7 +112,9 @@ However, in some cases, custom settings are needed - this can be done by configu
### Configuring JVM test task
-If you need to disable or filter instrumentation for a test task, you may configure the Kover extension for it.
+In rare cases, you may need to disable instrumentation for certain classes if it causes execution errors.
+It may also be convenient to ignore the test task when calculating coverage.
+You may configure the Kover extension for it.
For example, to configure a standard test task for Kotlin/JVM named `test`, you need to add the following code to the build script of the project where this task is declared:
@@ -118,10 +124,10 @@ For example, to configure a standard test task for Kotlin/JVM named `test`, you
```kotlin
tasks.test {
extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) {
- isDisabled = false
- binaryReportFile.set(file("$buildDir/custom/result.bin"))
- includes = listOf("com.example.*")
- excludes = listOf("com.example.subpackage.*")
+ isDisabled.set(false) // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
+ binaryReportFile.set(file("$buildDir/custom/result.bin")) // set file name of binary report
+ includes = listOf("com.example.*") // see "Instrumentation inclusion rules" below
+ excludes = listOf("com.example.subpackage.*") // see "Instrumentation exclusion rules" below
}
}
```
@@ -133,16 +139,16 @@ tasks.test {
```groovy
tasks.test {
kover {
- disabled = false
- binaryReportFile.set(file("$buildDir/custom/result.bin"))
- includes = ['com.example.*']
- excludes = ['com.example.subpackage.*']
+ disabled = false // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
+ binaryReportFile.set(file("$buildDir/custom/result.bin")) // set file name of binary report
+ includes = ['com.example.*'] // see "Instrumentation inclusion rules" below
+ excludes = ['com.example.subpackage.*'] // see "Instrumentation exclusion rules" below
}
}
```
-**For other platforms (Android, Kotlin-Multiplatform) the names may differ and you may also have several test tasks, so you first need to determine the name of the required task.**
+**For other platforms (Android, Kotlin-Multiplatform) the names may differ, and you may also have several test tasks, so you first need to determine the name of the required task.**
Example of configuring test task for build type `debug` in Android:
@@ -156,10 +162,10 @@ android {
unitTests.all {
if (it.name == "testDebugUnitTest") {
it.extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) {
- isDisabled = false
- binaryReportFile.set(file("$buildDir/custom/debug-report.bin"))
- includes = listOf("com.example.*")
- excludes = listOf("com.example.subpackage.*")
+ isDisabled = false // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
+ binaryReportFile.set(file("$buildDir/custom/debug-report.bin")) // set file name of binary report
+ includes = listOf("com.example.*") // see "Instrumentation inclusion rules" below
+ excludes = listOf("com.example.subpackage.*") // see "Instrumentation exclusion rules" below
}
}
}
@@ -180,10 +186,10 @@ android {
unitTests.all {
if (name == "testDebugUnitTest") {
kover {
- disabled = false
- binaryReportFile.set(file("$buildDir/custom/debug-report.bin"))
- includes = ['com.example.*']
- excludes = ['com.example.subpackage.*']
+ disabled = false // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
+ binaryReportFile.set(file("$buildDir/custom/debug-report.bin")) // set file name of binary report
+ includes = ['com.example.*'] // see "Instrumentation inclusion rules" below
+ excludes = ['com.example.subpackage.*'] // see "Instrumentation exclusion rules" below
}
}
}
@@ -193,79 +199,85 @@ android {
-### Configuring merged reports
-Merged reports combine classpath and coverage stats from the project in which the plugin is applied and all of its subprojects.
-
-If you need to change the name of the XML report file or HTML directory, you may configure the corresponding tasks in
-the project in which the plugin is applied (usually this is the root project):
+**Instrumentation inclusion rules**
-
-Kotlin
+Only the specified classes will be instrumented, the remaining (non-included) classes will still be present in the report, but their coverage will be zero.
-```kotlin
-tasks.koverMergedHtmlReport {
- isEnabled = true // false to disable report generation
- htmlReportDir.set(layout.buildDirectory.dir("my-merged-report/html-result"))
+**Instrumentation exclusion rules**
- includes = listOf("com.example.*") // inclusion rules for classes
- excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
-}
+The specified classes will not be instrumented and their coverage will be zero.
-tasks.koverMergedXmlReport {
- isEnabled = true // false to disable report generation
- xmlReportFile.set(layout.buildDirectory.file("my-merged-report/result.xml"))
+Instrumentation inclusion/exclusion rules are represented as a fully-qualified name of the class (several classes if wildcards are used).
+File or directory names are not allowed.
+It is possible to use `*` (zero or several of any chars) and `?` (one any char) wildcards. Wildcard `**` is similar to the `*`.
- includes = listOf("com.example.*") // inclusion rules for classes
- excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
-}
-```
-
+Examples `my.package.ClassName` or `my.*.*Name` are allowed, while `my/package/ClassName.kt` or `src/my.**.ClassName` are not.
-
-Groovy
-
-```groovy
-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
-}
-```
-
-
-### Configuring project reports
-If you need to change the name of the XML report file or HTML directory for a specific project, you may configure
-the corresponding tasks in this project:
+Exclusion rules have priority over inclusion ones.
+### Configuring project
+In the project in which the plugin is applied, you can configure instrumentation and default Kover tasks:
Kotlin
```kotlin
-tasks.koverHtmlReport {
- isEnabled = true // false to disable report generation
- htmlReportDir.set(layout.buildDirectory.dir("my-project-report/html-result"))
+kover {
+ isDisabled.set(false) // true to disable instrumentation and all Kover tasks in this project
+ engine.set(DefaultIntellijEngine) // change Coverage Engine
+ filters { // common filters for all default Kover tasks
+ classes { // common class filter for all default Kover tasks
+ includes += "com.example.*" // class inclusion rules
+ excludes += listOf("com.example.subpackage.*") // class exclusion rules
+ }
+ }
- includes = listOf("com.example.*") // inclusion rules for classes
- excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
-}
+ instrumentation {
+ excludeTasks += "dummy-tests" // set of test tasks names to exclude from instrumentation. The results of their execution will not be presented in the report
+ }
+
+ xmlReport {
+ onCheck.set(false) // true to run koverXmlReport task during the execution of the check task
+ reportFile.set(layout.buildDirectory.file("my-project-report/result.xml")) // change report file name
+ overrideFilters {
+ classes { // override common class filter
+ includes += "com.example2.*" // override class inclusion rules
+ excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
+ }
-tasks.koverXmlReport {
- isEnabled = true // false to disable report generation
- xmlReportFile.set(layout.buildDirectory.file("my-project-report/result.xml"))
+ htmlReport {
+ onCheck.set(false) // true to run koverHtmlReport task during the execution of the check task
+ reportDir.set(layout.buildDirectory.dir("my-project-report/html-result")) // change report directory
+ overrideFilters {
+ classes { // override common class filter
+ includes += "com.example2.*" // class inclusion rules
+ excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
+ }
+
+ verify {
+ onCheck.set(true) // true to run koverVerify task during the execution of the check task
+ rule { // add verification rule
+ isEnabled = true // false to disable rule checking
+ name = null // custom name for the rule
+ target = kotlinx.kover.api.VerificationTarget.ALL // specify by which entity the code for separate coverage evaluation will be grouped
+
+ overrideClassFilter { // override common class filter
+ includes += "com.example.verify.*" // override class inclusion rules
+ excludes += listOf("com.example.verify.subpackage.*") // override class exclusion rules
+ }
- includes = listOf("com.example.*") // inclusion rules for classes
- excludes = listOf("com.example.subpackage.*") // exclusion rules for classes
+ bound { // add rule bound
+ minValue = 10
+ maxValue = 20
+ counter = kotlinx.kover.api.CounterType.LINE // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
+ valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
+ }
+ }
+ }
}
```
@@ -274,69 +286,139 @@ tasks.koverXmlReport {
Groovy
```groovy
-tasks.koverHtmlReport {
- enabled = true // false to disable report generation
- htmlReportDir.set(layout.buildDirectory.dir("my-project-report/html-result"))
+kover {
+ isDisabled.set(false) // true to disable instrumentation and all Kover tasks in this project
+ engine = kotlinx.kover.api.DefaultIntellijEngine.INSTANCE // change Coverage Engine
+ filters { // common filters for all default Kover tasks
+ classes { // common class filter for all default Kover tasks
+ includes.add("com.example.*") // class inclusion rules
+ excludes.addAll("com.example.subpackage.*") // class exclusion rules
+ }
+ }
- includes = ['com.example.*'] // inclusion rules for classes
- excludes = ['com.example.subpackage.*'] // exclusion rules for classes
-}
+ instrumentation {
+ excludeTasks.add("dummy-tests") // set of test tasks names to exclude from instrumentation. The results of their execution will not be presented in the report
+ }
-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
+ xmlReport {
+ onCheck.set(false) // true to run koverXmlReport task during the execution of the check task
+ reportFile.set(layout.buildDirectory.file("my-project-report/result.xml")) // change report file name
+ overrideFilters {
+ classes { // override common class filter
+ includes.add("com.example2.*") // override class inclusion rules
+ excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
+ }
+
+ htmlReport {
+ onCheck.set(false) // true to run koverHtmlReport task during the execution of the check task
+ reportDir.set(layout.buildDirectory.dir("my-project-report/html-result")) // change report directory
+ overrideFilters {
+ classes { // override common class filter
+ includes.add("com.example2.*") // class inclusion rules
+ excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
+ }
+
+ verify {
+ onCheck.set(true) // true to run koverVerify task during the execution of the check task
+ rule { // add verification rule
+ enabled = true // false to disable rule checking
+ name = null // custom name for the rule
+ target = 'ALL' // specify by which entity the code for separate coverage evaluation will be grouped
+
+ overrideClassFilter { // override common class filter
+ includes.add("com.example.verify.*") // override class inclusion rules
+ excludes.addAll("com.example.verify.subpackage.*") // override class exclusion rules
+ }
+
+ bound { // add rule bound
+ minValue = 10
+ maxValue = 20
+ counter = 'LINE' // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
+ valueType = 'COVERED_PERCENTAGE' // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
+ }
+ }
+ }
}
```
-By default, for tasks `koverHtmlReport` and `koverXmlReport` coverage is calculated only for the tests of the one project.
-If classes or functions are called from tests of another module, then you need to set a flag `runAllTestsForProjectTask` for `KoverExtension` to `true` ([see](#configuring-entire-plugin)).
+Engine version is specified separately, see [specifying coverage engine](#specifying-coverage-engine) section.
-**In this case, then running tasks `koverHtmlReport` or `koverXmlReport` will trigger the execution of all active tests from all projects!**
+### Configuring merged reports
+
+In order to create a merged report, it has to be enabled explicitly in a containing project with
+ `koverMerged.enable()` or
+```
+koverMerged {
+ enable()
+}
+```
+By default, merged reports include containing project along with all its subprojects.
-You may collect all project reports into one directory using the `koverCollectReports` task.
-Also, you may specify a custom directory to collect project reports in the build directory of the project in which the plugin
-is applied (usually this is the root project):
+Merged reports can also be configured in a similar manner. To do this, you need to configure the extension in the containing project (where `koverMerged.enable()` is called)
Kotlin
```kotlin
-tasks.koverCollectReports {
- outputDir.set(layout.buildDirectory.dir("all-projects-reports") )
-}
-```
-
+koverMerged {
+ enable() // create Kover merged reports
+
+ filters { // common filters for all default Kover merged tasks
+ classes { // common class filter for all default Kover merged tasks
+ includes += "com.example.*" // class inclusion rules
+ excludes += listOf("com.example.subpackage.*") // class exclusion rules
+ }
-
-Groovy
+ projects { // common projects filter for all default Kover merged tasks
+ excludes += listOf("project1", ":child:project") // Specifies the projects excluded from the merged tasks
+ }
+ }
-```groovy
-tasks.koverCollectReports {
- outputDir.set(layout.buildDirectory.dir("all-projects-reports") )
-}
-```
-
-### Configuring entire plugin
-In the project in which the plugin is applied, you can configure the following properties:
+ xmlReport {
+ onCheck.set(false) // true to run koverMergedXmlReport task during the execution of the check task
+ reportFile.set(layout.buildDirectory.file("my-merged-report/result.xml")) // change report file name
+ overrideClassFilter { // override common class filter
+ includes += "com.example2.*" // override class inclusion rules
+ excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
-
-Kotlin
+ htmlReport {
+ onCheck.set(false) // true to run koverMergedHtmlReport task during the execution of the check task
+ reportDir.set(layout.buildDirectory.dir("my-merged-report/html-result")) // change report directory
+ overrideClassFilter { // override common class filter
+ includes += "com.example2.*" // override class inclusion rules
+ excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
-```kotlin
-kover {
- isDisabled = false // true to disable instrumentation of all test tasks in all projects
- coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ) // change instrumentation agent and reporter
- 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") 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
+ verify {
+ onCheck.set(true) // true to run koverMergedVerify task during the execution of the check task
+ rule { // add verification rule
+ isEnabled = true // false to disable rule checking
+ name = null // custom name for the rule
+ target = kotlinx.kover.api.VerificationTarget.ALL // specify by which entity the code for separate coverage evaluation will be grouped
+
+ overrideClassFilter { // override common class filter
+ includes += "com.example.verify.*" // override class inclusion rules
+ excludes += listOf("com.example.verify.subpackage.*") // override class exclusion rules
+ }
+
+ bound { // add rule bound
+ minValue = 10
+ maxValue = 20
+ counter = kotlinx.kover.api.CounterType.LINE // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
+ valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
+ }
+ }
+ }
}
```
@@ -345,61 +427,71 @@ kover {
Groovy
```groovy
-kover {
- disabled = false // true to disable instrumentation of all test tasks in all projects
- coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ) // change instrumentation agent and reporter
- 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"] 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
+koverMerged {
+ enable() // create Kover merged reports
+
+ filters { // common filters for all default Kover merged tasks
+ classes { // common class filter for all default Kover merged tasks
+ includes.add("com.example.*") // class inclusion rules
+ excludes.addAll("com.example.subpackage.*") // class exclusion rules
+ }
+
+ projects { // common projects filter for all default Kover merged tasks
+ excludes.addAll("project1", ":child:project") // Specifies the projects excluded in the merged tasks
+ }
+ }
+
+
+ xmlReport {
+ onCheck.set(false) // true to run koverMergedXmlReport task during the execution of the check task
+ reportFile.set(layout.buildDirectory.file("my-merged-report/result.xml")) // change report file name
+ overrideClassFilter { // override common class filter
+ includes.add("com.example2.*") // override class inclusion rules
+ excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
+
+ htmlReport {
+ onCheck.set(false) // true to run koverMergedHtmlReport task during the execution of the check task
+ reportDir.set(layout.buildDirectory.dir("my-merged-report/html-result")) // change report directory
+ overrideClassFilter { // override common class filter
+ includes.add("com.example2.*") // override class inclusion rules
+ excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
+ }
+ }
+
+ verify {
+ onCheck.set(true) // true to run koverMergedVerify task during the execution of the check task
+ rule { // add verification rule
+ isEnabled = true // false to disable rule checking
+ name = null // custom name for the rule
+ target = 'ALL' // specify by which entity the code for separate coverage evaluation will be grouped
+
+ overrideClassFilter { // override common class filter
+ includes.add("com.example.verify.*") // override class inclusion rules
+ excludes.addAll("com.example.verify.subpackage.*") // override class exclusion rules
+ }
+
+ bound { // add rule bound
+ minValue = 10
+ maxValue = 20
+ counter = 'LINE' // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
+ valueType = 'COVERED_PERCENTAGE' // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
+ }
+ }
+ }
}
```
-## Verification
-You may specify one or more rules that check the values of the code coverage counters.
-
-Validation rules work for both types of agents.
-
-*The plugin currently only supports line counter values.*
-
-
-To add a rule to check coverage of the code of all projects, you need to add configuration to the project in which the plugin
-is applied (usually this is the root project):
+### Specifying Coverage Engine
+#### IntelliJ Coverage Engine with default version
Kotlin
```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 {
- minValue = 100000
- valueType = kotlinx.kover.api.VerificationValueType.COVERED_LINES_COUNT
- }
- }
- rule {
- // rule without a custom name
- bound {
- minValue = 1
- maxValue = 1000
- valueType = kotlinx.kover.api.VerificationValueType.MISSED_LINES_COUNT
- }
- }
- rule {
- name = "Minimal line coverage rate in percent"
- bound {
- minValue = 50
- // valueType is kotlinx.kover.api.VerificationValueType.COVERED_LINES_PERCENTAGE by default
- }
- }
-}
+kotlinx.kover.api.DefaultIntellijEngine
```
@@ -407,53 +499,21 @@ tasks.koverMergedVerify {
Groovy
```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 {
- minValue = 100000
- valueType = 'COVERED_LINES_COUNT'
- }
- }
- rule {
- // rule without a custom name
- bound {
- minValue = 1
- maxValue = 1000
- valueType = 'MISSED_LINES_COUNT'
- }
- }
- rule {
- name = "Minimal line coverage rate in percent"
- bound {
- minValue = 50
- // valueType is 'COVERED_LINES_PERCENTAGE' by default
- }
- }
-}
+kotlinx.kover.api.DefaultIntellijEngine.INSTANCE
```
-To add rules for code coverage checks for the code of one specific project, you need to add a configuration to this project:
+#### IntelliJ Coverage Engine with custom version
+```
+kotlinx.kover.api.IntellijEngine("1.0.668")
+```
+#### JaCoCo Coverage Engine with default version
Kotlin
```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 {
- minValue = 75
- }
- }
-}
+kotlinx.kover.api.DefaultJacocoEngine
```
@@ -461,44 +521,33 @@ tasks.koverVerify {
Groovy
```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 {
- minValue = 75
- }
- }
-}
+kotlinx.kover.api.DefaultJacocoEngine.INSTANCE
```
-By default, for the task `koverVerify` coverage is calculated only for the tests of the one project.
-If classes or functions are called from tests of another module, then you need to set a flag `runAllTestsForProjectTask` for `KoverExtension` to `true` ([see](#configuring-entire-plugin)).
+#### JaCoCo Coverage Engine with custom version
+```
+kotlinx.kover.api.JacocoEngine("0.8.8")
+```
-**In this case, if verification rules are added, then running tasks `koverVerify` or `check` will trigger the execution of all active tests from all projects!**
+## Kover default tasks
+Tasks that are created for a project where the Kover plugin is applied:
+- `koverHtmlReport` - Generates code coverage HTML report for all enabled test tasks in the project.
+- `koverXmlReport` - Generates code coverage XML report for all enabled test tasks in the project.
+- `koverReport` - Executes both `koverXmlReport` and `koverHtmlReport` tasks.
+- `koverVerify` - Verifies code coverage metrics of the project based on configured rules. Always is executed before `check` task.
-## Tasks
-The plugin, when applied, automatically creates tasks for the project in which it is applied (usually this is the root project):
+Tasks that are created for project where the Kover plugin is applied and merged reports are enabled:
- `koverMergedHtmlReport` - Generates code coverage HTML report for all enabled test tasks in all projects.
-- `koverMergedXmlReport` - Generates code coverage XML report for all enabled test tasks in all projects.
-- `koverMergedReport` - Executes both `koverMergedXmlReport` and `koverMergedHtmlReport` tasks. Executes before `check` task if property `generateReportOnCheck` for `KoverExtension` is `true` ([see](#configuring-entire-plugin)).
+- `koverMergedXmlReport` - Generates code coverage XML report for all enabled test tasks in all projects.
+- `koverMergedReport` - Executes both `koverMergedXmlReport` and `koverMergedHtmlReport` tasks.
- `koverMergedVerify` - Verifies code coverage metrics of all projects based on specified rules. Always executes before `check` task.
-- `koverCollectReports` - Collects all projects reports into one directory. Default directory is `$buildDir/reports/kover/projects`, names for XML reports and dirs for HTML are projects names. Executing this task does not run `koverMergedXmlReport` or `koverMergedHtmlReport`, it only copies previously created reports if they exist to the output directory.
-
-Tasks that are created for all projects:
-- `koverHtmlReport` - Generates code coverage HTML report for all enabled test tasks in one project.
-- `koverXmlReport` - Generates code coverage XML report for all enabled test tasks in one project.
-- `koverReport` - Executes both `koverXmlReport` and `koverHtmlReport` tasks.
-- `koverVerify` - Verifies code coverage metrics of one project based on specified rules. Always executes before `check` task.
## Implicit plugin dependencies
While the plugin is being applied, the artifacts of the JaCoCo or IntelliJ toolkit are dynamically loaded. They are downloaded from the `mavenCentral` repository.
-For the plugin to work correctly, you need to make sure that the `mavenCentral` (or its mirror) is added to the repository list of the project in which the plugin is applied, if it doesn't already exist (usually this is the root project):
+For the plugin to work correctly, you need to make sure that the `mavenCentral` (or any of its mirrors) is added to the repository list of the project in which the plugin is applied, if it doesn't already exist (usually this is the root project):
Kotlin
diff --git a/build.gradle.kts b/build.gradle.kts
index 7d40d330..ac9a2de4 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,7 @@
+import org.jetbrains.kotlin.gradle.plugin.*
+
plugins {
- kotlin("jvm") version "1.6.10"
+ kotlin("jvm") version "1.7.10"
`java-gradle-plugin`
`maven-publish`
@@ -19,21 +21,26 @@ sourceSets {
}
}
+// adding the ability to use internal classes inside functional tests
+kotlin.target.compilations.run {
+ getByName("functionalTest").associateWith(getByName(KotlinCompilation.MAIN_COMPILATION_NAME))
+}
+
dependencies {
implementation(gradleApi())
// exclude transitive dependency on stdlib, the Gradle version should be used
compileOnly(kotlin("stdlib"))
- compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
+ compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:${property("kotlin.version")}")
compileOnly("com.android.tools.build:gradle:4.2.2")
testImplementation(kotlin("test"))
"functionalTestImplementation"(gradleTestKit())
// dependencies only for plugin's classpath to work with Kotlin Multi-Platform and Android plugins
- "functionalTestCompileOnly"("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
- "functionalTestCompileOnly"("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.10")
- "functionalTestCompileOnly"("org.jetbrains.kotlin:kotlin-compiler-runner:1.6.10")
+ "functionalTestCompileOnly"("org.jetbrains.kotlin:kotlin-gradle-plugin:${property("kotlin.version")}")
+ "functionalTestCompileOnly"("org.jetbrains.kotlin:kotlin-compiler-embeddable:${property("kotlin.version")}")
+ "functionalTestCompileOnly"("org.jetbrains.kotlin:kotlin-compiler-runner:${property("kotlin.version")}")
}
java {
@@ -73,7 +80,7 @@ tasks.withType().configureEach
// Kover works with the stdlib of at least version `1.4.x`
languageVersion = "1.4"
apiVersion = "1.4"
- // Kotlin compiler 1.6 issues a warning if `languageVersion` or `apiVersion` 1.4 is used - suppress it
+ // Kotlin compiler 1.7 issues a warning if `languageVersion` or `apiVersion` 1.4 is used - suppress it
freeCompilerArgs = freeCompilerArgs + "-Xsuppress-version-warnings"
}
}
diff --git a/buildSrc/src/main/kotlin/PublicationMavenCentral.kt b/buildSrc/src/main/kotlin/PublicationMavenCentral.kt
index ce2ee67c..14d0f56e 100644
--- a/buildSrc/src/main/kotlin/PublicationMavenCentral.kt
+++ b/buildSrc/src/main/kotlin/PublicationMavenCentral.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
import org.gradle.api.*
diff --git a/docs/migration-to-0.6.0.md b/docs/migration-to-0.6.0.md
new file mode 100644
index 00000000..1c8da9ca
--- /dev/null
+++ b/docs/migration-to-0.6.0.md
@@ -0,0 +1,552 @@
+The new API allows you to configure Kover in a more flexible manner, while being more concise than the previous API.
+From now on, there is no need to configure Kover or test tasks separately.
+
+# Main differences
+- applying the plugin to the root project no longer causes it to be recursively applied to all subprojects - you must explicitly apply it to all projects that will be covered
+- merged tasks are not created by default. You must explicitly enable it if necessary (for details [see](#merged-report-changes))
+- the extension `kover {}` is used to configure tasks `koverXmlReport`, `koverHtmlReport`, `koverVerify`, test tasks, instead of configuring these tasks directly
+- the extension `koverMerged {}` is used to configure tasks `koverMergedXmlReport`, `koverMergedHtmlReport`, `koverMergedVerify`, instead of configuring these tasks directly
+- task `koverCollectReports` was removed
+
+# Merged report changes
+
+In the new API, merged tasks are not created by default. To create them, you need to use the plugin in the `koverMerged` extension, it is necessary to call the function `enable()`.
+e.g.
+```
+koverMerged.enable()
+```
+or
+```
+koverMerged {
+ enable()
+}
+```
+or
+```
+extensions.configure {
+ enable()
+}
+```
+
+Now any merged tasks settings occur only in the `koverMerged` extension.
+By default, tasks use the results of measuring the coverage of the project in which they were created and all subprojects.
+At the same time, it is important that the Kover plugin is used in all these projects, as well as that they use the same variant (vendor and version) of the coverage engine.
+
+For example, it can be done this way
+```
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath("org.jetbrains.kotlinx:kover:0.6.0-BETA")
+ }
+}
+
+apply(plugin = "kover")
+
+extensions.configure {
+ enable()
+ // configure merged tasks
+}
+
+allprojects {
+ apply(plugin = "kover")
+
+ extensions.configure {
+ // `true` - to disable the collection of coverage metrics for tests from this project
+ isDisabled.set(false)
+
+ // configure engine variant
+ engine.set(kotlinx.kover.api.IntellijEngine("1.0.657"))
+
+ // configure project's tasks if needed
+ }
+}
+```
+
+In order not to use measurements from some projects now instead of `disabledProjects` property for `kover` extension you need to use `koverMerged` extension:
+```
+extensions.configure {
+ enable()
+ filters {
+ projects {
+ excludes.addAll("project-to-exclude", ":path:to:exclude")
+ }
+ }
+}
+```
+
+Instead of configuring the merged XML report task
+```
+koverMergedXmlReport {
+ // config task
+}
+```
+or
+```
+tasks.withType {
+ // config task
+}
+```
+you need to configure `koverMerged` extension:
+```
+extensions.configure {
+ enable()
+ xmlReport {
+ // config task
+ }
+}
+```
+
+Instead of configuring the merged HTML report task
+```
+koverMergedHtmlReport {
+ // config task
+}
+```
+or
+```
+tasks.withType {
+ // config task
+}
+```
+you need to configure `koverMerged` extension:
+```
+extensions.configure {
+ enable()
+ htmlReport {
+ // config task
+ }
+}
+```
+
+
+Instead of configuring the merged verification report task
+```
+koverMergedVerify {
+ // config task
+}
+```
+or
+```
+tasks.withType {
+ // config task
+}
+```
+you need to configure `koverMerged` extension:
+```
+extensions.configure {
+ enable()
+ verify {
+ // config task
+ }
+}
+```
+
+# Migration Issues
+
+## Root kover extension
+
+### type of `isDisabled` property changed from `Boolean` to `Property`.
+
+_Error message:_
+
+```
+Val cannot be reassigned
+```
+
+_Solution for Kotlin script:_ change `isDisabled = true` to `isDisabled.set(true)`
+
+### Properties `coverageEngine`, `intellijEngineVersion` and `jacocoEngineVersion` were removed.
+
+_Error messages:_
+
+```
+Using 'coverageEngine: Property' is an error
+```
+```
+Using 'intellijEngineVersion: Property' is an error
+```
+```
+Using 'jacocoEngineVersion: Property' is an error
+```
+
+_Solution:_
+
+Use property `engine` - it combines version and coverage engine vendor.
+
+To use IntelliJ Coverage Engine with default version write `engine.set(kotlinx.kover.api.DefaultIntellijEngine)`
+(Kotlin) or `engine = kotlinx.kover.api.DefaultIntellijEngine.INSTANCE` (Groovy).
+
+To use IntelliJ Coverage Engine with custom version write `engine.set(kotlinx.kover.api.IntellijEngine("version"))`
+(Kotlin) or `engine = kotlinx.kover.api.IntellijEngine("version")` (Groovy).
+
+To use JaCoCo Coverage Engine with default version write `engine.set(kotlinx.kover.api.DefaultJacocoEngine)`
+(Kotlin) or `engine = kotlinx.kover.api.DefaultJacocoEngine.INSTANCE` (Groovy).
+
+To use JaCoCo Coverage Engine with custom version write `engine.set(kotlinx.kover.api.JacocoEngine("version"))`
+(Kotlin) or `engine = kotlinx.kover.api.JacocoEngine("version")` (Groovy).
+
+### Property "generateReportOnCheck" was removed
+
+Use the properties individually for each report
+
+```
+kover {
+ xmlReport {
+ onCheck.set(true)
+ }
+
+ htmlReport {
+ onCheck.set(true)
+ }
+
+ verify {
+ onCheck.set(true)
+ }
+}
+```
+
+### property `disabledProjects` was removed
+
+_Error message:_
+
+```Using 'disabledProjects: Set' is an error.```
+
+_Solution_
+
+- read about [merged reports changes](#merged-report-changes)
+- use exclusion list in project filters of merged configuration extension
+
+```
+koverMerged {
+ enable()
+ filters {
+ projects {
+ excludes.add(":path or unique project name")
+ }
+ }
+}
+```
+
+If `includes` are empty, all subprojects and current project are used in merged reports.
+
+### Property `instrumentAndroidPackage` was removed
+
+There is no replacement. At the moment, all classes from the packages "android." and "com.android.*" excluded from
+instrumentation.
+
+### property `runAllTestsForProjectTask` was removed
+
+In the new API for single-project reports, it is impossible to call test tasks of another project. To account for coverage from tests of another module, use merged reports.
+
+## Kover extension for test task
+
+### type of `isDisabled` property changed from `Boolean` to `Property`.
+_Error message_
+
+```
+Val cannot be reassigned
+```
+
+_Solution for Kotlin script:_ change `isDisabled = true` to `isDisabled.set(true)`
+
+### `binaryReportFile` was renamed to `reportFile`
+
+Solution: change `binaryReportFile` to `reportFile`
+
+### Type of `includes` property changed from `List` to `ListProperty`
+
+Solution:
+
+```includes.addAll("com.example.*", "foo.bar.*")```
+
+### type of `excludes` property changed from `List` to `ListProperty`
+
+Solution for Kotlin: change `excludes = listOf("com.example.*", "foo.bar.*")`
+to `includes.addAll("com.example.*", "foo.bar.*")`
+
+Solution for Groovy: change `excludes = ["com.example.*", "foo.bar.*"]`
+to `includes.addAll("com.example.*", "foo.bar.*")`
+
+## `koverXmlReport` and `koverMergedXmlReport` tasks configuration
+
+### Property `xmlReportFile` was removed
+Solution: use property in Kover extension at the root of the project
+
+```
+kover {
+ xmlReport {
+ reportFile.set(yourFile)
+ }
+}
+```
+* for `xmlReportFile` task use `koverMerged { ... }` extension of the project.
+
+### Property `includes` was removed
+
+Solution for Kotlin: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ includes += listOf("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+
+Solution for Groovy: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ includes.addAll("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+* for `xmlReportFile` task use `koverMerged { ... }` extension of the project.
+
+### Property `excludes` was removed
+
+Solution for Kotlin: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ excludes += listOf("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+
+Solution for Groovy: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ excludes.addAll("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+* for `xmlReportFile` task use `koverMerged { ... }` extension of the project.
+
+## `KoverTaskExtension` configuration
+
+### Type of `excludes` and `includes` property changed from `List` to `ListProperty`
+_Error message:_
+```
+Val cannot be reassigned
+```
+
+_Solution:_
+
+```
+includes.addAll("com.example.*", "foo.bar.*")
+```
+and
+```
+excludes.addAll("com.example.*", "foo.bar.*")
+```
+
+
+## `koverHtmlReport` and `koverMergedHtmlReport` tasks configuration
+
+### Class `KoverHtmlReportTask` was removed
+
+_Error message:_
+```
+Using 'KoverHtmlReportTask' is an error
+```
+
+_Solution:_
+
+Configure report by Kover project extension
+
+```
+kover {
+ htmlReport {
+ // HTML report settings
+ }
+}
+```
+
+### Property `htmlReportDir` was removed
+
+_Error message:_
+```
+Using 'htmlReportDir: DirectoryProperty' is an error
+```
+
+Solution: use property `reportDir` in Kover extension at the root of the project
+
+```
+kover {
+ htmlReport {
+ reportDir.set(yourDir)
+ }
+}
+```
+* for `koverMergedHtmlReport` task use `koverMerged { ... }` extension of the project.
+
+### Property `includes` was removed
+
+_Error message:_
+```
+Using 'includes: List' is an error
+```
+
+_Solution for Kotlin:_ use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ includes += listOf("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+
+_Solution for Groovy:_ use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ includes.addAll("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+* for `koverMergedHtmlReport` task use `koverMerged { ... }` extension of the project.
+
+### Property `excludes` was removed
+
+Error message:
+```
+Using 'excludes: List' is an error
+```
+
+_Solution for Kotlin:_ use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ excludes += listOf("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+
+Solution for Groovy: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ excludes.addAll("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+* for `koverMergedHtmlReport` task use `koverMerged { ... }` extension of the project.
+
+## `koverVerify` and `koverMergedVerify` tasks configuration
+
+### Function `rule` was removed for single-project verification
+
+Error message:
+```
+Using 'rule(Action): Unit' is an error
+```
+
+_Solution:_
+
+use function `rule` in Kover project extension
+
+```
+kover {
+ verify {
+ rule {
+ // your verification rule
+ }
+ }
+}
+```
+
+* For `koverMergedVerify` task use `koverMerged { ... }` extension of the project.
+
+### Property `includes` was removed
+
+_Error message:_
+```
+Using 'includes: List' is an error
+```
+
+Solution for Kotlin: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ includes += listOf("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+
+Solution for Groovy: use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ includes.addAll("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+* for `koverMergedVerify` task use `koverMerged { ... }` extension of the project.
+
+### Property `excludes` was removed
+
+_Error message:_
+```
+Using 'excludes: List' is an error
+```
+
+_Solution for Kotlin:_ use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ excludes += listOf("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+
+_Solution for Groovy:_ use filter in Kover extension at the root of the project
+
+```
+kover {
+ filters {
+ classes {
+ excludes.addAll("foo.bar.*", "foo.biz.*")
+ }
+ }
+}
+```
+* For `koverMergedVerify` task use `koverMerged { ... }` extension of the project.
+
diff --git a/gradle.properties b/gradle.properties
index 92f2dfd3..11800157 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,5 @@
-version=0.5.1
+version=0.6.0-SNAPSHOT
group=org.jetbrains.kotlinx
+kotlin.version=1.7.10
kotlin.code.style=official
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/AdaptersTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/AdaptersTests.kt
index 4b392e11..57440f2b 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/AdaptersTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/AdaptersTests.kt
@@ -14,7 +14,7 @@ internal class AdaptersTests : BaseGradleScriptTest() {
Classes from plugins applied in subproject not accessible for Kover in root project.
Therefore, Kover is forced to use reflection to work with extensions of the kotlin multiplatform plugin.
*/
- internalSample("different-plugins")
+ sampleBuild("different-plugins")
.run("koverMergedXmlReport") {
xml(defaultMergedXmlReport()) {
classCounter("org.jetbrains.CommonClass").assertFullyCovered()
@@ -22,7 +22,7 @@ internal class AdaptersTests : BaseGradleScriptTest() {
}
}
- internalSample("different-plugins")
+ sampleBuild("different-plugins")
.run("koverXmlReport") {
subproject("subproject-multiplatform") {
xml(defaultXmlReport()) {
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ConfigurationCacheTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ConfigurationCacheTests.kt
index 5c2d89bb..125a3be0 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ConfigurationCacheTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ConfigurationCacheTests.kt
@@ -3,12 +3,25 @@ package kotlinx.kover.test.functional.cases
import kotlinx.kover.test.functional.core.*
import kotlin.test.*
-internal class ConfigurationCacheTests: BaseGradleScriptTest() {
+internal class ConfigurationCacheTests : BaseGradleScriptTest() {
@Test
fun testConfigCache() {
- builder("Testing configuration cache support")
- .sources("simple")
- .build()
- .run("build", "koverMergedReport", "koverMergedVerify", "koverReport", "koverVerify", "--configuration-cache")
+ val build = diverseBuild()
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ koverMerged {
+ enable()
+ }
+ }
+
+ val runner = build.prepare()
+ runner.run(
+ "build",
+ "koverMergedReport",
+ "koverMergedVerify",
+ "koverReport",
+ "koverVerify",
+ "--configuration-cache"
+ )
}
}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/DefaultConfigTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/DefaultConfigTests.kt
index a95f1a07..75486281 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/DefaultConfigTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/DefaultConfigTests.kt
@@ -7,15 +7,20 @@ import kotlin.test.*
internal class DefaultConfigTests : BaseGradleScriptTest() {
@Test
fun testImplicitConfigs() {
- builder("Test implicit default settings")
- .languages(GradleScriptLanguage.GROOVY, GradleScriptLanguage.KOTLIN)
- .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
- .sources("simple")
- .build()
- .run("build") {
- checkDefaultBinaryReport()
- checkDefaultMergedReports()
- }
+ val build = diverseBuild(
+ languages = ALL_LANGUAGES,
+ types = ALL_TYPES
+ )
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ }
+ val runner = build.prepare()
+
+ runner.run("koverReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports()
+ }
+
}
}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/InstrumentationFilteringTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/InstrumentationFilteringTests.kt
index b318bbed..ea7d857b 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/InstrumentationFilteringTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/InstrumentationFilteringTests.kt
@@ -1,6 +1,5 @@
package kotlinx.kover.test.functional.cases
-import kotlinx.kover.api.*
import kotlinx.kover.test.functional.cases.utils.*
import kotlinx.kover.test.functional.core.*
import kotlinx.kover.test.functional.core.BaseGradleScriptTest
@@ -10,47 +9,59 @@ internal class InstrumentationFilteringTests : BaseGradleScriptTest() {
@Test
fun testExclude() {
- builder("Test exclusion of classes from instrumentation")
- .languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
- .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("simple")
- .configTest(
- """excludes = listOf("org.jetbrains.*Exa?ple*")""",
- """excludes = ['org.jetbrains.*Exa?ple*']"""
- )
- .build()
- .run("build") {
- xml(defaultMergedXmlReport()) {
- classCounter("org.jetbrains.ExampleClass").assertFullyMissed()
- classCounter("org.jetbrains.SecondClass").assertCovered()
- }
+ val build = diverseBuild(ALL_LANGUAGES, ALL_ENGINES, ALL_TYPES)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ testTasks {
+ excludes("org.jetbrains.*Exa?ple*")
+ }
+ }
+ val runner = build.prepare()
+ runner.run("build", "koverXmlReport") {
+ xml(defaultXmlReport()) {
+ classCounter("org.jetbrains.ExampleClass").assertFullyMissed()
+ classCounter("org.jetbrains.SecondClass").assertCovered()
}
+ }
+
}
@Test
fun testExcludeInclude() {
- builder("Test inclusion and exclusion of classes in instrumentation")
- .languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
- .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("simple")
- .configTest(
- """includes = listOf("org.jetbrains.*Cla?s")""",
- """includes = ['org.jetbrains.*Cla?s']"""
- )
- .configTest(
- """excludes = listOf("org.jetbrains.*Exa?ple*")""",
- """excludes = ['org.jetbrains.*Exa?ple*']"""
- )
- .build()
- .run("build") {
- xml(defaultMergedXmlReport()) {
- classCounter("org.jetbrains.ExampleClass").assertFullyMissed()
- classCounter("org.jetbrains.Unused").assertFullyMissed()
- classCounter("org.jetbrains.SecondClass").assertCovered()
+ val build = diverseBuild(ALL_LANGUAGES, ALL_ENGINES, ALL_TYPES)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ testTasks {
+ includes("org.jetbrains.*Cla?s")
+ excludes("org.jetbrains.*Exa?ple*")
+ }
+ }
+ val runner = build.prepare()
+ runner.run("build", "koverXmlReport") {
+ xml(defaultXmlReport()) {
+ classCounter("org.jetbrains.ExampleClass").assertFullyMissed()
+ classCounter("org.jetbrains.Unused").assertFullyMissed()
+ classCounter("org.jetbrains.SecondClass").assertCovered()
+ }
+ }
+ }
+
+ @Test
+ fun testDisableInstrumentationOfTask() {
+ val build = diverseBuild(ALL_LANGUAGES, ALL_ENGINES, listOf(ProjectType.KOTLIN_JVM))
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ kover {
+ instrumentation {
+ excludeTasks += "test"
}
}
+ }
+ val runner = build.prepare()
+ runner.run("build", "koverXmlReport") {
+ // if task `test` is excluded from instrumentation then the binary report is not created for it
+ checkDefaultBinaryReport(false)
+ }
}
}
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 48b7d475..413b60b8 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/MultiProjectTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/MultiProjectTests.kt
@@ -1,175 +1,223 @@
package kotlinx.kover.test.functional.cases
-import kotlinx.kover.api.*
import kotlinx.kover.test.functional.cases.utils.*
import kotlinx.kover.test.functional.cases.utils.defaultMergedXmlReport
+import kotlinx.kover.test.functional.core.*
import kotlinx.kover.test.functional.core.BaseGradleScriptTest
-import kotlinx.kover.test.functional.core.ProjectType
import org.gradle.testkit.runner.*
import kotlin.test.*
internal class MultiProjectTests : BaseGradleScriptTest() {
private val subprojectName = "common"
+ private val rootName = "kover-functional-test"
+
@Test
fun testMergedReports() {
- builder("Testing the generation of merged reports")
- .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("multiproject-user")
- .subproject(subprojectName) {
- sources("multiproject-common")
- }
- .build()
- .run("build") {
- xml(defaultMergedXmlReport()) {
- classCounter("org.jetbrains.CommonClass").assertFullyCovered()
- classCounter("org.jetbrains.CommonInternalClass").assertFullyCovered()
- classCounter("org.jetbrains.UserClass").assertFullyCovered()
- }
- }
- }
+ val build = diverseBuild(engines = ALL_ENGINES, types = ALL_TYPES)
+ val subPath = build.addKoverSubproject(subprojectName) {
+ sourcesFrom("multiproject-common")
+ }
- @Test
- fun testIsolatedProjectsReports() {
- builder("Testing the generation of project reports with running tests only for this project")
- .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("multiproject-user")
- .subproject(subprojectName) {
- sources("multiproject-common")
+ build.addKoverRootProject {
+ sourcesFrom("multiproject-user")
+ subproject(subPath)
+
+ koverMerged {
+ enable()
}
- .build()
- .run("koverReport") {
- xml(defaultXmlReport()) {
- classCounter("org.jetbrains.CommonClass").assertAbsent()
- classCounter("org.jetbrains.CommonInternalClass").assertAbsent()
- classCounter("org.jetbrains.UserClass").assertFullyCovered()
- }
+ }
- subproject(subprojectName) {
- xml(defaultXmlReport()) {
- classCounter("org.jetbrains.UserClass").assertAbsent()
- // common class covered partially because calls from the root project are not counted
- classCounter("org.jetbrains.CommonClass").assertCovered()
- classCounter("org.jetbrains.CommonInternalClass").assertCovered()
- }
- }
+ val runner = build.prepare()
+ runner.run(":koverMergedXmlReport") {
+ xml(defaultMergedXmlReport()) {
+ classCounter("org.jetbrains.CommonClass").assertFullyCovered()
+ classCounter("org.jetbrains.CommonInternalClass").assertFullyCovered()
+ classCounter("org.jetbrains.UserClass").assertFullyCovered()
}
+ }
}
@Test
- fun testLinkedProjectsReports() {
- builder("Testing the generation of project reports with running all tests for all projects")
- .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .configKover { runAllTestsForProjectTask = true }
- .sources("multiproject-user")
- .subproject(subprojectName) {
- sources("multiproject-common")
+ fun testIsolatedProjectsReports() {
+ val build = diverseBuild(engines = ALL_ENGINES, types = ALL_TYPES)
+ val subPath = build.addKoverSubproject(subprojectName) {
+ sourcesFrom("multiproject-common")
+ }
+
+ build.addKoverRootProject {
+ sourcesFrom("multiproject-user")
+ subproject(subPath)
+ }
+
+
+ val runner = build.prepare()
+ runner.run("koverXmlReport") {
+ xml(defaultXmlReport()) {
+ classCounter("org.jetbrains.CommonClass").assertAbsent()
+ classCounter("org.jetbrains.CommonInternalClass").assertAbsent()
+ classCounter("org.jetbrains.UserClass").assertFullyCovered()
}
- .build()
- .run("koverReport") {
- xml(defaultXmlReport()) {
- classCounter("org.jetbrains.CommonClass").assertAbsent()
- classCounter("org.jetbrains.CommonInternalClass").assertAbsent()
- classCounter("org.jetbrains.UserClass").assertFullyCovered()
- }
- subproject(subprojectName) {
- xml(defaultXmlReport()) {
- classCounter("org.jetbrains.UserClass").assertAbsent()
+ subproject(subprojectName) {
+ xml(defaultXmlReport()) {
+ classCounter("org.jetbrains.UserClass").assertAbsent()
- // common class fully covered because calls from the root project are counted too
- classCounter("org.jetbrains.CommonClass").assertFullyCovered()
- classCounter("org.jetbrains.CommonInternalClass").assertFullyCovered()
- }
+ // common class covered partially because calls from the root project are not counted
+ classCounter("org.jetbrains.CommonClass").assertCovered()
+ classCounter("org.jetbrains.CommonInternalClass").assertCovered()
}
}
+ }
}
+//
+// @Test
+// fun testLinkedProjectsReports() {
+// builder("Testing the generation of project reports with running all tests for all projects")
+// .types(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
+// .engines(CoverageEngineVendor.INTELLIJ, CoverageEngineVendor.JACOCO)
+// .configKover { runAllTestsForProjectTask = true }
+// .sources("multiproject-user")
+// .subproject(subprojectName) {
+// sources("multiproject-common")
+// }
+// .build()
+// .run("koverReport") {
+// xml(defaultXmlReport()) {
+// classCounter("org.jetbrains.CommonClass").assertAbsent()
+// classCounter("org.jetbrains.CommonInternalClass").assertAbsent()
+// classCounter("org.jetbrains.UserClass").assertFullyCovered()
+// }
+//
+// subproject(subprojectName) {
+// xml(defaultXmlReport()) {
+// classCounter("org.jetbrains.UserClass").assertAbsent()
+//
+// // common class fully covered because calls from the root project are counted too
+// classCounter("org.jetbrains.CommonClass").assertFullyCovered()
+// classCounter("org.jetbrains.CommonInternalClass").assertFullyCovered()
+// }
+// }
+// }
+// }
@Test
fun testDisabledKover() {
- builder("Testing disabling whole Kover")
- .sources("multiproject-user")
- .subproject(subprojectName) {
- sources("multiproject-common")
+ val build = diverseBuild(engines = ALL_ENGINES, types = ALL_TYPES)
+ val subPath = build.addKoverSubproject(subprojectName) {
+ sourcesFrom("multiproject-common")
+ kover {
+ isDisabled = true
}
- .configKover { disabled = true }
- .build()
- .run("build", "koverHtmlReport") {
- checkDefaultBinaryReport(false)
- checkOutcome("koverMergedHtmlReport", TaskOutcome.SKIPPED)
- checkOutcome("koverMergedVerify", TaskOutcome.SKIPPED)
+ }
+ build.addKoverRootProject {
+ sourcesFrom("multiproject-user")
+ subproject(subPath)
+ kover {
+ isDisabled = true
+ }
+ }
+
+ val runner = build.prepare()
+ runner.run("koverReport", "koverVerify") {
+ checkDefaultBinaryReport(false)
+
+ checkOutcome("koverHtmlReport", TaskOutcome.SKIPPED)
+ checkOutcome("koverXmlReport", TaskOutcome.SKIPPED)
+ checkOutcome("koverVerify", TaskOutcome.SKIPPED)
+
+ subproject(subprojectName) {
+ checkDefaultBinaryReport(false)
checkOutcome("koverHtmlReport", TaskOutcome.SKIPPED)
+ checkOutcome("koverXmlReport", TaskOutcome.SKIPPED)
checkOutcome("koverVerify", TaskOutcome.SKIPPED)
- checkOutcome("koverMergedHtmlReport", TaskOutcome.SKIPPED)
-
- subproject(subprojectName) {
- checkDefaultBinaryReport(false)
- checkOutcome("koverHtmlReport", TaskOutcome.SKIPPED)
- checkOutcome("koverVerify", TaskOutcome.SKIPPED)
- }
}
+ }
}
@Test
- fun testDisableSubproject() {
- builder("Testing disabling one of subproject")
- .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()
+ fun testExcludeProject() {
+ val build = diverseBuild(engines = ALL_ENGINES, types = ALL_TYPES)
+ val subPath = build.addKoverSubproject(subprojectName) {
+ sourcesFrom("multiproject-common")
+ }
+
+ build.addKoverRootProject {
+ sourcesFrom("multiproject-user")
+ subproject(subPath)
+
+ koverMerged {
+ enable()
+ filters {
+ projects {
+ excludes += subprojectName
+ }
}
+ }
+ }
+
+ val runner = build.prepare()
+ runner.run("koverMergedReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports(false)
+ checkDefaultMergedReports()
+ 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)
- }
+ subproject(subprojectName) {
+ checkDefaultBinaryReport(false)
+ checkDefaultMergedReports(false)
+ checkDefaultReports(false)
}
+ }
}
@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()
+ fun testExcludeProjectByPath() {
+ val build = diverseBuild(engines = ALL_ENGINES, types = ALL_TYPES)
+ val subPath = build.addKoverSubproject(subprojectName) {
+ sourcesFrom("multiproject-common")
+ }
+
+ build.addKoverRootProject {
+ sourcesFrom("multiproject-user")
+ subproject(subPath)
+
+ koverMerged {
+ enable()
+ filters {
+ projects {
+ excludes += subPath
+ }
}
+ }
+ }
+
+ val runner = build.prepare()
+ runner.run("koverMergedReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports(false)
+ checkDefaultMergedReports()
+ 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)
- }
+ subproject(subprojectName) {
+ checkDefaultBinaryReport(false)
+ checkDefaultMergedReports(false)
+ checkDefaultReports(false)
}
+ }
}
}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsCachingTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsCachingTests.kt
index f1491a82..f2ea6cd8 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsCachingTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsCachingTests.kt
@@ -1,7 +1,8 @@
package kotlinx.kover.test.functional.cases
-import kotlinx.kover.api.*
import kotlinx.kover.test.functional.cases.utils.*
+import kotlinx.kover.test.functional.core.*
+import kotlinx.kover.test.functional.core.ALL_ENGINES
import kotlinx.kover.test.functional.core.BaseGradleScriptTest
import org.gradle.testkit.runner.*
import kotlin.test.*
@@ -9,56 +10,56 @@ import kotlin.test.*
internal class ReportsCachingTests : BaseGradleScriptTest() {
@Test
fun testCaching() {
- builder("Test caching of merged reports")
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("simple")
- .withLocalCache()
- .build()
- .run("build", "--build-cache") {
- checkDefaultBinaryReport()
- checkDefaultMergedReports()
- checkOutcome("test", TaskOutcome.SUCCESS)
- checkOutcome("koverMergedXmlReport", TaskOutcome.SUCCESS)
- checkOutcome("koverMergedHtmlReport", TaskOutcome.SUCCESS)
- }
- .run("clean", "--build-cache") {
- checkDefaultBinaryReport(false)
- checkDefaultMergedReports(false)
- }
- .run("build", "--build-cache") {
- checkDefaultBinaryReport()
- checkDefaultMergedReports()
- checkOutcome("test", TaskOutcome.FROM_CACHE)
- checkOutcome("koverMergedXmlReport", TaskOutcome.FROM_CACHE)
- checkOutcome("koverMergedHtmlReport", TaskOutcome.FROM_CACHE)
- }
+ val build = diverseBuild(engines = ALL_ENGINES, withCache = true)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ }
+ val runner = build.prepare()
+ runner.run("koverReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports()
+ checkOutcome("test", TaskOutcome.SUCCESS)
+ checkOutcome("koverXmlReport", TaskOutcome.SUCCESS)
+ checkOutcome("koverHtmlReport", TaskOutcome.SUCCESS)
+ }
+ runner.run("clean") {
+ checkDefaultBinaryReport(false)
+ checkDefaultReports(false)
+ }
+ runner.run("koverReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports()
+ checkOutcome("test", TaskOutcome.FROM_CACHE)
+ checkOutcome("koverXmlReport", TaskOutcome.FROM_CACHE)
+ checkOutcome("koverHtmlReport", TaskOutcome.FROM_CACHE)
+ }
}
@Test
fun testProjectReportCaching() {
- builder("Test caching projects reports")
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("simple")
- .withLocalCache()
- .build()
- .run("koverReport", "--build-cache") {
- checkDefaultBinaryReport()
- checkDefaultReports()
- checkOutcome("test", TaskOutcome.SUCCESS)
- checkOutcome("koverXmlReport", TaskOutcome.SUCCESS)
- checkOutcome("koverHtmlReport", TaskOutcome.SUCCESS)
- }
- .run("clean", "--build-cache") {
- checkDefaultBinaryReport(false)
- checkDefaultReports(false)
- }
- .run("koverReport", "--build-cache") {
- checkDefaultBinaryReport()
- checkDefaultReports()
- checkOutcome("test", TaskOutcome.FROM_CACHE)
- checkOutcome("koverXmlReport", TaskOutcome.FROM_CACHE)
- checkOutcome("koverHtmlReport", TaskOutcome.FROM_CACHE)
- }
+ val build = diverseBuild(engines = ALL_ENGINES, withCache = true)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+ }
+ val runner = build.prepare()
+ runner.run("koverReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports()
+ checkOutcome("test", TaskOutcome.SUCCESS)
+ checkOutcome("koverXmlReport", TaskOutcome.SUCCESS)
+ checkOutcome("koverHtmlReport", TaskOutcome.SUCCESS)
+ }
+ runner.run("clean") {
+ checkDefaultBinaryReport(false)
+ checkDefaultReports(false)
+ }
+ runner.run("koverReport") {
+ checkDefaultBinaryReport()
+ checkDefaultReports()
+ checkOutcome("test", TaskOutcome.FROM_CACHE)
+ checkOutcome("koverXmlReport", TaskOutcome.FROM_CACHE)
+ checkOutcome("koverHtmlReport", TaskOutcome.FROM_CACHE)
+ }
}
}
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 c310e411..8d5e4ce2 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsFilteringTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/ReportsFilteringTests.kt
@@ -9,54 +9,50 @@ 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(defaultMergedXmlReport()) {
- classCounter("org.jetbrains.ExampleClass").assertAbsent()
- classCounter("org.jetbrains.SecondClass").assertCovered()
+ val build = diverseBuild(languages = ALL_LANGUAGES)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+
+ kover {
+ filters {
+ classes {
+ excludes += "org.jetbrains.*Exa?ple*"
+ }
}
}
+ }
+ val runner = build.prepare()
+ runner.run("koverXmlReport") {
+ xml(defaultXmlReport()) {
+ classCounter("org.jetbrains.ExampleClass").assertAbsent()
+ classCounter("org.jetbrains.SecondClass").assertCovered()
+ }
+ }
}
@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(),
+ val build = diverseBuild(languages = ALL_LANGUAGES)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
- """
- tasks.koverMergedXmlReport {
- includes = ['org.jetbrains.*Cla?s']
- excludes = ['org.jetbrains.*Exa?ple*']
- }""".trimIndent()
- )
- .build()
- .run("build") {
- xml(defaultMergedXmlReport()) {
- classCounter("org.jetbrains.ExampleClass").assertAbsent()
- classCounter("org.jetbrains.Unused").assertAbsent()
- classCounter("org.jetbrains.SecondClass").assertFullyCovered()
+ kover {
+ filters {
+ classes {
+ excludes += "org.jetbrains.*Exa?ple*"
+ includes += "org.jetbrains.*Cla?s"
+ }
}
}
+ }
+ val runner = build.prepare()
+ runner.run("koverXmlReport") {
+ xml(defaultXmlReport()) {
+ classCounter("org.jetbrains.ExampleClass").assertAbsent()
+ classCounter("org.jetbrains.Unused").assertAbsent()
+ classCounter("org.jetbrains.SecondClass").assertFullyCovered()
+ }
+ }
}
}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/VerificationTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/VerificationTests.kt
index b423b3b1..678599c5 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/VerificationTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/VerificationTests.kt
@@ -12,84 +12,102 @@ import kotlin.test.*
internal class VerificationTests : BaseGradleScriptTest() {
@Test
fun testVerified() {
- builder("Test verification passed")
- .languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
- .engines(CoverageEngine.INTELLIJ, CoverageEngine.JACOCO)
- .sources("simple")
- .rule("test rule") {
- bound {
- minValue = 50
- maxValue = 60
- }
- bound {
- valueType = VerificationValueType.COVERED_LINES_COUNT
- minValue = 2
- maxValue = 10
+ val build = diverseBuild(languages = ALL_LANGUAGES, engines = ALL_ENGINES)
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+
+ kover {
+ verify {
+ rule {
+ name = "test rule"
+ bound {
+ minValue = 50
+ maxValue = 60
+ }
+ bound {
+ valueType = VerificationValueType.COVERED_COUNT
+ minValue = 2
+ maxValue = 10
+ }
+ }
}
}
- .build()
- .run("koverVerify")
+ }
+
+ build.prepare().run("koverVerify", "--stacktrace")
}
@Test
fun testNotVerifiedIntelliJ() {
- builder("Test verification failed for IntelliJ Engine")
- .languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
- .engines(CoverageEngine.INTELLIJ)
- .sources("simple")
- .rule("test rule") {
- bound {
- minValue = 58
- maxValue = 60
- }
- bound {
- valueType = VerificationValueType.COVERED_LINES_COUNT
- minValue = 2
- maxValue = 3
+ val build = diverseBuild(languages = ALL_LANGUAGES, engines = listOf(CoverageEngineVendor.INTELLIJ))
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+
+ kover {
+ verify {
+ rule {
+ name = "test rule"
+ bound {
+ minValue = 58
+ maxValue = 60
+ }
+ bound {
+ valueType = VerificationValueType.COVERED_COUNT
+ minValue = 2
+ maxValue = 3
+ }
+ }
}
}
- .build()
- .runWithError("koverVerify") {
- output {
- assertTrue {
- this.contains(
- "> Rule 'test rule' violated:\n" +
- " covered lines percentage is 57.142900, but expected minimum is 58\n" +
- " covered lines count is 4, but expected maximum is 3"
- )
- }
+ }
+
+ build.prepare().runWithError("koverVerify") {
+ output {
+ assertTrue {
+ this.contains(
+ "> Rule 'test rule' violated:\n" +
+ " covered lines percentage is 57.142900, but expected minimum is 58\n" +
+ " covered lines count is 4, but expected maximum is 3"
+ )
}
}
+ }
}
@Test
fun testNotVerifiedJaCoCo() {
- builder("Test verification failed for JaCoCo Engine")
- .languages(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
- .engines(CoverageEngine.JACOCO)
- .sources("simple")
- .rule("test rule") {
- bound {
- minValue = 58
- maxValue = 60
- }
- bound {
- valueType = VerificationValueType.COVERED_LINES_COUNT
- minValue = 2
- maxValue = 3
+ val build = diverseBuild(languages = ALL_LANGUAGES, engines = listOf(CoverageEngineVendor.JACOCO))
+ build.addKoverRootProject {
+ sourcesFrom("simple")
+
+ kover {
+ verify {
+ rule {
+ name = "test rule"
+ bound {
+ minValue = 58
+ maxValue = 60
+ }
+ bound {
+ valueType = VerificationValueType.COVERED_COUNT
+ minValue = 2
+ maxValue = 3
+ }
+ }
}
}
- .build()
- .runWithError("koverVerify") {
- output {
- assertTrue {
- this.contains(
- "[ant:jacocoReport] Rule violated for bundle :: lines covered ratio is 0.50, but expected minimum is 0.58\n" +
- "[ant:jacocoReport] Rule violated for bundle :: lines covered count is 4, but expected maximum is 3"
- )
- }
+ }
+
+ build.prepare().runWithError("koverVerify") {
+ output {
+ assertTrue {
+ this.contains(
+ "[ant:jacocoReport] Rule violated for bundle :: lines covered ratio is 0.50, but expected minimum is 0.58\n" +
+ "[ant:jacocoReport] Rule violated for bundle :: lines covered count is 4, but expected maximum is 3"
+ )
}
}
+ }
}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/counters/CountersValueTests.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/counters/CountersValueTests.kt
index 8a9e3bb1..040a7d25 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/counters/CountersValueTests.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/counters/CountersValueTests.kt
@@ -1,45 +1,51 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
package kotlinx.kover.test.functional.cases.counters
import kotlinx.kover.api.*
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 CountersValueTests : BaseGradleScriptTest() {
@Test
fun testBasicCounterCases() {
- builder("Testing of basic counting capabilities by an IntelliJ Agent")
- .engines(CoverageEngine.INTELLIJ)
- .sources("counters")
- .build()
- .run("build") {
- xml(defaultMergedXmlReport()) {
- // test on branch counter
- methodCounter("org.jetbrains.MyBranchedClass", "foo", type = "BRANCH").assertCovered(1, 3)
-
- // test on constructor of used sealed classes
- methodCounter("org.jetbrains.Sealed", "").assertFullyCovered()
- methodCounter("org.jetbrains.SealedWithInit", "").assertFullyCovered()
- methodCounter("org.jetbrains.SealedWithConstructor", "").assertFullyCovered()
-
- // test on empty objects
- classCounter("org.jetbrains.UnusedObject").assertFullyMissed()
- classCounter("org.jetbrains.UsedObject").assertFullyCovered()
-
- // deprecated functions (ERROR and HIDDEN) should not be included in the report.
- methodCounter("org.jetbrains.Different", "deprecatedError").assertAbsent()
- methodCounter("org.jetbrains.Different", "deprecatedHidden").assertAbsent()
- // WARNING deprecated functions should be included in the report.
- methodCounter("org.jetbrains.Different", "deprecatedWarn").assertFullyMissed()
-
- // empty functions must be included in the report with line counter
- methodCounter("org.jetbrains.Different", "emptyFun", type = "LINE").assertFullyMissed()
-
- // Instruction counters
- // helloWorld contains 4 instructions. `return` ignored by reporter and the first instruction is not covered because of a bug in the IR compiler
- // FIXME after https://youtrack.jetbrains.com/issue/KT-51080 is fixed, the number should become 3
- methodCounter("org.jetbrains.Different", "helloWorld").assertTotal(2)
- }
+ val build = diverseBuild(
+ engines = listOf(CoverageEngineVendor.INTELLIJ)
+ )
+ build.addKoverRootProject {
+ sourcesFrom("counters")
+ }
+
+ val runner = build.prepare()
+ runner.run("koverXmlReport") {
+ xml(defaultXmlReport()) {
+ // test on branch counter
+ methodCounter("org.jetbrains.MyBranchedClass", "foo", type = "BRANCH").assertCovered(1, 3)
+
+ // test on constructor of used sealed classes
+ methodCounter("org.jetbrains.Sealed", "").assertFullyCovered()
+ methodCounter("org.jetbrains.SealedWithInit", "").assertFullyCovered()
+ methodCounter("org.jetbrains.SealedWithConstructor", "").assertFullyCovered()
+
+ // test on empty objects
+ classCounter("org.jetbrains.UnusedObject").assertFullyMissed()
+ classCounter("org.jetbrains.UsedObject").assertFullyCovered()
+
+ // deprecated functions (ERROR and HIDDEN) should not be included in the report.
+ methodCounter("org.jetbrains.Different", "deprecatedError").assertAbsent()
+ methodCounter("org.jetbrains.Different", "deprecatedHidden").assertAbsent()
+ // WARNING deprecated functions should be included in the report.
+ methodCounter("org.jetbrains.Different", "deprecatedWarn").assertFullyMissed()
+
+ // empty functions must be included in the report with line counter
+ methodCounter("org.jetbrains.Different", "emptyFun", type = "LINE").assertFullyMissed()
+
+ // Instruction counters - value may depend on Kotlin compiler version
+ methodCounter("org.jetbrains.Different", "helloWorld").assertTotal(4)
}
+ }
}
}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Commons.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Commons.kt
index 48383530..2d6b2b4a 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Commons.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Commons.kt
@@ -60,7 +60,7 @@ internal fun RunResult.checkReports(xmlPath: String, htmlPath: String, mustExist
}
internal fun RunResult.checkIntellijErrors(errorExpected: Boolean = false) {
- if (engine != CoverageEngine.INTELLIJ) return
+ if (engine != CoverageEngineVendor.INTELLIJ) return
file(errorsDirectory()) {
if (this.exists() && !errorExpected) {
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Defaults.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Defaults.kt
index 947f69a3..09e836af 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Defaults.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/cases/utils/Defaults.kt
@@ -1,22 +1,30 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
package kotlinx.kover.test.functional.cases.utils
import kotlinx.kover.api.*
import kotlinx.kover.test.functional.core.ProjectType
-internal fun defaultBinaryReport(engine: CoverageEngine, projectType: ProjectType): String {
- val extension = if (engine == CoverageEngine.INTELLIJ) "ic" else "exec"
+internal fun defaultTestTask(engine: CoverageEngineVendor, projectType: ProjectType): String {
+ val extension = if (engine == CoverageEngineVendor.INTELLIJ) "ic" else "exec"
return when (projectType) {
- ProjectType.KOTLIN_JVM -> "kover/test.$extension"
- ProjectType.KOTLIN_MULTIPLATFORM -> "kover/jvmTest.$extension"
- ProjectType.ANDROID -> "kover/jvmTest.$extension"
+ ProjectType.KOTLIN_JVM -> "test.$extension"
+ ProjectType.KOTLIN_MULTIPLATFORM -> "jvmTest.$extension"
+ ProjectType.ANDROID -> "jvmTest.$extension"
}
}
-internal fun defaultMergedXmlReport() = "reports/kover/report.xml"
-internal fun defaultMergedHtmlReport() = "reports/kover/html"
+internal fun defaultBinaryReport(engine: CoverageEngineVendor, projectType: ProjectType): String {
+ return "kover/" + defaultTestTask(engine, projectType)
+}
+
+internal fun defaultMergedXmlReport() = "reports/kover/merged/xml/report.xml"
+internal fun defaultMergedHtmlReport() = "reports/kover/merged/html"
-internal fun defaultXmlReport() = "reports/kover/project-xml/report.xml"
-internal fun defaultHtmlReport() = "reports/kover/project-html"
+internal fun defaultXmlReport() = "reports/kover/xml/report.xml"
+internal fun defaultHtmlReport() = "reports/kover/html"
internal fun errorsDirectory() = "kover/errors"
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/BaseGradleScriptTest.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/BaseGradleScriptTest.kt
index f5e2a3ba..1895ad58 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/BaseGradleScriptTest.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/BaseGradleScriptTest.kt
@@ -1,19 +1,83 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
package kotlinx.kover.test.functional.core
+import kotlinx.kover.api.*
import org.junit.*
import org.junit.rules.*
+/**
+ * Kotlin by default.
+ */
+internal val DEFAULT_LANGUAGE = listOf(GradleScriptLanguage.KOTLIN)
+
+/**
+ * Used engine from configuration if specified. Otherwise, used [DefaultIntellijEngine] engine.
+ */
+internal val DEFAULT_ENGINE = listOf()
+
+/**
+ * Kotlin JVM project by default.
+ */
+internal val DEFAULT_TYPE = listOf(ProjectType.KOTLIN_JVM)
+
+internal val ALL_LANGUAGES = listOf(GradleScriptLanguage.KOTLIN, GradleScriptLanguage.GROOVY)
+internal val ALL_ENGINES = listOf(CoverageEngineVendor.INTELLIJ, CoverageEngineVendor.JACOCO)
+internal val ALL_TYPES = listOf(ProjectType.KOTLIN_JVM, ProjectType.KOTLIN_MULTIPLATFORM)
+
internal open class BaseGradleScriptTest {
@Rule
@JvmField
internal val rootFolder: TemporaryFolder = TemporaryFolder()
- fun builder(description: String): TestCaseBuilder {
- return createBuilder(rootFolder.root, description)
+
+ internal fun diverseBuild(
+ languages: List = DEFAULT_LANGUAGE,
+ engines: List = DEFAULT_ENGINE,
+ types: List = DEFAULT_TYPE,
+ withCache: Boolean = false
+ ): DiverseBuild {
+ return DiverseBuildState(rootFolder.root, languages, engines, types, withCache)
}
- fun internalSample(name: String): GradleRunner {
- return createInternalSample(name, rootFolder.root)
+ internal fun sampleBuild(templateName: String): GradleRunner {
+ return createInternalSample(templateName, rootFolder.root)
}
}
+
+
+internal fun DiverseBuild.addKoverRootProject(builder: ProjectBuilder.() -> Unit) {
+ addProject("root", ":") {
+ plugins {
+ kotlin("1.7.10")
+ kover("DEV")
+ }
+
+ repositories {
+ repository("mavenCentral()")
+ }
+
+ builder()
+ }
+
+}
+
+internal fun DiverseBuild.addKoverSubproject(name: String, builder: ProjectBuilder.() -> Unit): String {
+ addProject(name, ":$name") {
+ plugins {
+ kotlin()
+ kover()
+ }
+
+ repositories {
+ repository("mavenCentral()")
+ }
+
+ builder()
+ }
+
+ return ":$name"
+}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Builder.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Builder.kt
index b638381c..6d33425e 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Builder.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Builder.kt
@@ -5,163 +5,247 @@
package kotlinx.kover.test.functional.core
import kotlinx.kover.api.*
+import kotlinx.kover.test.functional.core.writer.initSlice
import org.gradle.api.*
import java.io.*
-internal fun createBuilder(rootDir: File, description: String): TestCaseBuilder {
- return TestCaseBuilderImpl(rootDir, description)
-}
+internal class DiverseBuildState(
+ private val rootDir: File,
+ private val languages: List,
+ private val engines: List,
+ private val types: List,
+ private val withCache: Boolean
+) : DiverseBuild {
+ private val projects: MutableMap = mutableMapOf()
-internal class CommonBuilderState(val description: String) {
- var pluginVersion: String? = null
- val languages: MutableSet = mutableSetOf()
- val types: MutableSet = mutableSetOf()
- val engines: MutableSet = mutableSetOf()
- val koverConfig: KoverRootConfig = KoverRootConfig()
- val rootProject: ProjectBuilderState = ProjectBuilderState()
- val subprojects: MutableMap = mutableMapOf()
- var localCache: Boolean = false
-}
+ override fun addProject(name: String, path: String, builder: ProjectBuilder.() -> Unit) {
+ projects[path] = ProjectBuilderState(name).also(builder)
+ }
+
+ override fun prepare(): GradleRunner {
+ val initSlices: MutableMap = mutableMapOf()
+
+ val targetEngines = engines.ifEmpty { listOf(null) }
+
+
+ languages.forEach { language ->
+ types.forEach { type ->
+ targetEngines.forEach { engine ->
+ val slice = ProjectSlice(language, type, engine)
+ initSlices[slice] = initSlice(rootDir, slice, projects, withCache)
+ }
+ }
+ }
-internal class ProjectBuilderState {
- val sourceTemplates: MutableList = mutableListOf()
- val scripts: MutableList = mutableListOf()
- val testScripts: MutableList = mutableListOf()
- val dependencies: MutableList = mutableListOf()
- val rules: MutableList = mutableListOf()
- val mainSources: MutableMap = mutableMapOf()
- val testSources: MutableMap = mutableMapOf()
+ val extraArgs = if (withCache) {
+ listOf("--build-cache")
+ } else {
+ listOf()
+ }
+
+ return DiverseGradleRunner(initSlices, extraArgs)
+ }
}
-internal data class GradleScript(val kotlin: String, val groovy: String)
-private class TestCaseBuilderImpl(
- val rootDir: File,
- description: String,
- private val state: CommonBuilderState = CommonBuilderState(description)
-) : ProjectBuilderImpl(state.rootProject), TestCaseBuilder {
+internal class ProjectBuilderState(val name: String) : ProjectBuilder {
+ val sourceTemplates: MutableSet = mutableSetOf()
+ val plugins: PluginsState = PluginsState()
+ val repositories: RepositoriesState = RepositoriesState()
+ var kover: TestKoverProjectConfigState? = null
+ var merged: TestKoverMergedConfigState? = null
+ val testTasks: TestTaskConfigState = TestTaskConfigState()
+ val subprojects: MutableList = mutableListOf()
- override fun languages(vararg languages: GradleScriptLanguage) = also {
- state.languages += languages
+ override fun plugins(block: Plugins.() -> Unit) {
+ plugins.also(block)
}
- override fun engines(vararg engines: CoverageEngine) = also {
- state.engines += engines
+ override fun repositories(block: Repositories.() -> Unit) {
+ repositories.also(block)
}
- override fun types(vararg types: ProjectType) = also {
- state.types += types
+ override fun kover(config: TestKoverProjectConfig.() -> Unit) {
+ kover = TestKoverProjectConfigState().also(config)
}
- override fun withLocalCache(): TestCaseBuilder = also {
- state.localCache = true
+ override fun koverMerged(config: TestKoverMergedConfig.() -> Unit) {
+ merged = TestKoverMergedConfigState().also(config)
}
- override fun configKover(config: KoverRootConfig.() -> Unit) = also {
- state.koverConfig.config()
+ override fun subproject(path: String) {
+ subprojects += path
}
- override fun subproject(name: String, builder: ProjectBuilder<*>.() -> Unit) = also {
- val projectState = state.subprojects.computeIfAbsent(name) { ProjectBuilderState() }
- @Suppress("UPPER_BOUND_VIOLATED_WARNING")
- ProjectBuilderImpl>(projectState).builder()
+ override fun testTasks(block: TestTaskConfig.() -> Unit) {
+ testTasks.also(block)
}
- override fun build(): GradleRunner {
- if (state.languages.isEmpty()) {
- state.languages += GradleScriptLanguage.KOTLIN
- }
- if (state.types.isEmpty()) {
- state.types += ProjectType.KOTLIN_JVM
- }
- if (state.engines.isEmpty()) {
- state.engines += null
- }
- if (state.pluginVersion == null) {
- state.pluginVersion = "0.5.0"
- }
+ override fun sourcesFrom(template: String) {
+ sourceTemplates += template
+ }
+}
- val projects: MutableMap = mutableMapOf()
+internal class TestKoverProjectConfigState : TestKoverProjectConfig {
+ override var isDisabled: Boolean? = null
+ override var engine: CoverageEngineVariant? = null
+ val filters: TestKoverProjectFiltersState = TestKoverProjectFiltersState()
+ val instrumentation: KoverProjectInstrumentation = KoverProjectInstrumentation()
+ val xml: TestKoverProjectXmlConfigState = TestKoverProjectXmlConfigState()
+ val html: TestKoverProjectHtmlConfigState = TestKoverProjectHtmlConfigState()
+ val verify: TestKoverVerifyConfigState = TestKoverVerifyConfigState()
+ override fun filters(config: TestKoverProjectFilters.() -> Unit) {
+ filters.also(config)
+ }
- state.languages.forEach { language ->
- state.types.forEach { type ->
- state.engines.forEach { engine ->
- val slice = ProjectSlice(language, type, engine ?: CoverageEngine.INTELLIJ)
- projects[slice] = state.createProject(rootDir, slice)
- }
- }
- }
+ override fun instrumentation(config: KoverProjectInstrumentation.() -> Unit) {
+ instrumentation.also(config)
+ }
- return GradleRunnerImpl(projects)
+ override fun xmlReport(config: TestKoverProjectXmlConfig.() -> Unit) {
+ xml.also(config)
}
+ override fun htmlReport(config: TestKoverProjectHtmlConfig.() -> Unit) {
+ html.also(config)
+ }
+
+ override fun verify(config: TestKoverVerifyConfig.() -> Unit) {
+ verify.also(config)
+ }
}
+internal class TestKoverProjectFiltersState : TestKoverProjectFilters {
+ var classes: KoverClassFilter? = null
+ var sourceSets: KoverSourceSetFilter? = null
-@Suppress("UNCHECKED_CAST")
-private open class ProjectBuilderImpl>(val projectState: ProjectBuilderState) : ProjectBuilder {
+ override fun classes(config: KoverClassFilter.() -> Unit) {
+ classes = KoverClassFilter().also(config)
+ }
- override fun rule(name: String?, builder: RuleBuilder.() -> Unit): B {
- projectState.rules += TestVerificationRule(projectState.rules.size, name).apply(builder)
- return this as B
+ override fun sourceSets(config: KoverSourceSetFilter.() -> Unit) {
+ sourceSets = KoverSourceSetFilter().also(config)
}
+}
+
+internal class TestKoverProjectXmlConfigState : TestKoverProjectXmlConfig {
+ override var onCheck: Boolean? = null
+ override var reportFile: File? = null
+ var overrideFilters: TestKoverProjectFiltersState? = null
- override fun configTest(script: String): B {
- projectState.testScripts += GradleScript(script, script)
- return this as B
+ override fun overrideFilters(config: TestKoverProjectFilters.() -> Unit) {
+ if (overrideFilters == null) {
+ overrideFilters = TestKoverProjectFiltersState()
+ }
+ overrideFilters!!.also(config)
}
+}
- override fun configTest(kotlin: String, groovy: String): B {
- projectState.testScripts += GradleScript(kotlin, groovy)
- return this as B
+internal class TestKoverProjectHtmlConfigState : TestKoverProjectHtmlConfig {
+ override var onCheck: Boolean? = null
+ override var reportDir: File? = null
+ var overrideFilters: TestKoverProjectFiltersState? = null
+
+ override fun overrideFilters(config: TestKoverProjectFilters.() -> Unit) {
+ if (overrideFilters == null) {
+ overrideFilters = TestKoverProjectFiltersState()
+ }
+ overrideFilters!!.also(config)
}
+}
- override fun config(script: String): B {
- projectState.scripts += GradleScript(script, script)
- return this as B
+internal class TestKoverVerifyConfigState : TestKoverVerifyConfig {
+ override val onCheck: Boolean? = null
+ val rules: MutableList = mutableListOf()
+
+ override fun rule(config: TestVerificationRule.() -> Unit) {
+ rules += TestVerificationRule().also(config)
}
+}
+
+internal class TestVerificationRule {
+ var isEnabled: Boolean? = null
+ var name: String? = null
+ var target: VerificationTarget? = null
+
+ var overrideClassFilter: KoverClassFilter? = null
+ val bounds: MutableList = mutableListOf()
- override fun config(kotlin: String, groovy: String): B {
- projectState.scripts += GradleScript(kotlin, groovy)
- return this as B
+ fun overrideClassFilter(config: Action) {
+ overrideClassFilter = KoverClassFilter().also { config.execute(it) }
}
- override fun dependency(script: String): B {
- projectState.dependencies += GradleScript(script, script)
- return this as B
+ fun bound(configureBound: VerificationBoundState.() -> Unit) {
+ bounds += VerificationBoundState().also(configureBound)
}
+}
+
+internal class VerificationBoundState {
+ var minValue: Int? = null
+ var maxValue: Int? = null
+ var counter: CounterType? = null
+ var valueType: VerificationValueType? = null
+}
- override fun dependency(kotlin: String, groovy: String): B {
- projectState.dependencies += GradleScript(kotlin, groovy)
- return this as B
+internal class TestTaskConfigState : TestTaskConfig {
+ var excludes: List? = null
+ var includes: List? = null
+ override fun excludes(vararg classes: String) {
+ excludes = classes.toList()
}
- override fun sources(template: String): B {
- projectState.sourceTemplates += template
- return this as B
+ override fun includes(vararg classes: String) {
+ includes = classes.toList()
}
+
}
-private data class TestVerificationRule(
- override val id: Int,
- override var name: String?
-) : VerificationRule, RuleBuilder {
- override val bounds: MutableList = mutableListOf()
- override fun bound(configureBound: Action) {
- bounds += TestVerificationBound(bounds.size).also { configureBound.execute(it) }
+internal class RepositoriesState : Repositories {
+ val repositories: MutableList = mutableListOf()
+ override fun repository(name: String) {
+ repositories += name
}
- override fun bound(builder: VerificationBound.() -> Unit) {
- bounds += TestVerificationBound(bounds.size).apply(builder)
+}
+
+internal class TestKoverMergedConfigState : TestKoverMergedConfig {
+ var enabled: Boolean = false
+ val filters: TestKoverMergedFiltersState = TestKoverMergedFiltersState()
+ override fun enable() {
+ enabled = true
+ }
+
+ override fun filters(config: TestKoverMergedFilters.() -> Unit) {
+ filters.also(config)
}
}
-private data class TestVerificationBound(
- override val id: Int,
- override var minValue: Int? = null,
- override var maxValue: Int? = null,
- override var valueType: VerificationValueType = VerificationValueType.COVERED_LINES_PERCENTAGE
-) : VerificationBound
+internal class TestKoverMergedFiltersState : TestKoverMergedFilters {
+ var classes: KoverClassFilter? = null
+ var projects: KoverProjectsFilter? = null
+ override fun classes(config: KoverClassFilter.() -> Unit) {
+ classes = KoverClassFilter().also(config)
+ }
+ override fun projects(config: KoverProjectsFilter.() -> Unit) {
+ projects = KoverProjectsFilter().also(config)
+ }
+}
+
+internal class PluginsState : Plugins {
+ var useKotlin: Boolean = false
+ var useKover: Boolean = false
+ var kotlinVersion: String? = null
+ var koverVersion: String? = null
+
+ override fun kotlin(version: String?) {
+ useKotlin = true
+ kotlinVersion = version
+ }
+ override fun kover(version: String?) {
+ useKover = true
+ koverVersion = version
+ }
+}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Runner.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Runner.kt
index 9966d57a..f90583f9 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Runner.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Runner.kt
@@ -12,11 +12,11 @@ import java.io.*
import javax.xml.parsers.*
-internal class GradleRunnerImpl(private val projects: Map) :
+internal class DiverseGradleRunner(private val projects: Map, private val extraArgs: List) :
GradleRunner {
- override fun run(vararg args: String, checker: RunResult.() -> Unit): GradleRunnerImpl {
- val argsList = listOf(*args)
+ override fun run(vararg args: String, checker: RunResult.() -> Unit): DiverseGradleRunner {
+ val argsList = listOf(*args) + extraArgs
projects.forEach { (slice, project) ->
try {
val buildResult = project.runGradle(argsList)
@@ -27,8 +27,8 @@ internal class GradleRunnerImpl(private val projects: Map) :
}
return this
}
- override fun runWithError(vararg args: String, errorChecker: RunResult.() -> Unit): GradleRunnerImpl {
- val argsList = listOf(*args)
+ override fun runWithError(vararg args: String, errorChecker: RunResult.() -> Unit): DiverseGradleRunner {
+ val argsList = listOf(*args) + extraArgs
projects.forEach { (slice, project) ->
try {
project.runGradleWithError(argsList)
@@ -98,11 +98,11 @@ private class RunResultImpl(
private val buildScriptFile: File = buildFile()
private val buildScript: String by lazy { buildScriptFile.readText() }
- override val engine: CoverageEngine by lazy {
+ override val engine: CoverageEngineVendor by lazy {
if (buildScript.contains("set(kotlinx.kover.api.CoverageEngine.JACOCO)")) {
- CoverageEngine.JACOCO
+ CoverageEngineVendor.JACOCO
} else {
- CoverageEngine.INTELLIJ
+ CoverageEngineVendor.INTELLIJ
}
}
@@ -116,9 +116,9 @@ private class RunResultImpl(
throw IllegalArgumentException("Impossible to determine the type of project")
}
} else {
- if (buildScript.contains("""id 'org.jetbrains.kotlin.jvm'""")) {
+ if (buildScript.contains("""id "org.jetbrains.kotlin.jvm"""")) {
ProjectType.KOTLIN_JVM
- } else if (buildScript.contains("""id 'org.jetbrains.kotlin.multiplatform'""")) {
+ } else if (buildScript.contains("""id "org.jetbrains.kotlin.multiplatform"""")) {
ProjectType.KOTLIN_MULTIPLATFORM
} else {
throw IllegalArgumentException("Impossible to determine the type of project")
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Types.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Types.kt
index 4ee1689a..602ed310 100644
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Types.kt
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Types.kt
@@ -12,55 +12,109 @@ internal enum class GradleScriptLanguage { KOTLIN, GROOVY }
internal enum class ProjectType { KOTLIN_JVM, KOTLIN_MULTIPLATFORM, ANDROID }
-internal interface ProjectBuilder> {
- fun sources(template: String): B
+internal interface DiverseBuild {
+ fun addProject(name: String, path: String, builder: ProjectBuilder.() -> Unit)
+ fun prepare(): GradleRunner
+}
+
+internal interface ProjectBuilder {
+ fun plugins(block: Plugins.() -> Unit)
+
+ fun repositories(block: Repositories.() -> Unit)
- fun rule(name: String? = null, builder: RuleBuilder.() -> Unit): B
+ fun kover(config: TestKoverProjectConfig.() -> Unit)
- fun configTest(script: String): B
- fun configTest(kotlin: String, groovy: String): B
+ fun koverMerged(config: TestKoverMergedConfig.() -> Unit)
- fun config(script: String): B
- fun config(kotlin: String, groovy: String): B
+ fun testTasks(block: TestTaskConfig.() -> Unit)
- fun dependency(script: String): B
- fun dependency(kotlin: String, groovy: String): B
+ fun subproject(path: String)
+
+ fun sourcesFrom(template: String)
}
-internal interface RuleBuilder {
- fun bound(builder: VerificationBound.() -> Unit)
+
+interface Plugins {
+ fun kotlin(version: String? = null)
+ fun kover(version: String? = null)
}
-internal interface TestCaseBuilder : ProjectBuilder {
- fun languages(vararg languages: GradleScriptLanguage): TestCaseBuilder
- fun engines(vararg engines: CoverageEngine): TestCaseBuilder
- fun types(vararg types: ProjectType): TestCaseBuilder
+interface Repositories {
+ fun repository(name: String)
+}
+
+interface TestTaskConfig {
+ fun excludes(vararg classes: String)
+ fun includes(vararg classes: String)
+}
- fun withLocalCache(): TestCaseBuilder
+/**
+ * Same as [KoverProjectConfig]
+ */
+internal interface TestKoverProjectConfig {
+ var isDisabled: Boolean?
- fun configKover(config: KoverRootConfig.() -> Unit): TestCaseBuilder
+ var engine: CoverageEngineVariant?
- fun subproject(name: String, builder: ProjectBuilder<*>.() -> Unit): TestCaseBuilder
+ fun filters(config: TestKoverProjectFilters.() -> Unit)
- fun build(): GradleRunner
+ fun instrumentation(config: KoverProjectInstrumentation.() -> Unit)
+
+ fun xmlReport(config: TestKoverProjectXmlConfig.() -> Unit)
+
+ fun htmlReport(config: TestKoverProjectHtmlConfig.() -> Unit)
+
+ fun verify(config: TestKoverVerifyConfig.() -> Unit)
}
-internal data class ProjectSlice(val language: GradleScriptLanguage, val type: ProjectType, val engine: CoverageEngine?) {
- fun encodedString(): String {
- return "${language.ordinal}_${type.ordinal}_${engine?.ordinal?:"default"}"
- }
+
+internal interface TestKoverVerifyConfig {
+ val onCheck: Boolean?
+
+ fun rule(config: TestVerificationRule.() -> Unit)
+}
+
+internal interface TestKoverProjectXmlConfig {
+ var onCheck: Boolean?
+ var reportFile: File?
+
+ fun overrideFilters(config: TestKoverProjectFilters.() -> Unit)
+}
+
+internal interface TestKoverProjectHtmlConfig {
+ var onCheck: Boolean?
+ var reportDir: File?
+
+ fun overrideFilters(config: TestKoverProjectFilters.() -> Unit)
}
-internal data class KoverRootConfig(
- var disabled: Boolean? = null,
- var intellijVersion: String? = null,
- var jacocoVersion: String? = null,
- var generateReportOnCheck: Boolean? = null,
- var runAllTestsForProjectTask: Boolean? = null,
- val disabledProjects: MutableSet = mutableSetOf()
+internal interface TestKoverProjectFilters {
+ fun classes(config: KoverClassFilter.() -> Unit)
+
+ fun sourceSets(config: KoverSourceSetFilter.() -> Unit)
+}
+
+internal interface TestKoverMergedConfig {
+ public fun enable()
+
+ public fun filters(config: TestKoverMergedFilters.() -> Unit)
+}
+
+public interface TestKoverMergedFilters {
+ public fun classes(config: KoverClassFilter.() -> Unit)
+
+ public fun projects(config: KoverProjectsFilter.() -> Unit)
+}
+
+
+internal data class ProjectSlice(
+ val language: GradleScriptLanguage,
+ val type: ProjectType,
+ val engine: CoverageEngineVendor?
) {
- val isDefault =
- disabled == null && intellijVersion == null && jacocoVersion == null && generateReportOnCheck == null
+ fun encodedString(): String {
+ return "${language.ordinal}_${type.ordinal}_${engine?.ordinal ?: "default"}"
+ }
}
internal interface GradleRunner {
@@ -69,7 +123,7 @@ internal interface GradleRunner {
}
internal interface RunResult {
- val engine: CoverageEngine
+ val engine: CoverageEngineVendor
val projectType: ProjectType
fun subproject(name: String, checker: RunResult.() -> Unit)
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Writer.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Writer.kt
deleted file mode 100644
index 4e068906..00000000
--- a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/Writer.kt
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.kover.test.functional.core
-
-import kotlinx.kover.api.*
-import kotlinx.kover.test.functional.core.GradleScriptLanguage.KOTLIN
-import java.io.*
-
-private const val TEMPLATES_PATH = "src/functionalTest/templates"
-private const val BUILD_SCRIPTS_PATH = "$TEMPLATES_PATH/scripts/buildscripts"
-private const val SETTINGS_PATH = "$TEMPLATES_PATH/scripts/settings"
-private const val SOURCES_PATH = "$TEMPLATES_PATH/sources"
-
-internal fun CommonBuilderState.createProject(rootDir: File, slice: ProjectSlice): File {
- val projectDir = File(rootDir, slice.encodedString()).also { it.mkdirs() }
-
- val extension = slice.scriptExtension
-
- val buildScript = loadScriptTemplate(true, slice)
- .processRootBuildScript(this, slice)
- .processProjectBuildScript(rootProject, slice, subprojects.keys)
-
- File(projectDir, "build.$extension").writeText(buildScript)
- File(projectDir, "settings.$extension").writeText(buildSettings(slice))
-
- rootProject.writeSources(projectDir, slice)
-
- subprojects.forEach { (name, state) -> state.writeSubproject(File(projectDir, name), slice) }
-
- return projectDir
-}
-
-private fun ProjectBuilderState.writeSubproject(directory: File, slice: ProjectSlice) {
- directory.mkdirs()
-
- val extension = slice.scriptExtension
-
- val buildScript = loadScriptTemplate(false, slice).processProjectBuildScript(this, slice)
-
- File(directory, "build.$extension").writeText(buildScript)
-
- writeSources(directory, slice)
-}
-
-
-private val ProjectSlice.scriptExtension get() = if (language == KOTLIN) "gradle.kts" else "gradle"
-
-private val ProjectSlice.srcPath: String
- get() {
- return when (type) {
- ProjectType.KOTLIN_JVM -> "src/main"
- ProjectType.KOTLIN_MULTIPLATFORM -> "src/jvmMain"
- ProjectType.ANDROID -> "src/jvmMain"
- }
- }
-
-private val ProjectSlice.testPath: String
- get() {
- return when (type) {
- ProjectType.KOTLIN_JVM -> "src/test"
- ProjectType.KOTLIN_MULTIPLATFORM -> "src/jvmTest"
- ProjectType.ANDROID -> "src/jvmTest"
- }
- }
-
-
-private fun String.processRootBuildScript(state: CommonBuilderState, slice: ProjectSlice): String {
- return replace("//PLUGIN_VERSION", state.pluginVersion!!)
- .replace("//KOVER", state.buildRootExtension(slice))
-}
-
-private fun String.processProjectBuildScript(
- state: ProjectBuilderState,
- slice: ProjectSlice,
- subprojects: Set = emptySet()
-): String {
- return replace("//REPOSITORIES", "")
- .replace("//DEPENDENCIES", state.buildDependencies(slice, subprojects))
- .replace("//SCRIPTS", state.buildScripts(slice))
- .replace("//TEST_TASK", state.buildTestTask(slice))
- .replace("//VERIFICATIONS", state.buildVerifications(slice))
-}
-
-
-private fun ProjectBuilderState.writeSources(projectDir: File, slice: ProjectSlice) {
- fun File.processDir(result: MutableMap, targetRootPath: String, relativePath: String = "") {
- listFiles()?.forEach { file ->
- val filePath = "$relativePath/${file.name}"
- if (file.isDirectory) {
- file.processDir(result, targetRootPath, filePath)
- } else if (file.exists() && file.length() > 0) {
- val targetFile = File(projectDir, "$targetRootPath/$filePath")
- targetFile.parentFile.mkdirs()
- file.copyTo(targetFile)
- }
- }
- }
-
- val srcPath = slice.srcPath
- val testPath = slice.testPath
-
- sourceTemplates.forEach { template ->
- File(SOURCES_PATH, "$template/main").processDir(mainSources, srcPath)
- File(SOURCES_PATH, "$template/test").processDir(testSources, testPath)
- }
-}
-
-private fun ProjectSlice.scriptPath(): String {
- val languageString = if (language == KOTLIN) "kotlin" else "groovy"
- val typeString = when (type) {
- ProjectType.KOTLIN_JVM -> "kjvm"
- ProjectType.KOTLIN_MULTIPLATFORM -> "kmp"
- ProjectType.ANDROID -> "android"
- }
- return "$BUILD_SCRIPTS_PATH/$languageString/$typeString"
-}
-
-private fun buildSubprojectsIncludes(subprojects: Set): String {
- if (subprojects.isEmpty()) return ""
-
- return subprojects.joinToString("\n", "\n", "\n") {
- """include("$it")"""
- }
-}
-
-private fun CommonBuilderState.buildExtraSettings(): String {
- return if (localCache) {
- """
-buildCache {
- local {
- directory = "${"$"}settingsDir/build-cache"
- }
-}
-"""
- } else {
- ""
- }
-}
-
-private fun CommonBuilderState.buildRootExtension(slice: ProjectSlice): String {
- if (slice.engine == null && koverConfig.isDefault) {
- return ""
- }
-
- val builder = StringBuilder()
- builder.appendLine()
- builder.appendLine("kover {")
-
- if (koverConfig.disabled != null) {
- val property = if (slice.language == KOTLIN) "isDisabled" else "disabled"
- builder.appendLine("$property = ${koverConfig.disabled}")
- }
-
- if (slice.engine == CoverageEngine.INTELLIJ) {
- builder.appendLine(" coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ)")
- if (koverConfig.intellijVersion != null) {
- builder.appendLine(""" intellijEngineVersion.set("${koverConfig.intellijVersion}")""")
- }
- }
- if (slice.engine == CoverageEngine.JACOCO) {
- builder.appendLine(" coverageEngine.set(kotlinx.kover.api.CoverageEngine.JACOCO)")
- if (koverConfig.jacocoVersion != null) {
- builder.appendLine(""" jacocoEngineVersion.set("${koverConfig.jacocoVersion}")""")
- }
- }
-
- if (koverConfig.disabledProjects.isNotEmpty()) {
- val prefix = if (slice.language == KOTLIN) "setOf(" else "["
- val postfix = if (slice.language == KOTLIN) ")" else "]"
- val value = koverConfig.disabledProjects.joinToString(prefix = prefix, postfix = postfix) { "\"$it\"" }
- builder.appendLine(" disabledProjects = $value")
- }
-
- if (koverConfig.runAllTestsForProjectTask != null) {
- builder.appendLine(" runAllTestsForProjectTask = ${koverConfig.runAllTestsForProjectTask}")
- }
-
- builder.appendLine("}")
-
- return builder.toString()
-}
-
-private fun ProjectBuilderState.buildTestTask(slice: ProjectSlice): String {
- if (testScripts.isEmpty()) {
- return ""
- }
-
- val configs = testScripts.map { if (slice.language == KOTLIN) it.kotlin else it.groovy }
-
- return loadTestTaskTemplate(slice).replace("//KOVER_TEST_CONFIG", configs.joinToString("\n"))
-}
-
-@Suppress("UNUSED_PARAMETER")
-private fun ProjectBuilderState.buildVerifications(slice: ProjectSlice): String {
- if (rules.isEmpty()) {
- return ""
- }
-
- val builder = StringBuilder()
- builder.appendLine()
- builder.appendLine("tasks.koverVerify {")
-
- for (rule in rules) {
- builder.appendLine(" rule {")
- rule.name?.also { builder.appendLine(""" name = "$it"""") }
- for (bound in rule.bounds) {
- builder.appendLine(" bound {")
- bound.minValue?.let { builder.appendLine(" minValue = $it") }
- bound.maxValue?.let { builder.appendLine(" maxValue = $it") }
- if (bound.valueType != VerificationValueType.COVERED_LINES_PERCENTAGE) {
- builder.appendLine(" valueType = kotlinx.kover.api.VerificationValueType.${bound.valueType}")
- }
- builder.appendLine(" }")
- }
- builder.appendLine(" }")
- }
- builder.appendLine("}")
-
- return builder.toString()
-}
-
-private fun CommonBuilderState.buildSettings(slice: ProjectSlice): String {
- return loadSettingsTemplate(slice)
- .replace("//SUBPROJECTS", buildSubprojectsIncludes(subprojects.keys))
- .replace("//EXTRA_SETTINGS", buildExtraSettings())
-}
-
-private fun ProjectBuilderState.buildScripts(slice: ProjectSlice): String {
- if (scripts.isEmpty()) {
- return ""
- }
- val configs = scripts.map { if (slice.language == KOTLIN) it.kotlin else it.groovy }
- return configs.joinToString("\n", "\n", "\n")
-}
-
-private fun ProjectBuilderState.buildDependencies(slice: ProjectSlice, subprojects: Set): String {
- if (dependencies.isEmpty() && subprojects.isEmpty()) {
- return ""
- }
-
- val configs: MutableList = mutableListOf();
-
- configs += subprojects.map {
- if (slice.language == KOTLIN) "implementation(project(\":$it\"))" else "implementation project(':$it')"
- }
- configs += dependencies.map { if (slice.language == KOTLIN) it.kotlin else it.groovy }
- return configs.joinToString("\n", "\n", "\n")
-}
-
-
-private fun loadSettingsTemplate(slice: ProjectSlice): String {
- return File("$SETTINGS_PATH/settings.${slice.scriptExtension}").readText()
-}
-
-private fun loadScriptTemplate(root: Boolean, slice: ProjectSlice): String {
- val filename = if (root) "root" else "subproject"
- return File("${slice.scriptPath()}/$filename").readText()
-}
-
-private fun loadTestTaskTemplate(slice: ProjectSlice): String {
- return File("${slice.scriptPath()}/testTask").readText()
-}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/CommonWriter.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/CommonWriter.kt
new file mode 100644
index 00000000..0a946715
--- /dev/null
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/CommonWriter.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.test.functional.core.writer
+
+import kotlinx.kover.api.*
+import kotlinx.kover.test.functional.core.*
+import java.io.PrintWriter
+
+internal fun PrintWriter.printClassFilter(classFilter: KoverClassFilter, slice: ProjectSlice, indents: Int) {
+ if (classFilter.excludes.isNotEmpty()) {
+ indented(indents, "excludes".addAllList(classFilter.excludes, slice.language))
+ }
+ if (classFilter.includes.isNotEmpty()) {
+ indented(indents, "includes".addAllList(classFilter.includes, slice.language))
+ }
+}
+
+internal fun PrintWriter.printVerify(state: TestKoverVerifyConfigState, slice: ProjectSlice, indents: Int) {
+ val onCheck = state.onCheck
+ val rules = state.rules
+ if (onCheck == null && rules.isEmpty()) return
+
+ indented(indents, "verify {")
+ if (onCheck != null) {
+ indented(indents + 1, "onCheck".setProperty(onCheck.toString(), slice.language))
+ }
+ rules.forEach { rule ->
+ indented(indents + 1, "rule {")
+ if (rule.isEnabled != null) {
+ indented(indents + 2, "isEnabled = ${rule.isEnabled}")
+ }
+ if (rule.name != null) {
+ indented(indents + 2, "name = ${rule.name?.text(slice.language)}")
+ }
+ if (rule.target != null) {
+ indented(indents + 2, "target = ${rule.target?.enum(slice.language)}")
+ }
+ if (rule.overrideClassFilter != null) {
+ indented(indents + 2, "overrideClassFilter {")
+ printClassFilter(rule.overrideClassFilter!!, slice, indents + 3)
+ indented(indents + 2, "}")
+ }
+ rule.bounds.forEach { bound ->
+ indented(indents + 2, "bound {")
+ if (bound.minValue != null) {
+ indented(indents + 3, "minValue = ${bound.minValue}")
+ }
+ if (bound.maxValue != null) {
+ indented(indents + 3, "maxValue = ${bound.maxValue}")
+ }
+ if (bound.counter != null) {
+ indented(indents + 3, "counter = ${bound.counter?.enum(slice.language)}")
+ }
+ if (bound.valueType != null) {
+ indented(indents + 3, "valueType = ${bound.valueType?.enum(slice.language)}")
+ }
+ indented(indents + 2, "}")
+ }
+ indented(indents + 1, "}")
+ }
+ indented(indents, "}")
+}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/KoverExtensionWriter.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/KoverExtensionWriter.kt
new file mode 100644
index 00000000..8325dc99
--- /dev/null
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/KoverExtensionWriter.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.test.functional.core.writer
+
+import kotlinx.kover.api.*
+import kotlinx.kover.test.functional.core.*
+import kotlinx.kover.test.functional.core.ProjectSlice
+import kotlinx.kover.test.functional.core.TestKoverProjectConfigState
+import java.io.*
+
+internal fun PrintWriter.printKover(kover: TestKoverProjectConfigState?, slice: ProjectSlice, indents: Int) {
+ if (kover == null) return
+
+ indented(indents, "kover {")
+ printDisabled(kover.isDisabled, slice, indents + 1)
+ printEngine(kover.engine, slice, indents + 1)
+ printInstrumentation(kover.instrumentation, slice, indents + 1)
+ printFilters(kover.filters, slice, indents + 1)
+ printVerify(kover.verify, slice, indents + 1)
+ indented(indents, "}")
+}
+
+
+private fun PrintWriter.printEngine(scriptEngine: CoverageEngineVariant?, slice: ProjectSlice, indents: Int) {
+ if (scriptEngine == null && slice.engine == null) return
+
+ val value = if (slice.engine != null) {
+ val clazz =
+ if (slice.engine == CoverageEngineVendor.INTELLIJ) DefaultIntellijEngine::class else DefaultJacocoEngine::class
+ clazz.obj(slice.language)
+ } else {
+ val clazz =
+ if (scriptEngine!!.vendor == CoverageEngineVendor.INTELLIJ) IntellijEngine::class else JacocoEngine::class
+ val new = if (slice.language == GradleScriptLanguage.KOTLIN) {
+ clazz.qualifiedName
+ } else {
+ clazz.qualifiedName
+ }
+ "$new(\"${scriptEngine.version}\")"
+ }
+
+ indented(indents, "engine".setProperty(value, slice.language))
+}
+
+private fun PrintWriter.printDisabled(isDisabled: Boolean?, slice: ProjectSlice, indents: Int) {
+ if (isDisabled == null) return
+
+ if (slice.language == GradleScriptLanguage.KOTLIN) {
+ indented(indents, "isDisabled.set($isDisabled)")
+ } else {
+ indented(indents, "disabled = $isDisabled")
+ }
+}
+
+private fun PrintWriter.printFilters(state: TestKoverProjectFiltersState, slice: ProjectSlice, indents: Int) {
+ val classes = state.classes
+ val sourceSets = state.sourceSets
+ if (sourceSets == null && classes == null) return
+
+ indented(indents, "filters {")
+ if (classes != null && (classes.excludes.isNotEmpty() || classes.includes.isNotEmpty())) {
+ indented(indents + 1, "classes {")
+ printClassFilter(classes, slice, indents + 2)
+ indented(indents + 1, "}")
+ }
+
+ if (sourceSets != null) {
+ indented(indents + 1, "sourceSets {")
+ if (sourceSets.excludes.isNotEmpty()) {
+ indented(indents + 2, "excludes".addAllList(sourceSets.excludes, slice.language))
+ }
+ indented(indents + 2, "excludeTests = " + sourceSets.excludeTests)
+ indented(indents + 1, "}")
+ }
+
+ indented(indents, "}")
+}
+
+private fun PrintWriter.printInstrumentation(state: KoverProjectInstrumentation, slice: ProjectSlice, indents: Int) {
+ if (state.excludeTasks.isEmpty()) return
+
+ indented(indents, "instrumentation {")
+ indented(indents + 1, "excludeTasks".addAllList(state.excludeTasks, slice.language))
+ indented(indents, "}")
+}
+
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/Languages.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/Languages.kt
new file mode 100644
index 00000000..46efb89c
--- /dev/null
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/Languages.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.test.functional.core.writer
+
+import kotlinx.kover.test.functional.core.GradleScriptLanguage
+import kotlinx.kover.test.functional.core.ProjectSlice
+import java.io.PrintWriter
+import kotlin.reflect.*
+
+internal val ProjectSlice.scriptExtension get() = if (language == GradleScriptLanguage.KOTLIN) "gradle.kts" else "gradle"
+
+internal fun Iterable.formatList(language: GradleScriptLanguage): String {
+ val prefix = if (language == GradleScriptLanguage.KOTLIN) "listOf(" else "["
+ val postfix = if (language == GradleScriptLanguage.KOTLIN) ")" else "]"
+
+ return prefix + this.joinToString(separator = ",") { "\"$it\"" } + postfix
+}
+
+internal fun String.addAllList(list: Iterable, language: GradleScriptLanguage): String {
+ val listString = list.formatList(language)
+ return this + if (language == GradleScriptLanguage.KOTLIN) " += $listString" else ".addAll($listString)"
+}
+
+internal fun KClass<*>.obj(language: GradleScriptLanguage): String {
+ return if (language == GradleScriptLanguage.KOTLIN) {
+ qualifiedName!!
+ } else {
+ "$qualifiedName.INSTANCE"
+ }
+}
+
+internal fun String.setProperty(value: String, language: GradleScriptLanguage): String {
+ return this + if (language == GradleScriptLanguage.KOTLIN) ".set($value)" else " = $value"
+}
+
+private fun indent(count: Int): String {
+ return when (count) {
+ 0 -> ""
+ 1 -> " "
+ 2 -> " "
+ 3 -> " "
+ 4 -> " "
+ 5 -> " "
+ else -> " ".repeat(count)
+ }
+}
+
+@Suppress("UNUSED_PARAMETER")
+internal fun String.text(language: GradleScriptLanguage): String {
+ return "\"$this\""
+}
+
+internal fun Enum<*>.enum(language: GradleScriptLanguage): String {
+ return if (language == GradleScriptLanguage.KOTLIN) {
+ this::class.qualifiedName + '.' + this.name
+ } else {
+ "\"$name\""
+ }
+}
+
+internal fun PrintWriter.indented(indents: Int, content: String) {
+ println(indent(indents) + content)
+}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/MergedExtensionWriter.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/MergedExtensionWriter.kt
new file mode 100644
index 00000000..f42a5a7f
--- /dev/null
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/MergedExtensionWriter.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.test.functional.core.writer
+
+import kotlinx.kover.test.functional.core.*
+import kotlinx.kover.test.functional.core.ProjectSlice
+import java.io.*
+
+internal fun PrintWriter.printKoverMerged(merged: TestKoverMergedConfigState?, slice: ProjectSlice, indents: Int) {
+ if (merged == null) return
+
+ indented(indents, "koverMerged {")
+ printEnabled(merged.enabled, indents + 1)
+ printFilters(merged.filters, slice, indents + 1)
+ indented(indents, "}")
+}
+
+private fun PrintWriter.printEnabled(isEnabled: Boolean, indents: Int) {
+ if (isEnabled) {
+ indented(indents, "enable()")
+ }
+}
+
+private fun PrintWriter.printFilters(state: TestKoverMergedFiltersState, slice: ProjectSlice, indents: Int) {
+ val classes = state.classes
+ val projects = state.projects
+ if (projects == null && classes == null) return
+
+ indented(indents, "filters {")
+ if (classes != null && (classes.excludes.isNotEmpty() || classes.includes.isNotEmpty())) {
+ indented(indents + 1, "classes {")
+ printClassFilter(classes, slice, indents + 2)
+ indented(indents + 1, "}")
+ }
+
+ if (projects != null) {
+ indented(indents + 1, "projects {")
+ if (projects.excludes.isNotEmpty()) {
+ indented(indents + 2, "excludes".addAllList(projects.excludes, slice.language))
+ }
+ indented(indents + 1, "}")
+ }
+
+ indented(indents, "}")
+}
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/SettingsFileWriter.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/SettingsFileWriter.kt
new file mode 100644
index 00000000..5af72278
--- /dev/null
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/SettingsFileWriter.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.test.functional.core.writer
+
+import kotlinx.kover.test.functional.core.*
+import java.io.*
+
+internal fun generateSettingsFile(
+ sliceDir: File,
+ slice: ProjectSlice,
+ projects: Map,
+ localCache: Boolean
+) {
+ File(sliceDir, "settings.${slice.scriptExtension}").printWriter().use {
+ it.println("""rootProject.name = "kover-functional-test"""")
+ it.println()
+ projects.keys.forEach { path ->
+ if (path != ":") {
+ it.println("""include("$path")""")
+ }
+ }
+
+ if (localCache) {
+ it.println(LOCAL_CACHE)
+ }
+ }
+}
+
+private const val LOCAL_CACHE = """
+buildCache {
+ local {
+ directory = "${"$"}settingsDir/build-cache"
+ }
+}
+"""
diff --git a/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/Writer.kt b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/Writer.kt
new file mode 100644
index 00000000..3d83cb30
--- /dev/null
+++ b/src/functionalTest/kotlin/kotlinx/kover/test/functional/core/writer/Writer.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.test.functional.core.writer
+
+import kotlinx.kover.test.functional.core.GradleScriptLanguage.GROOVY
+import kotlinx.kover.test.functional.core.GradleScriptLanguage.KOTLIN
+import kotlinx.kover.test.functional.core.PluginsState
+import kotlinx.kover.test.functional.core.ProjectBuilderState
+import kotlinx.kover.test.functional.core.ProjectSlice
+import kotlinx.kover.test.functional.core.ProjectType
+import kotlinx.kover.test.functional.core.RepositoriesState
+import kotlinx.kover.test.functional.core.TestTaskConfigState
+import java.io.*
+
+private const val SAMPLES_PATH = "src/functionalTest/templates"
+private const val SAMPLES_SOURCES_PATH = "$SAMPLES_PATH/sources"
+
+internal fun initSlice(
+ rootDir: File,
+ slice: ProjectSlice,
+ projects: Map,
+ localCache: Boolean
+): File {
+ val sliceDir = File(rootDir, slice.encodedString()).also { it.mkdirs() }
+ generateSettingsFile(sliceDir, slice, projects, localCache)
+ projects.forEach { (path, state) -> state.generateProject(sliceDir, path, slice) }
+ return sliceDir
+}
+
+private fun ProjectBuilderState.generateProject(sliceDir: File, path: String, slice: ProjectSlice) {
+ val subpath = path.replace(':', '/')
+ val projectDir = File(sliceDir, subpath).also { it.mkdirs() }
+ copySources(projectDir, slice)
+ File(projectDir, "build.${slice.scriptExtension}").printWriter().use {
+ it.printPlugins(plugins, slice)
+ it.printRepositories(repositories)
+ it.printDependencies(subprojects, slice)
+ it.printKover(kover, slice, 0)
+ it.printKoverMerged(merged, slice, 0)
+ it.printTestTasks(testTasks, slice)
+ }
+}
+
+
+private fun PrintWriter.printPlugins(plugins: PluginsState, slice: ProjectSlice) {
+ if (!plugins.useKotlin && !plugins.useKover) return
+
+ println("plugins {")
+ if (plugins.useKotlin) {
+ when {
+ slice.language == GROOVY && slice.type == ProjectType.KOTLIN_JVM -> print(""" id "org.jetbrains.kotlin.jvm"""")
+ slice.language == GROOVY && slice.type == ProjectType.KOTLIN_MULTIPLATFORM -> print(""" id "org.jetbrains.kotlin.multiplatform"""")
+ slice.language == KOTLIN && slice.type == ProjectType.KOTLIN_JVM -> print(""" kotlin("jvm")""")
+ slice.language == KOTLIN && slice.type == ProjectType.KOTLIN_MULTIPLATFORM -> print(""" kotlin("multiplatform")""")
+ else -> throw Exception("Unsupported test combination: language ${slice.language} and project type ${slice.type}")
+ }
+ plugins.kotlinVersion?.let { print(""" version "$it"""") }
+ println()
+ }
+
+ if (plugins.useKover) {
+ if (slice.language == KOTLIN) {
+ print(""" id("org.jetbrains.kotlinx.kover")""")
+ } else {
+ print(""" id "org.jetbrains.kotlinx.kover"""")
+ }
+ plugins.koverVersion?.let { print(""" version "$it"""") }
+ println()
+ }
+ println("}")
+}
+
+private fun PrintWriter.printRepositories(repositories: RepositoriesState) {
+ if (repositories.repositories.isEmpty()) return
+
+ println("repositories {")
+ repositories.repositories.forEach {
+ print(" ")
+ println(it)
+ }
+ println("}")
+}
+
+private fun PrintWriter.printDependencies(subprojects: List, slice: ProjectSlice) {
+ val subprojectsPart = subprojects.joinToString(separator = "\n") {
+ if (slice.language == KOTLIN) "implementation(project(\"$it\"))" else "implementation project('$it')"
+ }
+
+ val template = when {
+ slice.language == GROOVY && slice.type == ProjectType.KOTLIN_JVM -> GROOVY_JVM_DEPS
+ slice.language == GROOVY && slice.type == ProjectType.KOTLIN_MULTIPLATFORM -> GROOVY_KMP_DEPS
+ slice.language == KOTLIN && slice.type == ProjectType.KOTLIN_JVM -> KOTLIN_JVM_DEPS
+ slice.language == KOTLIN && slice.type == ProjectType.KOTLIN_MULTIPLATFORM -> KOTLIN_KMP_DEPS
+ else -> throw Exception("Unsupported test combination: language ${slice.language} and project type ${slice.type}")
+ }
+ println(template.replace(DEPS_PLACEHOLDER, subprojectsPart))
+}
+
+private fun PrintWriter.printTestTasks(state: TestTaskConfigState, slice: ProjectSlice) {
+ if (state.excludes == null && state.includes == null) return
+
+ val testTaskName = when {
+ slice.type == ProjectType.KOTLIN_JVM -> "test"
+ slice.type == ProjectType.KOTLIN_MULTIPLATFORM && slice.language == KOTLIN -> "named(\"jvmTest\").configure"
+ slice.type == ProjectType.KOTLIN_MULTIPLATFORM -> "jvmTest"
+ else -> throw Exception("Project with type ${slice.type} and language ${slice.language} not supported for test task configuring")
+ }
+ val extension =
+ if (slice.language == KOTLIN) "extensions.configure(kotlinx.kover.api.KoverTaskExtension::class)" else "kover"
+
+ println("tasks.$testTaskName {")
+ println(" $extension {")
+ if (state.excludes != null) {
+ val excludesString = state.excludes!!.joinToString(separator = ",") { "\"$it\"" }
+ println(" excludes.addAll($excludesString)")
+ }
+ if (state.includes != null) {
+ val includesString = state.includes!!.joinToString(separator = ",") { "\"$it\"" }
+ println(" includes.addAll($includesString)")
+ }
+ println(" }")
+ println("}")
+}
+
+private const val DEPS_PLACEHOLDER = "/*DEPS*/"
+
+private const val KOTLIN_JVM_DEPS = """
+dependencies {
+ $DEPS_PLACEHOLDER
+ testImplementation(kotlin("test"))
+}
+"""
+
+private const val KOTLIN_KMP_DEPS = """
+kotlin {
+ jvm() {
+ withJava()
+ }
+ dependencies {
+ commonTestImplementation(kotlin("test"))
+ }
+ sourceSets {
+ val jvmMain by getting {
+ dependencies {
+ $DEPS_PLACEHOLDER
+ }
+ }
+ }
+}
+"""
+
+private const val GROOVY_JVM_DEPS = """
+dependencies {
+ $DEPS_PLACEHOLDER
+ testImplementation 'org.jetbrains.kotlin:kotlin-test'
+}
+"""
+
+private const val GROOVY_KMP_DEPS = """
+kotlin {
+ jvm() {
+ withJava()
+ }
+ dependencies {
+ commonTestImplementation 'org.jetbrains.kotlin:kotlin-test'
+ }
+ sourceSets {
+ jvmMain {
+ dependencies {
+ $DEPS_PLACEHOLDER
+ }
+ }
+ }
+}
+"""
+
+private fun ProjectBuilderState.copySources(projectDir: File, slice: ProjectSlice) {
+ fun File.copyInto(targetFile: File) {
+ listFiles()?.forEach { src ->
+ val subTarget = File(targetFile, src.name)
+ if (src.isDirectory) {
+ subTarget.mkdirs()
+ src.copyInto(subTarget)
+ } else if (src.exists() && src.length() > 0) {
+ src.copyTo(subTarget)
+ }
+ }
+ }
+
+ sourceTemplates.forEach { template ->
+ File(SAMPLES_SOURCES_PATH, "$template/main").copyInto(File(projectDir, slice.mainPath))
+ File(SAMPLES_SOURCES_PATH, "$template/test").copyInto(File(projectDir, slice.testPath))
+ }
+}
+
+private val ProjectSlice.mainPath: String
+ get() {
+ return when (type) {
+ ProjectType.KOTLIN_JVM -> "src/main"
+ ProjectType.KOTLIN_MULTIPLATFORM -> "src/jvmMain"
+ ProjectType.ANDROID -> "src/jvmMain"
+ }
+ }
+
+private val ProjectSlice.testPath: String
+ get() {
+ return when (type) {
+ ProjectType.KOTLIN_JVM -> "src/test"
+ ProjectType.KOTLIN_MULTIPLATFORM -> "src/jvmTest"
+ ProjectType.ANDROID -> "src/jvmTest"
+ }
+ }
+
+
+//
+//private fun CommonBuilderState.buildRootExtension(slice: ProjectSlice): String {
+// if (slice.engine == null && koverConfig.isDefault) {
+// return ""
+// }
+//
+// val builder = StringBuilder()
+// builder.appendLine()
+// builder.appendLine("kover {")
+//
+// if (koverConfig.disabled != null) {
+// val property = if (slice.language == KOTLIN) "isDisabled" else "disabled"
+// builder.appendLine("$property = ${koverConfig.disabled}")
+// }
+//
+// if (slice.engine == CoverageEngineVendor.INTELLIJ) {
+// builder.appendLine(" coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ)")
+// if (koverConfig.intellijVersion != null) {
+// builder.appendLine(""" intellijEngineVersion.set("${koverConfig.intellijVersion}")""")
+// }
+// }
+// if (slice.engine == CoverageEngineVendor.JACOCO) {
+// builder.appendLine(" coverageEngine.set(kotlinx.kover.api.CoverageEngine.JACOCO)")
+// if (koverConfig.jacocoVersion != null) {
+// builder.appendLine(""" jacocoEngineVersion.set("${koverConfig.jacocoVersion}")""")
+// }
+// }
+//
+// if (koverConfig.disabledProjects.isNotEmpty()) {
+// val prefix = if (slice.language == KOTLIN) "setOf(" else "["
+// val postfix = if (slice.language == KOTLIN) ")" else "]"
+// val value = koverConfig.disabledProjects.joinToString(prefix = prefix, postfix = postfix) { "\"$it\"" }
+// builder.appendLine(" disabledProjects = $value")
+// }
+//
+// if (koverConfig.runAllTestsForProjectTask != null) {
+// builder.appendLine(" runAllTestsForProjectTask = ${koverConfig.runAllTestsForProjectTask}")
+// }
+//
+// builder.appendLine("}")
+//
+// return builder.toString()
+//}
+
+//
+//@Suppress("UNUSED_PARAMETER")
+//private fun ProjectBuilderState.buildVerifications(slice: ProjectSlice): String {
+// if (rules.isEmpty()) {
+// return ""
+// }
+//
+// val builder = StringBuilder()
+// builder.appendLine()
+// builder.appendLine("tasks.koverVerify {")
+//
+// for (rule in rules) {
+// builder.appendLine(" rule {")
+// rule.name?.also { builder.appendLine(""" name = "$it"""") }
+// for (bound in rule.bounds) {
+// builder.appendLine(" bound {")
+// bound.minValue?.let { builder.appendLine(" minValue = $it") }
+// bound.maxValue?.let { builder.appendLine(" maxValue = $it") }
+// if (bound.valueType != VerificationValueType.COVERED_PERCENTAGE) {
+// builder.appendLine(" valueType = kotlinx.kover.api.VerificationValueType.${bound.valueType}")
+// }
+// builder.appendLine(" }")
+// }
+// builder.appendLine(" }")
+// }
+// builder.appendLine("}")
+//
+// return builder.toString()
+//}
+
diff --git a/src/functionalTest/templates/samples/different-plugins/build.gradle.kts b/src/functionalTest/templates/samples/different-plugins/build.gradle.kts
index 095535e8..4207547b 100644
--- a/src/functionalTest/templates/samples/different-plugins/build.gradle.kts
+++ b/src/functionalTest/templates/samples/different-plugins/build.gradle.kts
@@ -5,3 +5,5 @@ plugins {
repositories {
mavenCentral()
}
+
+koverMerged.enable()
diff --git a/src/functionalTest/templates/samples/different-plugins/subproject-multiplatform/build.gradle.kts b/src/functionalTest/templates/samples/different-plugins/subproject-multiplatform/build.gradle.kts
index 15fecee9..2dc779a1 100644
--- a/src/functionalTest/templates/samples/different-plugins/subproject-multiplatform/build.gradle.kts
+++ b/src/functionalTest/templates/samples/different-plugins/subproject-multiplatform/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
kotlin("multiplatform")
+ id("org.jetbrains.kotlinx.kover")
}
repositories {
diff --git a/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/root b/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/root
deleted file mode 100644
index 8a66aba6..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/root
+++ /dev/null
@@ -1,13 +0,0 @@
-plugins {
- id 'org.jetbrains.kotlin.jvm' version '1.6.10'
- id 'org.jetbrains.kotlinx.kover' version '//PLUGIN_VERSION'
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-dependencies {//DEPENDENCIES
- testImplementation 'org.jetbrains.kotlin:kotlin-test'
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/subproject b/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/subproject
deleted file mode 100644
index 9cc59b26..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/subproject
+++ /dev/null
@@ -1,12 +0,0 @@
-plugins {
- id 'org.jetbrains.kotlin.jvm'
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-dependencies {//DEPENDENCIES
- testImplementation 'org.jetbrains.kotlin:kotlin-test'
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/testTask b/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/testTask
deleted file mode 100644
index a9d184d8..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/groovy/kjvm/testTask
+++ /dev/null
@@ -1,5 +0,0 @@
-tasks.test {
- kover {
- //KOVER_TEST_CONFIG
- }
-}
diff --git a/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/root b/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/root
deleted file mode 100644
index 7c1727f6..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/root
+++ /dev/null
@@ -1,26 +0,0 @@
-plugins {
- id 'org.jetbrains.kotlin.multiplatform' version '1.6.10'
- id 'org.jetbrains.kotlinx.kover' version '//PLUGIN_VERSION'
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-kotlin {
- jvm() {
- withJava()
- }
-
- dependencies {
- commonTestImplementation 'org.jetbrains.kotlin:kotlin-test'
- }
-
- sourceSets {
- jvmMain {
- dependencies {//DEPENDENCIES
- }
- }
- }
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/subproject b/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/subproject
deleted file mode 100644
index 3f710e4d..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/subproject
+++ /dev/null
@@ -1,25 +0,0 @@
-plugins {
- id 'org.jetbrains.kotlin.multiplatform'
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-kotlin {
- jvm() {
- withJava()
- }
-
- dependencies {
- commonTestImplementation 'org.jetbrains.kotlin:kotlin-test'
- }
-
- sourceSets {
- jvmMain {
- dependencies {//DEPENDENCIES
- }
- }
- }
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/testTask b/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/testTask
deleted file mode 100644
index 2ccf87c9..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/groovy/kmp/testTask
+++ /dev/null
@@ -1,5 +0,0 @@
-tasks.jvmTest {
- kover {
- //KOVER_TEST_CONFIG
- }
-}
diff --git a/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/root b/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/root
deleted file mode 100644
index 2217fed6..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/root
+++ /dev/null
@@ -1,13 +0,0 @@
-plugins {
- kotlin("jvm") version "1.6.10"
- id("org.jetbrains.kotlinx.kover") version "//PLUGIN_VERSION"
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-dependencies {//DEPENDENCIES
- testImplementation(kotlin("test"))
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/subproject b/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/subproject
deleted file mode 100644
index 3fa1382e..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/subproject
+++ /dev/null
@@ -1,12 +0,0 @@
-plugins {
- kotlin("jvm")
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-dependencies {//DEPENDENCIES
- testImplementation(kotlin("test"))
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/testTask b/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/testTask
deleted file mode 100644
index 3a225fde..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/kotlin/kjvm/testTask
+++ /dev/null
@@ -1,5 +0,0 @@
-tasks.test {
- extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) {
- //KOVER_TEST_CONFIG
- }
-}
diff --git a/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/root b/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/root
deleted file mode 100644
index 410231c5..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/root
+++ /dev/null
@@ -1,26 +0,0 @@
-plugins {
- kotlin("multiplatform") version "1.6.10"
- id("org.jetbrains.kotlinx.kover") version "//PLUGIN_VERSION"
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-kotlin {
- jvm() {
- withJava()
- }
-
- dependencies {
- commonTestImplementation(kotlin("test"))
- }
-
- sourceSets {
- val jvmMain by getting {
- dependencies {//DEPENDENCIES
- }
- }
- }
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/subproject b/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/subproject
deleted file mode 100644
index d826c33d..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/subproject
+++ /dev/null
@@ -1,25 +0,0 @@
-plugins {
- kotlin("multiplatform")
-}
-
-repositories {
- mavenCentral()//REPOSITORIES
-}
-
-kotlin {
- jvm() {
- withJava()
- }
-
- dependencies {
- commonTestImplementation(kotlin("test"))
- }
-
- sourceSets {
- val jvmMain by getting {
- dependencies {//DEPENDENCIES
- }
- }
- }
-}
-//KOVER//SCRIPTS//TEST_TASK//VERIFICATIONS
diff --git a/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/testTask b/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/testTask
deleted file mode 100644
index 0c1f5175..00000000
--- a/src/functionalTest/templates/scripts/buildscripts/kotlin/kmp/testTask
+++ /dev/null
@@ -1,5 +0,0 @@
-tasks.named("jvmTest").configure {
- extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) {
- //KOVER_TEST_CONFIG
- }
-}
diff --git a/src/functionalTest/templates/scripts/settings/settings.gradle b/src/functionalTest/templates/scripts/settings/settings.gradle
deleted file mode 100644
index 368b66a0..00000000
--- a/src/functionalTest/templates/scripts/settings/settings.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-pluginManagement {
- repositories {
- maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
- mavenCentral()
- maven { url 'https://plugins.gradle.org/m2/' }
- }
-}
-rootProject.name = 'plugin-test-project-groovy'
-//SUBPROJECTS//EXTRA_SETTINGS
diff --git a/src/functionalTest/templates/scripts/settings/settings.gradle.kts b/src/functionalTest/templates/scripts/settings/settings.gradle.kts
deleted file mode 100644
index 60f592ab..00000000
--- a/src/functionalTest/templates/scripts/settings/settings.gradle.kts
+++ /dev/null
@@ -1,9 +0,0 @@
-pluginManagement {
- repositories {
- maven { url=uri("https://oss.sonatype.org/content/repositories/snapshots") }
- mavenCentral()
- maven { url=uri("https://plugins.gradle.org/m2/") }
- }
-}
-rootProject.name = "plugin-test-project-kotlin"
-//SUBPROJECTS//EXTRA_SETTINGS
diff --git a/src/main/kotlin/kotlinx/kover/KoverPlugin.kt b/src/main/kotlin/kotlinx/kover/KoverPlugin.kt
index 013bf80d..9aadc639 100644
--- a/src/main/kotlin/kotlinx/kover/KoverPlugin.kt
+++ b/src/main/kotlin/kotlinx/kover/KoverPlugin.kt
@@ -1,398 +1,15 @@
/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.kover
-import kotlinx.kover.adapters.*
-import kotlinx.kover.api.*
-import kotlinx.kover.api.KoverPaths.MERGED_HTML_REPORT_DEFAULT_PATH
-import kotlinx.kover.api.KoverNames.CHECK_TASK_NAME
-import kotlinx.kover.api.KoverNames.COLLECT_REPORTS_TASK_NAME
-import kotlinx.kover.api.KoverNames.MERGED_HTML_REPORT_TASK_NAME
-import kotlinx.kover.api.KoverNames.HTML_REPORT_TASK_NAME
-import kotlinx.kover.api.KoverNames.REPORT_TASK_NAME
-import kotlinx.kover.api.KoverNames.VERIFY_TASK_NAME
-import kotlinx.kover.api.KoverNames.MERGED_REPORT_TASK_NAME
-import kotlinx.kover.api.KoverNames.XML_REPORT_TASK_NAME
-import kotlinx.kover.api.KoverNames.ROOT_EXTENSION_NAME
-import kotlinx.kover.api.KoverNames.TASK_EXTENSION_NAME
-import kotlinx.kover.api.KoverNames.VERIFICATION_GROUP
-import kotlinx.kover.api.KoverNames.MERGED_VERIFY_TASK_NAME
-import kotlinx.kover.api.KoverNames.MERGED_XML_REPORT_TASK_NAME
-import kotlinx.kover.api.KoverPaths.ALL_PROJECTS_REPORTS_DEFAULT_PATH
-import kotlinx.kover.api.KoverPaths.PROJECT_HTML_REPORT_DEFAULT_PATH
-import kotlinx.kover.api.KoverPaths.MERGED_XML_REPORT_DEFAULT_PATH
-import kotlinx.kover.api.KoverPaths.PROJECT_XML_REPORT_DEFAULT_PATH
-import kotlinx.kover.engines.commons.*
-import kotlinx.kover.engines.commons.CoverageAgent
-import kotlinx.kover.engines.intellij.*
-import kotlinx.kover.tasks.*
+import kotlinx.kover.appliers.*
import org.gradle.api.*
-import org.gradle.api.provider.*
-import org.gradle.api.tasks.*
-import org.gradle.api.tasks.testing.*
-import org.gradle.process.*
-import java.io.File
-import kotlin.reflect.*
class KoverPlugin : Plugin {
- private val defaultJacocoVersion = "0.8.7"
-
override fun apply(target: Project) {
- val koverExtension = target.createKoverExtension()
- val agents = AgentsFactory.createAgents(target, koverExtension)
-
- val providers = target.createProviders(agents)
-
- target.allprojects {
- it.applyToProject(providers, agents)
- }
- target.createCollectingTask()
-
- target.createMergedTasks(providers)
- }
-
- private fun Project.applyToProject(providers: BuildProviders, agents: Map) {
- val projectProviders =
- providers.projects[path]
- ?: throw GradleException("Kover: Providers for project '$name' ('$path') was not found")
-
- val xmlReportTask = createKoverProjectTask(
- XML_REPORT_TASK_NAME,
- KoverXmlReportTask::class,
- providers,
- projectProviders
- ) {
- it.xmlReportFile.set(layout.buildDirectory.file(PROJECT_XML_REPORT_DEFAULT_PATH))
- it.description = "Generates code coverage XML report for all enabled test tasks in one project."
- }
-
- val htmlReportTask = createKoverProjectTask(
- HTML_REPORT_TASK_NAME,
- KoverHtmlReportTask::class,
- providers,
- projectProviders
- ) {
- it.htmlReportDir.set(it.project.layout.buildDirectory.dir(PROJECT_HTML_REPORT_DEFAULT_PATH))
- it.description = "Generates code coverage HTML report for all enabled test tasks in one project."
- }
-
- val verifyTask = createKoverProjectTask(
- VERIFY_TASK_NAME,
- KoverVerificationTask::class,
- providers,
- projectProviders
- ) {
- it.onlyIf { t -> (t as KoverVerificationTask).rules.isNotEmpty() }
- it.description = "Verifies code coverage metrics of one project based on specified rules."
- }
-
- tasks.create(REPORT_TASK_NAME) {
- it.group = VERIFICATION_GROUP
- it.dependsOn(xmlReportTask)
- it.dependsOn(htmlReportTask)
- it.description = "Generates code coverage HTML and XML reports for all enabled test tasks in one project."
- }
-
- tasks.configureEach {
- if (it.name == CHECK_TASK_NAME) {
- it.dependsOn(verifyTask)
- }
- }
-
- tasks.withType(Test::class.java).configureEach { t ->
- t.configTestTask(providers, agents)
- }
- }
-
- private fun Project.createMergedTasks(providers: BuildProviders) {
- val xmlReportTask = createKoverMergedTask(
- MERGED_XML_REPORT_TASK_NAME,
- KoverMergedXmlReportTask::class,
- providers
- ) {
- it.xmlReportFile.set(layout.buildDirectory.file(MERGED_XML_REPORT_DEFAULT_PATH))
- it.description = "Generates code coverage XML report for all enabled test tasks in all projects."
- }
-
- val htmlReportTask = createKoverMergedTask(
- MERGED_HTML_REPORT_TASK_NAME,
- KoverMergedHtmlReportTask::class,
- providers
- ) {
- it.htmlReportDir.set(layout.buildDirectory.dir(MERGED_HTML_REPORT_DEFAULT_PATH))
- it.description = "Generates code coverage HTML report for all enabled test tasks in all projects."
- }
-
- val reportTask = tasks.create(MERGED_REPORT_TASK_NAME) {
- it.group = VERIFICATION_GROUP
- it.dependsOn(xmlReportTask)
- it.dependsOn(htmlReportTask)
- it.description = "Generates code coverage HTML and XML reports for all enabled test tasks in all projects."
- }
-
- val verifyTask = createKoverMergedTask(
- MERGED_VERIFY_TASK_NAME,
- KoverMergedVerificationTask::class,
- providers
- ) {
- it.onlyIf { t -> (t as KoverMergedVerificationTask).rules.isNotEmpty() }
- it.description = "Verifies code coverage metrics of all projects based on specified rules."
- }
-
- tasks.configureEach {
- if (it.name == CHECK_TASK_NAME) {
- it.dependsOn(provider {
- val koverExtension = extensions.getByType(KoverExtension::class.java)
- if (koverExtension.generateReportOnCheck) {
- listOf(reportTask, verifyTask)
- } else {
- listOf(verifyTask)
- }
- })
- }
- }
- }
-
-
- private fun Project.createKoverMergedTask(
- taskName: String,
- type: KClass,
- providers: BuildProviders,
- block: (T) -> Unit
- ): T {
- val task = tasks.create(taskName, type.java)
-
- task.group = VERIFICATION_GROUP
-
- 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)
- task.classpath.set(providers.classpath)
- task.dependsOn(providers.merged.tests)
-
- val disabledProvider = providers.merged.disabled
- task.onlyIf { !disabledProvider.get() }
-
- block(task)
- return task
- }
-
- private fun Project.createCollectingTask() {
- tasks.create(COLLECT_REPORTS_TASK_NAME, KoverCollectingTask::class.java) { task ->
- task.group = VERIFICATION_GROUP
- task.description = "Collects all projects reports into one directory."
- task.outputDir.set(project.layout.buildDirectory.dir(ALL_PROJECTS_REPORTS_DEFAULT_PATH))
- // disable UP-TO-DATE check for task: it will be executed every time
- task.outputs.upToDateWhen { false }
-
- allprojects { proj ->
- val xmlReportTask =
- proj.tasks.withType(KoverXmlReportTask::class.java).getByName(XML_REPORT_TASK_NAME)
- val htmlReportTask =
- proj.tasks.withType(KoverHtmlReportTask::class.java).getByName(HTML_REPORT_TASK_NAME)
-
- task.mustRunAfter(xmlReportTask)
- task.mustRunAfter(htmlReportTask)
-
- task.xmlFiles[proj.path] = xmlReportTask.xmlReportFile
- task.htmlDirs[proj.path] = htmlReportTask.htmlReportDir
- }
- }
- }
-
-
- private fun Project.createKoverProjectTask(
- taskName: String,
- type: KClass,
- providers: BuildProviders,
- projectProviders: ProjectProviders,
- block: (T) -> Unit
- ): T {
- tasks.findByName(taskName)?.let {
- throw GradleException("Kover task '$taskName' already exist. Plugin should not be applied in child project if it has already been applied in one of the parent projects.")
- }
-
- val task = tasks.create(taskName, type.java)
- task.group = VERIFICATION_GROUP
-
- task.coverageEngine.set(providers.engine)
- task.classpath.set(providers.classpath)
- task.srcDirs.set(projectProviders.sources)
- task.outputDirs.set(projectProviders.output)
-
- // it is necessary to read all binary reports because project's classes can be invoked in another project
- task.binaryReportFiles.set(projectProviders.reports)
- task.dependsOn(projectProviders.tests)
-
- val disabledProvider = projectProviders.disabled
- task.onlyIf { !disabledProvider.get() }
- task.onlyIf { !(it as KoverProjectTask).binaryReportFiles.get().isEmpty }
-
- block(task)
-
- return task
- }
-
- private fun Project.createKoverExtension(): KoverExtension {
- val extension = extensions.create(ROOT_EXTENSION_NAME, KoverExtension::class.java, objects)
- extension.isDisabled = false
- extension.coverageEngine.set(CoverageEngine.INTELLIJ)
- extension.intellijEngineVersion.set(defaultIntellijVersion.toString())
- extension.jacocoEngineVersion.set(defaultJacocoVersion)
-
- afterEvaluate(CollectDisabledProjectsPathsAction(extension))
-
- return extension
- }
-
- private fun Test.configTestTask(
- providers: BuildProviders,
- agents: Map
- ) {
- val taskExtension = extensions.create(TASK_EXTENSION_NAME, KoverTaskExtension::class.java, project.objects)
-
- taskExtension.isDisabled = false
- taskExtension.binaryReportFile.set(project.provider {
- val koverExtension = providers.koverExtension.get()
- val suffix = if (koverExtension.coverageEngine.get() == CoverageEngine.INTELLIJ) ".ic" else ".exec"
- project.layout.buildDirectory.get().file("kover/$name$suffix").asFile
- })
-
- val pluginContainer = project.plugins
- val excludeAndroidPackages =
- project.provider { pluginContainer.androidPluginIsApplied && !providers.koverExtension.get().instrumentAndroidPackage }
-
- jvmArgumentProviders.add(
- CoverageArgumentProvider(
- this,
- agents,
- taskExtension,
- providers.koverExtension,
- excludeAndroidPackages
- )
- )
-
- val sourceErrorProvider = project.provider {
- File(taskExtension.binaryReportFile.get().parentFile, "coverage-error.log")
- }
- val targetErrorProvider = project.layout.buildDirectory.file("kover/errors/$name.log").map { it.asFile }
-
- doFirst(BinaryReportCleanupAction(project.path, providers.koverExtension, taskExtension))
- doLast(MoveIntellijErrorLogAction(sourceErrorProvider, targetErrorProvider))
- }
-}
-
-/*
- To support parallel tests, both Coverage Engines work in append to data file mode.
- 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 projectPath: String,
- private val koverExtensionProvider: Provider,
- private val taskExtension: KoverTaskExtension
-) : Action {
- override fun execute(task: Task) {
- val koverExtension = koverExtensionProvider.get()
- val file = taskExtension.binaryReportFile.get()
-
- // always delete previous data file
- file.delete()
-
- if (!taskExtension.isDisabled
- && !koverExtension.isDisabled
- && !koverExtension.disabledProjectsPaths.contains(projectPath)
- && koverExtension.coverageEngine.get() == CoverageEngine.INTELLIJ
- ) {
- // IntelliJ engine expected empty file for parallel test execution.
- // Since it is impossible to know in advance whether the tests will be run in parallel, we always create an empty file.
- file.createNewFile()
- }
- }
-}
-
-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("Kover configuring error: ambiguous name of the excluded project '$it': suitable projects with paths $projectPaths. Consider using fully-qualified name starting with ':'")
- }
- result += projectPaths
- }
- }
-
- koverExtension.disabledProjectsPaths = result
- }
-}
-
-private class MoveIntellijErrorLogAction(
- private val sourceFile: Provider,
- private val targetFile: Provider
-) : Action {
- override fun execute(task: Task) {
- val origin = sourceFile.get()
- if (origin.exists() && origin.isFile) {
- origin.copyTo(targetFile.get(), true)
- origin.delete()
- }
- }
-}
-
-private class CoverageArgumentProvider(
- private val task: Task,
- private val agents: Map,
- @get:Nested val taskExtension: KoverTaskExtension,
- @get:Nested val koverExtension: Provider,
- @get:Input val excludeAndroidPackage: Provider
-) : CommandLineArgumentProvider, Named {
-
- private val projectPath: String = task.project.path
-
- @Internal
- override fun getName(): String {
- return "koverArgumentsProvider"
- }
-
- override fun asArguments(): MutableIterable {
- val koverExtensionValue = koverExtension.get()
-
- if (taskExtension.isDisabled
- || koverExtensionValue.isDisabled
- || koverExtensionValue.disabledProjectsPaths.contains(projectPath)
- ) {
- return mutableListOf()
- }
-
- if (excludeAndroidPackage.get()) {
- /*
- The instrumentation of android classes often causes errors when using third-party
- frameworks (see https://github.com/Kotlin/kotlinx-kover/issues/89).
-
- Because android classes are not part of the project, in any case they do not get into the report,
- and they can be excluded from instrumentation.
-
- FIXME Remove this code if the IntelliJ Agent stops changing project classes during instrumentation
- */
- taskExtension.excludes = taskExtension.excludes + "android.*" + "com.android.*"
- }
-
- return agents.getFor(koverExtensionValue.coverageEngine.get()).buildCommandLineArgs(task, taskExtension)
+ target.applyToProject()
+ target.applyMerged()
}
}
diff --git a/src/main/kotlin/kotlinx/kover/Providers.kt b/src/main/kotlin/kotlinx/kover/Providers.kt
deleted file mode 100644
index 6b33a3b7..00000000
--- a/src/main/kotlin/kotlinx/kover/Providers.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-package kotlinx.kover
-
-import kotlinx.kover.adapters.*
-import kotlinx.kover.api.*
-import kotlinx.kover.engines.commons.*
-import kotlinx.kover.engines.commons.CoverageAgent
-import org.gradle.api.*
-import org.gradle.api.file.*
-import org.gradle.api.provider.*
-import org.gradle.api.tasks.testing.*
-import java.io.*
-
-
-internal fun Project.createProviders(agents: Map): BuildProviders {
- val projects: MutableMap = mutableMapOf()
-
- allprojects {
- 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 },
- it.provider { it.collectDirs(this).second },
- it.provider { it.isDisabled(this) }
- )
- }
-
- val engineProvider = provider { extensions.getByType(KoverExtension::class.java).coverageEngine.get() }
-
- val classpathProvider: Provider = provider {
- val koverExtension = extensions.getByType(KoverExtension::class.java)
- agents.getFor(koverExtension.coverageEngine.get()).classpath
- }
-
- val extensionProvider = provider { extensions.getByType(KoverExtension::class.java) }
-
-
- val allReportsProvider: Provider = provider { files(allBinaryReports()) }
- val allTestsProvider = provider { allTestTasks() }
- val koverDisabledProvider = provider { extensions.getByType(KoverExtension::class.java).isDisabled }
-
-
- // all sources and all outputs providers are unused, so NOW it can return empty file collection
- val emptyProvider: Provider = provider { files() }
- val mergedProviders =
- ProjectProviders(
- allReportsProvider,
- allTestsProvider,
- emptyProvider,
- emptyProvider,
- koverDisabledProvider)
-
- return BuildProviders(projects, mergedProviders, engineProvider, classpathProvider, extensionProvider)
-}
-
-
-internal fun Project.allTestTasks(): List {
- return allprojects.flatMap { it.testTasks(this) }
-}
-
-internal fun Project.allBinaryReports(): List {
- return allprojects.flatMap { it.binaryReports(this) }
-}
-
-
-internal fun Project.testTasks(rootProject: Project): List {
- if (isDisabled(rootProject)) {
- return emptyList()
- }
-
- return tasks.withType(Test::class.java)
- .filterNot { t -> t.extensions.getByType(KoverTaskExtension::class.java).isDisabled }
-}
-
-internal fun Project.binaryReports(root: Project): List {
- 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
- .filterNot { e -> e.isDisabled }
- .map { e -> e.binaryReportFile.get() }
- // process binary report only from tasks with sources
- .filter { f -> f.exists() }
- .toList()
-}
-
-private fun Project.collectDirs(root: Project): Pair {
- if (isDisabled(root)) {
- return files() to files()
- }
-
- val srcDirs = HashMap()
- val outDirs = HashMap()
-
- 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 {
- val koverExtension = root.extensions.getByType(KoverExtension::class.java)
- return koverExtension.isDisabled || koverExtension.disabledProjectsPaths.contains(path)
-}
-
-private fun Project.runAllTests(): Boolean {
- return extensions.getByType(KoverExtension::class.java).runAllTestsForProjectTask
-}
-
-
-internal class BuildProviders(
- val projects: Map,
- val merged: ProjectProviders,
-
- val engine: Provider,
- val classpath: Provider,
- val koverExtension: Provider
-)
-
-internal class ProjectProviders(
- val reports: Provider,
- val tests: Provider>,
- val sources: Provider,
- val output: Provider,
- val disabled: Provider
-)
-
diff --git a/src/main/kotlin/kotlinx/kover/adapters/AndroidPluginAdapter.kt b/src/main/kotlin/kotlinx/kover/adapters/AndroidPluginAdapter.kt
deleted file mode 100644
index d9228841..00000000
--- a/src/main/kotlin/kotlinx/kover/adapters/AndroidPluginAdapter.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 com.android.build.gradle.*
-import kotlinx.kover.adapters.api.*
-import org.gradle.api.*
-
-class AndroidPluginAdapter : CompilationPluginAdapter {
-
- override fun findDirs(project: Project): PluginDirs {
- return safe(project) {
- this.plugins.findPlugin("android") ?: return@safe PluginDirs(emptyList(), emptyList())
-
- val extension = project.extensions.findByType(BaseExtension::class.java) ?: return@safe PluginDirs(
- emptyList(),
- emptyList()
- )
-
- val sourceDirs = extension.sourceSets.asSequence()
- .filter { !it.name.startsWith("test") && !it.name.startsWith("androidTest") }
- .map { it.java }.toList()
- .flatMap { it.srcDirs }
-
-
-
- return@safe PluginDirs(sourceDirs, emptyList())
- }
- }
-
-}
diff --git a/src/main/kotlin/kotlinx/kover/adapters/KotlinAndroidPluginAdapter.kt b/src/main/kotlin/kotlinx/kover/adapters/KotlinAndroidPluginAdapter.kt
deleted file mode 100644
index 56aa6648..00000000
--- a/src/main/kotlin/kotlinx/kover/adapters/KotlinAndroidPluginAdapter.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.jetbrains.kotlin.gradle.dsl.*
-
-class KotlinAndroidPluginAdapter : CompilationPluginAdapter {
-
- override fun findDirs(project: Project): PluginDirs {
- return safe(project) {
- this.plugins.findPlugin("kotlin-android") ?: return@safe PluginDirs(emptyList(), emptyList())
-
- val extension = project.extensions.findByType(KotlinAndroidProjectExtension::class.java) ?: return@safe PluginDirs(
- emptyList(),
- emptyList()
- )
-
-
- val sourceDirs = extension.target.compilations
- .filter { !it.name.endsWith("Test") }
- .flatMap { it.allKotlinSourceSets }
- .map { it.kotlin }
- .flatMap { it.srcDirs }
-
- val outputDirs = extension.target.compilations
- .filter { !it.name.endsWith("Test") }
- .flatMap { it.output.classesDirs }
-
- return@safe PluginDirs(sourceDirs, outputDirs)
- }
- }
-
-}
diff --git a/src/main/kotlin/kotlinx/kover/adapters/OldJavaPluginAdapter.kt b/src/main/kotlin/kotlinx/kover/adapters/OldJavaPluginAdapter.kt
deleted file mode 100644
index 9640083c..00000000
--- a/src/main/kotlin/kotlinx/kover/adapters/OldJavaPluginAdapter.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.tasks.*
-
-class OldJavaPluginAdapter : CompilationPluginAdapter {
-
- override fun findDirs(project: Project): PluginDirs {
- return safe(project) {
- this.plugins.findPlugin("java") ?: return@safe PluginDirs(emptyList(), emptyList())
-
- val sourceSetContainer = project.extensions.findByType(
- SourceSetContainer::class.java
- ) ?: return@safe PluginDirs(emptyList(), emptyList())
-
- val sourceSets = sourceSetContainer.filter { it.name != SourceSet.TEST_SOURCE_SET_NAME }
-
- val sourceDirs = sourceSets.flatMap { it.allSource.srcDirs }
- val outputDirs = sourceSets.flatMap { it.output.classesDirs }
-
- return@safe PluginDirs(sourceDirs, outputDirs)
- }
- }
-
-}
diff --git a/src/main/kotlin/kotlinx/kover/adapters/PluginAdaptersFactory.kt b/src/main/kotlin/kotlinx/kover/adapters/PluginAdaptersFactory.kt
deleted file mode 100644
index ec4d2ca6..00000000
--- a/src/main/kotlin/kotlinx/kover/adapters/PluginAdaptersFactory.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.plugins.PluginContainer
-
-internal fun createAdapters(): List {
- return listOf(
- OldJavaPluginAdapter(),
- KotlinMultiplatformPluginAdapter(),
- AndroidPluginAdapter(),
- KotlinAndroidPluginAdapter()
- )
-}
-
-val PluginContainer.androidPluginIsApplied: Boolean
- get() {
- return findPlugin("android") != null || findPlugin("kotlin-android") != null
- }
diff --git a/src/main/kotlin/kotlinx/kover/adapters/api/CompilationPluginAdapter.kt b/src/main/kotlin/kotlinx/kover/adapters/api/CompilationPluginAdapter.kt
deleted file mode 100644
index 9555412e..00000000
--- a/src/main/kotlin/kotlinx/kover/adapters/api/CompilationPluginAdapter.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.kover.adapters.api
-
-import org.gradle.api.*
-import java.io.*
-
-interface CompilationPluginAdapter {
- fun findDirs(project: Project): PluginDirs
-}
-
-
-data class PluginDirs(val sources: List, val output: List)
-
-internal inline fun safe(project: Project, block: Project.() -> PluginDirs): PluginDirs {
- return try {
- project.block()
- } catch (e: Throwable) {
- when (e) {
- is NoSuchMethodError, is NoSuchFieldError, is ClassNotFoundException, is NoClassDefFoundError -> {
- project.logger.info("Problem occurred in Kover source set adapter", e)
- PluginDirs(emptyList(), emptyList())
- }
- else -> throw e
- }
- }
-}
diff --git a/src/main/kotlin/kotlinx/kover/api/CoverageEngines.kt b/src/main/kotlin/kotlinx/kover/api/CoverageEngines.kt
new file mode 100644
index 00000000..7058dd52
--- /dev/null
+++ b/src/main/kotlin/kotlinx/kover/api/CoverageEngines.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.api
+
+import kotlinx.kover.api.KoverVersions.DEFAULT_INTELLIJ_VERSION
+import kotlinx.kover.api.KoverVersions.DEFAULT_JACOCO_VERSION
+import org.gradle.api.tasks.*
+
+public sealed class CoverageEngineVariant(
+ @get:Input
+ internal val vendor: CoverageEngineVendor,
+ @get:Input
+ internal val version: String
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as CoverageEngineVariant
+
+ if (vendor != other.vendor) return false
+ if (version != other.version) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = vendor.hashCode()
+ result = 31 * result + version.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "$vendor Coverage Engine $version"
+ }
+}
+
+internal enum class CoverageEngineVendor {
+ INTELLIJ,
+ JACOCO
+}
+
+// TODO make internal in 0.7 version
+@Deprecated(
+ message = "Class was removed in Kover API version 2. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ level = DeprecationLevel.WARNING
+)
+public enum class CoverageEngine {
+ INTELLIJ,
+ JACOCO
+}
+
+/**
+ * Coverage Engine by IntelliJ.
+ */
+public class IntellijEngine(version: String): CoverageEngineVariant(CoverageEngineVendor.INTELLIJ, version)
+
+/**
+ * IntelliJ Coverage Engine with default version [DEFAULT_INTELLIJ_VERSION].
+ */
+public object DefaultIntellijEngine: CoverageEngineVariant(CoverageEngineVendor.INTELLIJ, DEFAULT_INTELLIJ_VERSION)
+
+/**
+ * Coverage Engine by [JaCoCo](https://www.jacoco.org/jacoco/).
+ */
+public class JacocoEngine(version: String): CoverageEngineVariant(CoverageEngineVendor.JACOCO, version)
+
+/**
+ * JaCoCo Coverage Engine with default version [DEFAULT_JACOCO_VERSION].
+ */
+public object DefaultJacocoEngine: CoverageEngineVariant(CoverageEngineVendor.JACOCO, DEFAULT_JACOCO_VERSION)
diff --git a/src/main/kotlin/kotlinx/kover/api/KoverConfig.kt b/src/main/kotlin/kotlinx/kover/api/KoverConfig.kt
new file mode 100644
index 00000000..ba6351b5
--- /dev/null
+++ b/src/main/kotlin/kotlinx/kover/api/KoverConfig.kt
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.api
+
+import org.gradle.api.*
+import org.gradle.api.file.*
+import org.gradle.api.model.*
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.*
+import javax.annotation.*
+import javax.inject.*
+
+public open class KoverProjectConfig @Inject constructor(objects: ObjectFactory) {
+ internal val filters: KoverProjectFilters = objects.newInstance(KoverProjectFilters::class.java, objects)
+
+ internal val instrumentation: KoverProjectInstrumentation =
+ objects.newInstance(KoverProjectInstrumentation::class.java)
+
+ internal val xmlReport: KoverProjectXmlConfig = objects.newInstance(KoverProjectXmlConfig::class.java, objects)
+
+ internal val htmlReport: KoverProjectHtmlConfig = objects.newInstance(KoverProjectHtmlConfig::class.java, objects)
+
+ internal val verify: KoverVerifyConfig = objects.newInstance(KoverVerifyConfig::class.java, objects)
+
+ /**
+ * Specifies whether instrumentation is disabled for all test tasks of current project.
+ *
+ * `false` by default.
+ */
+ public val isDisabled: Property = objects.property(Boolean::class.java)
+
+ /**
+ * Specifies the coverage engine variant to be used to collect execution data.
+ */
+ public val engine: Property = objects.property(CoverageEngineVariant::class.java)
+
+ /**
+ * Configures filtering for all Kover's tasks of current project by class names and source sets.
+ */
+ public fun filters(config: Action) {
+ config.execute(filters)
+ }
+
+ /**
+ * Configures a list of tasks, the execution of tests from which is registered in the coverage counters.
+ *
+ */
+ public fun instrumentation(config: Action) {
+ config.execute(instrumentation)
+ }
+
+ /**
+ * Configures the task of generating an XML report, including XML report location and whether it should be
+ * generated during the 'check' task.
+ *
+ * By default, [KoverPaths.PROJECT_XML_REPORT_DEFAULT_PATH] location in build directory is used.
+ */
+ public fun xmlReport(config: Action) {
+ config.execute(xmlReport)
+ }
+
+ /**
+ * Configures the task of generating an HTML report, including HTML report location and whether it should be
+ * generated during the 'check' task.
+ *
+ * By default, [KoverPaths.PROJECT_HTML_REPORT_DEFAULT_PATH] location in build directory is used.
+ */
+ public fun htmlReport(config: Action) {
+ config.execute(htmlReport)
+ }
+
+ /**
+ * Configures the verification task, including adding verification rules and whether it should be
+ * verified during the 'check' task.
+ */
+ public fun verify(config: Action) {
+ config.execute(verify)
+ }
+
+
+ // DEPRECATIONS
+ // TODO delete in 0.7 version
+ @Suppress("DEPRECATION")
+ @get:Internal
+ @Deprecated(
+ message = "Property was removed in Kover API version 2, use `engine` property instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ replaceWith = ReplaceWith("engine"),
+ level = DeprecationLevel.ERROR
+ )
+ public val coverageEngine: Property = objects.property(CoverageEngine::class.java)
+
+ @get:Internal
+ @Deprecated(
+ message = "Property was removed in Kover API version 2, use `engine.set(kotlinx.kover.api.IntellijEngine(\"version\"))` instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ replaceWith = ReplaceWith("engine"),
+ level = DeprecationLevel.ERROR
+ )
+ public val intellijEngineVersion: Property = objects.property(String::class.java)
+
+ @get:Internal
+ @Deprecated(
+ message = "Property was removed in Kover API version 2, use `engine.set(kotlinx.kover.api.JacocoEngine(\"version\"))` instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ replaceWith = ReplaceWith("engine"),
+ level = DeprecationLevel.ERROR
+ )
+ public val jacocoEngineVersion: Property = objects.property(String::class.java)
+
+ @get:Internal
+ @Deprecated(
+ message = "Property was removed in Kover API version 2. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ level = DeprecationLevel.ERROR
+ )
+ public var generateReportOnCheck: Boolean = true
+
+ @get:Internal
+ @Deprecated(
+ message = "Property was removed in Kover API version 2. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ level = DeprecationLevel.ERROR
+ )
+ public var disabledProjects: Set = emptySet()
+
+ @Deprecated(
+ message = "Property was removed in Kover API version 2. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ level = DeprecationLevel.ERROR
+ )
+ @get:Internal
+ public var instrumentAndroidPackage: Boolean = false
+
+ @Deprecated(
+ message = "Property was removed in Kover API version 2. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_5_TO_0_6}",
+ level = DeprecationLevel.ERROR
+ )
+ @get:Internal
+ public var runAllTestsForProjectTask: Boolean = false
+}
+
+// DEPRECATIONS
+// TODO delete in 0.7 version
+@Deprecated(
+ message = "Class KoverExtension was renamed to KoverProjectConfig in Kover API version 2",
+ replaceWith = ReplaceWith("KoverProjectConfig"),
+ level = DeprecationLevel.ERROR
+)
+public open class KoverExtension
+
+public open class KoverProjectFilters @Inject constructor(private val objects: ObjectFactory) {
+ internal val classes: Property = objects.property(KoverClassFilter::class.java)
+
+ internal val sourceSets: Property = objects.property(KoverSourceSetFilter::class.java)
+
+ /**
+ * Configures class filter in order to include and exclude specific classes.
+ *
+ * Example:
+ * ```
+ * classes {
+ * excludes += "com.example.FooBar"
+ * includes += "com.example.*Bar"
+ * }
+ * ```
+ * Excludes have priority over includes.
+ */
+ public fun classes(config: Action) {
+ val classFilter = objects.newInstance(KoverClassFilter::class.java)
+ config.execute(classFilter)
+ classes.set(classFilter)
+ }
+
+ /**
+ * Configures source set filter.
+ */
+ public fun sourceSets(config: Action) {
+ val sourceSetFilters = objects.newInstance(KoverSourceSetFilter::class.java)
+ config.execute(sourceSetFilters)
+ sourceSets.set(sourceSetFilters)
+ }
+}
+
+public open class KoverProjectInstrumentation {
+ /**
+ * Specifies the names of test tasks for which instrumentation will be disabled.
+ */
+ public val excludeTasks: MutableSet = mutableSetOf()
+}
+
+public open class KoverProjectXmlConfig @Inject constructor(objects: ObjectFactory) {
+ internal val filters: KoverProjectFilters = objects.newInstance(KoverProjectFilters::class.java, objects)
+
+ /**
+ * Specifies whether the XML report generation task should be executed before the `check` task.
+ *
+ * `false` by default.
+ */
+ public val onCheck: Property = objects.property(Boolean::class.java)
+
+ /**
+ * Specifies file path of generated XML report file with coverage data.
+ *
+ * By default, is a value of [KoverPaths.PROJECT_XML_REPORT_DEFAULT_PATH] in the build directory.
+ */
+ public val reportFile: RegularFileProperty = objects.fileProperty()
+
+ /**
+ * Override filters for the XML report generation task.
+ * Only the explicitly specified filters will be overridden, the rest will be inherited from the common filters (see [KoverProjectConfig.filters]).
+ */
+ public fun overrideFilters(config: Action) {
+ config.execute(filters)
+ }
+}
+
+public open class KoverProjectHtmlConfig @Inject constructor(private val objects: ObjectFactory) {
+ internal val taskFilters: KoverProjectFilters = objects.newInstance(KoverProjectFilters::class.java, objects)
+
+ /**
+ * Specifies whether the HTML report generation task should be executed before the `check` task.
+ *
+ * `false` by default.
+ */
+ public val onCheck: Property = objects.property(Boolean::class.java)
+
+ /**
+ * Specifies directory path of generated HTML report.
+ *
+ * By default, is a value of [KoverPaths.PROJECT_HTML_REPORT_DEFAULT_PATH] in the build directory.
+ */
+ public val reportDir: DirectoryProperty = objects.directoryProperty()
+
+ /**
+ * Override filters for the HTML report generation task.
+ * Only the explicitly specified filters will be overrided, the rest will be inherited from the common filters (see [KoverProjectConfig.filters]).
+ */
+ public fun overrideFilters(config: Action) {
+ config.execute(taskFilters)
+ }
+}
+
+
+public open class KoverMergedConfig @Inject constructor(objects: ObjectFactory) {
+ internal var isEnabled: Property = objects.property(Boolean::class.java)
+ internal val filters: KoverMergedFilters = objects.newInstance(KoverMergedFilters::class.java, objects)
+ internal val xmlReport: KoverMergedXmlConfig = objects.newInstance(KoverMergedXmlConfig::class.java, objects)
+ internal val htmlReport: KoverMergedHtmlConfig = objects.newInstance(KoverMergedHtmlConfig::class.java, objects)
+ internal val verify: KoverVerifyConfig = objects.newInstance(KoverVerifyConfig::class.java, objects)
+
+ /**
+ * Create Kover tasks for generating merged reports.
+ */
+ public fun enable() {
+ isEnabled.set(true)
+ }
+
+ /**
+ * Configures filters for all Kover merged tasks in current project.
+ */
+ public fun filters(config: Action) {
+ config.execute(filters)
+ }
+
+ /**
+ * Configures the task of generating a merged XML report.
+ */
+ public fun xmlReport(config: Action) {
+ config.execute(xmlReport)
+ }
+
+ /**
+ * Configures the task of generating a merged HTML report.
+ */
+ public fun htmlReport(config: Action) {
+ config.execute(htmlReport)
+ }
+
+ /**
+ * Configures the merged verification task.
+ */
+ public fun verify(config: Action) {
+ config.execute(verify)
+ }
+}
+
+public open class KoverMergedFilters @Inject constructor(private val objects: ObjectFactory) {
+ internal val classes: Property = objects.property(KoverClassFilter::class.java)
+
+ internal val projects: Property = objects.property(KoverProjectsFilter::class.java)
+
+ /**
+ * Configures class filter.
+ */
+ public fun classes(config: Action) {
+ val classFilter = objects.newInstance(KoverClassFilter::class.java)
+ config.execute(classFilter)
+ classes.set(classFilter)
+ }
+
+ /**
+ * Configures projects filter.
+ */
+ public fun projects(config: Action) {
+ val projectsFilters = objects.newInstance(KoverProjectsFilter::class.java)
+ config.execute(projectsFilters)
+ projects.set(projectsFilters)
+ }
+}
+
+
+public open class KoverMergedXmlConfig @Inject constructor(private val objects: ObjectFactory) {
+ internal val classFilter: Property = objects.property(KoverClassFilter::class.java)
+
+ /**
+ * Specifies whether the merged XML report generation task should be executed before the `check` task.
+ */
+ public val onCheck: Property = objects.property(Boolean::class.java)
+
+ /**
+ * Specifies file path of generated XML report file with coverage data.
+ */
+ public val reportFile: RegularFileProperty = objects.fileProperty()
+
+ /**
+ * Override class filter for the merged XML report generation task.
+ */
+ public fun overrideClassFilter(config: Action) {
+ val newClassFilter = objects.newInstance(KoverClassFilter::class.java)
+ config.execute(newClassFilter)
+ classFilter.set(newClassFilter)
+ }
+}
+
+public open class KoverMergedHtmlConfig @Inject constructor(private val objects: ObjectFactory) {
+ internal val classFilter: Property = objects.property(KoverClassFilter::class.java)
+
+ /**
+ * Specifies whether the merged HTML report generation task should be executed before the `check` task.
+ */
+ public val onCheck: Property = objects.property(Boolean::class.java)
+
+ /**
+ * Specifies directory path of generated HTML report.
+ */
+ public val reportDir: DirectoryProperty = objects.directoryProperty()
+
+ /**
+ * Override class filter for the merged HTML report generation task.
+ */
+ public fun overrideClassFilter(config: Action) {
+ val newClassFilter = objects.newInstance(KoverClassFilter::class.java)
+ config.execute(newClassFilter)
+ classFilter.set(newClassFilter)
+ }
+}
+
+public open class KoverProjectsFilter {
+ /**
+ * Specifies the projects excluded from subprojects (included current project) using in the merged tasks. Both the project name (if it is unique) and the project path can be used.
+ *
+ * If empty, the current project and all subprojects are used.
+ */
+ @get:Input
+ public val excludes: MutableList = mutableListOf()
+}
+
+
+public open class KoverVerifyConfig @Inject constructor(private val objects: ObjectFactory) {
+ internal val rules: ListProperty = objects.listProperty(VerificationRule::class.java)
+
+ /**
+ * Specifies whether the verification task should be executed before the `check` task.
+ *
+ * By default, `true` for project reports and `false` for merged.
+ */
+ public val onCheck: Property = objects.property(Boolean::class.java).value(true)
+
+ /**
+ * Add new coverage verification rule to check after test task execution.
+ */
+ public fun rule(configureRule: Action) {
+ rules.add(objects.newInstance(VerificationRule::class.java, objects).also { configureRule.execute(it) })
+ }
+}
+
+public open class KoverClassFilter {
+ /**
+ * Specifies class inclusion rules into report.
+ * Only the specified classes may be present in the report.
+ * 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.
+ */
+ @get:Input
+ public val includes: MutableList = mutableListOf()
+
+ /**
+ * Specifies class exclusion rules into report.
+ * The specified classes will definitely be missing from report.
+ * 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.
+ */
+ @get:Input
+ public val excludes: MutableList = mutableListOf()
+}
+
+public open class KoverSourceSetFilter {
+ /**
+ * Not implemented in beta version.
+ */
+ @get:Input
+ public val excludes: MutableSet = mutableSetOf()
+
+ /**
+ * Not implemented in beta version.
+ */
+ @get:Input
+ public var excludeTests: Boolean = true
+}
+
+
+public open class VerificationRule @Inject constructor(private val objects: ObjectFactory) {
+ @get:Nested
+ @get:Optional
+ internal val classFilter: Property = objects.property(KoverClassFilter::class.java)
+
+ @get:Nested
+ internal val bounds: ListProperty = objects.listProperty(VerificationBound::class.java)
+
+ /**
+ * Specifies that the rule will be checked during verification.
+ */
+ @get:Input
+ public var isEnabled: Boolean = true
+
+ /**
+ * Specifies custom name of the rule.
+ */
+ @get:Input
+ @get:Nullable
+ @get:Optional
+ public var name: String? = null
+
+ /**
+ * Specifies by which entity the code for separate coverage evaluation will be grouped.
+ */
+ @get:Input
+ public var target: VerificationTarget = VerificationTarget.ALL
+
+ /**
+ * Override class filter for the rule.
+ */
+ public fun overrideClassFilter(config: Action) {
+ if (!classFilter.isPresent) {
+ classFilter.set(objects.newInstance(KoverClassFilter::class.java))
+ }
+ config.execute(classFilter.get())
+ }
+
+ /**
+ * Add a constraint on the value of the code coverage metric.
+ */
+ public fun bound(configureBound: Action) {
+ bounds.add(objects.newInstance(VerificationBound::class.java).also { configureBound.execute(it) })
+ }
+}
+
+public open class VerificationBound {
+ /**
+ * Specifies minimal value to compare with counter value.
+ */
+ @get:Input
+ @get:Nullable
+ @get:Optional
+ public var minValue: Int? = null
+
+ /**
+ * Specifies maximal value to compare with counter value.
+ */
+ @get:Input
+ @get:Nullable
+ @get:Optional
+ public var maxValue: Int? = null
+
+ /**
+ * Specifies which metric will be evaluation code coverage.
+ */
+ @get:Input
+ public var counter: CounterType = CounterType.LINE
+
+ /**
+ * Specifies type of lines counter value to compare with minimal and maximal values if them defined.
+ * Default is [VerificationValueType.COVERED_PERCENTAGE]
+ */
+ @get:Input
+ public var valueType: VerificationValueType = VerificationValueType.COVERED_PERCENTAGE
+}
+
+/**
+ * Entity type for grouping code to coverage evaluation.
+ */
+public enum class VerificationTarget {
+ /**
+ * Counts the coverage for all code.
+ */
+ ALL,
+
+ /**
+ * Counts the coverage for each class separately.
+ */
+ CLASS,
+
+ /**
+ * Counts the coverage for each package that has classes separately.
+ */
+ PACKAGE
+}
+
+/**
+ * Type of the metric to evaluate code coverage.
+ */
+public enum class CounterType {
+ /**
+ * Evaluates coverage for lines.
+ */
+ LINE,
+
+ /**
+ * Evaluates coverage for JVM bytecode instructions.
+ */
+ INSTRUCTION,
+
+ /**
+ * Evaluates coverage for code branches excluded dead-branches.
+ */
+ BRANCH
+}
+
+
+/**
+ * Type of counter value to compare with minimal and maximal values if them defined.
+ */
+public enum class VerificationValueType {
+ COVERED_COUNT,
+ MISSED_COUNT,
+ COVERED_PERCENTAGE,
+ MISSED_PERCENTAGE
+}
diff --git a/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt b/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt
index 8353963d..b83bed7c 100644
--- a/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt
+++ b/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt
@@ -1,10 +1,15 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
package kotlinx.kover.api
public object KoverNames {
public const val CHECK_TASK_NAME = "check"
public const val VERIFICATION_GROUP = "verification"
- public const val ROOT_EXTENSION_NAME = "kover"
+ public const val PROJECT_EXTENSION_NAME = "kover"
+ public const val MERGED_EXTENSION_NAME = "koverMerged"
public const val TASK_EXTENSION_NAME = "kover"
public const val MERGED_XML_REPORT_TASK_NAME = "koverMergedXmlReport"
@@ -15,17 +20,28 @@ public object KoverNames {
public const val XML_REPORT_TASK_NAME = "koverXmlReport"
public const val HTML_REPORT_TASK_NAME = "koverHtmlReport"
public const val REPORT_TASK_NAME = "koverReport"
- public const val COLLECT_REPORTS_TASK_NAME = "koverCollectReports"
public const val VERIFY_TASK_NAME = "koverVerify"
+
+ public const val CONFIGURATION_NAME = "KoverEngineConfig"
}
public object KoverPaths {
- public const val MERGED_HTML_REPORT_DEFAULT_PATH = "reports/kover/html"
- public const val MERGED_XML_REPORT_DEFAULT_PATH = "reports/kover/report.xml"
+ public const val MERGED_HTML_REPORT_DEFAULT_PATH = "reports/kover/merged/html"
+ public const val MERGED_XML_REPORT_DEFAULT_PATH = "reports/kover/merged/xml/report.xml"
+ public const val MERGED_VERIFICATION_REPORT_DEFAULT_PATH = "reports/kover/merged/verification/errors.txt"
- public const val PROJECT_HTML_REPORT_DEFAULT_PATH = "reports/kover/project-html"
- public const val PROJECT_XML_REPORT_DEFAULT_PATH = "reports/kover/project-xml/report.xml"
+ public const val PROJECT_HTML_REPORT_DEFAULT_PATH = "reports/kover/html"
+ public const val PROJECT_XML_REPORT_DEFAULT_PATH = "reports/kover/xml/report.xml"
+ public const val PROJECT_VERIFICATION_REPORT_DEFAULT_PATH = "reports/kover/verification/errors.txt"
+}
- public const val ALL_PROJECTS_REPORTS_DEFAULT_PATH = "reports/kover/projects"
+public object KoverVersions {
+ internal const val MINIMAL_INTELLIJ_VERSION = "1.0.675"
+ internal const val DEFAULT_INTELLIJ_VERSION = "1.0.675"
+
+ internal const val DEFAULT_JACOCO_VERSION = "0.8.8"
+}
+public object KoverMigrations {
+ public const val MIGRATION_0_5_TO_0_6 = "https://github.com/Kotlin/kotlinx-kover/blob/v0.6.0-BETA/docs/migration-to-0.6.0.md"
}
diff --git a/src/main/kotlin/kotlinx/kover/api/KoverExtension.kt b/src/main/kotlin/kotlinx/kover/api/KoverExtension.kt
deleted file mode 100644
index d7fabfcb..00000000
--- a/src/main/kotlin/kotlinx/kover/api/KoverExtension.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.kover.api
-
-import org.gradle.api.model.*
-import org.gradle.api.provider.*
-import org.gradle.api.tasks.*
-
-open class KoverExtension(objects: ObjectFactory) {
-
- /**
- * Specifies whether instrumentation is disabled for all test tasks of all projects.
- */
- @get:Input
- public var isDisabled: Boolean = false
-
- /**
- * Specifies the coverage engine to be used to collect execution data.
- */
- @get:Input
- public val coverageEngine: Property = objects.property(CoverageEngine::class.java)
-
- /**
- * Specifies the version of Intellij-coverage dependency.
- */
- @get:Input
- public val intellijEngineVersion: Property = objects.property(String::class.java)
-
- /**
- * Specifies the version of JaCoCo dependency.
- */
- @get:Input
- public val jacocoEngineVersion: Property = objects.property(String::class.java)
-
- /**
- * Specifies whether the reports will be generated within 'check' task execution.
- */
- @get:Input
- public var generateReportOnCheck: Boolean = true
-
- /**
- * Specifies the projects to be disabled from instrumentation and reportings.
- */
- @get:Input
- public var disabledProjects: Set = emptySet()
-
- /**
- * Specifies whether the classes from 'android' and 'com.android' packages should be included if Android plugin is applied.
- */
- @get:Input
- public var instrumentAndroidPackage: Boolean = false
-
- /**
- * Specifies whether to perform all test tasks from all projects for Kover single-project tasks.
- * If the value is `false`, then executed only test tasks of the project for which its Kover task is called.
- */
- @get:Input
- public var runAllTestsForProjectTask: Boolean = false
-
- /**
- * Fully-qualified path of disabled projects.
- * It is filled in only automatically based on custom values in `disabledProjects'.
- */
- @get:Internal
- internal var disabledProjectsPaths: Set = emptySet()
-}
-
-public enum class CoverageEngine {
- INTELLIJ,
- JACOCO
-}
diff --git a/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt b/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt
index cf3caa4b..fa29c968 100644
--- a/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt
+++ b/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt
@@ -1,13 +1,13 @@
/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
-@file:Suppress("RedundantVisibilityModifier")
-
package kotlinx.kover.api
+import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.*
-import org.gradle.api.provider.*
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import java.io.*
@@ -15,18 +15,19 @@ import java.io.*
* Extension for Kover plugin that additionally configures test tasks and
* runs them with coverage agent to generate coverage execution data.
*/
-open class KoverTaskExtension(objects: ObjectFactory) {
+public open class KoverTaskExtension(objects: ObjectFactory) {
/**
* Specifies whether instrumentation is disabled for an extended test task.
*/
@get:Input
- public var isDisabled: Boolean = false
+ @get:JvmName("getIsDisabled")
+ public val isDisabled: Property = objects.property(Boolean::class.java)
/**
* Specifies file path of generated binary file with coverage data.
*/
@get:OutputFile
- public val binaryReportFile: Property = objects.property(File::class.java)
+ public val reportFile: RegularFileProperty = objects.fileProperty()
/**
* Specifies class instrumentation inclusion rules.
@@ -37,7 +38,7 @@ open class KoverTaskExtension(objects: ObjectFactory) {
* It's possible to use `*` and `?` wildcards.
*/
@get:Input
- public var includes: List = emptyList()
+ public val includes: ListProperty = objects.listProperty(String::class.java)
/**
* Specifies class instrumentation exclusion rules.
@@ -48,5 +49,16 @@ open class KoverTaskExtension(objects: ObjectFactory) {
* It's possible to use `*` and `?` wildcards.
*/
@get:Input
- public var excludes: List = emptyList()
+ public val excludes: ListProperty = objects.listProperty(String::class.java)
+
+
+ // DEPRECATIONS
+ // TODO delete in 0.7 version
+ @get:Internal
+ @Deprecated(
+ message = "Property was renamed in Kover API version 2",
+ replaceWith = ReplaceWith("reportFile"),
+ level = DeprecationLevel.ERROR
+ )
+ public val binaryReportFile: Property = objects.property(File::class.java)
}
diff --git a/src/main/kotlin/kotlinx/kover/api/Rules.kt b/src/main/kotlin/kotlinx/kover/api/Rules.kt
deleted file mode 100644
index 18ff50da..00000000
--- a/src/main/kotlin/kotlinx/kover/api/Rules.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.kover.api
-
-import org.gradle.api.*
-import org.gradle.api.tasks.*
-import javax.annotation.*
-
-/**
- * Simple verification rule for code coverage.
- * Works only with lines counter.
- */
-public interface VerificationRule {
- /**
- * Unique ID of the rule.
- */
- @get:Internal
- public val id: Int
-
- /**
- * Custom name of the rule.
- */
- @get:Input
- @get:Nullable
- @get:Optional
- public var name: String?
-
- /**
- * Added constraints on the values of code coverage metrics.
- */
- @get:Input
- public val bounds: List
-
- /**
- * Add a constraint on the value of the code coverage metric.
- */
- public fun bound(configureBound: Action)
-}
-
-public interface VerificationBound {
- /**
- * ID of the bound unique in the rule.
- */
- @get:Internal
- public val id: Int
-
- /**
- * Minimal value to compare with counter value.
- */
- @get:Input
- @get:Nullable
- @get:Optional
- public var minValue: Int?
-
- /**
- * Maximal value to compare with counter value.
- */
- @get:Input
- @get:Nullable
- @get:Optional
- public var maxValue: Int?
-
- /**
- * Type of lines counter value to compare with minimal and maximal values if them defined.
- * Default is [VerificationValueType.COVERED_LINES_PERCENTAGE]
- */
- @get:Input
- public var valueType: VerificationValueType
-}
-
-/**
- * Type of lines counter value to compare with minimal and maximal values if them defined.
- */
-public enum class VerificationValueType {
- COVERED_LINES_COUNT,
- MISSED_LINES_COUNT,
- COVERED_LINES_PERCENTAGE
-}
diff --git a/src/main/kotlin/kotlinx/kover/appliers/KoverMergedApplier.kt b/src/main/kotlin/kotlinx/kover/appliers/KoverMergedApplier.kt
new file mode 100644
index 00000000..0424a9e2
--- /dev/null
+++ b/src/main/kotlin/kotlinx/kover/appliers/KoverMergedApplier.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.kover.appliers
+
+import kotlinx.kover.api.*
+import kotlinx.kover.api.KoverNames.CHECK_TASK_NAME
+import kotlinx.kover.api.KoverNames.CONFIGURATION_NAME
+import kotlinx.kover.api.KoverNames.MERGED_HTML_REPORT_TASK_NAME
+import kotlinx.kover.api.KoverNames.MERGED_REPORT_TASK_NAME
+import kotlinx.kover.api.KoverNames.MERGED_VERIFY_TASK_NAME
+import kotlinx.kover.api.KoverNames.MERGED_XML_REPORT_TASK_NAME
+import kotlinx.kover.api.KoverNames.VERIFICATION_GROUP
+import kotlinx.kover.api.KoverPaths.MERGED_HTML_REPORT_DEFAULT_PATH
+import kotlinx.kover.api.KoverPaths.MERGED_VERIFICATION_REPORT_DEFAULT_PATH
+import kotlinx.kover.api.KoverPaths.MERGED_XML_REPORT_DEFAULT_PATH
+import kotlinx.kover.tasks.*
+import org.gradle.api.*
+import org.gradle.api.file.*
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.testing.*
+import org.gradle.configurationcache.extensions.*
+
+internal fun Project.applyMerged() {
+ val extension = createMergedExtension()
+ afterEvaluate(ProcessMergeExtensionAction(extension))
+}
+
+private fun Project.createMergedExtension(): KoverMergedConfig {
+ val extension = extensions.create(KoverNames.MERGED_EXTENSION_NAME, KoverMergedConfig::class.java, objects)
+ extension.isEnabled.set(false)
+ extension.filters.classes.set(KoverClassFilter())
+ extension.filters.projects.set(KoverProjectsFilter())
+ extension.xmlReport.onCheck.set(false)
+ extension.xmlReport.reportFile.set(layout.buildDirectory.file(MERGED_XML_REPORT_DEFAULT_PATH))
+ extension.xmlReport.classFilter.set(extension.filters.classes)
+ extension.htmlReport.onCheck.set(false)
+ extension.htmlReport.reportDir.set(layout.buildDirectory.dir(MERGED_HTML_REPORT_DEFAULT_PATH))
+ extension.htmlReport.classFilter.set(extension.filters.classes)
+ extension.verify.onCheck.set(false)
+ return extension
+}
+
+private class ProcessMergeExtensionAction(private val extension: KoverMergedConfig) : Action {
+ override fun execute(container: Project) {
+ // don't create tasks if merge wasn't enabled
+ if (!extension.isEnabled.get()) {
+ return
+ }
+
+ val extensionByProject = container.projectsExtensionsProvider(extension, container.allprojects)
+ val engineProvider = container.engineProvider(extensionByProject)
+ val testsProvider = container.instrumentedTasksProvider(extensionByProject)
+
+ val xmlTask = container.createMergedTask(
+ MERGED_XML_REPORT_TASK_NAME,
+ extension.xmlReport.classFilter,
+ extensionByProject,
+ engineProvider,
+ testsProvider,
+ { e -> e.xmlReport.filters.sourceSets.get() }
+ ) {
+ it.reportFile.set(extension.xmlReport.reportFile)
+ it.description = "Generates code coverage XML report for all enabled test tasks in specified projects."
+ }
+
+ val htmlTask = container.createMergedTask(
+ MERGED_HTML_REPORT_TASK_NAME,
+ extension.htmlReport.classFilter,
+ extensionByProject,
+ engineProvider,
+ testsProvider,
+ { e -> e.htmlReport.taskFilters.sourceSets.get() }
+ ) {
+ it.reportDir.set(extension.htmlReport.reportDir)
+ it.description = "Generates code coverage HTML report for all enabled test tasks in specified projects."
+ }
+
+ val verifyTask = container.createMergedTask(
+ MERGED_VERIFY_TASK_NAME,
+ extension.filters.classes,
+ extensionByProject,
+ engineProvider,
+ testsProvider,
+ { e -> e.filters.sourceSets.get() }
+ ) {
+ it.rules.set(extension.verify.rules)
+ it.resultFile.set(container.layout.buildDirectory.file(MERGED_VERIFICATION_REPORT_DEFAULT_PATH))
+ it.description = "Verifies code coverage metrics of specified projects based on specified rules."
+ }
+ // TODO `onlyIf` block moved out from config lambda because of bug in Kotlin compiler - it implicitly adds closure on `Project` inside onlyIf's lambda
+ verifyTask.onlyIf { extension.verify.hasActiveRules() }
+
+
+ container.tasks.create(MERGED_REPORT_TASK_NAME) {
+ it.group = VERIFICATION_GROUP
+ it.dependsOn(xmlTask)
+ it.dependsOn(htmlTask)
+ it.description = "Generates code coverage HTML and XML reports for all enabled test tasks in one project."
+ }
+
+ container.tasks.configureEach {
+ if (it.name == CHECK_TASK_NAME) {
+ it.dependsOn(container.provider {
+ val tasks = mutableListOf()
+ if (extension.xmlReport.onCheck.get()) {
+ tasks += xmlTask
+ }
+ if (extension.htmlReport.onCheck.get()) {
+ tasks += htmlTask
+ }
+ if (extension.verify.onCheck.get() && extension.verify.hasActiveRules()) {
+ // don't add dependency if there is no active verification rules https://github.com/Kotlin/kotlinx-kover/issues/168
+ tasks += verifyTask
+ }
+ tasks
+ })
+ }
+ }
+ }
+}
+
+private inline fun Project.createMergedTask(
+ taskName: String,
+ classFilter: Provider,
+ extensionByProject: Provider