@@ -9,6 +9,8 @@ import kotlinx.coroutines.internal.*
9
9
import kotlinx.coroutines.flow.*
10
10
import kotlin.coroutines.*
11
11
import kotlin.test.*
12
+ import kotlin.time.*
13
+ import kotlin.time.Duration.Companion.milliseconds
12
14
13
15
class RunTestTest {
14
16
@@ -52,7 +54,7 @@ class RunTestTest {
52
54
53
55
/* * Tests that even the dispatch timeout of `0` is fine if all the dispatches go through the same scheduler. */
54
56
@Test
55
- fun testRunTestWithZeroTimeoutWithControlledDispatches () = runTest(dispatchTimeoutMs = 0 ) {
57
+ fun testRunTestWithZeroDispatchTimeoutWithControlledDispatches () = runTest(dispatchTimeoutMs = 0 ) {
56
58
// below is some arbitrary concurrent code where all dispatches go through the same scheduler.
57
59
launch {
58
60
delay(2000 )
@@ -71,8 +73,13 @@ class RunTestTest {
71
73
72
74
/* * Tests that too low of a dispatch timeout causes crashes. */
73
75
@Test
74
- fun testRunTestWithSmallTimeout () = testResultMap({ fn ->
75
- assertFailsWith<UncompletedCoroutinesError > { fn() }
76
+ fun testRunTestWithSmallDispatchTimeout () = testResultMap({ fn ->
77
+ try {
78
+ fn()
79
+ fail(" shouldn't be reached" )
80
+ } catch (e: Throwable ) {
81
+ assertIs<UncompletedCoroutinesError >(e)
82
+ }
76
83
}) {
77
84
runTest(dispatchTimeoutMs = 100 ) {
78
85
withContext(Dispatchers .Default ) {
@@ -83,6 +90,48 @@ class RunTestTest {
83
90
}
84
91
}
85
92
93
+ /* *
94
+ * Tests that [runTest] times out after the specified time.
95
+ */
96
+ @Test
97
+ fun testRunTestWithSmallTimeout () = testResultMap({ fn ->
98
+ try {
99
+ fn()
100
+ fail(" shouldn't be reached" )
101
+ } catch (e: Throwable ) {
102
+ assertIs<UncompletedCoroutinesError >(e)
103
+ }
104
+ }) {
105
+ runTest(timeout = 100 .milliseconds) {
106
+ withContext(Dispatchers .Default ) {
107
+ delay(10000 )
108
+ 3
109
+ }
110
+ fail(" shouldn't be reached" )
111
+ }
112
+ }
113
+
114
+ /* * Tests that [runTest] times out after the specified time, even if the test framework always knows the test is
115
+ * still doing something. */
116
+ @Test
117
+ fun testRunTestWithSmallTimeoutAndManyDispatches () = testResultMap({ fn ->
118
+ try {
119
+ fn()
120
+ fail(" shouldn't be reached" )
121
+ } catch (e: Throwable ) {
122
+ assertIs<UncompletedCoroutinesError >(e)
123
+ }
124
+ }) {
125
+ runTest(timeout = 100 .milliseconds) {
126
+ while (true ) {
127
+ withContext(Dispatchers .Default ) {
128
+ delay(10 )
129
+ 3
130
+ }
131
+ }
132
+ }
133
+ }
134
+
86
135
/* * Tests that, on timeout, the names of the active coroutines are listed,
87
136
* whereas the names of the completed ones are not. */
88
137
@Test
@@ -119,26 +168,33 @@ class RunTestTest {
119
168
} catch (e: UncompletedCoroutinesError ) {
120
169
@Suppress(" INVISIBLE_MEMBER" )
121
170
val suppressed = unwrap(e).suppressedExceptions
122
- assertEquals(1 , suppressed.size)
171
+ assertEquals(1 , suppressed.size, " $suppressed " )
123
172
assertIs<TestException >(suppressed[0 ]).also {
124
173
assertEquals(" A" , it.message)
125
174
}
126
175
}
127
176
}) {
128
- runTest(dispatchTimeoutMs = 10 ) {
129
- launch {
130
- withContext(NonCancellable ) {
131
- awaitCancellation( )
177
+ runTest(timeout = 10 .milliseconds ) {
178
+ launch(start = CoroutineStart . UNDISPATCHED ) {
179
+ withContext(NonCancellable + Dispatchers . Default ) {
180
+ delay( 100 .milliseconds )
132
181
}
133
182
}
134
- yield ()
135
183
throw TestException (" A" )
136
184
}
137
185
}
138
186
139
187
/* * Tests that real delays can be accounted for with a large enough dispatch timeout. */
140
188
@Test
141
- fun testRunTestWithLargeTimeout () = runTest(dispatchTimeoutMs = 5000 ) {
189
+ fun testRunTestWithLargeDispatchTimeout () = runTest(dispatchTimeoutMs = 5000 ) {
190
+ withContext(Dispatchers .Default ) {
191
+ delay(50 )
192
+ }
193
+ }
194
+
195
+ /* * Tests that delays can be accounted for with a large enough timeout. */
196
+ @Test
197
+ fun testRunTestWithLargeTimeout () = runTest(timeout = 5000 .milliseconds) {
142
198
withContext(Dispatchers .Default ) {
143
199
delay(50 )
144
200
}
@@ -153,13 +209,13 @@ class RunTestTest {
153
209
} catch (e: UncompletedCoroutinesError ) {
154
210
@Suppress(" INVISIBLE_MEMBER" )
155
211
val suppressed = unwrap(e).suppressedExceptions
156
- assertEquals(1 , suppressed.size)
212
+ assertEquals(1 , suppressed.size, " $suppressed " )
157
213
assertIs<TestException >(suppressed[0 ]).also {
158
214
assertEquals(" A" , it.message)
159
215
}
160
216
}
161
217
}) {
162
- runTest(dispatchTimeoutMs = 1 ) {
218
+ runTest(timeout = 1 .milliseconds ) {
163
219
coroutineContext[CoroutineExceptionHandler ]!! .handleException(coroutineContext, TestException (" A" ))
164
220
withContext(Dispatchers .Default ) {
165
221
delay(10000 )
@@ -324,7 +380,7 @@ class RunTestTest {
324
380
}
325
381
}
326
382
327
- /* * Tests that [TestCoroutineScope .runTest] does not inherit the exception handler and works. */
383
+ /* * Tests that [TestScope .runTest] does not inherit the exception handler and works. */
328
384
@Test
329
385
fun testScopeRunTestExceptionHandler (): TestResult {
330
386
val scope = TestScope ()
@@ -349,7 +405,7 @@ class RunTestTest {
349
405
* The test will hang if this is not the case.
350
406
*/
351
407
@Test
352
- fun testCoroutineCompletingWithoutDispatch () = runTest(dispatchTimeoutMs = Long . MAX_VALUE ) {
408
+ fun testCoroutineCompletingWithoutDispatch () = runTest(timeout = Duration . INFINITE ) {
353
409
launch(Dispatchers .Default ) { delay(100 ) }
354
410
}
355
411
}
0 commit comments