• 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.WARNING
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 @Deprecated(
46     "Deprecated for removal without a replacement. " +
47         "It may be to define one's own `CoroutineExceptionHandler` if you just need to handle '" +
48         "uncaught exceptions without a special `TestCoroutineScope` integration.", level = DeprecationLevel.WARNING
49 )
50 // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
51 public class TestCoroutineExceptionHandler :
52     AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler, UncaughtExceptionCaptor {
53     private val _exceptions = mutableListOf<Throwable>()
54     private val _lock = SynchronizedObject()
55     private var _coroutinesCleanedUp = false
56 
57     @Suppress("INVISIBLE_MEMBER")
58     override fun handleException(context: CoroutineContext, exception: Throwable) {
59         synchronized(_lock) {
60             if (_coroutinesCleanedUp) {
61                 handleCoroutineExceptionImpl(context, exception)
62             }
63             _exceptions += exception
64         }
65     }
66 
67     public override val uncaughtExceptions: List<Throwable>
68         get() = synchronized(_lock) { _exceptions.toList() }
69 
70     public override fun cleanupTestCoroutines() {
71         synchronized(_lock) {
72             _coroutinesCleanedUp = true
73             val exception = _exceptions.firstOrNull() ?: return
74             // log the rest
75             _exceptions.drop(1).forEach { it.printStackTrace() }
76             throw exception
77         }
78     }
79 }
80