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 }