class RBS::Test::Tester
def builder
def builder @builder ||= DefinitionBuilder.new(env: env) end
def factory
def factory @factory ||= Factory.new end
def initialize(env:)
def initialize(env:) @env = env @targets = [] @instance_testers = {} @singleton_testers = {} end
def install!(klass, sample_size:, unchecked_classes:)
def install!(klass, sample_size:, unchecked_classes:) RBS.logger.info { "Installing runtime type checker in #{klass}..." } type_name = factory.type_name(klass.name).absolute! builder.build_instance(type_name).tap do |definition| instance_key = new_key(type_name, "InstanceChecker") tester, set = instance_testers[klass] ||= [ MethodCallTester.new(klass, builder, definition, kind: :instance, sample_size: sample_size, unchecked_classes: unchecked_classes), Set[] ] Observer.register(instance_key, tester) definition.methods.each do |name, method| if reason = skip_method?(type_name, method) unless reason == :implemented_in RBS.logger.info { "Skipping ##{name} because of `#{reason}`..." } end else if klass.instance_methods(false).include?(name) && !set.include?(name) RBS.logger.info { "Setting up method hook in ##{name}..." } Hook.hook_instance_method klass, name, key: instance_key set << name end end end end builder.build_singleton(type_name).tap do |definition| singleton_key = new_key(type_name, "SingletonChecker") tester, set = singleton_testers[klass] ||= [ MethodCallTester.new(klass.singleton_class, builder, definition, kind: :singleton, sample_size: sample_size, unchecked_classes: unchecked_classes), Set[] ] Observer.register(singleton_key, tester) definition.methods.each do |name, method| if reason = skip_method?(type_name, method) unless reason == :implemented_in RBS.logger.info { "Skipping .#{name} because of `#{reason}`..." } end else if klass.methods(false).include?(name) && !set.include?(name) RBS.logger.info { "Setting up method hook in .#{name}..." } Hook.hook_singleton_method klass, name, key: singleton_key set << name end end end end targets << klass end
def new_key(type_name, prefix)
def new_key(type_name, prefix) "#{prefix}__#{type_name}__#{SecureRandom.hex(10)}" end
def skip_method?(type_name, method)
def skip_method?(type_name, method) if method.implemented_in == type_name if method.annotations.any? {|a| a.string == "rbs:test:skip" } :skip else false end else if method.annotations.any? {|a| a.string == "rbs:test:target" } false else :implemented_in end end end