1 package kotlinx.coroutines 2 3 import kotlinx.coroutines.testing.* 4 import kotlinx.coroutines.selects.* 5 import kotlinx.coroutines.sync.* 6 import kotlin.test.* 7 8 class AtomicCancellationCommonTest : TestBase() { 9 @Test <lambda>null10 fun testCancellableLaunch() = runTest { 11 expect(1) 12 val job = launch { 13 expectUnreached() // will get cancelled before start 14 } 15 expect(2) 16 job.cancel() 17 finish(3) 18 } 19 20 @Test <lambda>null21 fun testAtomicLaunch() = runTest { 22 expect(1) 23 val job = launch(start = CoroutineStart.ATOMIC) { 24 finish(4) // will execute even after it was cancelled 25 } 26 expect(2) 27 job.cancel() 28 expect(3) 29 } 30 31 @Test <lambda>null32 fun testUndispatchedLaunch() = runTest { 33 expect(1) 34 assertFailsWith<CancellationException> { 35 withContext(Job()) { 36 cancel() 37 launch(start = CoroutineStart.UNDISPATCHED) { 38 expect(2) 39 yield() 40 expectUnreached() 41 } 42 } 43 } 44 finish(3) 45 } 46 47 @Test <lambda>null48 fun testUndispatchedLaunchWithUnconfinedContext() = runTest { 49 expect(1) 50 assertFailsWith<CancellationException> { 51 withContext(Dispatchers.Unconfined + Job()) { 52 cancel() 53 launch(start = CoroutineStart.UNDISPATCHED) { 54 expect(2) 55 yield() 56 expectUnreached() 57 } 58 } 59 } 60 finish(3) 61 } 62 63 @Test <lambda>null64 fun testDeferredAwaitCancellable() = runTest { 65 expect(1) 66 val deferred = async { // deferred, not yet complete 67 expect(4) 68 "OK" 69 } 70 assertEquals(false, deferred.isCompleted) 71 var job: Job? = null 72 launch { // will cancel job as soon as deferred completes 73 expect(5) 74 assertEquals(true, deferred.isCompleted) 75 job!!.cancel() 76 } 77 job = launch(start = CoroutineStart.UNDISPATCHED) { 78 expect(2) 79 try { 80 deferred.await() // suspends 81 expectUnreached() // will not execute -- cancelled while dispatched 82 } finally { 83 finish(7) // but will execute finally blocks 84 } 85 } 86 expect(3) // continues to execute when the job suspends 87 yield() // to deferred & canceller 88 expect(6) 89 } 90 91 @Test testJobJoinCancellablenull92 fun testJobJoinCancellable() = runTest { 93 expect(1) 94 val jobToJoin = launch { // not yet complete 95 expect(4) 96 } 97 assertEquals(false, jobToJoin.isCompleted) 98 var job: Job? = null 99 launch { // will cancel job as soon as jobToJoin completes 100 expect(5) 101 assertEquals(true, jobToJoin.isCompleted) 102 job!!.cancel() 103 } 104 job = launch(start = CoroutineStart.UNDISPATCHED) { 105 expect(2) 106 try { 107 jobToJoin.join() // suspends 108 expectUnreached() // will not execute -- cancelled while dispatched 109 } finally { 110 finish(7) // but will execute finally blocks 111 } 112 } 113 expect(3) // continues to execute when the job suspends 114 yield() // to jobToJoin & canceller 115 expect(6) 116 } 117 118 @Test <lambda>null119 fun testLockCancellable() = runTest { 120 expect(1) 121 val mutex = Mutex(true) // locked mutex 122 val job = launch(start = CoroutineStart.UNDISPATCHED) { 123 expect(2) 124 mutex.lock() // suspends 125 expectUnreached() // should NOT execute because of cancellation 126 } 127 expect(3) 128 mutex.unlock() // unlock mutex first 129 job.cancel() // cancel the job next 130 yield() // now yield 131 finish(4) 132 } 133 134 @Test <lambda>null135 fun testSelectLockCancellable() = runTest { 136 expect(1) 137 val mutex = Mutex(true) // locked mutex 138 val job = launch(start = CoroutineStart.UNDISPATCHED) { 139 expect(2) 140 select<String> { // suspends 141 mutex.onLock { 142 expect(4) 143 "OK" 144 } 145 } 146 expectUnreached() // should NOT execute because of cancellation 147 } 148 expect(3) 149 mutex.unlock() // unlock mutex first 150 job.cancel() // cancel the job next 151 yield() // now yield 152 finish(4) 153 } 154 } 155