Skip to content

Commit 1823f83

Browse files
authored
Implemented warn on verification error
Added property to suppress task execution failure if there has been a violation of the verification rules Resolves #339 PR #577
1 parent 8af377c commit 1823f83

File tree

19 files changed

+257
-82
lines changed

19 files changed

+257
-82
lines changed

Diff for: kover-features-jvm/build.gradle.kts

+5-8
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,10 @@ extensions.configure<Kover_publishing_conventions_gradle.KoverPublicationExtensi
2828
description.set("Implementation of calling the main features of Kover programmatically")
2929
}
3030

31-
java {
32-
sourceCompatibility = JavaVersion.VERSION_1_8
33-
targetCompatibility = JavaVersion.VERSION_1_8
34-
}
35-
36-
tasks.compileJava {
37-
options.release.set(8)
31+
kotlin {
32+
jvmToolchain {
33+
languageVersion.set(JavaLanguageVersion.of(8))
34+
}
3835
}
3936

4037
// Workaround:
@@ -48,7 +45,7 @@ afterEvaluate {
4845
jvmTarget.set(JvmTarget.JVM_1_8)
4946
languageVersion.set(KotlinVersion.KOTLIN_1_5)
5047
apiVersion.set(KotlinVersion.KOTLIN_1_5)
51-
freeCompilerArgs.addAll("-Xsuppress-version-warnings", "-Xjdk-release=1.8")
48+
freeCompilerArgs.addAll("-Xsuppress-version-warnings")
5249
}
5350
}
5451
}

Diff for: kover-gradle-plugin/api/kover-gradle-plugin.api

+1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantSour
230230
}
231231

232232
public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerificationRulesConfig {
233+
public abstract fun getWarningInsteadOfFailure ()Lorg/gradle/api/provider/Property;
233234
public abstract fun rule (Ljava/lang/String;Lorg/gradle/api/Action;)V
234235
public abstract fun rule (Lorg/gradle/api/Action;)V
235236
}

