<lambda>null1 @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
2
3 package kotlinx.coroutines
4
5 import kotlinx.coroutines.testing.*
6 import kotlin.test.*
7
8 /**
9 * Test for [CancellableContinuation.resume] with `onCancellation` parameter.
10 */
11 @Suppress("DEPRECATION")
12 class CancellableResumeOldTest : TestBase() {
13 @Test
14 fun testResumeImmediateNormally() = runTest {
15 expect(1)
16 val ok = suspendCancellableCoroutine<String> { cont ->
17 expect(2)
18 cont.invokeOnCancellation { expectUnreached() }
19 cont.resume("OK") { expectUnreached() }
20 expect(3)
21 }
22 assertEquals("OK", ok)
23 finish(4)
24 }
25
26 @Test
27 fun testResumeImmediateAfterCancel() = runTest(
28 expected = { it is TestException }
29 ) {
30 expect(1)
31 suspendCancellableCoroutine<String> { cont ->
32 expect(2)
33 cont.invokeOnCancellation { expect(3) }
34 cont.cancel(TestException("FAIL"))
35 expect(4)
36 cont.resume("OK") { cause ->
37 expect(5)
38 assertIs<TestException>(cause)
39 }
40 finish(6)
41 }
42 expectUnreached()
43 }
44
45 @Test
46 fun testResumeImmediateAfterCancelWithHandlerFailure() = runTest(
47 expected = { it is TestException },
48 unhandled = listOf(
49 { it is CompletionHandlerException && it.cause is TestException2 },
50 { it is CompletionHandlerException && it.cause is TestException3 }
51 )
52 ) {
53 expect(1)
54 suspendCancellableCoroutine<String> { cont ->
55 expect(2)
56 cont.invokeOnCancellation {
57 expect(3)
58 throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
59 }
60 cont.cancel(TestException("FAIL"))
61 expect(4)
62 cont.resume("OK") { cause ->
63 expect(5)
64 assertIs<TestException>(cause)
65 throw TestException3("FAIL") // onCancellation block fails with exception
66 }
67 finish(6)
68 }
69 expectUnreached()
70 }
71
72 @Test
73 fun testResumeImmediateAfterIndirectCancel() = runTest(
74 expected = { it is CancellationException }
75 ) {
76 expect(1)
77 val ctx = coroutineContext
78 suspendCancellableCoroutine<String> { cont ->
79 expect(2)
80 cont.invokeOnCancellation { expect(3) }
81 ctx.cancel()
82 expect(4)
83 cont.resume("OK") {
84 expect(5)
85 }
86 finish(6)
87 }
88 expectUnreached()
89 }
90
91 @Test
92 fun testResumeImmediateAfterIndirectCancelWithHandlerFailure() = runTest(
93 expected = { it is CancellationException },
94 unhandled = listOf(
95 { it is CompletionHandlerException && it.cause is TestException2 },
96 { it is CompletionHandlerException && it.cause is TestException3 }
97 )
98 ) {
99 expect(1)
100 val ctx = coroutineContext
101 suspendCancellableCoroutine<String> { cont ->
102 expect(2)
103 cont.invokeOnCancellation {
104 expect(3)
105 throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
106 }
107 ctx.cancel()
108 expect(4)
109 cont.resume("OK") {
110 expect(5)
111 throw TestException3("FAIL") // onCancellation block fails with exception
112 }
113 finish(6)
114 }
115 expectUnreached()
116 }
117
118 @Test
119 fun testResumeLaterNormally() = runTest {
120 expect(1)
121 lateinit var cc: CancellableContinuation<String>
122 launch(start = CoroutineStart.UNDISPATCHED) {
123 expect(2)
124 val ok = suspendCancellableCoroutine<String> { cont ->
125 expect(3)
126 cont.invokeOnCancellation { expectUnreached() }
127 cc = cont
128 }
129 assertEquals("OK", ok)
130 finish(6)
131 }
132 expect(4)
133 cc.resume("OK") { expectUnreached() }
134 expect(5)
135 }
136
137 @Test
138 fun testResumeLaterAfterCancel() = runTest {
139 expect(1)
140 lateinit var cc: CancellableContinuation<String>
141 val job = launch(start = CoroutineStart.UNDISPATCHED) {
142 expect(2)
143 try {
144 suspendCancellableCoroutine<String> { cont ->
145 expect(3)
146 cont.invokeOnCancellation { expect(5) }
147 cc = cont
148 }
149 expectUnreached()
150 } catch (e: CancellationException) {
151 finish(9)
152 }
153 }
154 expect(4)
155 job.cancel(TestCancellationException())
156 expect(6)
157 cc.resume("OK") { cause ->
158 expect(7)
159 assertIs<TestCancellationException>(cause)
160 }
161 expect(8)
162 }
163
164 @Test
165 fun testResumeLaterAfterCancelWithHandlerFailure() = runTest(
166 unhandled = listOf(
167 { it is CompletionHandlerException && it.cause is TestException2 },
168 { it is CompletionHandlerException && it.cause is TestException3 }
169 )
170 ) {
171 expect(1)
172 lateinit var cc: CancellableContinuation<String>
173 val job = launch(start = CoroutineStart.UNDISPATCHED) {
174 expect(2)
175 try {
176 suspendCancellableCoroutine<String> { cont ->
177 expect(3)
178 cont.invokeOnCancellation {
179 expect(5)
180 throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
181 }
182 cc = cont
183 }
184 expectUnreached()
185 } catch (e: CancellationException) {
186 finish(9)
187 }
188 }
189 expect(4)
190 job.cancel(TestCancellationException())
191 expect(6)
192 cc.resume("OK") { cause ->
193 expect(7)
194 assertIs<TestCancellationException>(cause)
195 throw TestException3("FAIL") // onCancellation block fails with exception
196 }
197 expect(8)
198 }
199
200 @Test
201 fun testResumeCancelWhileDispatched() = runTest {
202 expect(1)
203 lateinit var cc: CancellableContinuation<String>
204 val job = launch(start = CoroutineStart.UNDISPATCHED) {
205 expect(2)
206 try {
207 suspendCancellableCoroutine<String> { cont ->
208 expect(3)
209 // resumed first, dispatched, then cancelled, but still got invokeOnCancellation call
210 cont.invokeOnCancellation { cause ->
211 // Note: invokeOnCancellation is called before cc.resume(value) { ... } handler
212 expect(7)
213 assertIs<TestCancellationException>(cause)
214 }
215 cc = cont
216 }
217 expectUnreached()
218 } catch (e: CancellationException) {
219 expect(9)
220 }
221 }
222 expect(4)
223 cc.resume("OK") { cause ->
224 // Note: this handler is called after invokeOnCancellation handler
225 expect(8)
226 assertIs<TestCancellationException>(cause)
227 }
228 expect(5)
229 job.cancel(TestCancellationException()) // cancel while execution is dispatched
230 expect(6)
231 yield() // to coroutine -- throws cancellation exception
232 finish(10)
233 }
234
235 @Test
236 fun testResumeCancelWhileDispatchedWithHandlerFailure() = runTest(
237 unhandled = listOf(
238 { it is CompletionHandlerException && it.cause is TestException2 },
239 { it is CompletionHandlerException && it.cause is TestException3 }
240 )
241 ) {
242 expect(1)
243 lateinit var cc: CancellableContinuation<String>
244 val job = launch(start = CoroutineStart.UNDISPATCHED) {
245 expect(2)
246 try {
247 suspendCancellableCoroutine<String> { cont ->
248 expect(3)
249 // resumed first, dispatched, then cancelled, but still got invokeOnCancellation call
250 cont.invokeOnCancellation { cause ->
251 // Note: invokeOnCancellation is called before cc.resume(value) { ... } handler
252 expect(7)
253 assertIs<TestCancellationException>(cause)
254 throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
255 }
256 cc = cont
257 }
258 expectUnreached()
259 } catch (e: CancellationException) {
260 expect(9)
261 }
262 }
263 expect(4)
264 cc.resume("OK") { cause ->
265 // Note: this handler is called after invokeOnCancellation handler
266 expect(8)
267 assertIs<TestCancellationException>(cause)
268 throw TestException3("FAIL") // onCancellation block fails with exception
269 }
270 expect(5)
271 job.cancel(TestCancellationException()) // cancel while execution is dispatched
272 expect(6)
273 yield() // to coroutine -- throws cancellation exception
274 finish(10)
275 }
276
277 @Test
278 fun testResumeUnconfined() = runTest {
279 val outerScope = this
280 withContext(Dispatchers.Unconfined) {
281 val result = suspendCancellableCoroutine<String> {
282 outerScope.launch {
283 it.resume("OK") {
284 expectUnreached()
285 }
286 }
287 }
288 assertEquals("OK", result)
289 }
290 }
291 }
292