From 27f0d938c988294ab928fb4e3b21a371b0f925b8 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 14 May 2021 14:53:36 +0300 Subject: [PATCH] [Test] Add wrapping of failures from different parts of test pipeline --- .../kotlin/test/ExceptionFromTestError.kt | 14 --- .../org/jetbrains/kotlin/test/TestRunner.kt | 86 ++++++++++++------- .../jetbrains/kotlin/test/WrappedException.kt | 42 +++++++++ .../kotlin/test/model/AfterAnalysisChecker.kt | 7 +- .../test/backend/BlackBoxCodegenSuppressor.kt | 13 +-- .../handlers/FirIrDumpIdenticalCheckers.kt | 3 +- .../handlers/FirTestDataConsistencyHandler.kt | 3 +- .../frontend/fir/FirFailingTestSuppressor.kt | 8 +- .../fir/handlers/FirIdenticalChecker.kt | 3 +- .../compiler/based/IdeTestIgnoreHandler.kt | 9 +- 10 files changed, 124 insertions(+), 64 deletions(-) delete mode 100644 compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/ExceptionFromTestError.kt create mode 100644 compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/WrappedException.kt diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/ExceptionFromTestError.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/ExceptionFromTestError.kt deleted file mode 100644 index 5e0a80e446b..00000000000 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/ExceptionFromTestError.kt +++ /dev/null @@ -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!! -} diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt index 88a9d7b0d3f..0f6d9c308bb 100644 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt @@ -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() + private val failedAssertions = mutableListOf() 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()?.let { + filteredFailedAssertions.firstIsInstanceOrNull()?.let { throw it } services.assertions.assertAll(filteredFailedAssertions) } - private fun filterFailedExceptions(failedExceptions: List): List = testConfiguration.afterAnalysisCheckers - .fold(failedExceptions) { assertions, checker -> - checker.suppressIfNeeded(assertions) - } - .map { if (it is ExceptionFromTestError) it.cause else it } + private fun filterFailedExceptions(failedExceptions: List): List { + 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> = 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> = 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> = 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 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) } diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/WrappedException.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/WrappedException.kt new file mode 100644 index 00000000000..a0834d90dbb --- /dev/null +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/WrappedException.kt @@ -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 { + 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 + } +} diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/model/AfterAnalysisChecker.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/model/AfterAnalysisChecker.kt index 8cca687e6e5..3b5fd4cf324 100644 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/model/AfterAnalysisChecker.kt +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/model/AfterAnalysisChecker.kt @@ -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 get() = emptyList() - open fun check(failedAssertions: List) {} + open fun check(failedAssertions: List) {} - open fun suppressIfNeeded(failedAssertions: List): List = failedAssertions + open fun suppressIfNeeded(failedAssertions: List): List = failedAssertions + + protected fun Throwable.wrap(): WrappedException = WrappedException.FromAfterAnalysisChecker(this) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/BlackBoxCodegenSuppressor.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/BlackBoxCodegenSuppressor.kt index 9030f7b26f4..87503846cc8 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/BlackBoxCodegenSuppressor.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/BlackBoxCodegenSuppressor.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.test.backend import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.test.WrappedException import org.jetbrains.kotlin.test.directives.CodegenTestDirectives import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.IGNORE_BACKEND import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.IGNORE_BACKEND_FIR @@ -24,7 +25,7 @@ class BlackBoxCodegenSuppressor( get() = listOf(CodegenTestDirectives) @OptIn(ExperimentalStdlibApi::class) - override fun suppressIfNeeded(failedAssertions: List): List { + override fun suppressIfNeeded(failedAssertions: List): List { val moduleStructure = testServices.moduleStructure val targetBackends = buildList { testServices.defaultsProvider.defaultTargetBackend?.let { @@ -45,8 +46,8 @@ class BlackBoxCodegenSuppressor( moduleStructure: TestModuleStructure, directive: ValueDirective, targetBackends: List, - failedAssertions: List - ): List { + failedAssertions: List + ): List { val ignoredBackends = moduleStructure.allDirectives[directive] if (ignoredBackends.isEmpty()) return failedAssertions val matchedBackend = ignoredBackends.intersect(targetBackends) @@ -61,10 +62,10 @@ class BlackBoxCodegenSuppressor( private fun processAssertions( - failedAssertions: List, + failedAssertions: List, directive: ValueDirective, additionalMessage: String = "" - ): List { + ): List { return if (failedAssertions.isNotEmpty()) emptyList() else { val message = buildString { @@ -74,7 +75,7 @@ class BlackBoxCodegenSuppressor( append(additionalMessage) } } - listOf(AssertionError(message)) + listOf(AssertionError(message).wrap()) } } } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/FirIrDumpIdenticalCheckers.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/FirIrDumpIdenticalCheckers.kt index 89a93d9c695..5a11b2570c2 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/FirIrDumpIdenticalCheckers.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/FirIrDumpIdenticalCheckers.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.test.backend.handlers +import org.jetbrains.kotlin.test.WrappedException import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.FIR_IDENTICAL import org.jetbrains.kotlin.test.directives.model.DirectivesContainer @@ -39,7 +40,7 @@ class FirIrDumpIdenticalChecker(testServices: TestServices) : AfterAnalysisCheck } } - override fun check(failedAssertions: List) { + override fun check(failedAssertions: List) { if (failedAssertions.isNotEmpty()) return val testDataFile = testServices.moduleStructure.originalTestDataFiles.first() if (FIR_IDENTICAL in testServices.moduleStructure.allDirectives) { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/handlers/FirTestDataConsistencyHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/handlers/FirTestDataConsistencyHandler.kt index 5ad9c2842d3..1597ec7f883 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/handlers/FirTestDataConsistencyHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/handlers/FirTestDataConsistencyHandler.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.test.frontend.classic.handlers import org.jetbrains.kotlin.codeMetaInfo.clearTextFromDiagnosticMarkup +import org.jetbrains.kotlin.test.WrappedException import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives import org.jetbrains.kotlin.test.directives.model.DirectivesContainer import org.jetbrains.kotlin.test.model.AfterAnalysisChecker @@ -21,7 +22,7 @@ class FirTestDataConsistencyHandler(testServices: TestServices) : AfterAnalysisC override val directives: List get() = listOf(FirDiagnosticsDirectives) - override fun check(failedAssertions: List) { + override fun check(failedAssertions: List) { val moduleStructure = testServices.moduleStructure val testData = moduleStructure.originalTestDataFiles.first() if (testData.extension == "kts") return diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFailingTestSuppressor.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFailingTestSuppressor.kt index 1eafe5222a1..f1063319ec5 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFailingTestSuppressor.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFailingTestSuppressor.kt @@ -5,20 +5,20 @@ package org.jetbrains.kotlin.test.frontend.fir -import org.jetbrains.kotlin.test.ExceptionFromTestError +import org.jetbrains.kotlin.test.WrappedException import org.jetbrains.kotlin.test.model.AfterAnalysisChecker import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.moduleStructure class FirFailingTestSuppressor(testServices: TestServices) : AfterAnalysisChecker(testServices) { - override fun suppressIfNeeded(failedAssertions: List): List { + override fun suppressIfNeeded(failedAssertions: List): List { val testFile = testServices.moduleStructure.originalTestDataFiles.first() val failFile = testFile.parentFile.resolve("${testFile.nameWithoutExtension}.fail") - val exceptionFromFir = failedAssertions.firstOrNull { it is ExceptionFromTestError } + val exceptionFromFir = failedAssertions.firstOrNull { it is WrappedException.FromFacade.Frontend } return when { failFile.exists() && exceptionFromFir != null -> emptyList() failFile.exists() && exceptionFromFir == null -> { - failedAssertions + AssertionError("Fail file exists but no exception was throw. Please remove ${failFile.name}") + failedAssertions + AssertionError("Fail file exists but no exception was throw. Please remove ${failFile.name}").wrap() } else -> failedAssertions } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirIdenticalChecker.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirIdenticalChecker.kt index 161cdee9af6..eb9dd01549e 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirIdenticalChecker.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirIdenticalChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.test.frontend.fir.handlers +import org.jetbrains.kotlin.test.WrappedException import org.jetbrains.kotlin.test.model.AfterAnalysisChecker import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.moduleStructure @@ -25,7 +26,7 @@ class FirIdenticalChecker(testServices: TestServices) : AfterAnalysisChecker(tes } } - override fun check(failedAssertions: List) { + override fun check(failedAssertions: List) { if (failedAssertions.isNotEmpty()) return val testDataFile = testServices.moduleStructure.originalTestDataFiles.first() if (testDataFile.isFirTestData) { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/compiler/based/IdeTestIgnoreHandler.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/compiler/based/IdeTestIgnoreHandler.kt index 1c5cae1cc74..db0e1a3487e 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/compiler/based/IdeTestIgnoreHandler.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/compiler/based/IdeTestIgnoreHandler.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.compiler.based +import org.jetbrains.kotlin.test.WrappedException import org.jetbrains.kotlin.test.model.AfterAnalysisChecker import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives import org.jetbrains.kotlin.test.services.TestServices @@ -20,7 +21,7 @@ class IdeTestIgnoreHandler(testServices: TestServices) : AfterAnalysisChecker(te override val directives: List get() = listOf(FirIdeDirectives) - override fun check(failedAssertions: List) { + override fun check(failedAssertions: List) { if (!isFirIdeIgnoreDirectivePresent()) return if (failedAssertions.isNotEmpty()) return val moduleStructure = testServices.moduleStructure @@ -45,9 +46,9 @@ class IdeTestIgnoreHandler(testServices: TestServices) : AfterAnalysisChecker(te } - override fun suppressIfNeeded(failedAssertions: List): List { + override fun suppressIfNeeded(failedAssertions: List): List { return if (isFirIdeIgnoreDirectivePresent()) - failedAssertions.filterIsInstance() + failedAssertions.filter{ it.cause is TestWithFirIdeIgnoreAnnotationPassesException } else failedAssertions } @@ -57,4 +58,4 @@ class IdeTestIgnoreHandler(testServices: TestServices) : AfterAnalysisChecker(te } } -class TestWithFirIdeIgnoreAnnotationPassesException(override val message: String) : IllegalStateException() \ No newline at end of file +class TestWithFirIdeIgnoreAnnotationPassesException(override val message: String) : IllegalStateException()