Diff for: kover-gradle-plugin/docs/configuring.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ koverReport {
2929

3030
// verification rules for verification task
3131
verify {
32+
// fail on verification error
33+
warningInsteadOfFailure = false
34+
3235
// add common verification rule
3336
rule {
3437
// check this rule during verification
3538
disabled = false
36-
39+
3740
// specify the code unit for which coverage will be aggregated
3841
groupBy = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.APPLICATION
3942

@@ -162,6 +165,9 @@ koverReport {
162165
verify {
163166
// verify coverage when running the `check` task
164167
onCheck = true
168+
169+
// fail on verification error
170+
warningInsteadOfFailure = false
165171
}
166172

167173
// configure coverage logging
@@ -222,6 +228,9 @@ koverReport {
222228

223229
// verification rules for verification tasks in all variants
224230
verify {
231+
// fail on verification error
232+
warningInsteadOfFailure = false
233+
225234
// add common verification rule
226235
rule {
227236
// check this rule during verification
@@ -355,6 +364,10 @@ koverReport {
355364
// verify coverage when running the `check` task
356365
onCheck = true
357366

367+
368+
// fail on verification error
369+
warningInsteadOfFailure = false
370+
358371
// add verification rule
359372
rule {
360373
// check this rule during verification

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/BuildCacheRelocationTests.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ class BuildCacheRelocationTests {
5959
assertEquals("FROM-CACHE", result2.taskOutcome(":koverXmlReport"))
6060
assertEquals("FROM-CACHE", result2.taskOutcome(":koverHtmlReport"))
6161
assertEquals("FROM-CACHE", result2.taskOutcome(":koverBinaryReport"))
62-
assertEquals("FROM-CACHE", result2.taskOutcome(":koverVerify"))
62+
assertEquals("FROM-CACHE", result2.taskOutcome(":koverCachedVerify"))
63+
// should always be executed
64+
assertEquals("SUCCESS", result2.taskOutcome(":koverVerify"))
6365
} catch (e: Throwable) {
6466
throw AssertionError("Build log \n${result2.output}",e)
6567
}

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VerificationTests.kt

+65
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,71 @@ Rule violated: lines missed count for package 'org.jetbrains.kover.test.function
175175
}
176176
}
177177

178+
@SlicedGeneratedTest(allLanguages = true, allTools = true)
179+
fun BuildConfigurator.testRootRulesWarnOnFail() {
180+
addProjectWithKover {
181+
sourcesFrom("simple")
182+
183+
kover {
184+
reports {
185+
verify {
186+
warningInsteadOfFailure.set(true)
187+
rule("root rule") {
188+
bound {
189+
minValue.set(99)
190+
}
191+
}
192+
}
193+
}
194+
}
195+
}
196+
197+
run("koverVerify", errorExpected = false) {
198+
taskOutput(":koverVerify") {
199+
match {
200+
assertContains("Kover Verification Error")
201+
assertKoverContains("Rule 'root rule' violated: lines covered percentage is *, but expected minimum is 99\n")
202+
assertJaCoCoContains("Rule violated: lines covered percentage is *, but expected minimum is 99.0000\n")
203+
}
204+
}
205+
}
206+
}
207+
208+
@SlicedGeneratedTest(allLanguages = true, allTools = true)
209+
fun BuildConfigurator.testRootRulesOverrideWarnOnFail() {
210+
addProjectWithKover {
211+
sourcesFrom("simple")
212+
213+
kover {
214+
reports {
215+
verify {
216+
warningInsteadOfFailure.set(false)
217+
}
218+
total {
219+
verify {
220+
warningInsteadOfFailure.set(true)
221+
rule("root rule") {
222+
bound {
223+
minValue.set(99)
224+
}
225+
}
226+
}
227+
}
228+
}
229+
}
230+
}
231+
232+
run("koverVerify", errorExpected = false) {
233+
taskOutput(":koverVerify") {
234+
match {
235+
assertContains("Kover Verification Error")
236+
assertKoverContains("Rule 'root rule' violated: lines covered percentage is *, but expected minimum is 99\n")
237+
assertJaCoCoContains("Rule violated: lines covered percentage is *, but expected minimum is 99.0000\n")
238+
}
239+
}
240+
}
241+
}
242+
178243
@SlicedGeneratedTest(allLanguages = true, allTools = true)
179244
fun BuildConfigurator.testRootRulesOverride() {
180245
addProjectWithKover {

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/Checker.kt

+29
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ private class CheckerContextImpl(
117117
VerifyReportCheckerImpl(this, verificationResultFile.readText()).checker()
118118
}
119119

120+
override fun String.match(matcher: TextMatcher.() -> Unit) {
121+
TextMatcherImpl(this@CheckerContextImpl, this).matcher()
122+
}
123+
120124
override fun checkDefaultBinReport(mustExist: Boolean) {
121125
if (mustExist) {
122126
file(defaultBinReport) {
@@ -416,6 +420,31 @@ private class VerifyReportCheckerImpl(val context: CheckerContextImpl, val conte
416420
}
417421
}
418422

423+
private class TextMatcherImpl(val context: CheckerContextImpl, val content: String) : TextMatcher {
424+
override fun assertContains(expected: String) {
425+
val regex = KoverFeatures.koverWildcardToRegex(expected).toRegex()
426+
if (!content.contains(regex)) {
427+
throw AssertionError("Unexpected text.\n\tActual\n[\n$content\n]\nExpected regex\n[\n$expected\n]")
428+
}
429+
}
430+
431+
override fun assertKoverContains(expected: String) {
432+
if (context.project.toolVariant.vendor != CoverageToolVendor.KOVER) return
433+
val regex = KoverFeatures.koverWildcardToRegex(expected).toRegex()
434+
if (!content.contains(regex)) {
435+
throw AssertionError("Unexpected text for Kover Tool.\n\tActual\n[\n$content\n]\nExpected regex\n[\n$expected\n]")
436+
}
437+
}
438+
439+
override fun assertJaCoCoContains(expected: String) {
440+
if (context.project.toolVariant.vendor != CoverageToolVendor.JACOCO) return
441+
val regex = KoverFeatures.koverWildcardToRegex(expected).toRegex()
442+
if (!content.contains(regex)) {
443+
throw AssertionError("Unexpected text for JaCoCo Tool.\n\tActual\n[\n$content\n]\nExpected regex\n[\n$expected\n]")
444+
}
445+
}
446+
}
447+
419448
private fun Element.filter(tag: String, attributeName: String, attributeValue: String): Element? {
420449
val elements = getElementsByTagName(tag)
421450
for (i in 0 until elements.length) {

Diff for: kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/CheckerTypes.kt

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ internal interface CheckerContext {
3333

3434
fun verification(checker: VerifyReportChecker.() -> Unit)
3535

36+
fun String.match(matcher: TextMatcher.() -> Unit)
37+
3638
val defaultBinReport: String
3739

3840
fun checkXmlReport(variantName: String = "", mustExist: Boolean = true)
@@ -89,6 +91,12 @@ internal interface VerifyReportChecker {
8991
fun assertJaCoCoResult(expected: String)
9092
}
9193

94+
internal interface TextMatcher {
95+
fun assertContains(expected: String)
96+
fun assertKoverContains(expected: String)
97+
fun assertJaCoCoContains(expected: String)
98+
}
99+
92100
internal interface XmlReportChecker {
93101
fun classCounter(className: String, type: String = "INSTRUCTION"): Counter
94102
fun methodCounter(className: String, methodName: String, type: String = "INSTRUCTION"): Counter

Diff for: kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/tasks/VariantReportsSet.kt

+29-7
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal class VariantReportsSet(
3535
private val htmlTask: TaskProvider<KoverHtmlTask>
3636
private val xmlTask: TaskProvider<KoverXmlTask>
3737
private val binTask: TaskProvider<KoverBinaryTask>
38-
private val verifyTask: TaskProvider<KoverVerifyTask>
38+
private val doVerifyTask: TaskProvider<KoverDoVerifyTask>
3939
private val logTask: TaskProvider<KoverFormatCoverageTask>
4040

4141
init {
@@ -52,10 +52,12 @@ internal class VariantReportsSet(
5252
"Task to generate binary coverage report in IntelliJ format for ${variantSuffix()}"
5353
)
5454

55-
verifyTask = project.tasks.createReportTask<KoverVerifyTask>(
56-
verifyTaskName(variantName),
55+
doVerifyTask = project.tasks.createReportTask<KoverDoVerifyTask>(
56+
verifyCachedTaskName(variantName),
5757
"Task to validate coverage bounding rules for ${variantSuffix()}"
5858
)
59+
val verifyTask = project.tasks.register<KoverVerifyTask>(verifyTaskName(variantName))
60+
5961
logTask = project.tasks.createReportTask<KoverFormatCoverageTask>(
6062
logTaskName(variantName),
6163
"Task to print coverage to log for ${variantSuffix()}"
@@ -93,20 +95,37 @@ internal class VariantReportsSet(
9395
if (run) listOf(binTask) else emptyList()
9496
}
9597

96-
verifyTask.configure {
98+
99+
doVerifyTask.configure {
97100
val resultRules = config.verify.rules
98101
val converted = resultRules.map { rules -> rules.map { it.convert() } }
99102

103+
filters.set((config.filters).convert())
104+
rules.addAll(converted)
105+
100106
// path can't be changed
101107
resultFile.convention(project.layout.buildDirectory.file(verificationErrorsPath(variantName)))
102108

103-
filters.set((config.filters).convert())
104-
rules.addAll(converted)
109+
description = "Cacheable task for performing verification for ${variantSuffix()}"
110+
}
111+
verifyTask.configure {
112+
warningInsteadOfFailure.convention(config.verify.warningInsteadOfFailure)
113+
errorFile.convention(doVerifyTask.flatMap { it.resultFile })
105114

106115
shouldRunAfter(htmlTask)
107116
shouldRunAfter(xmlTask)
108117
shouldRunAfter(binTask)
109118
shouldRunAfter(logTask)
119+
120+
dependsOn(doVerifyTask)
121+
122+
group = LifecycleBasePlugin.VERIFICATION_GROUP
123+
124+
// always execute
125+
outputs.upToDateWhen { false }
126+
127+
val koverDisabledProvider = koverDisabled
128+
onlyIf { !koverDisabledProvider.get() }
110129
}
111130
runOnCheck += config.verify.onCheck.map { run ->
112131
if (run) listOf(verifyTask) else emptyList()
@@ -117,6 +136,9 @@ internal class VariantReportsSet(
117136
onlyIf {
118137
fileWithMessage.asFile.get().exists()
119138
}
139+
140+
// always execute
141+
outputs.upToDateWhen { false }
120142
}
121143

122144
logTask.configure {
@@ -146,7 +168,7 @@ internal class VariantReportsSet(
146168
htmlTask.assign(variant)
147169
xmlTask.assign(variant)
148170
binTask.assign(variant)
149-
verifyTask.assign(variant)
171+
doVerifyTask.assign(variant)
150172
logTask.assign(variant)
151173
}
152174

Diff for: kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Naming.kt

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ internal fun xmlReportTaskName(variant: String) = "$XML_REPORT_NAME${variant.cap
8484
*/
8585
internal fun binaryReportTaskName(variant: String) = "$BINARY_REPORT_NAME${variant.capitalized()}"
8686

87+
/**
88+
* Name for cached verifying task for specified report namespace.
89+
*/
90+
internal fun verifyCachedTaskName(variant: String) = "koverCachedVerify${variant.capitalized()}"
8791
/**
8892
* Name for verifying task for specified report namespace.
8993
*/

Diff for: kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportsConfig.kt

+21
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ public interface KoverReportsConfig {
103103
* rule("custom rule name") {
104104
* // named verification rule
105105
* }
106+
*
107+
* // fail on verification error
108+
* warningInsteadOfFailure = false
106109
* }
107110
* ```
108111
*/
@@ -313,6 +316,9 @@ public interface KoverReportSetConfig {
313316
* rule("Custom Name") {
314317
* // ...
315318
* }
319+
*
320+
* // fail on verification error
321+
* warningInsteadOfFailure = false
316322
* }
317323
* ```
318324
*/
@@ -884,6 +890,9 @@ public interface KoverBinaryTaskConfig {
884890
* rule("Custom Name") {
885891
* // ...
886892
* }
893+
*
894+
* // fail on verification error
895+
* warningInsteadOfFailure = false
887896
* }
888897
* ```
889898
*/
@@ -910,6 +919,9 @@ public interface KoverVerifyTaskConfig: KoverVerificationRulesConfig {
910919
* rule("custom rule name") {
911920
* // named verification rule
912921
* }
922+
*
923+
* // fail on verification error
924+
* warningInsteadOfFailure = false
913925
* }
914926
* ```
915927
*/
@@ -926,6 +938,15 @@ public interface KoverVerificationRulesConfig {
926938
* The name will be displayed in case of a verification error if Kover Tool was used.
927939
*/
928940
public fun rule(name: String, config: Action<KoverVerifyRule>)
941+
942+
/**
943+
* In case of a verification error, print a message to the log with the warn level instead of the Gradle task execution error.
944+
*
945+
* Gradle task error if `false`, warn message if `true`.
946+
*
947+
* `false` by default.
948+
*/
949+
public val warningInsteadOfFailure: Property<Boolean>
929950
}
930951

931952
/**

0 commit comments

Comments
 (0)