[Test] Add wrapping of failures from different parts of test pipeline
This commit is contained in:
committed by
TeamCityServer
parent
6aca0bb374
commit
27f0d938c9
-14
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.test
|
||||
|
||||
class ExceptionFromTestError(cause: Throwable) : AssertionError(cause) {
|
||||
override val message: String
|
||||
get() = "Exception was thrown"
|
||||
|
||||
override val cause: Throwable
|
||||
get() = super.cause!!
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.io.IOException
|
||||
|
||||
class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
private val failedAssertions = mutableListOf<Throwable>()
|
||||
private val failedAssertions = mutableListOf<WrappedException>()
|
||||
|
||||
fun runTest(@TestDataFile testDataFileName: String, beforeDispose: (TestConfiguration) -> Unit = {}) {
|
||||
try {
|
||||
@@ -46,7 +46,9 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
}
|
||||
} catch (e: ExceptionFromModuleStructureTransformer) {
|
||||
services.register(TestModuleStructure::class, e.alreadyParsedModuleStructure)
|
||||
val exception = filterFailedExceptions(listOf(e.cause)).singleOrNull() ?: return
|
||||
val exception = filterFailedExceptions(
|
||||
listOf(WrappedException.FromModuleStructureTransformer(e.cause))
|
||||
).singleOrNull() ?: return
|
||||
throw exception
|
||||
}
|
||||
|
||||
@@ -60,18 +62,26 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
val modules = moduleStructure.modules
|
||||
val dependencyProvider = DependencyProviderImpl(services, modules)
|
||||
services.registerDependencyProvider(dependencyProvider)
|
||||
var failedException: Throwable? = null
|
||||
var failedException: WrappedException? = null
|
||||
try {
|
||||
for (module in modules) {
|
||||
val shouldProcessNextModules = processModule(services, module, dependencyProvider, moduleStructure)
|
||||
if (!shouldProcessNextModules) break
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
} catch (e: WrappedException) {
|
||||
failedException = e
|
||||
} catch (e: Exception) {
|
||||
throw IllegalStateException("Unexpected exception type. Only WrappedException are expected here", e)
|
||||
}
|
||||
|
||||
for (handler in testConfiguration.getAllHandlers()) {
|
||||
withAssertionCatching {
|
||||
val wrapperFactory = when (handler) {
|
||||
is FrontendOutputHandler -> WrappedException::FromFrontendHandler
|
||||
is BackendInputHandler -> WrappedException::FromBackendHandler
|
||||
is BinaryArtifactHandler -> WrappedException::FromBinaryHandler
|
||||
else -> WrappedException::FromUnknownHandler
|
||||
}
|
||||
withAssertionCatching(wrapperFactory) {
|
||||
val thereWasAnException = failedException != null || failedAssertions.isNotEmpty()
|
||||
if (handler.shouldRun(thereWasAnException)) {
|
||||
handler.processAfterAllModules(thereWasAnException)
|
||||
@@ -79,32 +89,35 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
}
|
||||
}
|
||||
if (testConfiguration.metaInfoHandlerEnabled) {
|
||||
withAssertionCatching(insertExceptionInStart = true) {
|
||||
withAssertionCatching(WrappedException::FromMetaInfoHandler) {
|
||||
globalMetadataInfoHandler.compareAllMetaDataInfos()
|
||||
}
|
||||
}
|
||||
if (failedException != null) {
|
||||
failedAssertions.add(0, ExceptionFromTestError(failedException))
|
||||
failedAssertions.add(0, failedException)
|
||||
}
|
||||
|
||||
testConfiguration.afterAnalysisCheckers.forEach {
|
||||
withAssertionCatching {
|
||||
withAssertionCatching(WrappedException::FromAfterAnalysisChecker) {
|
||||
it.check(failedAssertions)
|
||||
}
|
||||
}
|
||||
|
||||
val filteredFailedAssertions = filterFailedExceptions(failedAssertions)
|
||||
filteredFailedAssertions.firstIsInstanceOrNull<ExceptionFromTestError>()?.let {
|
||||
filteredFailedAssertions.firstIsInstanceOrNull<WrappedException.FromFacade>()?.let {
|
||||
throw it
|
||||
}
|
||||
services.assertions.assertAll(filteredFailedAssertions)
|
||||
}
|
||||
|
||||
private fun filterFailedExceptions(failedExceptions: List<Throwable>): List<Throwable> = testConfiguration.afterAnalysisCheckers
|
||||
.fold(failedExceptions) { assertions, checker ->
|
||||
checker.suppressIfNeeded(assertions)
|
||||
}
|
||||
.map { if (it is ExceptionFromTestError) it.cause else it }
|
||||
private fun filterFailedExceptions(failedExceptions: List<WrappedException>): List<Throwable> {
|
||||
return testConfiguration.afterAnalysisCheckers
|
||||
.fold(failedExceptions) { assertions, checker ->
|
||||
checker.suppressIfNeeded(assertions)
|
||||
}
|
||||
.sorted()
|
||||
.map { it.cause }
|
||||
}
|
||||
|
||||
/*
|
||||
* If there was failure from handler with `failureDisablesNextSteps=true` then `processModule`
|
||||
@@ -121,11 +134,14 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
val frontendKind = module.frontendKind
|
||||
if (!frontendKind.shouldRunAnalysis) return true
|
||||
|
||||
val frontendArtifacts: ResultingArtifact.FrontendOutput<*> = testConfiguration.getFacade(SourcesKind, frontendKind)
|
||||
.transform(module, sourcesArtifact)?.also { dependencyProvider.registerArtifact(module, it) } ?: return true
|
||||
val frontendArtifacts: ResultingArtifact.FrontendOutput<*> = withExceptionWrapping(WrappedException.FromFacade::Frontend) {
|
||||
testConfiguration.getFacade(SourcesKind, frontendKind)
|
||||
.transform(module, sourcesArtifact)?.also { dependencyProvider.registerArtifact(module, it) }
|
||||
?: return true
|
||||
}
|
||||
val frontendHandlers: List<AnalysisHandler<*>> = testConfiguration.getHandlers(frontendKind)
|
||||
for (frontendHandler in frontendHandlers) {
|
||||
val thereWasAnException = withAssertionCatching {
|
||||
val thereWasAnException = withAssertionCatching(WrappedException::FromFrontendHandler) {
|
||||
if (frontendHandler.shouldRun(failedAssertions.isNotEmpty())) {
|
||||
frontendHandler.hackyProcess(module, frontendArtifacts)
|
||||
}
|
||||
@@ -136,12 +152,14 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
val backendKind = services.backendKindExtractor.backendKind(module.targetBackend)
|
||||
if (!backendKind.shouldRunAnalysis) return true
|
||||
|
||||
val backendInputInfo = testConfiguration.getFacade(frontendKind, backendKind)
|
||||
.hackyTransform(module, frontendArtifacts)?.also { dependencyProvider.registerArtifact(module, it) } ?: return true
|
||||
val backendInputInfo = withExceptionWrapping(WrappedException.FromFacade::Converter) {
|
||||
testConfiguration.getFacade(frontendKind, backendKind)
|
||||
.hackyTransform(module, frontendArtifacts)?.also { dependencyProvider.registerArtifact(module, it) } ?: return true
|
||||
}
|
||||
|
||||
val backendHandlers: List<AnalysisHandler<*>> = testConfiguration.getHandlers(backendKind)
|
||||
for (backendHandler in backendHandlers) {
|
||||
val thereWasAnException = withAssertionCatching {
|
||||
val thereWasAnException = withAssertionCatching(WrappedException::FromBackendHandler) {
|
||||
if (backendHandler.shouldRun(failedAssertions.isNotEmpty())) {
|
||||
backendHandler.hackyProcess(module, backendInputInfo)
|
||||
}
|
||||
@@ -151,14 +169,16 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
|
||||
for (artifactKind in moduleStructure.getTargetArtifactKinds(module)) {
|
||||
if (!artifactKind.shouldRunAnalysis) continue
|
||||
val binaryArtifact = testConfiguration.getFacade(backendKind, artifactKind)
|
||||
.hackyTransform(module, backendInputInfo)?.also {
|
||||
dependencyProvider.registerArtifact(module, it)
|
||||
} ?: return true
|
||||
val binaryArtifact = withExceptionWrapping(WrappedException.FromFacade::Backend) {
|
||||
testConfiguration.getFacade(backendKind, artifactKind)
|
||||
.hackyTransform(module, backendInputInfo)?.also {
|
||||
dependencyProvider.registerArtifact(module, it)
|
||||
} ?: return true
|
||||
}
|
||||
|
||||
val binaryHandlers: List<AnalysisHandler<*>> = testConfiguration.getHandlers(artifactKind)
|
||||
for (binaryHandler in binaryHandlers) {
|
||||
val thereWasAnException = withAssertionCatching {
|
||||
val thereWasAnException = withAssertionCatching(WrappedException::FromBinaryHandler) {
|
||||
if (binaryHandler.shouldRun(failedAssertions.isNotEmpty())) {
|
||||
binaryHandler.hackyProcess(module, binaryArtifact)
|
||||
}
|
||||
@@ -173,20 +193,24 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
||||
/*
|
||||
* Returns true if there was an exception in block
|
||||
*/
|
||||
private inline fun withAssertionCatching(insertExceptionInStart: Boolean = false, block: () -> Unit): Boolean {
|
||||
private inline fun withAssertionCatching(exceptionWrapper: (Throwable) -> WrappedException, block: () -> Unit): Boolean {
|
||||
return try {
|
||||
block()
|
||||
false
|
||||
} catch (e: Throwable) {
|
||||
if (insertExceptionInStart) {
|
||||
failedAssertions.add(0, e)
|
||||
} else {
|
||||
failedAssertions += e
|
||||
}
|
||||
failedAssertions += exceptionWrapper(e)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <R> withExceptionWrapping(exceptionWrapper: (Throwable) -> WrappedException, block: () -> R): R {
|
||||
return try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
throw exceptionWrapper(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AnalysisHandler<*>.shouldRun(thereWasAnException: Boolean): Boolean {
|
||||
return !(doNotRunIfThereWerePreviousFailures && thereWasAnException)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.test
|
||||
|
||||
sealed class WrappedException(
|
||||
cause: Throwable,
|
||||
val priority: Int,
|
||||
val additionalPriority: Int
|
||||
) : Exception(cause), Comparable<WrappedException> {
|
||||
sealed class FromFacade(cause: Throwable, additionalPriority: Int) : WrappedException(cause, 0, additionalPriority) {
|
||||
class Frontend(cause: Throwable) : FromFacade(cause, 1)
|
||||
class Converter(cause: Throwable) : FromFacade(cause, 2)
|
||||
class Backend(cause: Throwable) : FromFacade(cause, 3)
|
||||
|
||||
override val message: String
|
||||
get() = "Exception was thrown"
|
||||
}
|
||||
|
||||
class FromMetaInfoHandler(cause: Throwable) : WrappedException(cause, 1, 1)
|
||||
|
||||
class FromFrontendHandler(cause: Throwable) : WrappedException(cause, 1, 1)
|
||||
class FromBackendHandler(cause: Throwable) : WrappedException(cause, 1, 2)
|
||||
class FromBinaryHandler(cause: Throwable) : WrappedException(cause, 1, 3)
|
||||
class FromUnknownHandler(cause: Throwable) : WrappedException(cause, 1, 4)
|
||||
|
||||
class FromAfterAnalysisChecker(cause: Throwable) : WrappedException(cause, 2, 1)
|
||||
|
||||
class FromModuleStructureTransformer(cause: Throwable) : WrappedException(cause, 2, 1)
|
||||
|
||||
override val cause: Throwable
|
||||
get() = super.cause!!
|
||||
|
||||
override fun compareTo(other: WrappedException): Int {
|
||||
if (priority == other.priority) {
|
||||
return additionalPriority - other.additionalPriority
|
||||
}
|
||||
return priority - other.priority
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.test.model
|
||||
|
||||
import org.jetbrains.kotlin.test.WrappedException
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
@@ -12,7 +13,9 @@ abstract class AfterAnalysisChecker(protected val testServices: TestServices) {
|
||||
open val directives: List<DirectivesContainer>
|
||||
get() = emptyList()
|
||||
|
||||
open fun check(failedAssertions: List<Throwable>) {}
|
||||
open fun check(failedAssertions: List<WrappedException>) {}
|
||||
|
||||
open fun suppressIfNeeded(failedAssertions: List<Throwable>): List<Throwable> = failedAssertions
|
||||
open fun suppressIfNeeded(failedAssertions: List<WrappedException>): List<WrappedException> = failedAssertions
|
||||
|
||||
protected fun Throwable.wrap(): WrappedException = WrappedException.FromAfterAnalysisChecker(this)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user