Skip to content

Commit 19ed1f0

Browse files
committed
Add more tests for 'runTest'
1 parent 42a345e commit 19ed1f0

File tree

6 files changed

+132
-4
lines changed

6 files changed

+132
-4
lines changed

kotlinx-coroutines-test/common/src/TestBuilders.kt

+7-2
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ public fun runTest(
170170
): TestResult {
171171
if (context[RunningInRunTest] != null)
172172
throw IllegalStateException("Calls to `runTest` can't be nested. Please read the docs on `TestResult` for details.")
173+
val testScope = TestCoroutineScope(context + RunningInRunTest())
174+
val scheduler = testScope.testScheduler
173175
return createTestResult {
174-
val testScope = TestCoroutineScope(context + RunningInRunTest())
175-
val scheduler = testScope.testScheduler
176176
val deferred = testScope.async {
177177
testScope.testBody()
178178
}
@@ -197,6 +197,11 @@ public fun runTest(
197197
}
198198
}
199199
} catch (e: TimeoutCancellationException) {
200+
try {
201+
testScope.cleanupTestCoroutines()
202+
} catch (e: UncompletedCoroutinesError) {
203+
// we expect these and will instead throw a more informative exception just below.
204+
}
200205
throw UncompletedCoroutinesError("The test coroutine was not completed after waiting for $dispatchTimeoutMs ms")
201206
}
202207
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
/**
8+
* Passes [test] as an argument to [block], but as a function returning not a [TestResult] but [Unit].
9+
*/
10+
expect fun testResultMap(block: (() -> Unit) -> Unit, test: () -> TestResult): TestResult

kotlinx-coroutines-test/common/test/RunTestTest.kt

+70-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
package kotlinx.coroutines.test
66

77
import kotlinx.coroutines.*
8-
import kotlinx.coroutines.flow.*
98
import kotlin.coroutines.*
109
import kotlin.test.*
11-
import kotlin.time.*
1210

1311
class RunTestTest {
1412

@@ -50,4 +48,74 @@ class RunTestTest {
5048
}
5149
}
5250

51+
/** Tests that even the dispatch timeout of `0` is fine if all the dispatches go through the same scheduler. */
52+
@Test
53+
fun testRunTestWithZeroTimeoutWithControlledDispatches() = runTest(dispatchTimeoutMs = 0) {
54+
// below is some arbitrary concurrent code where all dispatches go through the same scheduler.
55+
launch {
56+
delay(2000)
57+
}
58+
val deferred = async {
59+
val job = launch(TestCoroutineDispatcher(testScheduler)) {
60+
launch {
61+
delay(500)
62+
}
63+
delay(1000)
64+
}
65+
job.join()
66+
}
67+
deferred.await()
68+
}
69+
70+
/** Tests that a dispatch timeout of `0` will fail the test if there are some dispatches outside the scheduler. */
71+
@Test
72+
fun testRunTestWithZeroTimeoutWithUncontrolledDispatches() = testResultMap({ fn ->
73+
assertFailsWith<UncompletedCoroutinesError> { fn() }
74+
}) {
75+
runTest(dispatchTimeoutMs = 0) {
76+
withContext(Dispatchers.Default) {
77+
delay(10)
78+
3
79+
}
80+
throw IllegalStateException("shouldn't be reached")
81+
}
82+
}
83+
84+
/** Tests that too low of a dispatch timeout causes crashes. */
85+
@Test
86+
fun testRunTestWithSmallTimeout() = testResultMap({ fn ->
87+
assertFailsWith<UncompletedCoroutinesError> { fn() }
88+
}) {
89+
runTest(dispatchTimeoutMs = 100) {
90+
withContext(Dispatchers.Default) {
91+
delay(10000)
92+
3
93+
}
94+
throw RuntimeException("shouldn't be reached")
95+
}
96+
}
97+
98+
/** Tests that too low of a dispatch timeout causes crashes. */
99+
@Test
100+
fun testRunTestWithLargeTimeout() = runTest(dispatchTimeoutMs = 5000) {
101+
withContext(Dispatchers.Default) {
102+
delay(50)
103+
}
104+
}
105+
106+
/** Tests uncaught exceptions taking priority over dispatch timeout in error reports. */
107+
@Test
108+
fun testRunTestTimingOutAndThrowing() = testResultMap({ fn ->
109+
assertFailsWith<IllegalArgumentException> { fn() }
110+
}) {
111+
runTest(dispatchTimeoutMs = 1) {
112+
coroutineContext[CoroutineExceptionHandler]!!.handleException(coroutineContext, IllegalArgumentException())
113+
withContext(Dispatchers.Default) {
114+
delay(10000)
115+
3
116+
}
117+
throw RuntimeException("shouldn't be reached")
118+
}
119+
}
120+
53121
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
9+
actual fun testResultMap(block: (() -> Unit) -> Unit, test: () -> TestResult): TestResult =
10+
GlobalScope.promise {
11+
val promise = test()
12+
promise.then(
13+
{
14+
block {
15+
}
16+
}, {
17+
block {
18+
throw it
19+
}
20+
})
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
package kotlinx.coroutines.test
5+
6+
import kotlinx.coroutines.*
7+
8+
actual fun testResultMap(block: (() -> Unit) -> Unit, test: () -> TestResult) {
9+
block {
10+
test()
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
package kotlinx.coroutines.test
5+
6+
import kotlinx.coroutines.*
7+
8+
actual fun testResultMap(block: (() -> Unit) -> Unit, test: () -> TestResult) {
9+
block {
10+
test()
11+
}
12+
}

0 commit comments

Comments
 (0)