package kotlinx.coroutines.test import kotlinx.coroutines.* import kotlinx.coroutines.internal.* import kotlin.coroutines.* /** * Access uncaught coroutine exceptions captured during test execution. */ @Deprecated( "Deprecated for removal without a replacement. " + "Consider whether the default mechanism of handling uncaught exceptions is sufficient. " + "If not, try writing your own `CoroutineExceptionHandler` and " + "please report your use case at https://github.com/Kotlin/kotlinx.coroutines/issues.", level = DeprecationLevel.ERROR ) // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0 public interface UncaughtExceptionCaptor { /** * List of uncaught coroutine exceptions. * * The returned list is a copy of the currently caught exceptions. * During [cleanupTestCoroutines] the first element of this list is rethrown if it is not empty. */ public val uncaughtExceptions: List /** * Call after the test completes to ensure that there were no uncaught exceptions. * * The first exception in uncaughtExceptions is rethrown. All other exceptions are * printed using [Throwable.printStackTrace]. * * @throws Throwable the first uncaught exception, if there are any uncaught exceptions. */ public fun cleanupTestCoroutines() } /** * An exception handler that captures uncaught exceptions in tests. */ @Suppress("DEPRECATION_ERROR") @Deprecated( "Deprecated for removal without a replacement. " + "It may be to define one's own `CoroutineExceptionHandler` if you just need to handle '" + "uncaught exceptions without a special `TestCoroutineScope` integration.", level = DeprecationLevel.ERROR ) // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0 public class TestCoroutineExceptionHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler, UncaughtExceptionCaptor { private val _exceptions = mutableListOf() private val _lock = SynchronizedObject() private var _coroutinesCleanedUp = false @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") // do not remove the INVISIBLE_REFERENCE suppression: required in K2 override fun handleException(context: CoroutineContext, exception: Throwable) { synchronized(_lock) { if (_coroutinesCleanedUp) { handleUncaughtCoroutineException(context, exception) } _exceptions += exception } } public override val uncaughtExceptions: List get() = synchronized(_lock) { _exceptions.toList() } public override fun cleanupTestCoroutines() { synchronized(_lock) { _coroutinesCleanedUp = true val exception = _exceptions.firstOrNull() ?: return // log the rest _exceptions.drop(1).forEach { it.printStackTrace() } throw exception } } }