mirror of https://github.com/rails/rails
243 lines
4.9 KiB
Ruby
243 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "abstract_unit"
|
|
|
|
class ExecutorTest < ActiveSupport::TestCase
|
|
class DummyError < RuntimeError
|
|
end
|
|
|
|
def test_wrap_invokes_callbacks
|
|
called = []
|
|
executor.to_run { called << :run }
|
|
executor.to_complete { called << :complete }
|
|
|
|
executor.wrap do
|
|
called << :body
|
|
end
|
|
|
|
assert_equal [:run, :body, :complete], called
|
|
end
|
|
|
|
def test_callbacks_share_state
|
|
result = false
|
|
executor.to_run { @foo = true }
|
|
executor.to_complete { result = @foo }
|
|
|
|
executor.wrap {}
|
|
|
|
assert result
|
|
end
|
|
|
|
def test_separated_calls_invoke_callbacks
|
|
called = []
|
|
executor.to_run { called << :run }
|
|
executor.to_complete { called << :complete }
|
|
|
|
state = executor.run!
|
|
called << :body
|
|
state.complete!
|
|
|
|
assert_equal [:run, :body, :complete], called
|
|
end
|
|
|
|
def test_exceptions_unwind
|
|
called = []
|
|
executor.to_run { called << :run_1 }
|
|
executor.to_run { raise DummyError }
|
|
executor.to_run { called << :run_2 }
|
|
executor.to_complete { called << :complete }
|
|
|
|
assert_raises(DummyError) do
|
|
executor.wrap { called << :body }
|
|
end
|
|
|
|
assert_equal [:run_1, :complete], called
|
|
end
|
|
|
|
def test_avoids_double_wrapping
|
|
called = []
|
|
executor.to_run { called << :run }
|
|
executor.to_complete { called << :complete }
|
|
|
|
executor.wrap do
|
|
called << :early
|
|
executor.wrap do
|
|
called << :body
|
|
end
|
|
called << :late
|
|
end
|
|
|
|
assert_equal [:run, :early, :body, :late, :complete], called
|
|
end
|
|
|
|
def test_hooks_carry_state
|
|
supplied_state = :none
|
|
|
|
hook = Class.new do
|
|
define_method(:run) do
|
|
:some_state
|
|
end
|
|
|
|
define_method(:complete) do |state|
|
|
supplied_state = state
|
|
end
|
|
end.new
|
|
|
|
executor.register_hook(hook)
|
|
|
|
executor.wrap {}
|
|
|
|
assert_equal :some_state, supplied_state
|
|
end
|
|
|
|
def test_nil_state_is_sufficient
|
|
supplied_state = :none
|
|
|
|
hook = Class.new do
|
|
define_method(:run) do
|
|
nil
|
|
end
|
|
|
|
define_method(:complete) do |state|
|
|
supplied_state = state
|
|
end
|
|
end.new
|
|
|
|
executor.register_hook(hook)
|
|
|
|
executor.wrap {}
|
|
|
|
assert_nil supplied_state
|
|
end
|
|
|
|
def test_exception_skips_uninvoked_hook
|
|
supplied_state = :none
|
|
|
|
hook = Class.new do
|
|
define_method(:run) do
|
|
:some_state
|
|
end
|
|
|
|
define_method(:complete) do |state|
|
|
supplied_state = state
|
|
end
|
|
end.new
|
|
|
|
executor.to_run do
|
|
raise DummyError
|
|
end
|
|
executor.register_hook(hook)
|
|
|
|
assert_raises(DummyError) do
|
|
executor.wrap {}
|
|
end
|
|
|
|
assert_equal :none, supplied_state
|
|
end
|
|
|
|
def test_exception_unwinds_invoked_hook
|
|
supplied_state = :none
|
|
|
|
hook = Class.new do
|
|
define_method(:run) do
|
|
:some_state
|
|
end
|
|
|
|
define_method(:complete) do |state|
|
|
supplied_state = state
|
|
end
|
|
end.new
|
|
|
|
executor.register_hook(hook)
|
|
executor.to_run do
|
|
raise DummyError
|
|
end
|
|
|
|
assert_raises(DummyError) do
|
|
executor.wrap {}
|
|
end
|
|
|
|
assert_equal :some_state, supplied_state
|
|
end
|
|
|
|
def test_hook_insertion_order
|
|
invoked = []
|
|
supplied_state = []
|
|
|
|
hook_class = Class.new do
|
|
attr_accessor :letter
|
|
|
|
define_method(:initialize) do |letter|
|
|
self.letter = letter
|
|
end
|
|
|
|
define_method(:run) do
|
|
invoked << :"run_#{letter}"
|
|
:"state_#{letter}"
|
|
end
|
|
|
|
define_method(:complete) do |state|
|
|
invoked << :"complete_#{letter}"
|
|
supplied_state << state
|
|
end
|
|
end
|
|
|
|
executor.register_hook(hook_class.new(:a))
|
|
executor.register_hook(hook_class.new(:b))
|
|
executor.register_hook(hook_class.new(:c), outer: true)
|
|
executor.register_hook(hook_class.new(:d))
|
|
|
|
executor.wrap {}
|
|
|
|
assert_equal [:run_c, :run_a, :run_b, :run_d, :complete_a, :complete_b, :complete_d, :complete_c], invoked
|
|
assert_equal [:state_a, :state_b, :state_d, :state_c], supplied_state
|
|
end
|
|
|
|
def test_class_serial_is_unaffected
|
|
skip if !defined?(RubyVM)
|
|
|
|
hook = Class.new do
|
|
define_method(:run) do
|
|
nil
|
|
end
|
|
|
|
define_method(:complete) do |state|
|
|
nil
|
|
end
|
|
end.new
|
|
|
|
executor.register_hook(hook)
|
|
|
|
before = RubyVM.stat(:class_serial)
|
|
executor.wrap {}
|
|
executor.wrap {}
|
|
executor.wrap {}
|
|
after = RubyVM.stat(:class_serial)
|
|
|
|
assert_equal before, after
|
|
end
|
|
|
|
def test_separate_classes_can_wrap
|
|
other_executor = Class.new(ActiveSupport::Executor)
|
|
|
|
called = []
|
|
executor.to_run { called << :run }
|
|
executor.to_complete { called << :complete }
|
|
other_executor.to_run { called << :other_run }
|
|
other_executor.to_complete { called << :other_complete }
|
|
|
|
executor.wrap do
|
|
other_executor.wrap do
|
|
called << :body
|
|
end
|
|
end
|
|
|
|
assert_equal [:run, :other_run, :body, :other_complete, :complete], called
|
|
end
|
|
|
|
private
|
|
def executor
|
|
@executor ||= Class.new(ActiveSupport::Executor)
|
|
end
|
|
end
|