A GenServer that manages virtual time for testing time-dependent behaviors.
The VirtualClock maintains a virtual timestamp and scheduled events. Time can be advanced manually, triggering all events scheduled up to that point.
Example
iex> {:ok, clock} = VirtualClock.start_link()
iex> VirtualClock.now(clock)
0
iex> VirtualClock.advance(clock, 1000)
{:ok, 1000}
iex> VirtualClock.now(clock)
1000
Summary
Functions
Advances the virtual clock by the specified amount of milliseconds. All events scheduled up to the new time will be triggered.
Advances the virtual clock to the next scheduled event. Returns the amount advanced in milliseconds, or 0 if no events are scheduled.
Cancels a scheduled timer.
Returns a specification to start this module under a supervisor.
Gets the current virtual time.
Returns the number of events currently scheduled.
Returns the count of events scheduled up to a specific virtual time.
Schedules a message to be sent after a delay in virtual time (in milliseconds). Returns a reference that can be used to cancel the timer.
Starts a new virtual clock.
Waits for quiescence - when all scheduled events have been processed and no new events are being scheduled.
Waits for quiescence within a specific virtual time frame.
Functions
Advances the virtual clock by the specified amount of milliseconds. All events scheduled up to the new time will be triggered.
This ensures that:
- All events up to the target time are processed
- The system reaches quiescence at the target time
- All callbacks scheduled for the target time are executed
Examples
# Advance by 1000ms
VirtualClock.advance(clock, 1000)
# Advance by 0 (process all events at current time and wait for quiescence)
VirtualClock.advance(clock, 0)
Advances the virtual clock to the next scheduled event. Returns the amount advanced in milliseconds, or 0 if no events are scheduled.
Cancels a scheduled timer.
Mirrors Process.cancel_timer/1: returns the remaining virtual time in
milliseconds until the cancelled event would have fired, or false if no
event is scheduled under that reference.
Examples
iex> {:ok, clock} = VirtualClock.start_link()
iex> ref = VirtualClock.send_after(clock, self(), :later, 500)
iex> VirtualClock.cancel_timer(clock, ref)
500
iex> VirtualClock.cancel_timer(clock, ref)
false
Returns a specification to start this module under a supervisor.
See Supervisor.
Gets the current virtual time.
Returns the number of events currently scheduled.
Returns the count of events scheduled up to a specific virtual time.
This is useful for waiting for quiescence within a time frame, ignoring events scheduled for later times.
Examples
# Count events scheduled up to current time
VirtualClock.scheduled_count_until(clock)
# Count events scheduled up to 5000ms
VirtualClock.scheduled_count_until(clock, 5000)
Schedules a message to be sent after a delay in virtual time (in milliseconds). Returns a reference that can be used to cancel the timer.
Starts a new virtual clock.
Waits for quiescence - when all scheduled events have been processed and no new events are being scheduled.
Retries every 10ms for up to 1000ms (1 second) by default.
Parameters
clock: The virtual clock processtimeout_ms: Real-time timeout in milliseconds (default: 1000)retry_interval_ms: Retry interval in milliseconds (default: 10)
Waits for quiescence within a specific virtual time frame.
This function waits for all events scheduled up to the given virtual time to be processed, but ignores events scheduled for later times.
Parameters
clock: The virtual clock processopts: Keyword list of options::until_time_ms- Maximum virtual time in milliseconds to consider (default: current time):timeout_ms- Real-time timeout in milliseconds (default: 1000):retry_interval_ms- Retry interval in milliseconds (default: 10)
Examples
# Wait for quiescence up to current time
VirtualClock.wait_for_quiescence_until(clock)
# Wait for quiescence up to a specific virtual time
VirtualClock.wait_for_quiescence_until(clock, until_time_ms: 5000)
# Wait with custom timeout and retry interval
VirtualClock.wait_for_quiescence_until(clock,
until_time_ms: 1000,
timeout_ms: 500,
retry_interval_ms: 5
)