After doing some local testing and looking through the source code, I think I understand how it works.
I tested it locally with a simple workflow that looks like this:
class TimeSkipWorkflowImpl : TimeSkipWorkflow {
private val logger = wfLogger()
private var cond: Boolean = false
override fun start() {
logger.info("Starting, awaiting up to 1 day for cond")
val awaitResult = Workflow.await(1.days.toJavaDuration()) { cond }
logger.info("Await done, cond = $cond, awaitResult = $awaitResult")
}
override fun setCond() {
cond = true
}
And I see that when I execute the workflow, it blocks at the Workflow.await
until I either do one of two things:
- I do
TestEnvironment.sleep
for more than 1 day to trigger the timeout, OR
- I send the
setCond
signal, OR
- I request a result from the workflow.
The first and second behavior makes perfect sense for testing. The third behavior was surprising to me, but also makes sense for testing, and is consistent with the statement:
It skips the time automatically when the workflow under test is blocked.
Looking at the code, I see that the test service has a “time locking counter”. This counter allows the test code to switch between moving time forward “normally” (real-time pace), or to skip time.
The test workflow starts out with time skipping locked/disabled. The workflow will therefore proceed at real-time, allowing the first and second behaviors above to work as expected.
However, when behavior three is run, the test environment has explicit logic to decrement the “time locking counter”, which unlocks/activates time skipping and allows the await
to complete/timeout. Once the result has been obtained, the test environment increments the counter and therefore locks/deactivates time skipping again.
The fact that it is a counter allows multiple test threads to do this safely.
And of course time skipping can be disabled in the TestWorkflowEnvironment
via setting useTimeskipping
in the TestEnvironmentOptions
to false. This simply increments the counter on environment startup so that time skipping never unlocks/activates.
Useful code references:
All the calls to unlock time skipping are from calls to get a result from the workflow. All the calls to lock time skipping are after the result has been obtained.