[Gradle] Rework KotlinTestReport to be compatible with configuration cache
Minimal supported version is effectively changed to 6.1 due to usage of Gradle build services Relates to #KT-43605
This commit is contained in:
committed by
TeamCityServer
parent
3e09bb3d3f
commit
ef890464d8
+1
-1
@@ -22,7 +22,7 @@ open class KotlinAndroid36GradleIT : KotlinAndroid33GradleIT() {
|
||||
get() = AGPVersion.v3_6_0
|
||||
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("6.0")
|
||||
get() = GradleVersionRequired.AtLeast("6.1")
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ sealed class GradleVersionRequired(val minVersion: String, val maxVersion: Strin
|
||||
companion object {
|
||||
const val OLDEST_SUPPORTED = "5.3"
|
||||
|
||||
val FOR_MPP_SUPPORT = AtLeast("6.0")
|
||||
val FOR_MPP_SUPPORT = AtLeast("6.1")
|
||||
}
|
||||
|
||||
class Exact(version: String) : GradleVersionRequired(version, version)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class KlibBasedMppIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion = GradleVersionRequired.AtLeast("6.0")
|
||||
override val defaultGradleVersion = GradleVersionRequired.AtLeast("6.1")
|
||||
|
||||
companion object {
|
||||
private const val MODULE_GROUP = "com.example"
|
||||
|
||||
+2
-2
@@ -88,13 +88,11 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
|
||||
val appProject = transformProjectWithPluginsDsl(
|
||||
projectName = "app",
|
||||
directoryPrefix = rootProjectName,
|
||||
wrapperVersion = GradleVersionRequired.AtLeast("5.4")
|
||||
)
|
||||
|
||||
val libProject = transformProjectWithPluginsDsl(
|
||||
projectName = "lib",
|
||||
directoryPrefix = rootProjectName,
|
||||
wrapperVersion = GradleVersionRequired.AtLeast("5.4")
|
||||
)
|
||||
|
||||
libProject.gradleProperties().appendText(jsCompilerType(KotlinJsCompilerType.IR))
|
||||
@@ -300,6 +298,8 @@ class Kotlin2JsGradlePluginIT : AbstractKotlin2JsGradlePluginIT(false) {
|
||||
}
|
||||
|
||||
abstract class AbstractKotlin2JsGradlePluginIT(val irBackend: Boolean) : BaseGradleIT() {
|
||||
override val defaultGradleVersion = GradleVersionRequired.AtLeast("6.1")
|
||||
|
||||
override fun defaultBuildOptions(): BuildOptions =
|
||||
super.defaultBuildOptions().copy(
|
||||
jsIrBackend = irBackend,
|
||||
|
||||
+2
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.junit.Test
|
||||
|
||||
class KotlinJsIrLibraryGradlePluginIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion = GradleVersionRequired.AtLeast("6.1")
|
||||
|
||||
override fun defaultBuildOptions(): BuildOptions =
|
||||
super.defaultBuildOptions().copy(
|
||||
jsIrBackend = true,
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import org.junit.Test
|
||||
class CleanDataTaskIT : BaseGradleIT() {
|
||||
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("5.5.1")
|
||||
get() = GradleVersionRequired.AtLeast("6.1")
|
||||
|
||||
@Test
|
||||
fun testDownloadedFolderDeletion() {
|
||||
|
||||
+47
-51
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.testing.internal
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.execution.TaskExecutionGraph
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
@@ -47,9 +48,12 @@ open class KotlinTestReport : TestReport() {
|
||||
|
||||
@Transient
|
||||
private var parent: KotlinTestReport? = null
|
||||
private val parentPaths = project.provider {
|
||||
computeAllParentTasksPaths()
|
||||
}
|
||||
|
||||
// TODO: this field is used in task action when the task is aggregate report, which makes it incompatible with configuration cache
|
||||
@Internal
|
||||
@Transient
|
||||
val children = mutableListOf<TaskProvider<KotlinTestReport>>()
|
||||
|
||||
@Transient
|
||||
@@ -66,14 +70,24 @@ open class KotlinTestReport : TestReport() {
|
||||
@Input
|
||||
var ignoreFailures: Boolean = false
|
||||
|
||||
private val hasOwnFailedTests
|
||||
get() = failedTestsListener.hasOwnFailedTests ?: false
|
||||
private val testReportServiceProvider = TestReportService.registerIfAbsent(project.gradle)
|
||||
private val testReportService
|
||||
get() = testReportServiceProvider.get()
|
||||
|
||||
private val hasFailedTests: Boolean
|
||||
get() = hasOwnFailedTests || children.any { it.get().hasFailedTests }
|
||||
get() = testReportService.hasFailedTests(path)
|
||||
|
||||
private val suppressedRunningFailureListeners: MutableSet<SuppressedTestRunningFailureListener> = mutableSetOf()
|
||||
private val failedTestsListener = FailedTestListener(parentPaths, testReportServiceProvider)
|
||||
|
||||
private val failedTestsListener = FailedTestListener()
|
||||
private fun computeAllParentTasksPaths(): List<String> {
|
||||
val allParents = mutableListOf<String>()
|
||||
var cur: KotlinTestReport? = this
|
||||
while (cur != null) {
|
||||
allParents.add(cur.path)
|
||||
cur = cur.parent
|
||||
}
|
||||
return allParents
|
||||
}
|
||||
|
||||
fun addChild(childProvider: TaskProvider<KotlinTestReport>) {
|
||||
val child = childProvider.get()
|
||||
@@ -85,7 +99,7 @@ open class KotlinTestReport : TestReport() {
|
||||
reportOnChildTasks(childProvider)
|
||||
}
|
||||
|
||||
private fun reportOnChildTasks(childProvider: TaskProvider<KotlinTestReport>): Unit {
|
||||
private fun reportOnChildTasks(childProvider: TaskProvider<KotlinTestReport>) {
|
||||
val child = childProvider.get()
|
||||
|
||||
child.testTasks.forEach {
|
||||
@@ -101,9 +115,8 @@ open class KotlinTestReport : TestReport() {
|
||||
|
||||
task.addTestListener(failedTestsListener)
|
||||
if (task is KotlinTest) {
|
||||
val listener = SuppressedTestRunningFailureListener(task.path)
|
||||
val listener = SuppressedTestRunningFailureListener(parentPaths, task.path, testReportServiceProvider)
|
||||
task.addRunListener(listener)
|
||||
suppressedRunningFailureListeners.add(listener)
|
||||
}
|
||||
reportOn(task)
|
||||
|
||||
@@ -119,7 +132,7 @@ open class KotlinTestReport : TestReport() {
|
||||
}
|
||||
|
||||
private fun reportOn(task: AbstractTestTask) {
|
||||
reportOn(task.binResultsDir)
|
||||
reportOn(task.binaryResultsDirectory)
|
||||
}
|
||||
|
||||
open val htmlReportUrl: String?
|
||||
@@ -133,7 +146,6 @@ open class KotlinTestReport : TestReport() {
|
||||
fun checkFailedTests() {
|
||||
if (checkFailedTests) {
|
||||
checkSuppressedRunningFailures()
|
||||
|
||||
if (hasFailedTests) {
|
||||
if (ignoreFailures) {
|
||||
logger.warn(getFailingTestsMessage())
|
||||
@@ -155,27 +167,12 @@ open class KotlinTestReport : TestReport() {
|
||||
}
|
||||
|
||||
private fun checkSuppressedRunningFailures() {
|
||||
val allSuppressedRunningFailures = mutableListOf<Pair<String, Error>>()
|
||||
|
||||
fun visitSuppressedRunningFailures(report: KotlinTestReport) {
|
||||
report.suppressedRunningFailureListeners.forEach { listener ->
|
||||
listener.failures.forEach { failure ->
|
||||
allSuppressedRunningFailures.add(listener.taskPath to failure)
|
||||
}
|
||||
}
|
||||
|
||||
report.children.forEach {
|
||||
visitSuppressedRunningFailures(it.get())
|
||||
}
|
||||
}
|
||||
|
||||
visitSuppressedRunningFailures(this)
|
||||
|
||||
if (allSuppressedRunningFailures.isNotEmpty()) {
|
||||
val taskFailures = testReportService.getAggregatedTaskFailures(path)
|
||||
if (taskFailures.isNotEmpty()) {
|
||||
val allErrors = mutableListOf<Error>()
|
||||
val msg = buildString {
|
||||
appendln("Failed to execute all tests:")
|
||||
allSuppressedRunningFailures.groupBy { it.first }.forEach { path, errors ->
|
||||
taskFailures.groupBy { it.first }.forEach { (path, errors) ->
|
||||
append(path)
|
||||
append(": ")
|
||||
var first = true
|
||||
@@ -246,40 +243,39 @@ open class KotlinTestReport : TestReport() {
|
||||
task.reports.junitXml.isEnabled = false
|
||||
}
|
||||
|
||||
private class SuppressedTestRunningFailureListener(val taskPath: String) : KotlinTestRunnerListener {
|
||||
@Transient
|
||||
private var failuresMutable: MutableList<Error>? = mutableListOf()
|
||||
|
||||
val failures: List<Error>
|
||||
get() = failuresMutable ?: emptyList()
|
||||
|
||||
private class SuppressedTestRunningFailureListener(
|
||||
private val allListenedTaskParentsPaths: Provider<List<String>>,
|
||||
private val failedTaskPath: String,
|
||||
private val testReportServiceProvider: Provider<TestReportService>
|
||||
) : KotlinTestRunnerListener {
|
||||
override fun runningFailure(failure: Error) {
|
||||
var failures = failuresMutable
|
||||
if (failures == null) {
|
||||
// it is possible after deserialization
|
||||
failures = mutableListOf()
|
||||
failuresMutable = failures
|
||||
allListenedTaskParentsPaths.get().forEach {
|
||||
testReportServiceProvider.get().reportFailure(failedTaskPath, it, failure)
|
||||
}
|
||||
failures.add(failure)
|
||||
}
|
||||
}
|
||||
|
||||
private class FailedTestListener : TestListener {
|
||||
@Transient
|
||||
var hasOwnFailedTests: Boolean? = false
|
||||
|
||||
override fun beforeTest(testDescriptor: TestDescriptor) {
|
||||
}
|
||||
private class FailedTestListener(
|
||||
private val allListenedTaskParentsPaths: Provider<List<String>>,
|
||||
private val testReportServiceProvider: Provider<TestReportService>
|
||||
) : TestListener {
|
||||
override fun beforeTest(testDescriptor: TestDescriptor) {}
|
||||
|
||||
override fun afterSuite(suite: TestDescriptor, result: TestResult) {
|
||||
reportFailure(result)
|
||||
}
|
||||
|
||||
override fun beforeSuite(suite: TestDescriptor) {
|
||||
}
|
||||
override fun beforeSuite(suite: TestDescriptor) {}
|
||||
|
||||
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
|
||||
reportFailure(result)
|
||||
}
|
||||
|
||||
private fun reportFailure(result: TestResult) {
|
||||
if (result.failedTestCount > 0) {
|
||||
hasOwnFailedTests = true
|
||||
allListenedTaskParentsPaths.get().forEach {
|
||||
testReportServiceProvider.get().testFailed(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.testing.internal
|
||||
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
|
||||
typealias TaskError = Pair<String, Error>
|
||||
|
||||
abstract class TestReportService : BuildService<BuildServiceParameters.None> {
|
||||
private val taskHasFailedTests = mutableMapOf<String, Boolean>()
|
||||
private val taskSuppressedFailures = mutableMapOf<String, MutableList<TaskError>>()
|
||||
|
||||
fun testFailed(taskPath: String) {
|
||||
taskHasFailedTests[taskPath] = true
|
||||
}
|
||||
|
||||
fun hasFailedTests(path: String): Boolean {
|
||||
return taskHasFailedTests[path] ?: false
|
||||
}
|
||||
|
||||
fun reportFailure(failedTaskPath: String, parentTaskPath: String, failure: Error) {
|
||||
taskSuppressedFailures.computeIfAbsent(parentTaskPath) { mutableListOf() }.add(failedTaskPath to failure)
|
||||
}
|
||||
|
||||
fun getAggregatedTaskFailures(taskPath: String): List<TaskError> {
|
||||
return taskSuppressedFailures[taskPath] ?: emptyList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun registerIfAbsent(gradle: Gradle): Provider<TestReportService> {
|
||||
// Use class loader hashcode in case there are multiple class loaders in the same build
|
||||
return gradle.sharedServices
|
||||
.registerIfAbsent(
|
||||
"${TestReportService::class.java.canonicalName}_${TestReportService::class.java.classLoader.hashCode()}",
|
||||
TestReportService::class.java
|
||||
) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user