• 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 
5 @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
6 
7 package kotlinx.coroutines
8 
9 import kotlinx.coroutines.internal.*
10 import kotlin.coroutines.*
11 import kotlin.test.*
12 
13 class CancellableContinuationHandlersTest : TestBase() {
14 
15     @Test
16     fun testDoubleSubscription() = runTest({ it is IllegalStateException }) {
17         suspendCancellableCoroutine<Unit> { c ->
18             c.invokeOnCancellation { finish(1) }
19             c.invokeOnCancellation { expectUnreached() }
20         }
21     }
22 
23     @Test
24     fun testDoubleSubscriptionAfterCompletion() = runTest {
25         suspendCancellableCoroutine<Unit> { c ->
26             c.resume(Unit)
27             // First invokeOnCancellation is Ok
28             c.invokeOnCancellation { expectUnreached() }
29             // Second invokeOnCancellation is not allowed
30             assertFailsWith<IllegalStateException> { c.invokeOnCancellation { expectUnreached() } }
31         }
32     }
33 
34     @Test
35     fun testDoubleSubscriptionAfterCompletionWithException() = runTest {
36         assertFailsWith<TestException> {
37             suspendCancellableCoroutine<Unit> { c ->
38                 c.resumeWithException(TestException())
39                 // First invokeOnCancellation is Ok
40                 c.invokeOnCancellation { expectUnreached() }
41                 // Second invokeOnCancellation is not allowed
42                 assertFailsWith<IllegalStateException> { c.invokeOnCancellation { expectUnreached() } }
43             }
44         }
45     }
46 
47     @Test
48     fun testDoubleSubscriptionAfterCancellation() = runTest {
49         try {
50             suspendCancellableCoroutine<Unit> { c ->
51                 c.cancel()
52                 c.invokeOnCancellation {
53                     assertTrue(it is CancellationException)
54                     expect(1)
55                 }
56                 assertFailsWith<IllegalStateException> { c.invokeOnCancellation { expectUnreached() } }
57             }
58         } catch (e: CancellationException) {
59             finish(2)
60         }
61     }
62 
63     @Test
64     fun testSecondSubscriptionAfterCancellation() = runTest {
65         try {
66             suspendCancellableCoroutine<Unit> { c ->
67                 // Set IOC first
68                 c.invokeOnCancellation {
69                     assertNull(it)
70                     expect(2)
71                 }
72                 expect(1)
73                 // then cancel (it gets called)
74                 c.cancel()
75                 // then try to install another one
76                 assertFailsWith<IllegalStateException> { c.invokeOnCancellation { expectUnreached() } }
77             }
78         } catch (e: CancellationException) {
79             finish(3)
80         }
81     }
82 
83     @Test
84     fun testSecondSubscriptionAfterResumeCancelAndDispatch() = runTest {
85         var cont: CancellableContinuation<Unit>? = null
86         val job = launch(start = CoroutineStart.UNDISPATCHED) {
87             // will be cancelled during dispatch
88             assertFailsWith<CancellationException> {
89                 suspendCancellableCoroutine<Unit> { c ->
90                     cont = c
91                     // Set IOC first -- not called (completed)
92                     c.invokeOnCancellation {
93                         assertTrue(it is CancellationException)
94                         expect(4)
95                     }
96                     expect(1)
97                 }
98             }
99             expect(5)
100         }
101         expect(2)
102         // then resume it
103         cont!!.resume(Unit) // schedule cancelled continuation for dispatch
104         // then cancel the job during dispatch
105         job.cancel()
106         expect(3)
107         yield() // finish dispatching (will call IOC handler here!)
108         expect(6)
109         // then try to install another one after we've done dispatching it
110         assertFailsWith<IllegalStateException> {
111             cont!!.invokeOnCancellation { expectUnreached() }
112         }
113         finish(7)
114     }
115 
116     @Test
117     fun testDoubleSubscriptionAfterCancellationWithCause() = runTest {
118         try {
119             suspendCancellableCoroutine<Unit> { c ->
120                 c.cancel(AssertionError())
121                 c.invokeOnCancellation {
122                     require(it is AssertionError)
123                     expect(1)
124                 }
125                 assertFailsWith<IllegalStateException> { c.invokeOnCancellation { expectUnreached() } }
126             }
127         } catch (e: AssertionError) {
128             finish(2)
129         }
130     }
131 
132     @Test
133     fun testDoubleSubscriptionMixed() = runTest {
134         try {
135             suspendCancellableCoroutine<Unit> { c ->
136                 c.invokeOnCancellation {
137                     require(it is IndexOutOfBoundsException)
138                     expect(1)
139                 }
140                 c.cancel(IndexOutOfBoundsException())
141                 assertFailsWith<IllegalStateException> { c.invokeOnCancellation { expectUnreached() } }
142             }
143         } catch (e: IndexOutOfBoundsException) {
144             finish(2)
145         }
146     }
147 
148     @Test
149     fun testExceptionInHandler() = runTest(
150         unhandled = listOf({ it -> it is CompletionHandlerException })
151     ) {
152         expect(1)
153         try {
154             suspendCancellableCoroutine<Unit> { c ->
155                 c.invokeOnCancellation { throw AssertionError() }
156                 c.cancel()
157             }
158         } catch (e: CancellationException) {
159             expect(2)
160         }
161         finish(3)
162     }
163 
164     @Test
165     fun testSegmentAsHandler() = runTest {
166         class MySegment : Segment<MySegment>(0, null, 0) {
167             override val numberOfSlots: Int get() = 0
168 
169             var invokeOnCancellationCalled = false
170             override fun onCancellation(index: Int, cause: Throwable?, context: CoroutineContext) {
171                 invokeOnCancellationCalled = true
172             }
173         }
174         val s = MySegment()
175         expect(1)
176         try {
177             suspendCancellableCoroutine<Unit> { c ->
178                 expect(2)
179                 c as CancellableContinuationImpl<*>
180                 c.invokeOnCancellation(s, 0)
181                 c.cancel()
182             }
183         } catch (e: CancellationException) {
184             expect(3)
185         }
186         expect(4)
187         check(s.invokeOnCancellationCalled)
188         finish(5)
189     }
190 }
191