Skip to content

Commit

Permalink
Added ability to generate reports even if there are no tests in the p…
Browse files Browse the repository at this point in the history
…roject

Resolves #409

PR #411
  • Loading branch information
shanshin committed Jun 26, 2023
1 parent 6575caf commit 826b909
Show file tree
Hide file tree
Showing 15 changed files with 164 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ internal class AccessorsTests {
val result = build.runWithParams("custom")

// skipped because there is no tests, but tasks are triggered
assertEquals("SKIPPED", result.taskOutcome(":koverXmlReport"))
assertEquals("SKIPPED", result.taskOutcome(":koverHtmlReport"))
assertEquals("SKIPPED", result.taskOutcome(":koverVerify"))
assertEquals("SUCCESS", result.taskOutcome(":koverXmlReport"))
assertEquals("SUCCESS", result.taskOutcome(":koverHtmlReport"))
assertEquals("SUCCESS", result.taskOutcome(":koverVerify"))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal class MultiProjectTests {
}

@SlicedGeneratedTest(allTypes = true, allTools = true)
fun BuildConfigurator.testDisabledKover() {
fun SlicedBuildConfigurator.testDisabledKover() {
addProjectWithKover(subprojectPath) {
sourcesFrom("multiproject-common")
kover {
Expand All @@ -76,16 +76,12 @@ internal class MultiProjectTests {

run("koverXmlReport", "koverHtmlReport", "koverVerify") {
checkDefaultBinReport(false)
taskNotCalled(defaultTestTaskName(slice.type))

checkOutcome("koverHtmlReport", "SKIPPED")
checkOutcome("koverXmlReport", "SKIPPED")
checkOutcome("koverVerify", "SKIPPED")

subproject(subprojectPath) {
checkDefaultBinReport(false)
checkOutcome("koverHtmlReport", "SKIPPED")
checkOutcome("koverXmlReport", "SKIPPED")
checkOutcome("koverVerify", "SKIPPED")
taskNotCalled(defaultTestTaskName(slice.type))
}
}
}
Expand Down Expand Up @@ -113,16 +109,14 @@ internal class MultiProjectTests {

run("koverXmlReport", "koverHtmlReport", "koverVerify") {
checkDefaultBinReport(false)

checkOutcome("koverHtmlReport", "SKIPPED")
checkOutcome("koverXmlReport", "SKIPPED")
checkOutcome("koverVerify", "SKIPPED")
taskNotCalled(defaultTestTaskName(slice.type))

subproject(subprojectPath) {
checkDefaultBinReport(false)
checkOutcome("koverHtmlReport", "SKIPPED")
checkOutcome("koverXmlReport", "SKIPPED")
checkOutcome("koverVerify", "SKIPPED")
taskNotCalled(defaultTestTaskName(slice.type))
checkOutcome("koverXmlReport", "SUCCESS")
checkOutcome("koverHtmlReport", "SUCCESS")
checkOutcome("koverVerify", "SUCCESS")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package kotlinx.kover.gradle.plugin.test.functional.cases

import kotlinx.kover.gradle.plugin.test.functional.framework.checker.createCheckerContext
import kotlinx.kover.gradle.plugin.test.functional.framework.checker.defaultXmlReport
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.BuildSource
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.buildFromTemplate
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.runWithParams
import org.junit.jupiter.api.Test
import kotlin.test.assertContains
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class NoTestReportsTests {
@Test
fun testNoTestsJvm() {
val build = buildFromTemplate("no-tests-jvm")
check(build)
checkWithVerify(build)
}

@Test
fun testNoTestsMpp() {
val build = buildFromTemplate("no-tests-mpp")
check(build)
checkWithVerify(build)
}

private fun check(buildSource: BuildSource) {
val build = buildSource.generate()
val buildResult = build.runWithParams("koverXmlReport", "koverHtmlReport")
val checkerContext = build.createCheckerContext(buildResult)

checkerContext.xml(defaultXmlReport()) {
classCounter("kotlinx.kover.templates.ExampleClass").assertFullyMissed()
}
assertTrue(buildResult.isSuccessful)
}

private fun checkWithVerify(buildSource: BuildSource) {
val build = buildSource.generate()
val buildResult = build.runWithParams("koverXmlReport", "koverHtmlReport", "koverVerify")
val checkerContext = build.createCheckerContext(buildResult)

checkerContext.xml(defaultXmlReport()) {
classCounter("kotlinx.kover.templates.ExampleClass").assertFullyMissed()
}
checkerContext.checkHtmlReport()
assertFalse(buildResult.isSuccessful)
assertContains(buildResult.output, "Rule violated: lines covered percentage is 0.000000, but expected minimum is 50")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ internal class TaskFilteringTests {
// compile tasks must be invoked
checkOutcome("compileKotlin", "SUCCESS")
checkOutcome("compileJava", "NO-SOURCE")
checkOutcome("koverXmlReport", "SKIPPED")

taskNotCalled(defaultTestTaskName(slice.type))

// reason must be printed
taskOutput("koverXmlReport") {
contains("Task '${defaultTestTaskName(slice.type)}' will be skipped because no tests were executed")
}

// if task `test` is excluded from instrumentation then the binary report is not created for it
checkDefaultBinReport(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ internal inline fun CheckerContext.check(
}
}

internal fun File.createCheckerContext(result: BuildResult): CheckerContext {
return CheckerContextImpl(this.analyzeProject(), result)
internal fun GradleBuild.createCheckerContext(result: BuildResult): CheckerContext {
return CheckerContextImpl(this.targetDir.analyzeProject(), result)
}

internal fun File.analyzeProject(): ProjectAnalysisData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ ${this.targetDir.buildScript()}
when (step) {
is TestGradleStep -> {
val runResult = this.runWithParams(step.args)
targetDir.createCheckerContext(runResult).check(description, step.errorExpected, step.checker)
createCheckerContext(runResult).check(description, step.errorExpected, step.checker)
}

is TestFileEditStep -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ internal abstract class DirectoryBasedGradleTest : BeforeTestExecutionCallback,
logInfo("Before building ${args.buildSource.buildType} '${args.buildSource.buildName}' in target directory ${build.targetDir.uri}")

val runResult = build.runWithParams(args.gradleArgs)
val checkerContext = build.targetDir.createCheckerContext(runResult)
val checkerContext = build.createCheckerContext(runResult)

val store = context.getStore(ExtensionContext.Namespace.GLOBAL)
store.put(DIR_PARAM, build)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
kotlin("jvm") version "1.8.20"
id("org.jetbrains.kotlinx.kover")
}

repositories {
mavenCentral()
}

koverReport {
defaults {
verify {
rule {
minBound(50)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}

rootProject.name = "no-tests-jvm"
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kotlinx.kover.templates

class ExampleClass {
fun formatInt(i: Int): String {
if (i == 0) return "ZERO"
return if (i > 0) {
"POSITIVE=$i"
} else {
"NEGATIVE=${-i}"
}
}

fun printClass() {
val name = this::class.qualifiedName
println(name)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
kotlin("multiplatform") version ("1.8.20")
id ("org.jetbrains.kotlinx.kover") version "0.7.1"
}

kotlin {
jvm()
jvmToolchain(8)
}

repositories {
mavenCentral()
}

/*
* Kover configs
*/

koverReport {
defaults {
verify {
rule {
minBound(50)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}

rootProject.name = "no-tests-mpp"
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kotlinx.kover.templates

class ExampleClass {
fun formatInt(i: Int): String {
if (i == 0) return "ZERO"
return if (i > 0) {
"POSITIVE=$i"
} else {
"NEGATIVE=${-i}"
}
}

fun printClass() {
val name = this::class.qualifiedName
println(name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,6 @@ internal sealed class ReportsVariantApplier(

localArtifact.set(artifactGenTask.flatMap { task -> task.artifactFile })
externalArtifacts.from(dependencies)

// task can't be executed if where is no raw report files (no any executed test task)
onlyIf { hasBinReportsAndLog() }
}
return task
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,6 @@ internal abstract class AbstractKoverReportTask : DefaultTask() {
@get:Inject
protected abstract val workerExecutor: WorkerExecutor

fun hasBinReportsAndLog(): Boolean {
val hasReports = collectAllFiles().reports.isNotEmpty()
if (!hasReports) {
logger.lifecycle("Task '$name' will be skipped because no tests were executed")
}
return hasReports
}

protected fun context(): ReportContext {
val services = GradleReportServices(workerExecutor, ant, obj)
return ReportContext(collectAllFiles(), filters.get(), reportClasspath, temporaryDir, projectPath, services)
Expand Down

0 comments on commit 826b909

Please sign in to comment.