From dad41dfc1f21faa99272e7e0e37d5a7fe3c80bb3 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sun, 24 Sep 2023 13:29:44 -0400 Subject: [PATCH 1/3] use virtual time for timeout --- src/commonMain/kotlin/app/cash/turbine/channel.kt | 7 +------ .../kotlin/app/cash/turbine/ChannelTest.kt | 12 ++++++------ .../kotlin/app/cash/turbine/FlowInScopeTest.kt | 4 +++- src/commonTest/kotlin/app/cash/turbine/FlowTest.kt | 11 ++++++----- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/commonMain/kotlin/app/cash/turbine/channel.kt b/src/commonMain/kotlin/app/cash/turbine/channel.kt index e77d69c6..ba4662a5 100644 --- a/src/commonMain/kotlin/app/cash/turbine/channel.kt +++ b/src/commonMain/kotlin/app/cash/turbine/channel.kt @@ -94,12 +94,7 @@ private suspend fun withAppropriateTimeout( timeout: Duration, block: suspend CoroutineScope.() -> T, ): T { - return if (coroutineContext[TestCoroutineScheduler] != null) { - // withTimeout uses virtual time, which will hang. - withWallclockTimeout(timeout, block) - } else { - withTimeout(timeout, block) - } + return withTimeout(timeout, block) } private suspend fun withWallclockTimeout( diff --git a/src/commonTest/kotlin/app/cash/turbine/ChannelTest.kt b/src/commonTest/kotlin/app/cash/turbine/ChannelTest.kt index 8d86d7c7..3cf2fe12 100644 --- a/src/commonTest/kotlin/app/cash/turbine/ChannelTest.kt +++ b/src/commonTest/kotlin/app/cash/turbine/ChannelTest.kt @@ -238,15 +238,15 @@ class ChannelTest { } @Test fun awaitHonorsCoroutineContextTimeoutNoTimeout() = runTest { - withTurbineTimeout(1500.milliseconds) { - val job = launch { - neverFlow().collectIntoChannel(this).awaitItem() - } + withContext(Dispatchers.Default) { + withTurbineTimeout(1500.milliseconds) { + val job = launch { + neverFlow().collectIntoChannel(this).awaitItem() + } - withContext(Dispatchers.Default) { delay(1100) + job.cancel() } - job.cancel() } } diff --git a/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt b/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt index 9c6b11e1..41a1899a 100644 --- a/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt +++ b/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt @@ -208,7 +208,9 @@ class FlowInScopeTest { delay(1100.milliseconds) } }.testIn(this, timeout = 1500.milliseconds) - turbine.awaitComplete() + withContext(Default) { + turbine.awaitComplete() + } } @Test fun awaitHonorsCoroutineContextTimeoutTimeout() = runTestTurbine { diff --git a/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt b/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt index 83e9e5a6..70032bde 100644 --- a/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt +++ b/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt @@ -608,6 +608,7 @@ class FlowTest { flow { delay(5.seconds) }.test { + advanceTimeBy(5.seconds) awaitComplete() } } @@ -629,13 +630,13 @@ class FlowTest { } @Test fun awaitHonorsTestTimeoutNoTimeout() = runTest { - flow { withContext(Default) { - delay(1100.milliseconds) + flow { + delay(1100.milliseconds) + }.test(timeout = 1500.milliseconds) { + awaitComplete() + } } - }.test(timeout = 1500.milliseconds) { - awaitComplete() - } } @Test fun awaitHonorsCoroutineContextTimeoutTimeout() = runTest { From 570fe472371631e2e015b2f72e93cb660f3c3008 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sun, 24 Sep 2023 14:04:43 -0400 Subject: [PATCH 2/3] add a test --- .../kotlin/app/cash/turbine/FlowTest.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt b/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt index 70032bde..3042c3fe 100644 --- a/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt +++ b/src/commonTest/kotlin/app/cash/turbine/FlowTest.kt @@ -735,6 +735,32 @@ class FlowTest { } } + @Test + fun delaysCanBeTested() = runTest { + val took = measureTime { + flow { + delay(5000) + emit("1") + delay(5000) + emit("2") + }.test { + expectNoEvents() + + advanceTimeBy(5000) + expectNoEvents() + + runCurrent() + assertEquals("1", awaitItem()) + + val exception = assertFailsWith { + awaitItem() + } + assertEquals(exception.message, "No value produced in 3s") + } + } + assertTrue(took < 1.seconds, "$took > 1s") + } + @Test fun timeoutsAreCaptured() = runTest { flow { From 7ba8b1fa078adbd78e8bcc65a2cdaefefec6e57e Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sun, 24 Sep 2023 17:31:08 -0400 Subject: [PATCH 3/3] add new tests suggested by @jingibus --- .../app/cash/turbine/FlowInScopeTest.kt | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt b/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt index 41a1899a..d7d1778b 100644 --- a/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt +++ b/src/commonTest/kotlin/app/cash/turbine/FlowInScopeTest.kt @@ -9,24 +9,16 @@ import kotlin.test.assertSame import kotlin.test.assertTrue import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CompletionHandlerException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers.Default -import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.withContext class FlowInScopeTest { @Test fun multipleFlows() = runTestTurbine { @@ -38,6 +30,27 @@ class FlowInScopeTest { turbine2.awaitComplete() } + @Test fun awaitFailsOnVirtualTime() = runTestTurbine { + assertFailsWith { + flow { + awaitCancellation() + }.test { + awaitComplete() + } + } + } + + @Test + fun awaitFailsOnDelayVirtualTime() = runTestTurbine { + assertFailsWith { + flow { + delay(1500.milliseconds) + }.test(timeout = 1000.milliseconds) { + awaitComplete() + } + } + } + @Test fun channelCancellation() = runTestTurbine { kotlin.runCatching {