Class: Phronomy::Testing::FakeClock
- Inherits:
-
Object
- Object
- Phronomy::Testing::FakeClock
- 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.
Instance Attribute Summary collapse
-
#now ⇒ Float
readonly
The current logical time in seconds since the epoch (t=0).
Instance Method Summary collapse
-
#advance(seconds) ⇒ self
private
Advance the clock by
secondsand fire any registered callbacks whose deadline has passed. -
#advance_to_next_timer ⇒ self
private
Advance the clock exactly to the next pending callback and fire it.
-
#at(at) { ... } ⇒ self
private
Register a one-shot callback that fires when the clock reaches
at. -
#initialize ⇒ FakeClock
constructor
A new instance of FakeClock.
-
#next_timer_at ⇒ Float?
private
Returns the logical time of the next pending callback, or
nilif there are no pending callbacks. -
#pending_callbacks ⇒ Integer
private
Returns the number of pending (un-fired) callbacks.
-
#pending_timer_entries ⇒ Array<Hash>
private
Returns descriptive entries for all pending callbacks.
-
#schedule(seconds:) { ... } ⇒ self
private
Schedule a one-shot callback to fire after
secondsfrom the current logical time.
Constructor Details
#initialize ⇒ FakeClock
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
#now ⇒ Float (readonly)
Returns 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.
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_timer ⇒ 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 exactly to the next pending callback and fire it.
Raises RuntimeError when there are no pending callbacks.
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.
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_at ⇒ Float?
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.
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_callbacks ⇒ Integer
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.
65 66 67 |
# File 'lib/phronomy/testing/fake_clock.rb', line 65 def pending_callbacks @mutex.synchronize { @callbacks.size } end |
#pending_timer_entries ⇒ Array<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.
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.
58 59 60 |
# File 'lib/phronomy/testing/fake_clock.rb', line 58 def schedule(seconds:, &block) at(@now + seconds.to_f, &block) end |