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