blob: a06958b90b434c4b675a3b6491ca5cd53390928b [file] [log] [blame]
package kotlinx.coroutines.testing.exceptions
import kotlinx.coroutines.*
import java.io.*
import java.util.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.test.*
inline fun <reified T : Throwable> checkException(exception: Throwable) {
assertIs<T>(exception)
assertTrue(exception.suppressed.isEmpty())
assertNull(exception.cause)
}
fun checkCycles(t: Throwable) {
val sw = StringWriter()
t.printStackTrace(PrintWriter(sw))
assertFalse(sw.toString().contains("CIRCULAR REFERENCE"))
}
class CapturingHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler),
CoroutineExceptionHandler
{
private var unhandled: ArrayList<Throwable>? = ArrayList()
override fun handleException(context: CoroutineContext, exception: Throwable) = synchronized<Unit>(this) {
unhandled!!.add(exception)
}
fun getException(): Throwable = synchronized(this) {
val size = unhandled!!.size
assert(size == 1) { "Expected one unhandled exception, but have $size: $unhandled" }
return unhandled!![0].also { unhandled = null }
}
}
fun captureExceptionsRun(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> Unit
): Throwable {
val handler = CapturingHandler()
runBlocking(context + handler, block = block)
return handler.getException()
}
@OptIn(ExperimentalContracts::class)
suspend inline fun <reified E: Throwable> assertCallsExceptionHandlerWith(
crossinline operation: suspend (CoroutineExceptionHandler) -> Unit): E {
contract {
callsInPlace(operation, InvocationKind.EXACTLY_ONCE)
}
val handler = CapturingHandler()
return withContext(handler) {
operation(handler)
assertIs<E>(handler.getException())
}
}