mirror of https://github.com/KLayout/klayout.git
296 lines
8.9 KiB
Ruby
296 lines
8.9 KiB
Ruby
|
|
module Test
|
||
|
|
module Unit
|
||
|
|
module Fixture
|
||
|
|
class << self
|
||
|
|
def included(base)
|
||
|
|
base.extend(ClassMethods)
|
||
|
|
|
||
|
|
[:setup, :cleanup, :teardown].each do |type|
|
||
|
|
observer = lambda do |test_case, _, _, value, callback|
|
||
|
|
if value.nil?
|
||
|
|
test_case.fixture[type].unregister(callback)
|
||
|
|
else
|
||
|
|
test_case.fixture[type].register(callback, value)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
base.register_attribute_observer(type, &observer)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
class Fixture
|
||
|
|
attr_reader :setup
|
||
|
|
attr_reader :cleanup
|
||
|
|
attr_reader :teardown
|
||
|
|
def initialize(test_case)
|
||
|
|
@test_case = test_case
|
||
|
|
@setup = HookPoint.new(@test_case, :setup, :after => :append)
|
||
|
|
@cleanup = HookPoint.new(@test_case, :cleanup, :before => :prepend)
|
||
|
|
@teardown = HookPoint.new(@test_case, :teardown, :before => :prepend)
|
||
|
|
@cached_before_callbacks = {}
|
||
|
|
@cached_after_callbacks = {}
|
||
|
|
end
|
||
|
|
|
||
|
|
def [](type)
|
||
|
|
case type
|
||
|
|
when :setup
|
||
|
|
@setup
|
||
|
|
when :cleanup
|
||
|
|
@cleanup
|
||
|
|
when :teardown
|
||
|
|
@teardown
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def before_callbacks(type)
|
||
|
|
@cached_before_callbacks[type] ||= collect_before_callbacks(type)
|
||
|
|
end
|
||
|
|
|
||
|
|
def after_callbacks(type)
|
||
|
|
@cached_after_callbacks[type] ||= collect_after_callbacks(type)
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
def target_test_cases
|
||
|
|
@cached_target_test_cases ||= collect_target_test_cases
|
||
|
|
end
|
||
|
|
|
||
|
|
def collect_before_callbacks(type)
|
||
|
|
prepend_callbacks = []
|
||
|
|
append_callbacks = []
|
||
|
|
target_test_cases.each do |ancestor|
|
||
|
|
prepend_callbacks << ancestor.fixture[type].before_prepend_callbacks
|
||
|
|
append_callbacks << ancestor.fixture[type].before_append_callbacks
|
||
|
|
end
|
||
|
|
|
||
|
|
merge_callbacks(prepend_callbacks, append_callbacks)
|
||
|
|
end
|
||
|
|
|
||
|
|
def collect_after_callbacks(type)
|
||
|
|
prepend_callbacks = []
|
||
|
|
append_callbacks = []
|
||
|
|
target_test_cases.each do |ancestor|
|
||
|
|
prepend_callbacks << ancestor.fixture[type].after_prepend_callbacks
|
||
|
|
append_callbacks << ancestor.fixture[type].after_append_callbacks
|
||
|
|
end
|
||
|
|
|
||
|
|
merge_callbacks(prepend_callbacks, append_callbacks)
|
||
|
|
end
|
||
|
|
|
||
|
|
def collect_target_test_cases
|
||
|
|
ancestors = @test_case.ancestors
|
||
|
|
base_index = ancestors.index(::Test::Unit::Fixture)
|
||
|
|
interested_ancestors = ancestors[0, base_index].find_all do |ancestor|
|
||
|
|
ancestor.is_a?(Class)
|
||
|
|
end
|
||
|
|
interested_ancestors.reverse
|
||
|
|
end
|
||
|
|
|
||
|
|
def merge_callbacks(prepend_callbacks, append_callbacks)
|
||
|
|
all_callbacks = []
|
||
|
|
prepend_callbacks.reverse_each do |callbacks|
|
||
|
|
all_callbacks.concat(callbacks)
|
||
|
|
end
|
||
|
|
append_callbacks.each do |callbacks|
|
||
|
|
all_callbacks.concat(callbacks)
|
||
|
|
end
|
||
|
|
all_callbacks
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
class HookPoint
|
||
|
|
def initialize(test_case, type, default_options)
|
||
|
|
@test_case = test_case
|
||
|
|
@type = type
|
||
|
|
@default_options = default_options
|
||
|
|
@before_prepend_callbacks = []
|
||
|
|
@before_append_callbacks = []
|
||
|
|
@after_prepend_callbacks = []
|
||
|
|
@after_append_callbacks = []
|
||
|
|
@unregistered_callbacks = []
|
||
|
|
end
|
||
|
|
|
||
|
|
def register(method_name_or_callback, options=nil)
|
||
|
|
options ||= {}
|
||
|
|
unless valid_register_options?(options)
|
||
|
|
message = "must be {:before => :prepend}, " +
|
||
|
|
"{:before => :append}, {:after => :prepend} or " +
|
||
|
|
"{:after => :append}: #{options.inspect}"
|
||
|
|
raise ArgumentError, message
|
||
|
|
end
|
||
|
|
|
||
|
|
if options.empty?
|
||
|
|
options = @default_options
|
||
|
|
end
|
||
|
|
before_how = options[:before]
|
||
|
|
after_how = options[:after]
|
||
|
|
if method_name_or_callback.respond_to?(:call)
|
||
|
|
callback = method_name_or_callback
|
||
|
|
method_name = callback_method_name(callback)
|
||
|
|
@test_case.__send__(:define_method, method_name, &callback)
|
||
|
|
else
|
||
|
|
method_name = method_name_or_callback
|
||
|
|
end
|
||
|
|
add_callback(method_name, before_how, after_how)
|
||
|
|
end
|
||
|
|
|
||
|
|
def unregister(method_name_or_callback)
|
||
|
|
if method_name_or_callback.respond_to?(:call)
|
||
|
|
callback = method_name_or_callback
|
||
|
|
method_name = callback_method_name(callback)
|
||
|
|
else
|
||
|
|
method_name = method_name_or_callback
|
||
|
|
end
|
||
|
|
@unregistered_callbacks << method_name
|
||
|
|
end
|
||
|
|
|
||
|
|
def before_prepend_callbacks
|
||
|
|
@before_prepend_callbacks - @unregistered_callbacks
|
||
|
|
end
|
||
|
|
|
||
|
|
def before_append_callbacks
|
||
|
|
@before_append_callbacks - @unregistered_callbacks
|
||
|
|
end
|
||
|
|
|
||
|
|
def after_prepend_callbacks
|
||
|
|
@after_prepend_callbacks - @unregistered_callbacks
|
||
|
|
end
|
||
|
|
|
||
|
|
def after_append_callbacks
|
||
|
|
@after_append_callbacks - @unregistered_callbacks
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
def valid_register_options?(options)
|
||
|
|
return true if options.empty?
|
||
|
|
return false if options.size > 1
|
||
|
|
|
||
|
|
key = options.keys.first
|
||
|
|
[:before, :after].include?(key) and
|
||
|
|
[:prepend, :append].include?(options[key])
|
||
|
|
end
|
||
|
|
|
||
|
|
def callback_method_name(callback)
|
||
|
|
"#{@type}_#{callback.object_id}"
|
||
|
|
end
|
||
|
|
|
||
|
|
def add_callback(method_name_or_callback, before_how, after_how)
|
||
|
|
case before_how
|
||
|
|
when :prepend
|
||
|
|
@before_prepend_callbacks =
|
||
|
|
[method_name_or_callback] | @before_prepend_callbacks
|
||
|
|
when :append
|
||
|
|
@before_append_callbacks |= [method_name_or_callback]
|
||
|
|
else
|
||
|
|
case after_how
|
||
|
|
when :prepend
|
||
|
|
@after_prepend_callbacks =
|
||
|
|
[method_name_or_callback] | @after_prepend_callbacks
|
||
|
|
when :append
|
||
|
|
@after_append_callbacks |= [method_name_or_callback]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
module ClassMethods
|
||
|
|
def fixture
|
||
|
|
@fixture ||= Fixture.new(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
def setup(*method_names, &callback)
|
||
|
|
register_fixture(:setup, *method_names, &callback)
|
||
|
|
end
|
||
|
|
|
||
|
|
def unregister_setup(*method_names_or_callbacks)
|
||
|
|
unregister_fixture(:setup, *method_names_or_callbacks)
|
||
|
|
end
|
||
|
|
|
||
|
|
def cleanup(*method_names, &callback)
|
||
|
|
register_fixture(:cleanup, *method_names, &callback)
|
||
|
|
end
|
||
|
|
|
||
|
|
def unregister_cleanup(*method_names_or_callbacks)
|
||
|
|
unregister_fixture(:cleanup, *method_names_or_callbacks)
|
||
|
|
end
|
||
|
|
|
||
|
|
def teardown(*method_names, &callback)
|
||
|
|
register_fixture(:teardown, *method_names, &callback)
|
||
|
|
end
|
||
|
|
|
||
|
|
def unregister_teardown(*method_names_or_callbacks)
|
||
|
|
unregister_fixture(:teardown, *method_names_or_callbacks)
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
def register_fixture(fixture, *method_names, &callback)
|
||
|
|
options = {}
|
||
|
|
options = method_names.pop if method_names.last.is_a?(Hash)
|
||
|
|
callbacks = method_names
|
||
|
|
callbacks << callback if callback
|
||
|
|
attribute(fixture, options, *callbacks)
|
||
|
|
end
|
||
|
|
|
||
|
|
def unregister_fixture(fixture, *method_names_or_callbacks)
|
||
|
|
attribute(fixture, nil, *method_names_or_callbacks)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
def run_fixture(type, options={}, &block)
|
||
|
|
fixtures = [
|
||
|
|
self.class.fixture.before_callbacks(type),
|
||
|
|
type,
|
||
|
|
self.class.fixture.after_callbacks(type),
|
||
|
|
].flatten
|
||
|
|
if block
|
||
|
|
runner = create_fixtures_runner(fixtures, options, &block)
|
||
|
|
runner.call
|
||
|
|
else
|
||
|
|
fixtures.each do |method_name|
|
||
|
|
run_fixture_callback(method_name, options)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def create_fixtures_runner(fixtures, options, &block)
|
||
|
|
if fixtures.empty?
|
||
|
|
block
|
||
|
|
else
|
||
|
|
last_fixture = fixtures.pop
|
||
|
|
create_fixtures_runner(fixtures, options) do
|
||
|
|
block_is_called = false
|
||
|
|
run_fixture_callback(last_fixture, options) do
|
||
|
|
block_is_called = true
|
||
|
|
block.call
|
||
|
|
end
|
||
|
|
block.call unless block_is_called
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def run_fixture_callback(method_name, options, &block)
|
||
|
|
return unless respond_to?(method_name, true)
|
||
|
|
begin
|
||
|
|
__send__(method_name, &block)
|
||
|
|
rescue Exception
|
||
|
|
raise unless options[:handle_exception]
|
||
|
|
raise unless handle_exception($!)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def run_setup(&block)
|
||
|
|
run_fixture(:setup, &block)
|
||
|
|
end
|
||
|
|
|
||
|
|
def run_cleanup
|
||
|
|
run_fixture(:cleanup)
|
||
|
|
end
|
||
|
|
|
||
|
|
def run_teardown
|
||
|
|
run_fixture(:teardown, :handle_exception => true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|