blob: 55127e5c0bfe92f38e37bf7656956ab51d7a3aae [file] [log] [blame]
Roman Elizarov9d5abcd2017-12-21 16:54:30 +03001
Roman Elizarova7db8ec2017-12-21 22:45:12 +03002/*
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03003 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Roman Elizarova7db8ec2017-12-21 22:45:12 +03004 */
5
Louis CAD919284b2019-02-19 16:17:56 +01006@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-22237
Roman Elizarov4fe18012017-03-07 17:32:48 +03007
Roman Elizarov0950dfa2018-07-13 10:33:25 +03008package kotlinx.coroutines
Roman Elizarov4fe18012017-03-07 17:32:48 +03009
Roman Elizarov9d5abcd2017-12-21 16:54:30 +030010import kotlin.test.*
Roman Elizarov4fe18012017-03-07 17:32:48 +030011
Roman Elizarovaa461cf2018-04-11 13:20:29 +030012class WithContextTest : TestBase() {
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +030013
Roman Elizarov4fe18012017-03-07 17:32:48 +030014 @Test
Vsevolod Tolstopyatov06f57aa2018-07-24 19:51:21 +030015 fun testThrowException() = runTest {
16 expect(1)
17 try {
Vsevolod Tolstopyatovc022ab62019-05-14 15:10:09 +030018 withContext<Unit>(coroutineContext) {
Vsevolod Tolstopyatov06f57aa2018-07-24 19:51:21 +030019 expect(2)
20 throw AssertionError()
21 }
22 } catch (e: AssertionError) {
23 expect(3)
24 }
25
26 yield()
27 finish(4)
28 }
29
30 @Test
31 fun testThrowExceptionFromWrappedContext() = runTest {
32 expect(1)
33 try {
Vsevolod Tolstopyatovc022ab62019-05-14 15:10:09 +030034 withContext<Unit>(wrapperDispatcher(coroutineContext)) {
Vsevolod Tolstopyatov06f57aa2018-07-24 19:51:21 +030035 expect(2)
36 throw AssertionError()
37 }
38 } catch (e: AssertionError) {
39 expect(3)
40 }
41
42 yield()
43 finish(4)
44 }
45
46 @Test
Roman Elizarov8b38fa22017-09-27 17:44:31 +030047 fun testSameContextNoSuspend() = runTest {
Roman Elizarov4fe18012017-03-07 17:32:48 +030048 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +030049 launch(coroutineContext) { // make sure there is not early dispatch here
Roman Elizarov8b38fa22017-09-27 17:44:31 +030050 finish(5) // after main exits
Roman Elizarov4fe18012017-03-07 17:32:48 +030051 }
52 expect(2)
Roman Elizarovf9e13f52017-12-21 12:23:15 +030053 val result = withContext(coroutineContext) { // same context!
Roman Elizarov4fe18012017-03-07 17:32:48 +030054 expect(3) // still here
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +030055 "OK".wrap()
56 }.unwrap()
Roman Elizarov9d5abcd2017-12-21 16:54:30 +030057 assertEquals("OK", result)
Roman Elizarov8b38fa22017-09-27 17:44:31 +030058 expect(4)
59 // will wait for the first coroutine
Roman Elizarov4fe18012017-03-07 17:32:48 +030060 }
61
62 @Test
Roman Elizarov8b38fa22017-09-27 17:44:31 +030063 fun testSameContextWithSuspend() = runTest {
Roman Elizarov4fe18012017-03-07 17:32:48 +030064 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +030065 launch(coroutineContext) { // make sure there is not early dispatch here
Roman Elizarov4fe18012017-03-07 17:32:48 +030066 expect(4)
67 }
68 expect(2)
Roman Elizarovf9e13f52017-12-21 12:23:15 +030069 val result = withContext(coroutineContext) { // same context!
Roman Elizarov4fe18012017-03-07 17:32:48 +030070 expect(3) // still here
71 yield() // now yields to launch!
72 expect(5)
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +030073 "OK".wrap()
74 }.unwrap()
Roman Elizarov9d5abcd2017-12-21 16:54:30 +030075 assertEquals("OK", result)
Roman Elizarov4fe18012017-03-07 17:32:48 +030076 finish(6)
77 }
78
79 @Test
Roman Elizarov8b38fa22017-09-27 17:44:31 +030080 fun testCancelWithJobNoSuspend() = runTest {
Roman Elizarov4fe18012017-03-07 17:32:48 +030081 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +030082 launch(coroutineContext) { // make sure there is not early dispatch to here
Roman Elizarov8b38fa22017-09-27 17:44:31 +030083 finish(6) // after main exits
Roman Elizarov4fe18012017-03-07 17:32:48 +030084 }
85 expect(2)
86 val job = Job()
Vsevolod Tolstopyatov78832e32018-11-04 15:32:34 +030087 try {
88 withContext(coroutineContext + job) {
89 // same context + new job
90 expect(3) // still here
91 job.cancel() // cancel out job!
92 try {
93 yield() // shall throw CancellationException
94 expectUnreached()
95 } catch (e: CancellationException) {
96 expect(4)
97 }
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +030098 "OK".wrap()
Roman Elizarov4fe18012017-03-07 17:32:48 +030099 }
Vsevolod Tolstopyatov78832e32018-11-04 15:32:34 +0300100
101 expectUnreached()
102 } catch (e: CancellationException) {
103 expect(5)
104 // will wait for the first coroutine
Roman Elizarov4fe18012017-03-07 17:32:48 +0300105 }
Roman Elizarov4fe18012017-03-07 17:32:48 +0300106 }
107
108 @Test
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300109 fun testCancelWithJobWithSuspend() = runTest(
110 expected = { it is CancellationException }
111 ) {
Roman Elizarov4fe18012017-03-07 17:32:48 +0300112 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +0300113 launch(coroutineContext) { // make sure there is not early dispatch to here
Roman Elizarov4fe18012017-03-07 17:32:48 +0300114 expect(4)
115 }
116 expect(2)
117 val job = Job()
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300118 withContext(coroutineContext + job) { // same context + new job
Roman Elizarov4fe18012017-03-07 17:32:48 +0300119 expect(3) // still here
120 yield() // now yields to launch!
121 expect(5)
122 job.cancel() // cancel out job!
123 try {
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300124 yield() // shall throw CancellationException
Roman Elizarov4fe18012017-03-07 17:32:48 +0300125 expectUnreached()
126 } catch (e: CancellationException) {
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300127 finish(6)
Roman Elizarov4fe18012017-03-07 17:32:48 +0300128 }
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300129 "OK".wrap()
Roman Elizarov4fe18012017-03-07 17:32:48 +0300130 }
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300131 // still fails, because parent job was cancelled
132 expectUnreached()
Roman Elizarov4fe18012017-03-07 17:32:48 +0300133 }
134
135 @Test
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300136 fun testRunCancellableDefault() = runTest(
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300137 expected = { it is CancellationException }
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300138 ) {
Roman Elizarov4eae2a82017-05-17 20:55:27 +0300139 val job = Job()
140 job.cancel() // cancel before it has a chance to run
Roman Elizarovf9e13f52017-12-21 12:23:15 +0300141 withContext(job + wrapperDispatcher(coroutineContext)) {
Roman Elizarov4eae2a82017-05-17 20:55:27 +0300142 expectUnreached() // will get cancelled
143 }
144 }
145
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300146 @Test
Roman Elizarov0f943042018-10-08 16:13:01 +0300147 fun testRunCancellationUndispatchedVsException() = runTest {
148 expect(1)
149 var job: Job? = null
150 job = launch(start = CoroutineStart.UNDISPATCHED) {
151 expect(2)
152 try {
153 // Same dispatcher, different context
Vsevolod Tolstopyatovc022ab62019-05-14 15:10:09 +0300154 withContext<Unit>(CoroutineName("testRunCancellationUndispatchedVsException")) {
Roman Elizarov0f943042018-10-08 16:13:01 +0300155 expect(3)
156 yield() // must suspend
157 expect(5)
158 job!!.cancel() // cancel this job _before_ it throws
159 throw TestException()
160 }
161 } catch (e: TestException) {
162 // must have caught TextException
163 expect(6)
164 }
165 }
166 expect(4)
167 yield() // to coroutineScope
168 finish(7)
169 }
170
171 @Test
172 fun testRunCancellationDispatchedVsException() = runTest {
173 expect(1)
174 var job: Job? = null
175 job = launch(start = CoroutineStart.UNDISPATCHED) {
176 expect(2)
177 try {
178 // "Different" dispatcher (schedules execution back and forth)
Vsevolod Tolstopyatovc022ab62019-05-14 15:10:09 +0300179 withContext<Unit>(wrapperDispatcher(coroutineContext)) {
Roman Elizarov0f943042018-10-08 16:13:01 +0300180 expect(4)
181 yield() // must suspend
182 expect(6)
183 job!!.cancel() // cancel this job _before_ it throws
184 throw TestException()
185 }
186 } catch (e: TestException) {
187 // must have caught TextException
188 expect(8)
189 }
190 }
191 expect(3)
192 yield() // withContext is next
193 expect(5)
194 yield() // withContext again
195 expect(7)
196 yield() // to catch block
197 finish(9)
198 }
199
200 @Test
Roman Elizarov0aad8f12019-03-01 12:08:43 +0300201 fun testRunSelfCancellationWithException() = runTest {
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800202 expect(1)
203 var job: Job? = null
Roman Elizarovc32579e2018-09-09 19:21:43 +0300204 job = launch(Job()) {
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800205 try {
206 expect(3)
Vsevolod Tolstopyatovc022ab62019-05-14 15:10:09 +0300207 withContext<Unit>(wrapperDispatcher(coroutineContext)) {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300208 require(isActive)
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800209 expect(5)
Vsevolod Tolstopyatovb058ba12018-10-17 15:15:17 +0300210 job!!.cancel()
Vsevolod Tolstopyatov4aa18aa2018-04-17 15:43:12 +0300211 require(!isActive)
Roman Elizarov0aad8f12019-03-01 12:08:43 +0300212 throw TestException() // but throw an exception
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800213 }
214 } catch (e: Throwable) {
215 expect(7)
Roman Elizarov0aad8f12019-03-01 12:08:43 +0300216 // make sure TestException, not CancellationException is thrown
Roman Elizarov0d7323a2018-01-29 20:59:15 +0300217 assertTrue(e is TestException, "Caught $e")
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800218 }
219 }
220 expect(2)
221 yield() // to the launched job
222 expect(4)
223 yield() // again to the job
224 expect(6)
225 yield() // again to exception handler
226 finish(8)
227 }
228
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300229 @Test
Roman Elizarov0aad8f12019-03-01 12:08:43 +0300230 fun testRunSelfCancellation() = runTest {
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300231 expect(1)
232 var job: Job? = null
Roman Elizarovc32579e2018-09-09 19:21:43 +0300233 job = launch(Job()) {
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300234 try {
235 expect(3)
236 withContext(wrapperDispatcher(coroutineContext)) {
Vsevolod Tolstopyatov4aa18aa2018-04-17 15:43:12 +0300237 require(isActive)
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300238 expect(5)
Vsevolod Tolstopyatovb058ba12018-10-17 15:15:17 +0300239 job!!.cancel() // cancel itself
Vsevolod Tolstopyatov4aa18aa2018-04-17 15:43:12 +0300240 require(!isActive)
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300241 "OK".wrap()
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300242 }
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300243 expectUnreached()
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300244 } catch (e: Throwable) {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300245 expect(7)
Roman Elizarov0aad8f12019-03-01 12:08:43 +0300246 // make sure CancellationException is thrown
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300247 assertTrue(e is CancellationException, "Caught $e")
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300248 }
249 }
250
251 expect(2)
252 yield() // to the launched job
253 expect(4)
254 yield() // again to the job
255 expect(6)
256 yield() // again to exception handler
257 finish(8)
258 }
259
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300260 @Test
261 fun testWithContextScopeFailure() = runTest {
262 expect(1)
263 try {
264 withContext(wrapperDispatcher(coroutineContext)) {
265 expect(2)
266 // launch a child that fails
267 launch {
268 expect(4)
269 throw TestException()
270 }
271 expect(3)
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300272 "OK".wrap()
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300273 }
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300274 expectUnreached()
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300275 } catch (e: TestException) {
276 // ensure that we can catch exception outside of the scope
277 expect(5)
278 }
279 finish(6)
280 }
281
282 @Test
283 fun testWithContextChildWaitSameContext() = runTest {
284 expect(1)
285 withContext(coroutineContext) {
286 expect(2)
287 launch {
288 // ^^^ schedules to main thread
289 expect(4) // waits before return
290 }
291 expect(3)
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300292 "OK".wrap()
293 }.unwrap()
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300294 finish(5)
295 }
296
297 @Test
298 fun testWithContextChildWaitWrappedContext() = runTest {
299 expect(1)
300 withContext(wrapperDispatcher(coroutineContext)) {
301 expect(2)
302 launch {
303 // ^^^ schedules to main thread
304 expect(4) // waits before return
305 }
306 expect(3)
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300307 "OK".wrap()
308 }.unwrap()
Roman Elizarov0a656ff2018-09-25 16:33:42 +0300309 finish(5)
310 }
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300311
312 @Test
313 fun testIncompleteWithContextState() = runTest {
314 lateinit var ctxJob: Job
315 withContext(wrapperDispatcher(coroutineContext)) {
316 ctxJob = coroutineContext[Job]!!
317 ctxJob.invokeOnCompletion { }
318 }
319
320 ctxJob.join()
321 assertTrue(ctxJob.isCompleted)
322 assertFalse(ctxJob.isActive)
323 assertFalse(ctxJob.isCancelled)
324 }
325
Roman Elizarov4451d722019-02-22 14:27:20 +0300326 @Test
327 fun testWithContextCancelledJob() = runTest {
328 expect(1)
329 val job = Job()
330 job.cancel()
331 try {
332 withContext(job) {
333 expectUnreached()
334 }
335 } catch (e: CancellationException) {
336 expect(2)
337 }
338 finish(3)
339 }
340
341 @Test
342 fun testWithContextCancelledThisJob() = runTest(
343 expected = { it is CancellationException }
344 ) {
345 coroutineContext.cancel()
346 withContext(wrapperDispatcher(coroutineContext)) {
347 expectUnreached()
348 }
349 expectUnreached()
350 }
351
Vsevolod Tolstopyatovc022ab62019-05-14 15:10:09 +0300352 @Test
353 fun testSequentialCancellation() = runTest {
354 val job = launch {
355 expect(1)
356 withContext(wrapperDispatcher()) {
357 expect(2)
358 }
359 expectUnreached()
360 }
361
362 yield()
363 val job2 = launch {
364 expect(3)
365 job.cancel()
366 }
367
368 joinAll(job, job2)
369 finish(4)
370 }
371
Vsevolod Tolstopyatovecfd2272018-11-14 19:47:52 +0300372 private class Wrapper(val value: String) : Incomplete {
373 override val isActive: Boolean
374 get() = error("")
375 override val list: NodeList?
376 get() = error("")
377 }
378
379 private fun String.wrap() = Wrapper(this)
380 private fun Wrapper.unwrap() = value
Vsevolod Tolstopyatov931587a2018-04-16 17:51:12 +0300381}