diff --git a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api index 00d9fb659e..a41502b2e1 100644 --- a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api +++ b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api @@ -121,9 +121,11 @@ public final class kotlinx/coroutines/test/TestScopeKt { public static final fun advanceTimeBy (Lkotlinx/coroutines/test/TestScope;J)V public static final fun advanceTimeBy-HG0u8IE (Lkotlinx/coroutines/test/TestScope;J)V public static final fun advanceUntilIdle (Lkotlinx/coroutines/test/TestScope;)V + public static final fun getCatchNonTestRelatedExceptions ()Z public static final fun getCurrentTime (Lkotlinx/coroutines/test/TestScope;)J public static final fun getTestTimeSource (Lkotlinx/coroutines/test/TestScope;)Lkotlin/time/TimeSource$WithComparableMarks; public static final fun runCurrent (Lkotlinx/coroutines/test/TestScope;)V + public static final fun setCatchNonTestRelatedExceptions (Z)V } public abstract interface class kotlinx/coroutines/test/UncaughtExceptionCaptor { diff --git a/kotlinx-coroutines-test/common/src/TestScope.kt b/kotlinx-coroutines-test/common/src/TestScope.kt index e72f0f14f3..fa6e3930d8 100644 --- a/kotlinx-coroutines-test/common/src/TestScope.kt +++ b/kotlinx-coroutines-test/common/src/TestScope.kt @@ -233,7 +233,9 @@ internal class TestScopeImpl(context: CoroutineContext) : * after the previous one, and learning about such exceptions as soon is possible is nice. */ @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") run { ensurePlatformExceptionHandlerLoaded(ExceptionCollector) } - ExceptionCollector.addOnExceptionCallback(lock, this::reportException) + if (catchNonTestRelatedExceptions) { + ExceptionCollector.addOnExceptionCallback(lock, this::reportException) + } uncaughtExceptions } if (exceptions.isNotEmpty()) { @@ -321,3 +323,12 @@ internal class UncaughtExceptionsBeforeTest : IllegalStateException( */ @ExperimentalCoroutinesApi internal class UncompletedCoroutinesError(message: String) : AssertionError(message) + +/** + * A flag that controls whether [TestScope] should attempt to catch arbitrary exceptions flying through the system. + * If it is enabled, then any exception that is not caught by the user code will be reported as a test failure. + * By default, it is enabled, but some tests may want to disable it to test the behavior of the system when they have + * their own exception handling procedures. + */ +@PublishedApi +internal var catchNonTestRelatedExceptions: Boolean = true diff --git a/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt b/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt index 90fa763523..70fcb9487f 100644 --- a/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt +++ b/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt @@ -43,8 +43,10 @@ internal object ExceptionCollector : AbstractCoroutineContextElement(CoroutineEx * Unregisters the callback associated with [owner]. */ fun removeOnExceptionCallback(owner: Any) = synchronized(lock) { - val existingValue = callbacks.remove(owner) - check(existingValue !== null) + if (enabled) { + val existingValue = callbacks.remove(owner) + check(existingValue !== null) + } } /**