Class: Phronomy::Runtime::FakeScheduler Private
- Defined in:
- lib/phronomy/runtime/fake_scheduler.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Synchronous scheduler for use in tests.
Each spawned task is executed immediately on the calling thread using Task::ImmediateBackend. No new threads are created, so a Phronomy::Runtime that uses +FakeScheduler+ does not increase the process Thread count on #spawn.
In addition to the basic synchronous execution, +FakeScheduler+ records all task lifecycle events in #event_log and all spawned tasks in #tasks. This allows specs to assert event ordering and task state without relying on wall-clock sleeps.
=== tick / tick_until
Because +FakeScheduler+ uses Task::ImmediateBackend, every task runs to completion before #spawn returns. Consequently #tick is semantically a no-op -- the "ready task" has already executed. It is provided so that test code written against the cooperative scheduler interface compiles and documents intent (e.g. "advance by one step").
=== pending_timers
If a Testing::FakeClock is injected via #clock=, its pending callbacks are surfaced as +pending_timers+.
Constant Summary
Constants inherited from Scheduler
Instance Attribute Summary collapse
-
#clock ⇒ Phronomy::Testing::FakeClock?
private
Optional Testing::FakeClock used to timestamp events and surface pending timers.
-
#event_log ⇒ Array<Hash>
readonly
private
Ordered list of task lifecycle events.
-
#tasks ⇒ Array<Hash>
readonly
private
All tasks spawned by this scheduler.
Instance Method Summary collapse
-
#assert_cancelled(*names) ⇒ void
private
Assert that the named tasks reached +:cancelled+ state.
-
#assert_order(*names) ⇒ void
private
Assert that the named tasks completed in the given order.
-
#initialize ⇒ FakeScheduler
constructor
private
A new instance of FakeScheduler.
-
#pending_timers ⇒ Array<Hash>
private
Returns a list of pending timer entries surfaced from the injected #clock.
-
#spawn(name:, parent:, &block) ⇒ Task
private
Spawns +block+ as a Task backed by Task::ImmediateBackend.
-
#tick ⇒ self
private
Execute one ready task.
-
#tick_until(max_ticks: 1000) { ... } ⇒ Boolean
private
Run +block+ repeatedly until it returns truthy or +max_ticks+ is reached.
Methods inherited from Scheduler
current, #new_signal, #raise_signal, #raise_signal_all, #wait_for_signal, #yield
Constructor Details
#initialize ⇒ FakeScheduler
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 a new instance of FakeScheduler.
52 53 54 55 56 57 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 52 def initialize @event_log = [] @tasks = [] @clock = nil @mutex = Mutex.new end |
Instance Attribute Details
#clock ⇒ Phronomy::Testing::FakeClock?
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.
Optional Testing::FakeClock used to timestamp events and surface pending timers. When +nil+, a real monotonic clock is used.
50 51 52 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 50 def clock @clock end |
#event_log ⇒ Array<Hash> (readonly)
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 ordered list of task lifecycle events. Each entry is +{ type:, task_name:, at: }+ where +type+ is one of +:spawned+, +:started+, +:completed+, +:cancelled+, +:failed+ and +at+ is a Float monotonic timestamp (seconds).
41 42 43 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 41 def event_log @event_log end |
#tasks ⇒ Array<Hash> (readonly)
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 all tasks spawned by this scheduler. Each entry is +{ task:, name:, status: }+.
45 46 47 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 45 def tasks @tasks end |
Instance Method Details
#assert_cancelled(*names) ⇒ void
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.
This method returns an undefined value.
Assert that the named tasks reached +:cancelled+ state.
148 149 150 151 152 153 154 155 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 148 def assert_cancelled(*names) cancelled = @event_log.select { |e| e[:type] == :cancelled }.map { |e| e[:task_name] } missing = names.reject { |n| cancelled.include?(n) } return if missing.empty? raise RSpec::Expectations::ExpectationNotMetError, "Expected tasks #{missing.inspect} to be cancelled " + "but cancelled tasks were #{cancelled.inspect}" end |
#assert_order(*names) ⇒ void
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.
This method returns an undefined value.
Assert that the named tasks completed in the given order. Raises +RSpec::Expectations::ExpectationNotMetError+ if order is wrong. Intended for use inside RSpec examples.
134 135 136 137 138 139 140 141 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 134 def assert_order(*names) completed = @event_log.select { |e| e[:type] == :completed }.map { |e| e[:task_name] } indices = names.map { |n| completed.index(n) } unless indices.none?(&:nil?) && indices == indices.sort raise RSpec::Expectations::ExpectationNotMetError, "Expected tasks to complete in order #{names.inspect} " + "but completed order was #{completed.inspect}" end end |
#pending_timers ⇒ 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 a list of pending timer entries surfaced from the injected #clock. Returns an empty array when no clock is set.
121 122 123 124 125 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 121 def pending_timers return [] unless @clock @clock.pending_timer_entries end |
#spawn(name:, parent:, &block) ⇒ Task
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.
Spawns +block+ as a Task backed by Task::ImmediateBackend. The block executes synchronously before this method returns. Lifecycle events are recorded in #event_log.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 67 def spawn(name:, parent:, &block) _log_event(:spawned, name) task = Task.spawn(name: name, parent: parent, backend_class: Task::ImmediateBackend) do _log_event(:started, name) begin result = block.call _log_event(:completed, name) result rescue CancellationError _log_event(:cancelled, name) raise rescue => e _log_event(:failed, name) raise e end end @mutex.synchronize { @tasks << {task: task, name: name, status: task.status} } task end |
#tick ⇒ 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.
Execute one ready task.
Because Task::ImmediateBackend runs tasks synchronously inside #spawn, all ready tasks have already executed by the time this method is called. This method is a no-op provided for API compatibility with cooperative scheduler interfaces.
96 97 98 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 96 def tick self end |
#tick_until(max_ticks: 1000) { ... } ⇒ Boolean
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.
Run +block+ repeatedly until it returns truthy or +max_ticks+ is reached. Because tasks execute synchronously, the condition is evaluated once; if it is already met this method returns immediately.
108 109 110 111 112 113 114 |
# File 'lib/phronomy/runtime/fake_scheduler.rb', line 108 def tick_until(max_ticks: 1000) max_ticks.times do return true if yield tick end yield ? true : false end |