summaryrefslogtreecommitdiff
path: root/spec/ruby/core/unboundmethod/equal_value_spec.rb
blob: b21677687ec6551fd06d4afe4f6025731e275d94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

context "Creating UnboundMethods" do
  specify "there is no difference between Method#unbind and Module#instance_method" do
    UnboundMethodSpecs::Methods.instance_method(:foo).should be_kind_of(UnboundMethod)
    UnboundMethodSpecs::Methods.new.method(:foo).unbind.should be_kind_of(UnboundMethod)
  end
end

describe "UnboundMethod#==" do
  before :all do
    @from_module = UnboundMethodSpecs::Methods.instance_method(:foo)
    @from_unbind = UnboundMethodSpecs::Methods.new.method(:foo).unbind

    @with_block = UnboundMethodSpecs::Methods.instance_method(:with_block)

    @includee = UnboundMethodSpecs::Mod.instance_method(:from_mod)
    @includer = UnboundMethodSpecs::Methods.instance_method(:from_mod)

    @alias_1 = UnboundMethodSpecs::Methods.instance_method(:alias_1)
    @alias_2 = UnboundMethodSpecs::Methods.instance_method(:alias_2)

    @original_body = UnboundMethodSpecs::Methods.instance_method(:original_body)
    @identical_body = UnboundMethodSpecs::Methods.instance_method(:identical_body)

    @parent = UnboundMethodSpecs::Parent.instance_method(:foo)
    @child1 = UnboundMethodSpecs::Child1.instance_method(:foo)
    @child2 = UnboundMethodSpecs::Child2.instance_method(:foo)

    @child1_alt = UnboundMethodSpecs::Child1.instance_method(:foo)

    @discard_1 = UnboundMethodSpecs::Methods.instance_method(:discard_1)
    @discard_2 = UnboundMethodSpecs::Methods.instance_method(:discard_2)

    @method_one = UnboundMethodSpecs::Methods.instance_method(:one)
    @method_two = UnboundMethodSpecs::Methods.instance_method(:two)
  end

  it "returns true if objects refer to the same method" do
    (@from_module == @from_module).should == true
    (@from_unbind == @from_unbind).should == true
    (@from_module == @from_unbind).should == true
    (@from_unbind == @from_module).should == true
    (@with_block  == @with_block).should == true
  end

  it "returns true if either is an alias for the other" do
    (@from_module == @alias_1).should == true
    (@alias_1 == @from_module).should == true
  end

  it "returns true if both are aliases for a third method" do
    (@from_module == @alias_1).should == true
    (@alias_1 == @from_module).should == true

    (@from_module == @alias_2).should == true
    (@alias_2 == @from_module).should == true

    (@alias_1 == @alias_2).should == true
    (@alias_2 == @alias_1).should == true
  end

  it "returns true if same method is extracted from the same subclass" do
    (@child1 == @child1_alt).should == true
    (@child1_alt == @child1).should == true
  end

  it "returns false if UnboundMethods are different methods" do
    (@method_one == @method_two).should == false
    (@method_two == @method_one).should == false
  end

  it "returns false if both have identical body but are not the same" do
    (@original_body == @identical_body).should == false
    (@identical_body == @original_body).should == false
  end

  it "returns false if same method but one extracted from a subclass" do
    (@parent == @child1).should == false
    (@child1 == @parent).should == false
  end

  it "returns false if same method but extracted from two different subclasses" do
    (@child2 == @child1).should == false
    (@child1 == @child2).should == false
  end

  it "returns false if methods are the same but added from an included Module" do
    (@includee == @includer).should == false
    (@includer == @includee).should == false
  end

  it "returns false if both have same Module, same name, identical body but not the same" do
    class UnboundMethodSpecs::Methods
      def discard_1; :discard; end
    end

    (@discard_1 == UnboundMethodSpecs::Methods.instance_method(:discard_1)).should == false
  end

  it "considers methods through aliasing equal" do
    c = Class.new do
      class << self
        alias_method :n, :new
      end
    end

    c.method(:new).should == c.method(:n)
    c.method(:n).should == Class.instance_method(:new).bind(c)
  end

  # On CRuby < 3.2, the 2 specs below pass due to method/instance_method skipping zsuper methods.
  # We are interested in the general pattern working, i.e. the combination of method/instance_method
  # and #== exposes the wanted behavior.
  it "considers methods through visibility change equal" do
    c = Class.new do
      class << self
        private :new
      end
    end

    c.method(:new).should == Class.instance_method(:new).bind(c)
  end

  it "considers methods through aliasing and visibility change equal" do
    c = Class.new do
      class << self
        alias_method :n, :new
        private :new
      end
    end

    c.method(:new).should == c.method(:n)
    c.method(:n).should == Class.instance_method(:new).bind(c)
    c.method(:new).should == Class.instance_method(:new).bind(c)
  end
end