Class: Phronomy::Testing::FakeClock

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/testing/fake_clock.rb

Overview

A deterministic, manually-advanced clock for use in tests.

Replaces real +Process.clock_gettime+ calls so that time-sensitive code can be tested without relying on wall-clock sleeps.

Examples:

clock = Phronomy::Testing::FakeClock.new
clock.now        # => 0.0
clock.advance(5) # advance by 5 seconds
clock.now        # => 5.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFakeClock

Returns a new instance of FakeClock.



19
20
21
22
23
# File 'lib/phronomy/testing/fake_clock.rb', line 19

def initialize
  @now = 0.0
  @callbacks = [] # [[fire_at, block], ...]
  @mutex = Mutex.new
end

Instance Attribute Details

#nowFloat (readonly)

Returns the current logical time in seconds since the epoch (t=0).

Returns:

  • (Float)

    the current logical time in seconds since the epoch (t=0)



17
18
19
# File 'lib/phronomy/testing/fake_clock.rb', line 17

def now
  @now
end

Instance Method Details

#advance(seconds) ⇒ self

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Advance the clock by +seconds+ and fire any registered callbacks whose deadline has passed.

Parameters:

  • seconds (Numeric)

Returns:

  • (self)


31
32
33
34
35
36
37
# File 'lib/phronomy/testing/fake_clock.rb', line 31

def advance(seconds)
  @mutex.synchronize do
    @now += seconds.to_f
    fire_expired_callbacks!
  end
  self
end

#advance_to_next_timerself

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Advance the clock exactly to the next pending callback and fire it. Raises +RuntimeError+ when there are no pending callbacks.

Returns:

  • (self)


83
84
85
86
87
88
# File 'lib/phronomy/testing/fake_clock.rb', line 83

def advance_to_next_timer
  target = next_timer_at
  raise "No pending timers to advance to" unless target

  advance(target - @now)
end

#at(at) { ... } ⇒ self

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Register a one-shot callback that fires when the clock reaches +at+.

Parameters:

  • at (Numeric)

    logical time to fire

Yields:

  • called with no arguments when the clock reaches +at+

Returns:

  • (self)


45
46
47
48
# File 'lib/phronomy/testing/fake_clock.rb', line 45

def at(at, &block)
  @mutex.synchronize { @callbacks << [at.to_f, block] }
  self
end

#next_timer_atFloat?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the logical time of the next pending callback, or +nil+ if there are no pending callbacks.

Returns:

  • (Float, nil)


74
75
76
# File 'lib/phronomy/testing/fake_clock.rb', line 74

def next_timer_at
  @mutex.synchronize { @callbacks.min_by { |(t, _)| t }&.first }
end

#pending_callbacksInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the number of pending (un-fired) callbacks.

Returns:

  • (Integer)


65
66
67
# File 'lib/phronomy/testing/fake_clock.rb', line 65

def pending_callbacks
  @mutex.synchronize { @callbacks.size }
end

#pending_timer_entriesArray<Hash>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns descriptive entries for all pending callbacks. Used by Runtime::FakeScheduler#pending_timers.

Returns:

  • (Array<Hash>)

    each entry: +{ fire_at:, description: nil }+



95
96
97
98
99
# File 'lib/phronomy/testing/fake_clock.rb', line 95

def pending_timer_entries
  @mutex.synchronize do
    @callbacks.sort_by { |(t, _)| t }.map { |(t, _)| {fire_at: t, description: nil} }
  end
end

#schedule(seconds:) { ... } ⇒ self

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Schedule a one-shot callback to fire after +seconds+ from the current logical time. This is the same interface as Runtime::TimerQueue#schedule so that a +FakeClock+ can be passed as a +timer_queue:+ argument in tests.

Parameters:

  • seconds (Numeric)

    delay in logical seconds

Yields:

  • called when the clock reaches the scheduled time

Returns:

  • (self)


58
59
60
# File 'lib/phronomy/testing/fake_clock.rb', line 58

def schedule(seconds:, &block)
  at(@now + seconds.to_f, &block)
end