<lambda>null1 package kotlinx.coroutines.test
2
3 import kotlinx.coroutines.*
4 import kotlinx.coroutines.channels.*
5 import kotlinx.coroutines.flow.*
6 import kotlinx.coroutines.testing.*
7 import kotlin.test.*
8 import kotlin.test.assertFailsWith
9
10 /** Copy of [RunTestTest], but for [runBlockingTestOnTestScope], where applicable. */
11 @Suppress("DEPRECATION", "DEPRECATION_ERROR")
12 class RunBlockingTestOnTestScopeTest {
13
14 @Test
15 fun testRunTestWithIllegalContext() {
16 for (ctx in TestScopeTest.invalidContexts) {
17 assertFailsWith<IllegalArgumentException> {
18 runBlockingTestOnTestScope(ctx) { }
19 }
20 }
21 }
22
23 @Test
24 fun testThrowingInRunTestBody() {
25 assertFailsWith<RuntimeException> {
26 runBlockingTestOnTestScope {
27 throw RuntimeException()
28 }
29 }
30 }
31
32 @Test
33 fun testThrowingInRunTestPendingTask() {
34 assertFailsWith<RuntimeException> {
35 runBlockingTestOnTestScope {
36 launch {
37 delay(SLOW)
38 throw RuntimeException()
39 }
40 }
41 }
42 }
43
44 @Test
45 fun reproducer2405() = runBlockingTestOnTestScope {
46 val dispatcher = StandardTestDispatcher(testScheduler)
47 var collectedError = false
48 withContext(dispatcher) {
49 flow { emit(1) }
50 .combine(
51 flow<String> { throw IllegalArgumentException() }
52 ) { int, string -> int.toString() + string }
53 .catch { emit("error") }
54 .collect {
55 assertEquals("error", it)
56 collectedError = true
57 }
58 }
59 assertTrue(collectedError)
60 }
61
62 @Test
63 fun testChildrenCancellationOnTestBodyFailure() {
64 var job: Job? = null
65 assertFailsWith<AssertionError> {
66 runBlockingTestOnTestScope {
67 job = launch {
68 while (true) {
69 delay(1000)
70 }
71 }
72 throw AssertionError()
73 }
74 }
75 assertTrue(job!!.isCancelled)
76 }
77
78 @Test
79 fun testTimeout() {
80 assertFailsWith<TimeoutCancellationException> {
81 runBlockingTestOnTestScope {
82 withTimeout(50) {
83 launch {
84 delay(1000)
85 }
86 }
87 }
88 }
89 }
90
91 @Test
92 fun testRunTestThrowsRootCause() {
93 assertFailsWith<TestException> {
94 runBlockingTestOnTestScope {
95 launch {
96 throw TestException()
97 }
98 }
99 }
100 }
101
102 @Test
103 fun testCompletesOwnJob() {
104 var handlerCalled = false
105 runBlockingTestOnTestScope {
106 coroutineContext.job.invokeOnCompletion {
107 handlerCalled = true
108 }
109 }
110 assertTrue(handlerCalled)
111 }
112
113 @Test
114 fun testDoesNotCompleteGivenJob() {
115 var handlerCalled = false
116 val job = Job()
117 job.invokeOnCompletion {
118 handlerCalled = true
119 }
120 runBlockingTestOnTestScope(job) {
121 assertTrue(coroutineContext.job in job.children)
122 }
123 assertFalse(handlerCalled)
124 assertEquals(0, job.children.filter { it.isActive }.count())
125 }
126
127 @Test
128 fun testSuppressedExceptions() {
129 try {
130 runBlockingTestOnTestScope {
131 launch(SupervisorJob()) { throw TestException("x") }
132 launch(SupervisorJob()) { throw TestException("y") }
133 launch(SupervisorJob()) { throw TestException("z") }
134 throw TestException("w")
135 }
136 fail("should not be reached")
137 } catch (e: TestException) {
138 assertEquals("w", e.message)
139 val suppressed = e.suppressedExceptions +
140 (e.suppressedExceptions.firstOrNull()?.suppressedExceptions ?: emptyList())
141 assertEquals(3, suppressed.size)
142 assertEquals("x", suppressed[0].message)
143 assertEquals("y", suppressed[1].message)
144 assertEquals("z", suppressed[2].message)
145 }
146 }
147
148 @Test
149 fun testScopeRunTestExceptionHandler(): TestResult {
150 val scope = TestCoroutineScope()
151 return testResultMap({
152 try {
153 it()
154 fail("should not be reached")
155 } catch (e: TestException) {
156 // expected
157 }
158 }) {
159 scope.runTest {
160 launch(SupervisorJob()) { throw TestException("x") }
161 }
162 }
163 }
164
165 @Test
166 fun testBackgroundWorkBeingRun() = runBlockingTestOnTestScope {
167 var i = 0
168 var j = 0
169 backgroundScope.launch {
170 yield()
171 ++i
172 }
173 backgroundScope.launch {
174 yield()
175 delay(10)
176 ++j
177 }
178 assertEquals(0, i)
179 assertEquals(0, j)
180 delay(1)
181 assertEquals(1, i)
182 assertEquals(0, j)
183 delay(10)
184 assertEquals(1, i)
185 assertEquals(1, j)
186 }
187
188 @Test
189 fun testBackgroundWorkCancelled() {
190 var cancelled = false
191 runBlockingTestOnTestScope {
192 var i = 0
193 backgroundScope.launch {
194 yield()
195 try {
196 while (isActive) {
197 ++i
198 yield()
199 }
200 } catch (e: CancellationException) {
201 cancelled = true
202 }
203 }
204 repeat(5) {
205 assertEquals(i, it)
206 yield()
207 }
208 }
209 assertTrue(cancelled)
210 }
211
212 @Test
213 fun testBackgroundWorkTimeControl(): TestResult = runBlockingTestOnTestScope {
214 var i = 0
215 var j = 0
216 backgroundScope.launch {
217 yield()
218 while (true) {
219 ++i
220 delay(100)
221 }
222 }
223 backgroundScope.launch {
224 yield()
225 while (true) {
226 ++j
227 delay(50)
228 }
229 }
230 advanceUntilIdle() // should do nothing, as only background work is left.
231 assertEquals(0, i)
232 assertEquals(0, j)
233 val job = launch {
234 delay(1)
235 // the background work scheduled for earlier gets executed before the normal work scheduled for later does
236 assertEquals(1, i)
237 assertEquals(1, j)
238 }
239 job.join()
240 advanceTimeBy(199) // should work the same for the background tasks
241 assertEquals(2, i)
242 assertEquals(4, j)
243 advanceUntilIdle() // once again, should do nothing
244 assertEquals(2, i)
245 assertEquals(4, j)
246 runCurrent() // should behave the same way as for the normal work
247 assertEquals(3, i)
248 assertEquals(5, j)
249 launch {
250 delay(1001)
251 assertEquals(13, i)
252 assertEquals(25, j)
253 }
254 advanceUntilIdle() // should execute the normal work, and with that, the background one, too
255 }
256
257 @Test
258 fun testBackgroundWorkErrorReporting() {
259 var testFinished = false
260 val exception = RuntimeException("x")
261 try {
262 runBlockingTestOnTestScope {
263 backgroundScope.launch {
264 throw exception
265 }
266 delay(1000)
267 testFinished = true
268 }
269 fail("unreached")
270 } catch (e: Throwable) {
271 assertSame(e, exception)
272 assertTrue(testFinished)
273 }
274 }
275
276 @Test
277 fun testBackgroundWorkFinalizing() {
278 var taskEnded = 0
279 val nTasks = 10
280 try {
281 runBlockingTestOnTestScope {
282 repeat(nTasks) {
283 backgroundScope.launch {
284 try {
285 while (true) {
286 delay(1)
287 }
288 } finally {
289 ++taskEnded
290 if (taskEnded <= 2)
291 throw TestException()
292 }
293 }
294 }
295 delay(100)
296 throw TestException()
297 }
298 fail("unreached")
299 } catch (e: TestException) {
300 assertEquals(2, e.suppressedExceptions.size)
301 assertEquals(nTasks, taskEnded)
302 }
303 }
304
305 @Test
306 fun testExampleBackgroundJob1() = runBlockingTestOnTestScope {
307 val myFlow = flow {
308 yield()
309 var i = 0
310 while (true) {
311 emit(++i)
312 delay(1)
313 }
314 }
315 val stateFlow = myFlow.stateIn(backgroundScope, SharingStarted.Eagerly, 0)
316 var j = 0
317 repeat(100) {
318 assertEquals(j++, stateFlow.value)
319 delay(1)
320 }
321 }
322
323 @Test
324 fun testExampleBackgroundJob2() = runBlockingTestOnTestScope {
325 val channel = Channel<Int>()
326 backgroundScope.launch {
327 var i = 0
328 while (true) {
329 channel.send(i++)
330 }
331 }
332 repeat(100) {
333 assertEquals(it, channel.receive())
334 }
335 }
336
337 @Test
338 fun testAsyncFailureInBackgroundReported() =
339 try {
340 runBlockingTestOnTestScope {
341 backgroundScope.async {
342 throw TestException("x")
343 }
344 backgroundScope.produce<Unit> {
345 throw TestException("y")
346 }
347 delay(1)
348 throw TestException("z")
349 }
350 fail("unreached")
351 } catch (e: TestException) {
352 assertEquals("z", e.message)
353 assertEquals(setOf("x", "y"), e.suppressedExceptions.map { it.message }.toSet())
354 }
355
356 @Test
357 fun testNoDuplicateExceptions() =
358 try {
359 runBlockingTestOnTestScope {
360 backgroundScope.launch {
361 throw TestException("x")
362 }
363 delay(1)
364 throw TestException("y")
365 }
366 fail("unreached")
367 } catch (e: TestException) {
368 assertEquals("y", e.message)
369 assertEquals(listOf("x"), e.suppressedExceptions.map { it.message })
370 }
371 }
372