Skip to content

Commit 56192f1

Browse files
committed
#171 Migrate CoverageChecker (and its test) from Groovy to Kotlin
1 parent 6e95ca2 commit 56192f1

File tree

8 files changed

+144
-132
lines changed

8 files changed

+144
-132
lines changed

Diff for: build.gradle.kts

+15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
`java-gradle-plugin`
33
id("com.gradle.plugin-publish") version "0.15.0"
44
id("org.jetbrains.gradle.plugin.idea-ext") version "1.0"
5+
kotlin("jvm") version "1.5.31"
56
}
67

78
repositories {
@@ -38,17 +39,30 @@ pluginBundle {
3839
}
3940

4041
apply(plugin = "maven-publish")
42+
// TODO #171 remove once all groovy classes are migrated to kotlin
4143
apply(plugin = "groovy")
4244

4345
java {
4446
sourceCompatibility = JavaVersion.VERSION_1_8
4547
targetCompatibility = JavaVersion.VERSION_1_8
4648
}
4749

50+
// TODO #171 remove once all groovy classes are migrated to kotlin
51+
val compileKotlinTask = tasks.compileKotlin.get()
52+
tasks {
53+
named<GroovyCompile>("compileGroovy") {
54+
dependsOn(compileKotlin)
55+
classpath = classpath.plus(files(compileKotlinTask.destinationDirectory))
56+
}
57+
}
58+
4859
dependencies {
4960
compileOnly("org.scoverage:scalac-scoverage-plugin_2.13:1.4.2")
5061
implementation(group = "commons-io", name = "commons-io", version = "2.6")
5162

63+
implementation(kotlin("script-runtime"))
64+
testImplementation(kotlin("test"))
65+
5266
testImplementation("junit:junit:4.12")
5367
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
5468
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.5.2")
@@ -122,6 +136,7 @@ gradlePlugin {
122136
testSourceSets(sourceSets["functionalTest"], sourceSets["crossScalaVersionTest"])
123137
}
124138

139+
// TODO #171 remove once all groovy classes are migrated to kotlin
125140
val groovydocJar by tasks.registering(Jar::class) {
126141
from("$buildDir/docs/groovydoc")
127142
classifier = "groovydoc"

Diff for: src/functionalTest/java/org/scoverage/MultipleCheckTasksTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.junit.Assert;
44
import org.junit.Test;
5+
import org.scoverage.CoverageChecker.CoverageType;
56

67
public abstract class MultipleCheckTasksTest extends ScoverageFunctionalTest {
78

@@ -88,4 +89,4 @@ protected void assertResult(AssertableBuildResult result) {
8889
assertOutput(result, ScoverageExtension.DEFAULT_COVERAGE_TYPE, ScoverageExtension.DEFAULT_MINIMUM_RATE);
8990
}
9091
}
91-
}
92+
}

Diff for: src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.gradle.testkit.runner.GradleRunner;
88
import org.gradle.testkit.runner.TaskOutcome;
99
import org.junit.Assert;
10+
import org.scoverage.CoverageChecker.CoverageType;
1011
import org.xml.sax.SAXException;
1112

1213
import java.io.File;

Diff for: src/main/groovy/org/scoverage/CoverageChecker.groovy

-98
This file was deleted.

Diff for: src/main/groovy/org/scoverage/ScoverageExtension.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.gradle.api.plugins.JavaPlugin
55
import org.gradle.api.plugins.scala.ScalaPlugin
66
import org.gradle.api.provider.ListProperty
77
import org.gradle.api.provider.Property
8+
import org.scoverage.CoverageChecker.CoverageType
89

910
/**
1011
* Defines a new SourceSet for the code to be instrumented.

Diff for: src/main/groovy/org/scoverage/ScoveragePlugin.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.gradle.api.tasks.SourceSet
1111
import org.gradle.api.tasks.TaskProvider
1212
import org.gradle.api.tasks.scala.ScalaCompile
1313
import org.gradle.api.tasks.testing.Test
14+
import org.scoverage.CoverageChecker.CoverageChecker
1415

1516
import java.nio.file.Files
1617
import java.util.concurrent.ConcurrentHashMap

Diff for: src/main/kotlin/org/scoverage/CoverageChecker.kts

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.scoverage
2+
3+
import groovy.xml.XmlParser
4+
import org.gradle.api.GradleException
5+
import org.gradle.internal.impldep.com.google.common.annotations.VisibleForTesting
6+
import org.slf4j.Logger
7+
import java.io.File
8+
import java.io.FileNotFoundException
9+
10+
import java.text.DecimalFormat
11+
import java.text.NumberFormat
12+
import java.util.*
13+
import kotlin.jvm.Throws
14+
15+
/**
16+
* Handles different types of coverage Scoverage can measure.
17+
*
18+
* @param configurationName Name of enum option the way it appears in the build configuration.
19+
* @param fileName Name of file with coverage data.
20+
* @param paramName Name of param in XML file with coverage value.
21+
* @param factor Used to normalize coverage value.
22+
*/
23+
enum class CoverageType(
24+
private val configurationName: String,
25+
val fileName: String,
26+
val paramName: String,
27+
private val factor: Double
28+
) {
29+
Line("Line", "cobertura.xml", "line-rate", 1.0),
30+
Statement("Statement", "scoverage.xml", "statement-rate", 100.0),
31+
Branch("Branch", "scoverage.xml", "branch-rate", 100.0);
32+
33+
/** Normalize coverage value to [0, 1] */
34+
fun normalize(value: Double): Double = value / factor
35+
36+
companion object {
37+
fun find(configurationName: String): CoverageType? {
38+
return values().find { it -> it.configurationName.lowercase() == configurationName.lowercase() }
39+
}
40+
}
41+
}
42+
43+
/**
44+
* Throws a GradleException if overall coverage dips below the configured percentage.
45+
*/
46+
class CoverageChecker(private val logger: Logger) {
47+
48+
@JvmOverloads
49+
@Throws(GradleException::class)
50+
fun checkLineCoverage(
51+
reportDir: File,
52+
coverageType: CoverageType,
53+
minimumRate: Double,
54+
nf: NumberFormat = NumberFormat.getInstance(Locale.getDefault())
55+
) {
56+
logger.info("Checking coverage. Type: {}. Minimum rate: {}", coverageType, minimumRate)
57+
58+
val parser = XmlParser()
59+
parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
60+
parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
61+
62+
val df = DecimalFormat("#.##")
63+
64+
try {
65+
val reportFile = File(reportDir, coverageType.fileName)
66+
val xml = parser.parse(reportFile)
67+
val coverageValue: Double = nf.parse(xml.attribute(coverageType.paramName) as String).toDouble()
68+
val overallRate: Double = coverageType.normalize(coverageValue)
69+
70+
val difference = minimumRate - overallRate
71+
72+
if (difference > 1e-7) {
73+
val iss = df.format(overallRate * 100)
74+
val needed = df.format(minimumRate * 100)
75+
throw GradleException(errorMsg(iss, needed, coverageType))
76+
}
77+
} catch (fnfe: FileNotFoundException) {
78+
throw GradleException(fileNotFoundErrorMsg(coverageType), fnfe)
79+
}
80+
}
81+
82+
companion object {
83+
@VisibleForTesting
84+
internal fun errorMsg(actual: String, expected: String, type: CoverageType): String {
85+
return "Only $actual% of project is covered by tests instead of $expected% (coverageType: $type)"
86+
}
87+
88+
@VisibleForTesting
89+
internal fun fileNotFoundErrorMsg(coverageType: CoverageType): String {
90+
return "Coverage file (type: $coverageType) not found, check your configuration."
91+
}
92+
}
93+
94+
}

0 commit comments

Comments
 (0)