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..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 { @@ -208,7 +221,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..3042c3fe 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 { @@ -734,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 {