diff --git a/kotlinx-coroutines-test/common/test/Helpers.kt b/kotlinx-coroutines-test/common/test/Helpers.kt index 885ce15a04..c0117ccb2f 100644 --- a/kotlinx-coroutines-test/common/test/Helpers.kt +++ b/kotlinx-coroutines-test/common/test/Helpers.kt @@ -60,4 +60,6 @@ open class OrderedExecutionTestBase { fun ensureFinishCalls() { assertTrue(finished.value || actionIndex.value == 0, "Expected `finish` to be called") } -} \ No newline at end of file +} + +internal fun T.void() { } \ No newline at end of file diff --git a/kotlinx-coroutines-test/common/test/StandardTestDispatcherTest.kt b/kotlinx-coroutines-test/common/test/StandardTestDispatcherTest.kt index 09f9169a6e..7e8a6ad158 100644 --- a/kotlinx-coroutines-test/common/test/StandardTestDispatcherTest.kt +++ b/kotlinx-coroutines-test/common/test/StandardTestDispatcherTest.kt @@ -10,18 +10,48 @@ import kotlin.test.* class StandardTestDispatcherTest: OrderedExecutionTestBase() { + private val scope = createTestCoroutineScope(StandardTestDispatcher()) + + @AfterTest + fun cleanup() = scope.cleanupTestCoroutines() + /** Tests that the [StandardTestDispatcher] follows an execution order similar to `runBlocking`. */ @Test - fun testFlowsNotSkippingValues() { - val scope = createTestCoroutineScope(StandardTestDispatcher()) + fun testFlowsNotSkippingValues() = scope.launch { + // https://github.com/Kotlin/kotlinx.coroutines/issues/1626#issuecomment-554632852 + val list = flowOf(1).onStart { emit(0) } + .combine(flowOf("A")) { int, str -> "$str$int" } + .toList() + assertEquals(list, listOf("A0", "A1")) + }.void() + + /** Tests that each [launch] gets dispatched. */ + @Test + fun testLaunchDispatched() = scope.launch { + expect(1) + launch { + expect(3) + } + finish(2) + }.void() + + /** Tests that dispatching is done in a predictable order and [yield] puts this task at the end of the queue. */ + @Test + fun testYield() = scope.launch { + expect(1) + scope.launch { + expect(3) + yield() + expect(6) + } scope.launch { - // https://github.com/Kotlin/kotlinx.coroutines/issues/1626#issuecomment-554632852 - val list = flowOf(1).onStart { emit(0) } - .combine(flowOf("A")) { int, str -> "$str$int" } - .toList() - assertEquals(list, listOf("A0", "A1")) + expect(4) + yield() + finish(7) } - scope.cleanupTestCoroutines() - } + expect(2) + yield() + expect(5) + }.void() } \ No newline at end of file diff --git a/kotlinx-coroutines-test/common/test/TestCoroutineSchedulerTest.kt b/kotlinx-coroutines-test/common/test/TestCoroutineSchedulerTest.kt index d43daeb156..4917541bde 100644 --- a/kotlinx-coroutines-test/common/test/TestCoroutineSchedulerTest.kt +++ b/kotlinx-coroutines-test/common/test/TestCoroutineSchedulerTest.kt @@ -193,6 +193,46 @@ class TestCoroutineSchedulerTest { scope.runCurrent() } + /** Tests that dispatching the delayed tasks is ordered by their waking times. */ + @Test + fun testDelaysPriority() = forTestDispatchers { + val scope = createTestCoroutineScope(it) + var lastMeasurement = 0L + fun checkTime(time: Long) { + assertTrue(lastMeasurement < time) + assertEquals(time, scope.currentTime) + lastMeasurement = scope.currentTime + } + scope.launch { + launch { + delay(100) + checkTime(100) + val deferred = async { + delay(70) + checkTime(170) + } + delay(1) + checkTime(101) + deferred.await() + delay(1) + checkTime(171) + } + launch { + delay(200) + checkTime(200) + } + launch { + delay(150) + checkTime(150) + delay(22) + checkTime(172) + } + delay(201) + } + scope.advanceUntilIdle() + checkTime(201) + } + private fun TestCoroutineScope.checkTimeout( timesOut: Boolean, timeoutMillis: Long = SLOW, block: suspend () -> Unit ) = assertRunsFast { @@ -269,7 +309,13 @@ class TestCoroutineSchedulerTest { } private fun forTestDispatchers(block: (TestDispatcher) -> Unit): Unit = - listOf(TestCoroutineDispatcher(), StandardTestDispatcher(), UnconfinedTestDispatcher()).forEach { + @Suppress("DEPRECATION") + listOf( + TestCoroutineDispatcher(), + TestCoroutineDispatcher().also { it.pauseDispatcher() }, + StandardTestDispatcher(), + UnconfinedTestDispatcher() + ).forEach { try { block(it) } catch (e: Throwable) {