diff options
Diffstat (limited to 'test/test_forwardable.rb')
| -rw-r--r-- | test/test_forwardable.rb | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/test/test_forwardable.rb b/test/test_forwardable.rb new file mode 100644 index 0000000000..9ed330058a --- /dev/null +++ b/test/test_forwardable.rb @@ -0,0 +1,402 @@ +# frozen_string_literal: false +require 'test/unit' +require 'forwardable' + +class TestForwardable < Test::Unit::TestCase + INTEGER = 42 + RECEIVER = BasicObject.new + RETURNED1 = BasicObject.new + RETURNED2 = BasicObject.new + + class << RECEIVER + def delegated1 + RETURNED1 + end + + def delegated2 + RETURNED2 + end + + def delegated1_kw(**kw) + [RETURNED1, kw] + end + end + + def test_def_instance_delegator + %i[def_delegator def_instance_delegator].each do |m| + ret = nil + cls = forwardable_class do + ret = __send__ m, :@receiver, :delegated1 + end + + assert_same RETURNED1, cls.new.delegated1 + assert_equal :delegated1, ret + end + end + + def test_def_instance_delegator_constant + %i[def_delegator def_instance_delegator].each do |m| + cls = forwardable_class do + __send__ m, 'TestForwardable::INTEGER', :to_i + end + + assert_equal 42, cls.new.to_i + end + end + + def test_def_instance_delegator_kw + %i[def_delegator def_instance_delegator].each do |m| + cls = forwardable_class do + __send__ m, :@receiver, :delegated1_kw + end + + ary = cls.new.delegated1_kw b: 1 + assert_same RETURNED1, ary[0] + assert_equal({b: 1}, ary[1]) + end + end + + def test_def_instance_delegator_using_args_method_as_receiver + %i[def_delegator def_instance_delegator].each do |m| + cls = forwardable_class( + receiver_name: :args, + type: :method, + visibility: :private + ) do + __send__ m, :args, :delegated1 + end + + assert_same RETURNED1, cls.new.delegated1 + end + end + + def test_def_instance_delegator_using_block_method_as_receiver + %i[def_delegator def_instance_delegator].each do |m| + cls = forwardable_class( + receiver_name: :block, + type: :method, + visibility: :private + ) do + __send__ m, :block, :delegated1 + end + + assert_same RETURNED1, cls.new.delegated1 + end + end + + def test_def_instance_delegators + %i[def_delegators def_instance_delegators].each do |m| + cls = forwardable_class do + __send__ m, :@receiver, :delegated1, :delegated2 + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + end + end + + def test_def_instance_delegators_using_args_method_as_receiver + %i[def_delegators def_instance_delegators].each do |m| + cls = forwardable_class( + receiver_name: :args, + type: :method, + visibility: :private + ) do + __send__ m, :args, :delegated1, :delegated2 + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + end + end + + def test_def_instance_delegators_using_block_method_as_receiver + %i[def_delegators def_instance_delegators].each do |m| + cls = forwardable_class( + receiver_name: :block, + type: :method, + visibility: :private + ) do + __send__ m, :block, :delegated1, :delegated2 + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + end + end + + def test_def_instance_delegators_send_id + %i[def_delegators def_instance_delegators].each do |m| + cls = forwardable_class do + attr_reader :receiver + __send__ m, :@receiver, :__send__, :__id__ + end + + assert_not_equal cls.new.__id__, cls.new.receiver.__id__ + assert_not_equal cls.new.__send__(:__id__), cls.new.receiver.__send__(:__id__) + end + end + + def test_instance_delegate + %i[delegate instance_delegate].each do |m| + cls = forwardable_class do + __send__ m, delegated1: :@receiver, delegated2: :@receiver + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + + cls = forwardable_class do + __send__ m, %i[delegated1 delegated2] => :@receiver + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + end + end + + def test_def_instance_delegate_using_args_method_as_receiver + %i[delegate instance_delegate].each do |m| + cls = forwardable_class( + receiver_name: :args, + type: :method, + visibility: :private + ) do + __send__ m, delegated1: :args, delegated2: :args + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + end + end + + def test_def_instance_delegate_using_block_method_as_receiver + %i[delegate instance_delegate].each do |m| + cls = forwardable_class( + receiver_name: :block, + type: :method, + visibility: :private + ) do + __send__ m, delegated1: :block, delegated2: :block + end + + assert_same RETURNED1, cls.new.delegated1 + assert_same RETURNED2, cls.new.delegated2 + end + end + + def test_class_single_delegator + %i[def_delegator def_single_delegator].each do |m| + ret = nil + cls = single_forwardable_class do + ret = __send__ m, :@receiver, :delegated1 + end + + assert_same RETURNED1, cls.delegated1 + assert_equal :delegated1, ret + end + end + + def test_class_single_delegators + %i[def_delegators def_single_delegators].each do |m| + cls = single_forwardable_class do + __send__ m, :@receiver, :delegated1, :delegated2 + end + + assert_same RETURNED1, cls.delegated1 + assert_same RETURNED2, cls.delegated2 + end + end + + def test_class_single_delegate + %i[delegate single_delegate].each do |m| + cls = single_forwardable_class do + __send__ m, delegated1: :@receiver, delegated2: :@receiver + end + + assert_same RETURNED1, cls.delegated1 + assert_same RETURNED2, cls.delegated2 + + cls = single_forwardable_class do + __send__ m, %i[delegated1 delegated2] => :@receiver + end + + assert_same RETURNED1, cls.delegated1 + assert_same RETURNED2, cls.delegated2 + end + end + + def test_obj_single_delegator + %i[def_delegator def_single_delegator].each do |m| + obj = single_forwardable_object do + __send__ m, :@receiver, :delegated1 + end + + assert_same RETURNED1, obj.delegated1 + end + end + + def test_obj_single_delegators + %i[def_delegators def_single_delegators].each do |m| + obj = single_forwardable_object do + __send__ m, :@receiver, :delegated1, :delegated2 + end + + assert_same RETURNED1, obj.delegated1 + assert_same RETURNED2, obj.delegated2 + end + end + + def test_obj_single_delegators_send_id + %i[def_delegators def_single_delegators].each do |m| + obj = single_forwardable_object do + singleton_class.__send__ :attr_reader, :receiver + __send__ m, :@receiver, :__send__, :__id__ + end + + assert_not_equal obj.__id__, obj.receiver.__id__ + assert_not_equal obj.__send__(:__id__), obj.receiver.__send__(:__id__) + end + end + + def test_obj_single_delegate + %i[delegate single_delegate].each do |m| + obj = single_forwardable_object do + __send__ m, delegated1: :@receiver, delegated2: :@receiver + end + + assert_same RETURNED1, obj.delegated1 + assert_same RETURNED2, obj.delegated2 + + obj = single_forwardable_object do + __send__ m, %i[delegated1 delegated2] => :@receiver + end + + assert_same RETURNED1, obj.delegated1 + assert_same RETURNED2, obj.delegated2 + end + end + + class Foo + extend Forwardable + + attr_accessor :bar + def_delegator :bar, :baz + def_delegator :caller, :itself, :c + end + + def test_backtrace_adjustment + obj = Foo.new + def (obj.bar = Object.new).baz + foo + end + e = assert_raise(NameError) { + obj.baz + } + assert_not_match(/\/forwardable\.rb/, e.backtrace[0], + proc {RubyVM::InstructionSequence.of(obj.method(:baz)).disassemble}) + assert_equal(caller(0, 1)[0], Foo.new.c[0]) + end + + class Foo2 < BasicObject + extend ::Forwardable + + def_delegator :bar, :baz + end + + def test_basicobject_subclass + bug11616 = '[ruby-core:71176] [Bug #11616]' + assert_raise_with_message(NameError, /[`']bar'/, bug11616) { + Foo2.new.baz + } + end + + def test_aref + obj = Object.new.extend SingleForwardable + obj.instance_variable_set("@h", {foo: 42}) + obj.def_delegator("@h", :[]) + assert_equal(42, obj[:foo]) + end + + def test_aset + obj = Object.new.extend SingleForwardable + obj.instance_variable_set("@h", h = {foo: 0}) + obj.def_delegator("@h", :[]=) + obj[:foo] = 42 + assert_equal(42, h[:foo]) + end + + def test_binop + obj = Object.new.extend SingleForwardable + obj.instance_variable_set("@h", 40) + obj.def_delegator("@h", :+) + assert_equal(42, obj+2) + end + + def test_uniop + obj = Object.new.extend SingleForwardable + obj.instance_variable_set("@h", -42) + obj.def_delegator("@h", :-@) + assert_equal(42, -obj) + end + + def test_on_private_method + cls = Class.new do + private def foo; :foo end + extend Forwardable + def_delegator :itself, :foo, :bar + end + assert_warn(/forwarding to private method/) do + assert_equal(:foo, cls.new.bar) + end + end + + def test_non_module + str = String.new + str.extend Forwardable + str.instance_variable_set("@h", 42) + str.def_delegator("@h", :to_s, :forty_two) + assert_equal("42", str.forty_two) + end + + private + + def forwardable_class( + receiver_name: :receiver, + type: :ivar, + visibility: :public, + &block + ) + Class.new do + extend Forwardable + + define_method(:initialize) do + instance_variable_set("@#{receiver_name}", RECEIVER) + end + + if type == :method + attr_reader(receiver_name) + __send__(visibility, receiver_name) + end + + class_exec(&block) + end + end + + def single_forwardable_class(&block) + Class.new do + extend SingleForwardable + + @receiver = RECEIVER + + class_exec(&block) + end + end + + def single_forwardable_object(&block) + obj = Object.new.extend SingleForwardable + obj.instance_variable_set(:@receiver, RECEIVER) + obj.instance_eval(&block) + obj + end +end |
