aboutsummaryrefslogtreecommitdiffstats
path: root/kotlinx-coroutines-core/common/test/AsyncTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'kotlinx-coroutines-core/common/test/AsyncTest.kt')
-rw-r--r--kotlinx-coroutines-core/common/test/AsyncTest.kt275
1 files changed, 275 insertions, 0 deletions
diff --git a/kotlinx-coroutines-core/common/test/AsyncTest.kt b/kotlinx-coroutines-core/common/test/AsyncTest.kt
new file mode 100644
index 00000000..6fd4ebbe
--- /dev/null
+++ b/kotlinx-coroutines-core/common/test/AsyncTest.kt
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED", "UNREACHABLE_CODE", "USELESS_IS_CHECK") // KT-21913
+
+package kotlinx.coroutines
+
+import kotlin.test.*
+
+@Suppress("DEPRECATION") // cancel(cause)
+class AsyncTest : TestBase() {
+
+ @Test
+ fun testSimple() = runTest {
+ expect(1)
+ val d = async {
+ expect(3)
+ 42
+ }
+ expect(2)
+ assertTrue(d.isActive)
+ assertEquals(d.await(), 42)
+ assertTrue(!d.isActive)
+ expect(4)
+ assertEquals(d.await(), 42) // second await -- same result
+ finish(5)
+ }
+
+ @Test
+ fun testUndispatched() = runTest {
+ expect(1)
+ val d = async(start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ 42
+ }
+ expect(3)
+ assertTrue(!d.isActive)
+ assertEquals(d.await(), 42)
+ finish(4)
+ }
+
+ @Test
+ fun testSimpleException() = runTest(expected = { it is TestException }) {
+ expect(1)
+ val d = async {
+ finish(3)
+ throw TestException()
+ }
+ expect(2)
+ d.await() // will throw TestException
+ }
+
+ @Test
+ fun testCancellationWithCause() = runTest {
+ expect(1)
+ val d = async(NonCancellable, start = CoroutineStart.ATOMIC) {
+ expect(3)
+ yield()
+ }
+ expect(2)
+ d.cancel(TestCancellationException("TEST"))
+ try {
+ d.await()
+ } catch (e: TestCancellationException) {
+ finish(4)
+ assertEquals("TEST", e.message)
+ }
+ }
+
+ @Test
+ fun testLostException() = runTest {
+ expect(1)
+ val deferred = async(Job()) {
+ expect(2)
+ throw Exception()
+ }
+
+ // Exception is not consumed -> nothing is reported
+ deferred.join()
+ finish(3)
+ }
+
+ @Test
+ fun testParallelDecompositionCaughtException() = runTest {
+ val deferred = async(NonCancellable) {
+ val decomposed = async(NonCancellable) {
+ throw TestException()
+ 1
+ }
+ try {
+ decomposed.await()
+ } catch (e: TestException) {
+ 42
+ }
+ }
+ assertEquals(42, deferred.await())
+ }
+
+ @Test
+ fun testParallelDecompositionCaughtExceptionWithInheritedParent() = runTest {
+ expect(1)
+ val deferred = async(NonCancellable) {
+ expect(2)
+ val decomposed = async { // inherits parent job!
+ expect(3)
+ throw TestException()
+ 1
+ }
+ try {
+ decomposed.await()
+ } catch (e: TestException) {
+ expect(4) // Should catch this exception, but parent is already cancelled
+ 42
+ }
+ }
+ try {
+ // This will fail
+ assertEquals(42, deferred.await())
+ } catch (e: TestException) {
+ finish(5)
+ }
+ }
+
+ @Test
+ fun testParallelDecompositionUncaughtExceptionWithInheritedParent() = runTest(expected = { it is TestException }) {
+ val deferred = async(NonCancellable) {
+ val decomposed = async {
+ throw TestException()
+ 1
+ }
+
+ decomposed.await()
+ }
+
+ deferred.await()
+ expectUnreached()
+ }
+
+ @Test
+ fun testParallelDecompositionUncaughtException() = runTest(expected = { it is TestException }) {
+ val deferred = async(NonCancellable) {
+ val decomposed = async {
+ throw TestException()
+ 1
+ }
+
+ decomposed.await()
+ }
+
+ deferred.await()
+ expectUnreached()
+ }
+
+ @Test
+ fun testCancellationTransparency() = runTest {
+ val deferred = async(NonCancellable, start = CoroutineStart.ATOMIC) {
+ expect(2)
+ throw TestException()
+ }
+ expect(1)
+ deferred.cancel()
+ try {
+ deferred.await()
+ } catch (e: TestException) {
+ finish(3)
+ }
+ }
+
+ @Test
+ fun testDeferAndYieldException() = runTest(expected = { it is TestException }) {
+ expect(1)
+ val d = async {
+ expect(3)
+ yield() // no effect, parent waiting
+ finish(4)
+ throw TestException()
+ }
+ expect(2)
+ d.await() // will throw IOException
+ }
+
+ @Test
+ fun testDeferWithTwoWaiters() = runTest {
+ expect(1)
+ val d = async {
+ expect(5)
+ yield()
+ expect(9)
+ 42
+ }
+ expect(2)
+ launch {
+ expect(6)
+ assertEquals(d.await(), 42)
+ expect(11)
+ }
+ expect(3)
+ launch {
+ expect(7)
+ assertEquals(d.await(), 42)
+ expect(12)
+ }
+ expect(4)
+ yield() // this actually yields control to async, which produces results and resumes both waiters (in order)
+ expect(8)
+ yield() // yield again to "d", which completes
+ expect(10)
+ yield() // yield to both waiters
+ finish(13)
+ }
+
+ class BadClass {
+ override fun equals(other: Any?): Boolean = error("equals")
+ override fun hashCode(): Int = error("hashCode")
+ override fun toString(): String = error("toString")
+ }
+
+ @Test
+ fun testDeferBadClass() = runTest {
+ val bad = BadClass()
+ val d = async {
+ expect(1)
+ bad
+ }
+ assertSame(d.await(), bad)
+ finish(2)
+ }
+
+ @Test
+ fun testOverriddenParent() = runTest {
+ val parent = Job()
+ val deferred = async(parent, CoroutineStart.ATOMIC) {
+ expect(2)
+ delay(Long.MAX_VALUE)
+ }
+
+ parent.cancel()
+ try {
+ expect(1)
+ deferred.await()
+ } catch (e: CancellationException) {
+ finish(3)
+ }
+ }
+
+ @Test
+ fun testIncompleteAsyncState() = runTest {
+ val deferred = async {
+ coroutineContext[Job]!!.invokeOnCompletion { }
+ }
+
+ deferred.await().dispose()
+ assertTrue(deferred.getCompleted() is DisposableHandle)
+ assertNull(deferred.getCompletionExceptionOrNull())
+ assertTrue(deferred.isCompleted)
+ assertFalse(deferred.isActive)
+ assertFalse(deferred.isCancelled)
+ }
+
+ @Test
+ fun testIncompleteAsyncFastPath() = runTest {
+ val deferred = async(Dispatchers.Unconfined) {
+ coroutineContext[Job]!!.invokeOnCompletion { }
+ }
+
+ deferred.await().dispose()
+ assertTrue(deferred.getCompleted() is DisposableHandle)
+ assertNull(deferred.getCompletionExceptionOrNull())
+ assertTrue(deferred.isCompleted)
+ assertFalse(deferred.isActive)
+ assertFalse(deferred.isCancelled)
+ }
+
+}