<lambda>null1 package kotlinx.coroutines.testing
2
3 import kotlinx.coroutines.scheduling.*
4 import java.io.*
5 import java.util.*
6 import kotlin.coroutines.*
7 import kotlinx.coroutines.*
8 import kotlin.test.*
9
10 actual val VERBOSE = try {
11 System.getProperty("test.verbose")?.toBoolean() ?: false
12 } catch (e: SecurityException) {
13 false
14 }
15
16 /**
17 * Is `true` when running in a nightly stress test mode.
18 */
19 actual val isStressTest = System.getProperty("stressTest")?.toBoolean() ?: false
20
21 actual val stressTestMultiplierSqrt = if (isStressTest) 5 else 1
22
23 private const val SHUTDOWN_TIMEOUT = 1_000L // 1s at most to wait per thread
24
25 /**
26 * Multiply various constants in stress tests by this factor, so that they run longer during nightly stress test.
27 */
28 actual val stressTestMultiplier = stressTestMultiplierSqrt * stressTestMultiplierSqrt
29
30
31 @Suppress("ACTUAL_WITHOUT_EXPECT")
32 actual typealias TestResult = Unit
33
lastResortReportExceptionnull34 internal actual fun lastResortReportException(error: Throwable) {
35 System.err.println("${error.message}${error.cause?.let { ": $it" } ?: ""}")
36 error.cause?.printStackTrace(System.err)
37 System.err.println("--- Detected at ---")
38 Throwable().printStackTrace(System.err)
39 }
40
41 /**
42 * Base class for tests, so that tests for predictable scheduling of actions in multiple coroutines sharing a single
43 * thread can be written. Use it like this:
44 *
45 * ```
46 * class MyTest : TestBase() {
47 * @Test
48 * fun testSomething() = runBlocking { // run in the context of the main thread
49 * expect(1) // initiate action counter
50 * launch { // use the context of the main thread
51 * expect(3) // the body of this coroutine in going to be executed in the 3rd step
52 * }
53 * expect(2) // launch just scheduled coroutine for execution later, so this line is executed second
54 * yield() // yield main thread to the launched job
55 * finish(4) // fourth step is the last one. `finish` must be invoked or test fails
56 * }
57 * }
58 * ```
59 */
60 @Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS")
61 actual open class TestBase(
62 private var disableOutCheck: Boolean,
63 private val errorCatching: ErrorCatching.Impl = ErrorCatching.Impl()
64 ): OrderedExecutionTestBase(), ErrorCatching by errorCatching {
65
66 actual constructor(): this(false)
67
68 // Shutdown sequence
69 private lateinit var threadsBefore: Set<Thread>
70 private val uncaughtExceptions = Collections.synchronizedList(ArrayList<Throwable>())
71 private var originalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
72 /*
73 * System.out that we redefine in order to catch any debugging/diagnostics
74 * 'println' from main source set.
75 * NB: We do rely on the name 'previousOut' in the FieldWalker in order to skip its
76 * processing
77 */
78 private lateinit var previousOut: PrintStream
79
80 private object TestOutputStream : PrintStream(object : OutputStream() {
writenull81 override fun write(b: Int) {
82 error("Detected unexpected call to 'println' from source code")
83 }
84 })
85
printlnnull86 actual fun println(message: Any?) {
87 if (disableOutCheck) kotlin.io.println(message)
88 else previousOut.println(message)
89 }
90
91 @BeforeTest
beforenull92 fun before() {
93 initPoolsBeforeTest()
94 threadsBefore = currentThreads()
95 originalUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
96 Thread.setDefaultUncaughtExceptionHandler { t, e ->
97 println("Exception in thread $t: $e") // The same message as in default handler
98 e.printStackTrace()
99 uncaughtExceptions.add(e)
100 }
101 if (!disableOutCheck) {
102 previousOut = System.out
103 System.setOut(TestOutputStream)
104 }
105 }
106
107 @AfterTest
onCompletionnull108 fun onCompletion() {
109 // onCompletion should not throw exceptions before it finishes all cleanup, so that other tests always
110 // start in a clear, restored state
111 checkFinishCall()
112 if (!disableOutCheck) { // Restore global System.out first
113 System.setOut(previousOut)
114 }
115 // Shutdown all thread pools
116 shutdownPoolsAfterTest()
117 // Check that are now leftover threads
118 runCatching {
119 checkTestThreads(threadsBefore)
120 }.onFailure {
121 reportError(it)
122 }
123 // Restore original uncaught exception handler after the main shutdown sequence
124 Thread.setDefaultUncaughtExceptionHandler(originalUncaughtExceptionHandler)
125 if (uncaughtExceptions.isNotEmpty()) {
126 error("Expected no uncaught exceptions, but got $uncaughtExceptions")
127 }
128 // The very last action -- throw error if any was detected
129 errorCatching.close()
130 }
131
runTestnull132 actual fun runTest(
133 expected: ((Throwable) -> Boolean)?,
134 unhandled: List<(Throwable) -> Boolean>,
135 block: suspend CoroutineScope.() -> Unit
136 ): TestResult {
137 var exCount = 0
138 var ex: Throwable? = null
139 try {
140 runBlocking(block = block, context = CoroutineExceptionHandler { _, e ->
141 if (e is CancellationException) return@CoroutineExceptionHandler // are ignored
142 exCount++
143 when {
144 exCount > unhandled.size ->
145 error("Too many unhandled exceptions $exCount, expected ${unhandled.size}, got: $e", e)
146 !unhandled[exCount - 1](e) ->
147 error("Unhandled exception was unexpected: $e", e)
148 }
149 })
150 } catch (e: Throwable) {
151 ex = e
152 if (expected != null) {
153 if (!expected(e))
154 error("Unexpected exception: $e", e)
155 } else {
156 throw e
157 }
158 } finally {
159 if (ex == null && expected != null) error("Exception was expected but none produced")
160 }
161 if (exCount < unhandled.size)
162 error("Too few unhandled exceptions $exCount, expected ${unhandled.size}")
163 }
164
currentDispatchernull165 protected suspend fun currentDispatcher() = coroutineContext[ContinuationInterceptor]!!
166 }
167
168 @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
169 fun initPoolsBeforeTest() {
170 DefaultScheduler.usePrivateScheduler()
171 }
172
173 @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
shutdownPoolsAfterTestnull174 fun shutdownPoolsAfterTest() {
175 DefaultScheduler.shutdown(SHUTDOWN_TIMEOUT)
176 DefaultExecutor.shutdownForTests(SHUTDOWN_TIMEOUT)
177 DefaultScheduler.restore()
178 }
179
180 actual val isNative = false
181
182 actual val isBoundByJsTestTimeout = false
183
184 /*
185 * We ignore tests that test **real** non-virtualized tests with time on Windows, because
186 * our CI Windows is virtualized itself (oh, the irony) and its clock resolution is dozens of ms,
187 * which makes such tests flaky.
188 */
189 actual val isJavaAndWindows: Boolean = System.getProperty("os.name")!!.contains("Windows")
190
191 actual val usesSharedEventLoop: Boolean = false
192