1 @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED", "UNREACHABLE_CODE", "USELESS_IS_CHECK") // KT-21913 2 3 package kotlinx.coroutines 4 5 import kotlinx.coroutines.testing.* 6 import kotlin.test.* 7 8 class AsyncTest : TestBase() { 9 10 @Test <lambda>null11 fun testSimple() = runTest { 12 expect(1) 13 val d = async { 14 expect(3) 15 42 16 } 17 expect(2) 18 assertTrue(d.isActive) 19 assertEquals(d.await(), 42) 20 assertTrue(!d.isActive) 21 expect(4) 22 assertEquals(d.await(), 42) // second await -- same result 23 finish(5) 24 } 25 26 @Test testUndispatchednull27 fun testUndispatched() = runTest { 28 expect(1) 29 val d = async(start = CoroutineStart.UNDISPATCHED) { 30 expect(2) 31 42 32 } 33 expect(3) 34 assertTrue(!d.isActive) 35 assertEquals(d.await(), 42) 36 finish(4) 37 } 38 39 @Test <lambda>null40 fun testSimpleException() = runTest(expected = { it is TestException }) { 41 expect(1) <lambda>null42 val d = async<Unit> { 43 finish(3) 44 throw TestException() 45 } 46 expect(2) 47 d.await() // will throw TestException 48 } 49 50 @Test <lambda>null51 fun testCancellationWithCause() = runTest { 52 expect(1) 53 val d = async(NonCancellable, start = CoroutineStart.ATOMIC) { 54 expect(3) 55 yield() 56 } 57 expect(2) 58 d.cancel(TestCancellationException("TEST")) 59 try { 60 d.await() 61 } catch (e: TestCancellationException) { 62 finish(4) 63 assertEquals("TEST", e.message) 64 } 65 } 66 67 @Test <lambda>null68 fun testLostException() = runTest { 69 expect(1) 70 val deferred = async(Job()) { 71 expect(2) 72 throw Exception() 73 } 74 75 // Exception is not consumed -> nothing is reported 76 deferred.join() 77 finish(3) 78 } 79 80 @Test <lambda>null81 fun testParallelDecompositionCaughtException() = runTest { 82 val deferred = async(NonCancellable) { 83 val decomposed = async(NonCancellable) { 84 throw TestException() 85 1 86 } 87 try { 88 decomposed.await() 89 } catch (e: TestException) { 90 42 91 } 92 } 93 assertEquals(42, deferred.await()) 94 } 95 96 @Test <lambda>null97 fun testParallelDecompositionCaughtExceptionWithInheritedParent() = runTest { 98 expect(1) 99 val deferred = async(NonCancellable) { 100 expect(2) 101 val decomposed = async { // inherits parent job! 102 expect(3) 103 throw TestException() 104 1 105 } 106 try { 107 decomposed.await() 108 } catch (e: TestException) { 109 expect(4) // Should catch this exception, but parent is already cancelled 110 42 111 } 112 } 113 try { 114 // This will fail 115 assertEquals(42, deferred.await()) 116 } catch (e: TestException) { 117 finish(5) 118 } 119 } 120 121 @Test <lambda>null122 fun testParallelDecompositionUncaughtExceptionWithInheritedParent() = runTest(expected = { it is TestException }) { <lambda>null123 val deferred = async(NonCancellable) { 124 val decomposed = async { 125 throw TestException() 126 1 127 } 128 129 decomposed.await() 130 } 131 132 deferred.await() 133 expectUnreached() 134 } 135 136 @Test <lambda>null137 fun testParallelDecompositionUncaughtException() = runTest(expected = { it is TestException }) { <lambda>null138 val deferred = async(NonCancellable) { 139 val decomposed = async { 140 throw TestException() 141 1 142 } 143 144 decomposed.await() 145 } 146 147 deferred.await() 148 expectUnreached() 149 } 150 151 @Test <lambda>null152 fun testCancellationTransparency() = runTest { 153 val deferred = async(NonCancellable, start = CoroutineStart.ATOMIC) { 154 expect(2) 155 throw TestException() 156 } 157 expect(1) 158 deferred.cancel() 159 try { 160 deferred.await() 161 } catch (e: TestException) { 162 finish(3) 163 } 164 } 165 166 @Test <lambda>null167 fun testDeferAndYieldException() = runTest(expected = { it is TestException }) { 168 expect(1) <lambda>null169 val d = async<Unit> { 170 expect(3) 171 yield() // no effect, parent waiting 172 finish(4) 173 throw TestException() 174 } 175 expect(2) 176 d.await() // will throw IOException 177 } 178 179 @Test <lambda>null180 fun testDeferWithTwoWaiters() = runTest { 181 expect(1) 182 val d = async { 183 expect(5) 184 yield() 185 expect(9) 186 42 187 } 188 expect(2) 189 launch { 190 expect(6) 191 assertEquals(d.await(), 42) 192 expect(11) 193 } 194 expect(3) 195 launch { 196 expect(7) 197 assertEquals(d.await(), 42) 198 expect(12) 199 } 200 expect(4) 201 yield() // this actually yields control to async, which produces results and resumes both waiters (in order) 202 expect(8) 203 yield() // yield again to "d", which completes 204 expect(10) 205 yield() // yield to both waiters 206 finish(13) 207 } 208 209 @Test <lambda>null210 fun testDeferBadClass() = runTest { 211 val bad = BadClass() 212 val d = async { 213 expect(1) 214 bad 215 } 216 assertSame(d.await(), bad) 217 finish(2) 218 } 219 220 @Test <lambda>null221 fun testOverriddenParent() = runTest { 222 val parent = Job() 223 val deferred = async(parent, CoroutineStart.ATOMIC) { 224 expect(2) 225 delay(Long.MAX_VALUE) 226 } 227 228 parent.cancel() 229 try { 230 expect(1) 231 deferred.await() 232 } catch (e: CancellationException) { 233 finish(3) 234 } 235 } 236 237 @Test <lambda>null238 fun testIncompleteAsyncState() = runTest { 239 val deferred = async { 240 coroutineContext[Job]!!.invokeOnCompletion { } 241 } 242 243 deferred.await().dispose() 244 assertIs<DisposableHandle>(deferred.getCompleted()) 245 assertNull(deferred.getCompletionExceptionOrNull()) 246 assertTrue(deferred.isCompleted) 247 assertFalse(deferred.isActive) 248 assertFalse(deferred.isCancelled) 249 } 250 251 @Test <lambda>null252 fun testIncompleteAsyncFastPath() = runTest { 253 val deferred = async(Dispatchers.Unconfined) { 254 coroutineContext[Job]!!.invokeOnCompletion { } 255 } 256 257 deferred.await().dispose() 258 assertIs<DisposableHandle>(deferred.getCompleted()) 259 assertNull(deferred.getCompletionExceptionOrNull()) 260 assertTrue(deferred.isCompleted) 261 assertFalse(deferred.isActive) 262 assertFalse(deferred.isCancelled) 263 } 264 265 @Test <lambda>null266 fun testAsyncWithFinally() = runTest { 267 expect(1) 268 269 @Suppress("UNREACHABLE_CODE") 270 val d = async { 271 expect(3) 272 try { 273 yield() // to main, will cancel 274 } finally { 275 expect(6) // will go there on await 276 return@async "Fail" // result will not override cancellation 277 } 278 expectUnreached() 279 "Fail2" 280 } 281 expect(2) 282 yield() // to async 283 expect(4) 284 check(d.isActive && !d.isCompleted && !d.isCancelled) 285 d.cancel() 286 check(!d.isActive && !d.isCompleted && d.isCancelled) 287 check(!d.isActive && !d.isCompleted && d.isCancelled) 288 expect(5) 289 try { 290 d.await() // awaits 291 expectUnreached() // does not complete normally 292 } catch (e: Throwable) { 293 expect(7) 294 check(e is CancellationException) 295 } 296 check(!d.isActive && d.isCompleted && d.isCancelled) 297 finish(8) 298 } 299 } 300