1 /* <lambda>null2 * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines.tasks 6 7 import com.google.android.gms.tasks.* 8 import kotlinx.coroutines.* 9 import org.junit.* 10 import org.junit.Test 11 import java.util.concurrent.locks.* 12 import kotlin.concurrent.* 13 import kotlin.test.* 14 15 class TaskTest : TestBase() { 16 @Before 17 fun setup() { 18 ignoreLostThreads("ForkJoinPool.commonPool-worker-") 19 } 20 21 @Test 22 fun testCompletedDeferredAsTask() = runTest { 23 expect(1) 24 val deferred = async(start = CoroutineStart.UNDISPATCHED) { 25 expect(2) // Completed immediately 26 "OK" 27 } 28 expect(3) 29 val task = deferred.asTask() 30 assertEquals("OK", task.await()) 31 finish(4) 32 } 33 34 @Test 35 fun testDeferredAsTask() = runTest { 36 expect(1) 37 val deferred = async { 38 expect(3) // Completed later 39 "OK" 40 } 41 expect(2) 42 val task = deferred.asTask() 43 assertEquals("OK", task.await()) 44 finish(4) 45 } 46 47 @Test 48 fun testCancelledAsTask() = runTest { 49 val deferred = async(Dispatchers.Default) { 50 delay(100) 51 }.apply { cancel() } 52 53 val task = deferred.asTask() 54 try { 55 runTest { task.await() } 56 } catch (e: Exception) { 57 assertTrue(e is CancellationException) 58 assertTrue(task.isCanceled) 59 } 60 } 61 62 @Test 63 fun testThrowingAsTask() = runTest({ e -> e is TestException }) { 64 val deferred = async<Int>(Dispatchers.Default) { 65 throw TestException("Fail") 66 } 67 68 val task = deferred.asTask() 69 runTest(expected = { it is TestException }) { 70 task.await() 71 } 72 } 73 74 @Test 75 fun testStateAsTask() = runTest { 76 val lock = ReentrantLock().apply { lock() } 77 78 val deferred: Deferred<Int> = Tasks.call { 79 lock.withLock { 42 } 80 }.asDeferred() 81 82 assertFalse(deferred.isCompleted) 83 lock.unlock() 84 85 assertEquals(42, deferred.await()) 86 assertTrue(deferred.isCompleted) 87 } 88 89 @Test 90 fun testTaskAsDeferred() = runTest { 91 val deferred = Tasks.forResult(42).asDeferred() 92 assertEquals(42, deferred.await()) 93 } 94 95 @Test 96 fun testNullResultTaskAsDeferred() = runTest { 97 assertNull(Tasks.forResult(null).asDeferred().await()) 98 } 99 100 @Test 101 fun testCancelledTaskAsDeferred() = runTest { 102 val deferred = Tasks.forCanceled<Int>().asDeferred() 103 104 assertTrue(deferred.isCancelled) 105 try { 106 deferred.await() 107 fail("deferred.await() should be cancelled") 108 } catch (e: Exception) { 109 assertTrue(e is CancellationException) 110 } 111 } 112 113 @Test 114 fun testFailedTaskAsDeferred() = runTest { 115 val deferred = Tasks.forException<Int>(TestException("something went wrong")).asDeferred() 116 117 assertTrue(deferred.isCancelled && deferred.isCompleted) 118 val completionException = deferred.getCompletionExceptionOrNull()!! 119 assertTrue(completionException is TestException) 120 assertEquals("something went wrong", completionException.message) 121 122 try { 123 deferred.await() 124 fail("deferred.await() should throw an exception") 125 } catch (e: Exception) { 126 assertTrue(e is TestException) 127 assertEquals("something went wrong", e.message) 128 } 129 } 130 131 @Test 132 fun testFailingTaskAsDeferred() = runTest { 133 val lock = ReentrantLock().apply { lock() } 134 135 val deferred: Deferred<Int> = Tasks.call { 136 lock.withLock { throw TestException("something went wrong") } 137 }.asDeferred() 138 139 assertFalse(deferred.isCompleted) 140 lock.unlock() 141 142 try { 143 deferred.await() 144 fail("deferred.await() should throw an exception") 145 } catch (e: Exception) { 146 assertTrue(e is TestException) 147 assertEquals("something went wrong", e.message) 148 assertSame(e.cause, deferred.getCompletionExceptionOrNull()) // debug mode stack augmentation 149 } 150 } 151 152 @Test 153 fun testCancellableTaskAsDeferred() = runTest { 154 val cancellationTokenSource = CancellationTokenSource() 155 val deferred = Tasks.forResult(42).asDeferred(cancellationTokenSource) 156 assertEquals(42, deferred.await()) 157 assertTrue(cancellationTokenSource.token.isCancellationRequested) 158 } 159 160 @Test 161 fun testNullResultCancellableTaskAsDeferred() = runTest { 162 val cancellationTokenSource = CancellationTokenSource() 163 assertNull(Tasks.forResult(null).asDeferred(cancellationTokenSource).await()) 164 assertTrue(cancellationTokenSource.token.isCancellationRequested) 165 } 166 167 @Test 168 fun testCancelledCancellableTaskAsDeferred() = runTest { 169 val cancellationTokenSource = CancellationTokenSource() 170 val deferred = Tasks.forCanceled<Int>().asDeferred(cancellationTokenSource) 171 172 assertTrue(deferred.isCancelled) 173 try { 174 deferred.await() 175 fail("deferred.await() should be cancelled") 176 } catch (e: Exception) { 177 assertTrue(e is CancellationException) 178 } 179 assertTrue(cancellationTokenSource.token.isCancellationRequested) 180 } 181 182 @Test 183 fun testCancellingCancellableTaskAsDeferred() = runTest { 184 val cancellationTokenSource = CancellationTokenSource() 185 val task = TaskCompletionSource<Int>(cancellationTokenSource.token).task 186 val deferred = task.asDeferred(cancellationTokenSource) 187 188 deferred.cancel() 189 try { 190 deferred.await() 191 fail("deferred.await() should be cancelled") 192 } catch (e: Exception) { 193 assertTrue(e is CancellationException) 194 } 195 assertTrue(cancellationTokenSource.token.isCancellationRequested) 196 } 197 198 @Test 199 fun testExternallyCancelledCancellableTaskAsDeferred() = runTest { 200 val cancellationTokenSource = CancellationTokenSource() 201 val task = TaskCompletionSource<Int>(cancellationTokenSource.token).task 202 val deferred = task.asDeferred(cancellationTokenSource) 203 204 cancellationTokenSource.cancel() 205 206 try { 207 deferred.await() 208 fail("deferred.await() should be cancelled") 209 } catch (e: Exception) { 210 assertTrue(e is CancellationException) 211 } 212 assertTrue(cancellationTokenSource.token.isCancellationRequested) 213 } 214 215 @Test 216 fun testSeparatelyCancelledCancellableTaskAsDeferred() = runTest { 217 val cancellationTokenSource = CancellationTokenSource() 218 val task = TaskCompletionSource<Int>().task 219 task.asDeferred(cancellationTokenSource) 220 221 cancellationTokenSource.cancel() 222 223 assertTrue(cancellationTokenSource.token.isCancellationRequested) 224 } 225 226 @Test 227 fun testFailedCancellableTaskAsDeferred() = runTest { 228 val cancellationTokenSource = CancellationTokenSource() 229 val deferred = Tasks.forException<Int>(TestException("something went wrong")).asDeferred(cancellationTokenSource) 230 231 assertTrue(deferred.isCancelled && deferred.isCompleted) 232 val completionException = deferred.getCompletionExceptionOrNull()!! 233 assertTrue(completionException is TestException) 234 assertEquals("something went wrong", completionException.message) 235 236 try { 237 deferred.await() 238 fail("deferred.await() should throw an exception") 239 } catch (e: Exception) { 240 assertTrue(e is TestException) 241 assertEquals("something went wrong", e.message) 242 } 243 assertTrue(cancellationTokenSource.token.isCancellationRequested) 244 } 245 246 @Test 247 fun testFailingCancellableTaskAsDeferred() = runTest { 248 val cancellationTokenSource = CancellationTokenSource() 249 val lock = ReentrantLock().apply { lock() } 250 251 val deferred: Deferred<Int> = Tasks.call { 252 lock.withLock { throw TestException("something went wrong") } 253 }.asDeferred(cancellationTokenSource) 254 255 assertFalse(deferred.isCompleted) 256 lock.unlock() 257 258 try { 259 deferred.await() 260 fail("deferred.await() should throw an exception") 261 } catch (e: Exception) { 262 assertTrue(e is TestException) 263 assertEquals("something went wrong", e.message) 264 assertSame(e.cause, deferred.getCompletionExceptionOrNull()) // debug mode stack augmentation 265 } 266 assertTrue(cancellationTokenSource.token.isCancellationRequested) 267 } 268 269 @Test 270 fun testFastPathCompletedTaskWithCancelledTokenSourceAsDeferred() = runTest { 271 val cancellationTokenSource = CancellationTokenSource() 272 val deferred = Tasks.forResult(42).asDeferred(cancellationTokenSource) 273 cancellationTokenSource.cancel() 274 assertEquals(42, deferred.await()) 275 } 276 277 @Test 278 fun testAwaitCancellableTask() = runTest { 279 val cancellationTokenSource = CancellationTokenSource() 280 val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token) 281 282 val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) { 283 taskCompletionSource.task.await(cancellationTokenSource) 284 } 285 286 assertFalse(deferred.isCompleted) 287 taskCompletionSource.setResult(42) 288 289 assertEquals(42, deferred.await()) 290 assertTrue(deferred.isCompleted) 291 } 292 293 @Test 294 fun testFailedAwaitTask() = runTest(expected = { it is TestException }) { 295 val cancellationTokenSource = CancellationTokenSource() 296 val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token) 297 298 val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) { 299 taskCompletionSource.task.await(cancellationTokenSource) 300 } 301 302 assertFalse(deferred.isCompleted) 303 taskCompletionSource.setException(TestException("something went wrong")) 304 305 deferred.await() 306 } 307 308 @Test 309 fun testCancelledAwaitCancellableTask() = runTest { 310 val cancellationTokenSource = CancellationTokenSource() 311 val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token) 312 313 val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) { 314 taskCompletionSource.task.await(cancellationTokenSource) 315 } 316 317 assertFalse(deferred.isCompleted) 318 // Cancel the deferred 319 deferred.cancel() 320 321 try { 322 deferred.await() 323 fail("deferred.await() should be cancelled") 324 } catch (e: Exception) { 325 assertTrue(e is CancellationException) 326 } 327 328 assertTrue(cancellationTokenSource.token.isCancellationRequested) 329 } 330 331 @Test 332 fun testExternallyCancelledAwaitCancellableTask() = runTest { 333 val cancellationTokenSource = CancellationTokenSource() 334 val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token) 335 336 val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) { 337 taskCompletionSource.task.await(cancellationTokenSource) 338 } 339 340 assertFalse(deferred.isCompleted) 341 // Cancel the cancellation token source 342 cancellationTokenSource.cancel() 343 344 try { 345 deferred.await() 346 fail("deferred.await() should be cancelled") 347 } catch (e: Exception) { 348 assertTrue(e is CancellationException) 349 } 350 351 assertTrue(cancellationTokenSource.token.isCancellationRequested) 352 } 353 354 @Test 355 fun testFastPathCancellationTokenSourceCancelledAwaitCancellableTask() = runTest { 356 val cancellationTokenSource = CancellationTokenSource() 357 // Construct a task without the cancellation token source 358 val taskCompletionSource = TaskCompletionSource<Int>() 359 360 val deferred: Deferred<Int> = async(start = CoroutineStart.LAZY) { 361 taskCompletionSource.task.await(cancellationTokenSource) 362 } 363 364 assertFalse(deferred.isCompleted) 365 cancellationTokenSource.cancel() 366 367 // Cancelling the token doesn't cancel the deferred 368 assertTrue(cancellationTokenSource.token.isCancellationRequested) 369 assertFalse(deferred.isCompleted) 370 371 // Cleanup 372 deferred.cancel() 373 } 374 375 @Test 376 fun testSlowPathCancellationTokenSourceCancelledAwaitCancellableTask() = runTest { 377 val cancellationTokenSource = CancellationTokenSource() 378 // Construct a task without the cancellation token source 379 val taskCompletionSource = TaskCompletionSource<Int>() 380 381 val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) { 382 taskCompletionSource.task.await(cancellationTokenSource) 383 } 384 385 assertFalse(deferred.isCompleted) 386 cancellationTokenSource.cancel() 387 388 // Cancelling the token doesn't cancel the deferred 389 assertTrue(cancellationTokenSource.token.isCancellationRequested) 390 assertFalse(deferred.isCompleted) 391 392 // Cleanup 393 deferred.cancel() 394 } 395 396 @Test 397 fun testFastPathWithCompletedTaskAndCanceledTokenSourceAwaitTask() = runTest { 398 val firstCancellationTokenSource = CancellationTokenSource() 399 val secondCancellationTokenSource = CancellationTokenSource() 400 // Construct a task with a different cancellation token source 401 val taskCompletionSource = TaskCompletionSource<Int>(firstCancellationTokenSource.token) 402 403 val deferred: Deferred<Int> = async(start = CoroutineStart.LAZY) { 404 taskCompletionSource.task.await(secondCancellationTokenSource) 405 } 406 407 assertFalse(deferred.isCompleted) 408 secondCancellationTokenSource.cancel() 409 410 assertFalse(deferred.isCompleted) 411 taskCompletionSource.setResult(42) 412 413 assertEquals(42, deferred.await()) 414 assertTrue(deferred.isCompleted) 415 } 416 417 class TestException(message: String) : Exception(message) 418 } 419