VirtualClock (GenServerVirtualTime v0.6.0)

Copy Markdown View Source

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

advance(clock, amount_ms)

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)

advance_to_next(clock)

Advances the virtual clock to the next scheduled event. Returns the amount advanced in milliseconds, or 0 if no events are scheduled.

cancel_timer(clock, ref)

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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

now(clock)

Gets the current virtual time.

scheduled_count(clock)

Returns the number of events currently scheduled.

scheduled_count_until(clock, until_time_ms \\ nil)

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)

send_after(clock, dest, message, delay_ms)

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.

start_link(opts \\ [])

Starts a new virtual clock.

wait_for_quiescence(clock, timeout_ms \\ 1000, retry_interval_ms \\ 10)

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 process
  • timeout_ms: Real-time timeout in milliseconds (default: 1000)
  • retry_interval_ms: Retry interval in milliseconds (default: 10)

wait_for_quiescence_until(clock, opts \\ [])

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 process
  • opts: 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
)