• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
5 
6 package kotlinx.coroutines
7 
8 import kotlin.coroutines.*
9 import kotlin.test.*
10 
11 class CancellableContinuationTest : TestBase() {
12     @Test
13     fun testResumeWithExceptionAndResumeWithException() = runTest {
14         var continuation: Continuation<Unit>? = null
15         val job = launch {
16             try {
17                 expect(2)
18                 suspendCancellableCoroutine<Unit> { c ->
19                     continuation = c
20                 }
21             } catch (e: TestException) {
22                 expect(3)
23             }
24         }
25         expect(1)
26         yield()
27         continuation!!.resumeWithException(TestException())
28         yield()
29         assertFailsWith<IllegalStateException> { continuation!!.resumeWithException(TestException()) }
30         job.join()
31         finish(4)
32     }
33 
34     @Test
35     fun testResumeAndResumeWithException() = runTest {
36         var continuation: Continuation<Unit>? = null
37         val job = launch {
38             expect(2)
39             suspendCancellableCoroutine<Unit> { c ->
40                 continuation = c
41             }
42             expect(3)
43         }
44         expect(1)
45         yield()
46         continuation!!.resume(Unit)
47         job.join()
48         assertFailsWith<IllegalStateException> { continuation!!.resumeWithException(TestException()) }
49         finish(4)
50     }
51 
52     @Test
53     fun testResumeAndResume() = runTest {
54         var continuation: Continuation<Unit>? = null
55         val job = launch {
56             expect(2)
57             suspendCancellableCoroutine<Unit> { c ->
58                 continuation = c
59             }
60             expect(3)
61         }
62         expect(1)
63         yield()
64         continuation!!.resume(Unit)
65         job.join()
66         assertFailsWith<IllegalStateException> { continuation!!.resume(Unit) }
67         finish(4)
68     }
69 
70     /**
71      * Cancelling outer job may, in practise, race with attempt to resume continuation and resumes
72      * should be ignored. Here suspended coroutine is cancelled but then resumed with exception.
73      */
74     @Test
75     fun testCancelAndResumeWithException() = runTest {
76         var continuation: Continuation<Unit>? = null
77         val job = launch {
78             try {
79                 expect(2)
80                 suspendCancellableCoroutine<Unit> { c ->
81                     continuation = c
82                 }
83             } catch (e: CancellationException) {
84                 expect(3)
85             }
86         }
87         expect(1)
88         yield()
89         job.cancel() // Cancel job
90         yield()
91         continuation!!.resumeWithException(TestException()) // Should not fail
92         finish(4)
93     }
94 
95     /**
96      * Cancelling outer job may, in practise, race with attempt to resume continuation and resumes
97      * should be ignored. Here suspended coroutine is cancelled but then resumed with exception.
98      */
99     @Test
100     fun testCancelAndResume() = runTest {
101         var continuation: Continuation<Unit>? = null
102         val job = launch {
103             try {
104                 expect(2)
105                 suspendCancellableCoroutine<Unit> { c ->
106                     continuation = c
107                 }
108             } catch (e: CancellationException) {
109                 expect(3)
110             }
111         }
112         expect(1)
113         yield()
114         job.cancel() // Cancel job
115         yield()
116         continuation!!.resume(Unit) // Should not fail
117         finish(4)
118     }
119 
120     @Test
121     fun testCompleteJobWhileSuspended() = runTest {
122         expect(1)
123         val completableJob = Job()
124         val coroutineBlock = suspend {
125             assertFailsWith<CancellationException> {
126                 suspendCancellableCoroutine<Unit> { cont ->
127                     expect(2)
128                     assertSame(completableJob, cont.context[Job])
129                     completableJob.complete()
130                 }
131                 expectUnreached()
132             }
133             expect(3)
134         }
135         coroutineBlock.startCoroutine(Continuation(completableJob) {
136             assertEquals(Unit, it.getOrNull())
137             expect(4)
138         })
139         finish(5)
140     }
141 }