ca6ce151a2
This makes 'test.' an implicit package prefix, thus fixing the inspection "Package directive doesn't match file location" positive in almost all test files.
170 lines
6.7 KiB
Kotlin
170 lines
6.7 KiB
Kotlin
/*
|
|
* 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 test.exceptions
|
|
|
|
import test.supportsSuppressedExceptions
|
|
import kotlin.test.*
|
|
|
|
@Suppress("Reformat") // author's formatting
|
|
class ExceptionTest {
|
|
private val cause = Exception("cause")
|
|
|
|
@Test fun throwable() = testCreateException(::Throwable, ::Throwable, ::Throwable, ::Throwable)
|
|
@Test fun error() = testCreateException(::Error, ::Error, ::Error, ::Error)
|
|
@Test fun exception() = testCreateException(::Exception, ::Exception, ::Exception, ::Exception)
|
|
@Test fun runtimeException() = testCreateException(::RuntimeException, ::RuntimeException, ::RuntimeException, ::RuntimeException)
|
|
@Test fun illegalArgumentException() = testCreateException(::IllegalArgumentException, ::IllegalArgumentException, ::IllegalArgumentException, ::IllegalArgumentException)
|
|
@Test fun illegalStateException() = testCreateException(::IllegalStateException, ::IllegalStateException, ::IllegalStateException, ::IllegalStateException)
|
|
@Test fun indexOutOfBoundsException() = testCreateException(::IndexOutOfBoundsException, ::IndexOutOfBoundsException)
|
|
@Test fun unsupportedOperationException() = testCreateException(::UnsupportedOperationException, ::UnsupportedOperationException, ::UnsupportedOperationException, ::UnsupportedOperationException)
|
|
@Test fun numberFormatException() = testCreateException(::NumberFormatException, ::NumberFormatException)
|
|
@Test fun nullPointerException() = testCreateException(::NullPointerException, ::NullPointerException)
|
|
@Test fun classCastException() = testCreateException(::ClassCastException, ::ClassCastException)
|
|
@Test fun noSuchElementException() = testCreateException(::NoSuchElementException, ::NoSuchElementException)
|
|
@Test fun concurrentModificationException() = testCreateException(::ConcurrentModificationException, ::ConcurrentModificationException)
|
|
@Test fun arithmeticException() = testCreateException(::ArithmeticException, ::ArithmeticException)
|
|
|
|
@Test fun noWhenBranchMatchedException() = @Suppress("DEPRECATION_ERROR") testCreateException(::NoWhenBranchMatchedException, ::NoWhenBranchMatchedException, ::NoWhenBranchMatchedException, ::NoWhenBranchMatchedException)
|
|
@Test fun uninitializedPropertyAccessException() = @Suppress("DEPRECATION_ERROR") testCreateException(::UninitializedPropertyAccessException, ::UninitializedPropertyAccessException, ::UninitializedPropertyAccessException, ::UninitializedPropertyAccessException)
|
|
|
|
@Test fun assertionError() = testCreateException(::AssertionError, ::AssertionError, ::AssertionError)
|
|
|
|
|
|
private fun <T : Throwable> testCreateException(
|
|
noarg: () -> T,
|
|
fromMessage: (String?) -> T,
|
|
fromCause: ((Throwable?) -> T)? = null,
|
|
fromMessageCause: ((String?, Throwable?) -> T)? = null
|
|
) {
|
|
noarg().let { e ->
|
|
assertEquals(null, e.message)
|
|
assertEquals(null, e.cause)
|
|
}
|
|
|
|
fromMessage("message").let { e ->
|
|
assertEquals("message", e.message)
|
|
assertEquals(null, e.cause)
|
|
}
|
|
|
|
fromMessage(null).let { e ->
|
|
assertTrue(e.message == null || e.message == "null")
|
|
}
|
|
|
|
fromMessageCause?.run {
|
|
invoke("message", cause).let { e ->
|
|
assertEquals("message", e.message)
|
|
assertSame(cause, e.cause)
|
|
}
|
|
invoke(null, null).let { e ->
|
|
assertEquals(null, e.message)
|
|
assertEquals(null, e.cause)
|
|
}
|
|
}
|
|
|
|
fromCause?.invoke(cause)?.let { e ->
|
|
assertSame(cause, e.cause)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun suppressedExceptions() {
|
|
val e1 = Throwable()
|
|
|
|
val c1 = Exception("Suppressed 1")
|
|
val c2 = Exception("Suppressed 2")
|
|
|
|
assertTrue(e1.suppressedExceptions.isEmpty())
|
|
|
|
e1.addSuppressed(c1)
|
|
e1.addSuppressed(c2)
|
|
|
|
if (supportsSuppressedExceptions) {
|
|
assertEquals(listOf(c1, c2), e1.suppressedExceptions)
|
|
} else {
|
|
assertTrue(e1.suppressedExceptions.isEmpty())
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun exceptionDetailedTrace() {
|
|
fun root(): Nothing = throw IllegalStateException("Root cause\nDetails: root")
|
|
|
|
fun suppressedError(id: Int): Throwable = UnsupportedOperationException("Side error\nId: $id")
|
|
|
|
fun induced(): Nothing {
|
|
try {
|
|
root()
|
|
} catch (e: Throwable) {
|
|
for (id in 0..1)
|
|
e.addSuppressed(suppressedError(id))
|
|
throw RuntimeException("Induced", e)
|
|
}
|
|
}
|
|
|
|
val e = try {
|
|
induced()
|
|
} catch (e: Throwable) {
|
|
e.apply { addSuppressed(suppressedError(2)) }
|
|
}
|
|
|
|
val topLevelTrace = e.stackTraceToString()
|
|
fun assertInTrace(value: Any) {
|
|
if (value.toString() !in topLevelTrace) {
|
|
fail("Expected top level trace: $topLevelTrace\n\nto contain: $value")
|
|
}
|
|
}
|
|
|
|
assertInTrace(e)
|
|
|
|
val cause = assertNotNull(e.cause, "Should have cause")
|
|
assertInTrace(cause)
|
|
|
|
if (supportsSuppressedExceptions) {
|
|
val topLevelSuppressed = e.suppressedExceptions.single()
|
|
assertInTrace(topLevelSuppressed)
|
|
cause.suppressedExceptions.forEach {
|
|
assertInTrace(it)
|
|
}
|
|
}
|
|
|
|
// fail(topLevelTrace) // to dump the entire trace
|
|
}
|
|
|
|
@Test
|
|
fun circularSuppressedDetailedTrace() {
|
|
if (!supportsSuppressedExceptions) return
|
|
|
|
// Testing an exception of the following structure
|
|
// e1
|
|
// -- suppressed: e0 (same stack as e1)
|
|
// -- suppressed: e3
|
|
// -- suppressed: e1
|
|
// Caused by: e2
|
|
// -- suppressed: e1
|
|
// Caused by: e3
|
|
|
|
val e3 = Exception("e3")
|
|
val e2 = Error("e2", e3)
|
|
val (e1, e0) = listOf("e1", "e0").map { msg -> RuntimeException(msg, e2.takeIf { msg == "e1" }) }
|
|
e1.addSuppressed(e0)
|
|
e1.addSuppressed(e3)
|
|
e3.addSuppressed(e1)
|
|
e2.addSuppressed(e1)
|
|
|
|
val topLevelTrace = e1.stackTraceToString()
|
|
fun assertAppearsInTrace(value: Any, count: Int) {
|
|
if (Regex.fromLiteral(value.toString()).findAll(topLevelTrace).count() != count) {
|
|
fail("Expected to find $value $count times in $topLevelTrace")
|
|
}
|
|
}
|
|
assertAppearsInTrace(e1, 3)
|
|
assertAppearsInTrace(e0, 1)
|
|
assertAppearsInTrace(e2, 1)
|
|
assertAppearsInTrace(e3, 2)
|
|
// fail(topLevelTrace) // to dump the entire trace
|
|
}
|
|
|
|
} |