Skip to content

Commit

Permalink
Support disabling reporting of uncaught exceptions in tests (#3736)
Browse files Browse the repository at this point in the history
The solution for #1205 may be undesirable for those who already
have their own solution, like setting the default exception
handlers. In this case, there's a usability issue without the
corresponding benefit: instead of all tests being ran and the
exceptions from them being reported, unrelated tests may fail,
making looking for problems more difficult.

This is probably a very rare issue, so we don't provide public API
for that, instead introducing a need-to-know internal variable.
  • Loading branch information
dkhalanskyjb committed May 3, 2023
1 parent 25a3553 commit 41b4665
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 3 deletions.
2 changes: 2 additions & 0 deletions kotlinx-coroutines-test/api/kotlinx-coroutines-test.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 12 additions & 1 deletion kotlinx-coroutines-test/common/src/TestScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

/**
Expand Down

0 comments on commit 41b4665

Please sign in to comment.