Skip to content

Commit b4f99fa

Browse files
committed
Use test-specific unconfined when test scheduler is in use
This retains the test scheduler virtual time. Unfortunately this requires taking a dependency on the test library and also relying on unstable API.
1 parent 9b6c033 commit b4f99fa

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ kotlin {
4242
commonMain {
4343
dependencies {
4444
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"
45+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.coroutines}"
4546
}
4647
}
4748
commonTest {
4849
dependencies {
4950
implementation 'org.jetbrains.kotlin:kotlin-test'
50-
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.coroutines}"
5151
}
5252
}
5353
}

src/commonMain/kotlin/app/cash/turbine/flow.kt

+10-1
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ import kotlinx.coroutines.CancellationException
2121
import kotlinx.coroutines.CoroutineScope
2222
import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
2323
import kotlinx.coroutines.Dispatchers.Unconfined
24+
import kotlinx.coroutines.ExperimentalCoroutinesApi
2425
import kotlinx.coroutines.channels.Channel
2526
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
2627
import kotlinx.coroutines.coroutineScope
2728
import kotlinx.coroutines.flow.Flow
2829
import kotlinx.coroutines.job
2930
import kotlinx.coroutines.launch
31+
import kotlinx.coroutines.test.TestCoroutineScheduler
32+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
3033

3134
/**
3235
* Terminal flow operator that collects events from given flow and allows the [validate] lambda to
@@ -108,10 +111,16 @@ public fun <T> Flow<T>.testIn(scope: CoroutineScope): ReceiveTurbine<T> {
108111
return turbine
109112
}
110113

114+
@OptIn(ExperimentalCoroutinesApi::class) // New kotlinx.coroutines test APIs are not stable 😬
111115
private fun <T> Flow<T>.collectTurbineIn(scope: CoroutineScope): Turbine<T> {
112116
lateinit var channel: Channel<T>
113117

114-
val job = scope.launch(Unconfined, start = UNDISPATCHED) {
118+
// Use test-specific unconfined if test scheduler is in use to inherit its virtual time.
119+
val unconfined = scope.coroutineContext[TestCoroutineScheduler]
120+
?.let(::UnconfinedTestDispatcher)
121+
?: Unconfined
122+
123+
val job = scope.launch(unconfined, start = UNDISPATCHED) {
115124
channel = collectIntoChannel(this)
116125
}
117126

src/commonTest/kotlin/app/cash/turbine/FlowTest.kt

+16
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ import kotlin.test.assertFailsWith
2222
import kotlin.test.assertFalse
2323
import kotlin.test.assertSame
2424
import kotlin.test.assertTrue
25+
import kotlin.time.Duration.Companion.seconds
26+
import kotlin.time.ExperimentalTime
27+
import kotlin.time.measureTime
2528
import kotlinx.coroutines.CompletableDeferred
2629
import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
2730
import kotlinx.coroutines.Dispatchers.Unconfined
2831
import kotlinx.coroutines.NonCancellable
2932
import kotlinx.coroutines.channels.Channel
3033
import kotlinx.coroutines.channels.Channel.Factory.RENDEZVOUS
34+
import kotlinx.coroutines.delay
3135
import kotlinx.coroutines.flow.MutableSharedFlow
3236
import kotlinx.coroutines.flow.MutableStateFlow
3337
import kotlinx.coroutines.flow.emitAll
@@ -520,4 +524,16 @@ class FlowTest {
520524
assertSame(error, actual.cause)
521525
}
522526
}
527+
528+
@OptIn(ExperimentalTime::class)
529+
@Test fun turbineSkipsDelaysInRunTest() = runTest {
530+
val took = measureTime {
531+
flow<Nothing> {
532+
delay(5.seconds)
533+
}.test {
534+
awaitComplete()
535+
}
536+
}
537+
assertTrue(took < 5.seconds, "$took > 5s")
538+
}
523539
}

0 commit comments

Comments
 (0)