Description
Describe the bug
The time source or the underlaying currentTime
is not updated after switching the context to a non delay skipping dispatcher.
Our use-case: Ktor
We want to change Ktors public test framework testApplication
from runBlocking
to runTest
, including its delay skipping behavior in tests, to align with the common test behavior of the well-known coroutines-test
api.
But if the test contains a request to an external real server, Ktor executes the test on Dispatchers.Default/IO
and uses a real-time clock to not mess up http connections, eg TLS handshakes.
After the request, the virtual time should be used again for user tests, but the virtual time isn't increased after the switch. This is confusing because in fact the time was increased.
It also does not work with custom user plugins testing with which could be part of the user tests, resulting into different times, see timeSource test as an example.
Provide a Reproducer
@Test
fun clock() = runTest {
assertEquals(0, currentTime)
delay(1.seconds)
assertEquals(1000, currentTime)
withContext(Dispatchers.IO) {
delay(2.seconds)
}
assertEquals(3000, currentTime) // fails: expected 3000, actual 1000
delay(1.seconds)
assertEquals(4000, currentTime)
}
@Test
fun timeSource() = runTest {
val timeSource = testScheduler.timeSource
val now = timeSource.markNow()
assertEquals(0.seconds, now.elapsedNow())
delay(1.seconds)
assertEquals(1.seconds, now.elapsedNow())
val time = measureTime {
withContext(Dispatchers.IO) {
delay(2.seconds)
}
}
assertTrue(time in (2.seconds)..(2.1.seconds))
assertEquals(1.seconds + time, now.elapsedNow()) // fails: expected 3s, actual 1s
delay(1.seconds)
assertEquals(2.seconds + time, now.elapsedNow())
}