1# frozen_string_literal: false
2require 'test/unit'
3
4class TestObjectSpace < Test::Unit::TestCase
5  def self.deftest_id2ref(obj)
6    /:(\d+)/ =~ caller[0]
7    file = $`
8    line = $1.to_i
9    code = <<"End"
10    define_method("test_id2ref_#{line}") {\
11      o = ObjectSpace._id2ref(obj.object_id);\
12      assert_same(obj, o, "didn't round trip: \#{obj.inspect}");\
13    }
14End
15    eval code, binding, file, line
16  end
17
18  deftest_id2ref(-0x4000000000000001)
19  deftest_id2ref(-0x4000000000000000)
20  deftest_id2ref(-0x40000001)
21  deftest_id2ref(-0x40000000)
22  deftest_id2ref(-1)
23  deftest_id2ref(0)
24  deftest_id2ref(1)
25  deftest_id2ref(0x3fffffff)
26  deftest_id2ref(0x40000000)
27  deftest_id2ref(0x3fffffffffffffff)
28  deftest_id2ref(0x4000000000000000)
29  deftest_id2ref(:a)
30  deftest_id2ref(:abcdefghijilkjl)
31  deftest_id2ref(:==)
32  deftest_id2ref(Object.new)
33  deftest_id2ref(self)
34  deftest_id2ref(true)
35  deftest_id2ref(false)
36  deftest_id2ref(nil)
37
38  def test_count_objects
39    h = {}
40    ObjectSpace.count_objects(h)
41    assert_kind_of(Hash, h)
42    assert_empty(h.keys.delete_if {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
43    assert_empty(h.values.delete_if {|x| x.is_a?(Integer) })
44
45    h = ObjectSpace.count_objects
46    assert_kind_of(Hash, h)
47    assert_empty(h.keys.delete_if {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
48    assert_empty(h.values.delete_if {|x| x.is_a?(Integer) })
49
50    assert_raise(TypeError) { ObjectSpace.count_objects(1) }
51
52    h0 = {:T_FOO=>1000}
53    h = ObjectSpace.count_objects(h0)
54    assert_same(h0, h)
55    assert_equal(0, h0[:T_FOO])
56  end
57
58  def test_finalizer
59    assert_in_out_err(["-e", <<-END], "", %w(:ok :ok :ok :ok), [])
60      a = []
61      ObjectSpace.define_finalizer(a) { p :ok }
62      b = a.dup
63      ObjectSpace.define_finalizer(a) { p :ok }
64      !b
65    END
66    assert_raise(ArgumentError) { ObjectSpace.define_finalizer([], Object.new) }
67
68    code = proc do |priv|
69      <<-"CODE"
70      fin = Object.new
71      class << fin
72        #{priv}def call(id)
73          puts "finalized"
74        end
75      end
76      ObjectSpace.define_finalizer([], fin)
77      CODE
78    end
79    assert_in_out_err([], code[""], ["finalized"])
80    assert_in_out_err([], code["private "], ["finalized"])
81    c = EnvUtil.labeled_class("C\u{3042}").new
82    o = Object.new
83    assert_raise_with_message(ArgumentError, /C\u{3042}/) {
84      ObjectSpace.define_finalizer(o, c)
85    }
86  end
87
88  def test_finalizer_with_super
89    assert_in_out_err(["-e", <<-END], "", %w(:ok), [])
90      class A
91        def foo
92        end
93      end
94
95      class B < A
96        def foo
97          1.times { super }
98        end
99      end
100
101      class C
102        module M
103        end
104
105        FINALIZER = proc do
106          M.module_eval(__FILE__, "", __LINE__) do
107          end
108        end
109
110        def define_finalizer
111          ObjectSpace.define_finalizer(self, FINALIZER)
112        end
113      end
114
115      class D
116        def foo
117          B.new.foo
118        end
119      end
120
121      C::M.singleton_class.send :define_method, :module_eval do |src, id, line|
122      end
123
124      GC.stress = true
125      10.times do
126        C.new.define_finalizer
127        D.new.foo
128      end
129
130      p :ok
131    END
132  end
133
134  def test_each_object
135    klass = Class.new
136    new_obj = klass.new
137
138    found = []
139    count = ObjectSpace.each_object(klass) do |obj|
140      found << obj
141    end
142    assert_equal(1, count)
143    assert_equal(1, found.size)
144    assert_same(new_obj, found[0])
145  end
146
147  def test_each_object_enumerator
148    klass = Class.new
149    new_obj = klass.new
150
151    found = []
152    counter = ObjectSpace.each_object(klass)
153    assert_equal(1, counter.each {|obj| found << obj})
154    assert_equal(1, found.size)
155    assert_same(new_obj, found[0])
156  end
157
158  def test_each_object_no_gabage
159    assert_separately([], <<-End)
160    GC.disable
161    eval('begin; 1.times{}; rescue; ensure; end')
162    arys = []
163    ObjectSpace.each_object(Array){|ary|
164      arys << ary
165    }
166    GC.enable
167    arys.each{|ary|
168      begin
169        assert_equal(String, ary.inspect.class) # should not cause SEGV
170      rescue RuntimeError
171        # rescue "can't modify frozen File" error.
172      end
173    }
174    End
175  end
176
177  def test_each_object_recursive_key
178    assert_normal_exit(<<-'end;', '[ruby-core:66742] [Bug #10579]')
179      h = {["foo"]=>nil}
180      p Thread.current[:__recursive_key__]
181    end;
182  end
183
184  def test_each_object_singleton_class
185    assert_separately([], <<-End)
186      class C
187        class << self
188          $c = self
189        end
190      end
191
192      exist = false
193      ObjectSpace.each_object(Class){|o|
194        exist = true if $c == o
195      }
196      assert(exist, 'Bug #11360')
197    End
198
199    klass = Class.new
200    instance = klass.new
201    sclass = instance.singleton_class
202    meta = klass.singleton_class
203    assert_kind_of(meta, sclass)
204    assert_include(ObjectSpace.each_object(meta).to_a, sclass)
205  end
206end
207