• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines.test
6 
7 import kotlinx.coroutines.*
8 import kotlinx.coroutines.internal.*
9 import kotlin.coroutines.*
10 
11 /**
12  * Access uncaught coroutine exceptions captured during test execution.
13  */
14 @Deprecated(
15     "Deprecated for removal without a replacement. " +
16         "Consider whether the default mechanism of handling uncaught exceptions is sufficient. " +
17         "If not, try writing your own `CoroutineExceptionHandler` and " +
18         "please report your use case at https://github.com/Kotlin/kotlinx.coroutines/issues.",
19     level = DeprecationLevel.ERROR
20 )
21 // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
22 public interface UncaughtExceptionCaptor {
23     /**
24      * List of uncaught coroutine exceptions.
25      *
26      * The returned list is a copy of the currently caught exceptions.
27      * During [cleanupTestCoroutines] the first element of this list is rethrown if it is not empty.
28      */
29     public val uncaughtExceptions: List<Throwable>
30 
31     /**
32      * Call after the test completes to ensure that there were no uncaught exceptions.
33      *
34      * The first exception in uncaughtExceptions is rethrown. All other exceptions are
35      * printed using [Throwable.printStackTrace].
36      *
37      * @throws Throwable the first uncaught exception, if there are any uncaught exceptions.
38      */
cleanupTestCoroutinesnull39     public fun cleanupTestCoroutines()
40 }
41 
42 /**
43  * An exception handler that captures uncaught exceptions in tests.
44  */
45 @Suppress("DEPRECATION_ERROR")
46 @Deprecated(
47     "Deprecated for removal without a replacement. " +
48         "It may be to define one's own `CoroutineExceptionHandler` if you just need to handle '" +
49         "uncaught exceptions without a special `TestCoroutineScope` integration.", level = DeprecationLevel.ERROR
50 )
51 // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
52 public class TestCoroutineExceptionHandler :
53     AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler, UncaughtExceptionCaptor {
54     private val _exceptions = mutableListOf<Throwable>()
55     private val _lock = SynchronizedObject()
56     private var _coroutinesCleanedUp = false
57 
58     @Suppress("INVISIBLE_MEMBER")
59     override fun handleException(context: CoroutineContext, exception: Throwable) {
60         synchronized(_lock) {
61             if (_coroutinesCleanedUp) {
62                 handleUncaughtCoroutineException(context, exception)
63             }
64             _exceptions += exception
65         }
66     }
67 
68     public override val uncaughtExceptions: List<Throwable>
69         get() = synchronized(_lock) { _exceptions.toList() }
70 
71     public override fun cleanupTestCoroutines() {
72         synchronized(_lock) {
73             _coroutinesCleanedUp = true
74             val exception = _exceptions.firstOrNull() ?: return
75             // log the rest
76             _exceptions.drop(1).forEach { it.printStackTrace() }
77             throw exception
78         }
79     }
80 }
81