summaryrefslogtreecommitdiff
path: root/spec/rubyspec/core/objectspace
diff options
context:
space:
mode:
Diffstat (limited to 'spec/rubyspec/core/objectspace')
-rw-r--r--spec/rubyspec/core/objectspace/_id2ref_spec.rb25
-rw-r--r--spec/rubyspec/core/objectspace/add_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/call_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/count_objects_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/define_finalizer_spec.rb101
-rw-r--r--spec/rubyspec/core/objectspace/each_object_spec.rb225
-rw-r--r--spec/rubyspec/core/objectspace/finalizers_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/fixtures/classes.rb64
-rw-r--r--spec/rubyspec/core/objectspace/garbage_collect_spec.rb22
-rw-r--r--spec/rubyspec/core/objectspace/remove_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb5
11 files changed, 467 insertions, 0 deletions
diff --git a/spec/rubyspec/core/objectspace/_id2ref_spec.rb b/spec/rubyspec/core/objectspace/_id2ref_spec.rb
new file mode 100644
index 0000000000..6e0b6e03fb
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/_id2ref_spec.rb
@@ -0,0 +1,25 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace._id2ref" do
+ it "converts an object id to a reference to the object" do
+ s = "I am a string"
+ r = ObjectSpace._id2ref(s.object_id)
+ r.should == s
+ end
+
+ it "retrieves a Fixnum by object_id" do
+ f = 1
+ r = ObjectSpace._id2ref(f.object_id)
+ r.should == f
+ end
+
+ it "retrieves a Symbol by object_id" do
+ s = :sym
+ r = ObjectSpace._id2ref(s.object_id)
+ r.should == s
+ end
+
+ it 'raises RangeError when an object could not be found' do
+ proc { ObjectSpace._id2ref(1 << 60) }.should raise_error(RangeError)
+ end
+end
diff --git a/spec/rubyspec/core/objectspace/add_finalizer_spec.rb b/spec/rubyspec/core/objectspace/add_finalizer_spec.rb
new file mode 100644
index 0000000000..2d529f2d6c
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/add_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.add_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/rubyspec/core/objectspace/call_finalizer_spec.rb b/spec/rubyspec/core/objectspace/call_finalizer_spec.rb
new file mode 100644
index 0000000000..bb7cee68a4
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/call_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.call_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/rubyspec/core/objectspace/count_objects_spec.rb b/spec/rubyspec/core/objectspace/count_objects_spec.rb
new file mode 100644
index 0000000000..3e77d562a8
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/count_objects_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.count_objects" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/rubyspec/core/objectspace/define_finalizer_spec.rb b/spec/rubyspec/core/objectspace/define_finalizer_spec.rb
new file mode 100644
index 0000000000..969e8b16b0
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/define_finalizer_spec.rb
@@ -0,0 +1,101 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+# NOTE: A call to define_finalizer does not guarantee that the
+# passed proc or callable will be called at any particular time.
+# It is highly questionable whether these aspects of ObjectSpace
+# should be spec'd at all.
+describe "ObjectSpace.define_finalizer" do
+ it "raises an ArgumentError if the action does not respond to call" do
+ lambda {
+ ObjectSpace.define_finalizer("", mock("ObjectSpace.define_finalizer no #call"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "accepts an object and a proc" do
+ handler = lambda { |obj| obj }
+ ObjectSpace.define_finalizer("garbage", handler).should == [0, handler]
+ end
+
+ it "accepts an object and a callable" do
+ handler = mock("callable")
+ def handler.call(obj) end
+ ObjectSpace.define_finalizer("garbage", handler).should == [0, handler]
+ end
+
+ it "raises ArgumentError trying to define a finalizer on a non-reference" do
+ lambda {
+ ObjectSpace.define_finalizer(:blah) { 1 }
+ }.should raise_error(ArgumentError)
+ end
+
+ # see [ruby-core:24095]
+ with_feature :fork do
+ it "calls finalizer on process termination" do
+ rd, wr = IO.pipe
+ pid = Process.fork do
+ rd.close
+ handler = ObjectSpaceFixtures.scoped(wr)
+ obj = "Test"
+ ObjectSpace.define_finalizer(obj, handler)
+ exit 0
+ end
+
+ wr.close
+ begin
+ rd.read.should == "finalized"
+ ensure
+ rd.close
+ Process.wait pid
+ end
+ end
+
+ it "calls finalizer at exit even if it is self-referencing" do
+ rd, wr = IO.pipe
+ pid = Process.fork do
+ rd.close
+ obj = "Test"
+ handler = Proc.new { wr.write "finalized"; wr.close }
+ ObjectSpace.define_finalizer(obj, handler)
+ exit 0
+ end
+
+ wr.close
+ begin
+ rd.read.should == "finalized"
+ ensure
+ rd.close
+ Process.wait pid
+ end
+ end
+
+ # These specs are defined under the fork specs because there is no
+ # deterministic way to force finalizers to be run, except process exit, so
+ # we rely on that.
+ it "allows multiple finalizers with different 'callables' to be defined" do
+ rd1, wr1 = IO.pipe
+ rd2, wr2 = IO.pipe
+
+ pid = Kernel::fork do
+ rd1.close
+ rd2.close
+ obj = mock("ObjectSpace.define_finalizer multiple")
+
+ ObjectSpace.define_finalizer(obj, Proc.new { wr1.write "finalized1"; wr1.close })
+ ObjectSpace.define_finalizer(obj, Proc.new { wr2.write "finalized2"; wr2.close })
+
+ exit 0
+ end
+
+ wr1.close
+ wr2.close
+
+ rd1.read.should == "finalized1"
+ rd2.read.should == "finalized2"
+
+ rd1.close
+ rd2.close
+ Process.wait pid
+ end
+ end
+end
diff --git a/spec/rubyspec/core/objectspace/each_object_spec.rb b/spec/rubyspec/core/objectspace/each_object_spec.rb
new file mode 100644
index 0000000000..604b45cb34
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/each_object_spec.rb
@@ -0,0 +1,225 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "ObjectSpace.each_object" do
+ it "calls the block once for each living, non-immediate object in the Ruby process" do
+ klass = Class.new
+ new_obj = klass.new
+
+ yields = 0
+ count = ObjectSpace.each_object(klass) do |obj|
+ obj.should == new_obj
+ yields += 1
+ end
+ count.should == 1
+ yields.should == 1
+
+ # this is needed to prevent the new_obj from being GC'd too early
+ new_obj.should_not == nil
+ end
+
+ it "calls the block once for each class, module in the Ruby process" do
+ klass = Class.new
+ mod = Module.new
+
+ [klass, mod].each do |k|
+ yields = 0
+ got_it = false
+ count = ObjectSpace.each_object(k.class) do |obj|
+ got_it = true if obj == k
+ yields += 1
+ end
+ got_it.should == true
+ count.should == yields
+ end
+ end
+
+ it "returns an enumerator if not given a block" do
+ klass = Class.new
+ new_obj = klass.new
+
+ counter = ObjectSpace.each_object(klass)
+ counter.should be_an_instance_of(Enumerator)
+ counter.each{}.should == 1
+ # this is needed to prevent the new_obj from being GC'd too early
+ new_obj.should_not == nil
+ end
+
+ it "finds an object stored in a global variable" do
+ $object_space_global_variable = ObjectSpaceFixtures::ObjectToBeFound.new(:global)
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:global)
+ end
+
+ it "finds an object stored in a top-level constant" do
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:top_level_constant)
+ end
+
+ it "finds an object stored in a second-level constant" do
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:second_level_constant)
+ end
+
+ it "finds an object stored in a local variable" do
+ local = ObjectSpaceFixtures::ObjectToBeFound.new(:local)
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local)
+ end
+
+ it "finds an object stored in a local variable captured in a block explicitly" do
+ proc = Proc.new {
+ local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_explicit)
+ Proc.new { local_in_block }
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_explicit)
+ end
+
+ it "finds an object stored in a local variable captured in a block implicitly" do
+ proc = Proc.new {
+ local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_implicit)
+ Proc.new { }
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_implicit)
+ end
+
+ it "finds an object stored in a local variable captured in by a method defined with a block" do
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:captured_by_define_method)
+ end
+
+ it "finds an object stored in a local variable captured in a Proc#binding" do
+ binding = Proc.new {
+ local_in_proc_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_proc_binding)
+ Proc.new { }.binding
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_proc_binding)
+ end
+
+ it "finds an object stored in a local variable captured in a Kernel#binding" do
+ b = Proc.new {
+ local_in_kernel_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_kernel_binding)
+ binding
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_kernel_binding)
+ end
+
+ it "finds an object stored in a local variable set in a binding manually" do
+ b = binding
+ b.eval("local = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_manual_binding)")
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_manual_binding)
+ end
+
+ it "finds an object stored in an array" do
+ array = [ObjectSpaceFixtures::ObjectToBeFound.new(:array)]
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:array)
+ end
+
+ it "finds an object stored in a hash key" do
+ hash = {ObjectSpaceFixtures::ObjectToBeFound.new(:hash_key) => :value}
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_key)
+ end
+
+ it "finds an object stored in a hash value" do
+ hash = {a: ObjectSpaceFixtures::ObjectToBeFound.new(:hash_value)}
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_value)
+ end
+
+ it "finds an object stored in an instance variable" do
+ local = ObjectSpaceFixtures::ObjectWithInstanceVariable.new
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:instance_variable)
+ end
+
+ it "finds an object stored in a thread local" do
+ thread = Thread.new {}
+ thread.thread_variable_set(:object_space_thread_local, ObjectSpaceFixtures::ObjectToBeFound.new(:thread_local))
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:thread_local)
+ thread.join
+ end
+
+ it "finds an object stored in a fiber local" do
+ Thread.current[:object_space_fiber_local] = ObjectSpaceFixtures::ObjectToBeFound.new(:fiber_local)
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:fiber_local)
+ end
+
+ it "finds an object captured in an at_exit handler" do
+ Proc.new {
+ local = ObjectSpaceFixtures::ObjectToBeFound.new(:at_exit)
+
+ at_exit do
+ local
+ end
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:at_exit)
+ end
+
+ it "finds an object captured in finalizer" do
+ alive = Object.new
+
+ Proc.new {
+ local = ObjectSpaceFixtures::ObjectToBeFound.new(:finalizer)
+
+ ObjectSpace.define_finalizer(alive, Proc.new {
+ local
+ })
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:finalizer)
+
+ alive.should_not be_nil
+ end
+
+ describe "on singleton classes" do
+ before :each do
+ @klass = Class.new
+ instance = @klass.new
+ @sclass = instance.singleton_class
+ @meta = @klass.singleton_class
+ end
+
+ it "does not walk hidden metaclasses" do
+ klass = Class.new.singleton_class
+ ancestors = ObjectSpace.each_object(Class).select { |c| klass.is_a? c }
+ hidden = ancestors.find { |h| h.inspect.include? klass.inspect }
+ hidden.should == nil
+ end
+
+ ruby_version_is ""..."2.3" do
+ it "does not walk singleton classes" do
+ @sclass.should be_kind_of(@meta)
+ ObjectSpace.each_object(@meta).to_a.should_not include(@sclass)
+ end
+ end
+
+ ruby_version_is "2.3" do
+ it "walks singleton classes" do
+ @sclass.should be_kind_of(@meta)
+ ObjectSpace.each_object(@meta).to_a.should include(@sclass)
+ end
+ end
+ end
+
+ it "walks a class and its normal descendants when passed the class's singleton class" do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Class.new(b)
+
+ c_instance = c.new
+ c_sclass = c_instance.singleton_class
+
+ expected = [ a, b, c, d ]
+
+ # singleton classes should be walked only on >= 2.3
+ ruby_version_is "2.3" do
+ expected << c_sclass
+ c_sclass.should be_kind_of(a.singleton_class)
+ end
+
+ b.extend Enumerable # included modules should not be walked
+
+ classes = ObjectSpace.each_object(a.singleton_class).to_a
+
+ classes.sort_by(&:object_id).should == expected.sort_by(&:object_id)
+ end
+end
diff --git a/spec/rubyspec/core/objectspace/finalizers_spec.rb b/spec/rubyspec/core/objectspace/finalizers_spec.rb
new file mode 100644
index 0000000000..8d5f6cc029
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/finalizers_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.finalizers" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/rubyspec/core/objectspace/fixtures/classes.rb b/spec/rubyspec/core/objectspace/fixtures/classes.rb
new file mode 100644
index 0000000000..9a4b0aa865
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/fixtures/classes.rb
@@ -0,0 +1,64 @@
+module ObjectSpaceFixtures
+ def self.garbage
+ blah
+ end
+
+ def self.blah
+ o = "hello"
+ @garbage_objid = o.object_id
+ return o
+ end
+
+ @last_objid = nil
+
+ def self.last_objid
+ @last_objid
+ end
+
+ def self.garbage_objid
+ @garbage_objid
+ end
+
+ def self.make_finalizer
+ proc { |obj_id| @last_objid = obj_id }
+ end
+
+ def self.define_finalizer
+ handler = lambda { |obj| ScratchPad.record :finalized }
+ ObjectSpace.define_finalizer "#{rand 5}", handler
+ end
+
+ def self.scoped(wr)
+ return Proc.new { wr.write "finalized"; wr.close }
+ end
+
+ class ObjectToBeFound
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+ end
+
+ class ObjectWithInstanceVariable
+ def initialize
+ @instance_variable = ObjectToBeFound.new(:instance_variable)
+ end
+ end
+
+ def self.to_be_found_symbols
+ ObjectSpace.each_object(ObjectToBeFound).map do |o|
+ o.name
+ end
+ end
+
+ o = ObjectToBeFound.new(:captured_by_define_method)
+ define_method :capturing_method do
+ o
+ end
+
+ SECOND_LEVEL_CONSTANT = ObjectToBeFound.new(:second_level_constant)
+
+end
+
+OBJECT_SPACE_TOP_LEVEL_CONSTANT = ObjectSpaceFixtures::ObjectToBeFound.new(:top_level_constant)
diff --git a/spec/rubyspec/core/objectspace/garbage_collect_spec.rb b/spec/rubyspec/core/objectspace/garbage_collect_spec.rb
new file mode 100644
index 0000000000..aa7e233b25
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/garbage_collect_spec.rb
@@ -0,0 +1,22 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.garbage_collect" do
+
+ it "can be invoked without any exceptions" do
+ lambda { ObjectSpace.garbage_collect }.should_not raise_error
+ end
+
+ it "doesn't accept any arguments" do
+ lambda { ObjectSpace.garbage_collect(1) }.should raise_error(ArgumentError)
+ end
+
+ it "ignores the supplied block" do
+ lambda { ObjectSpace.garbage_collect {} }.should_not raise_error
+ end
+
+ it "always returns nil" do
+ ObjectSpace.garbage_collect.should == nil
+ ObjectSpace.garbage_collect.should == nil
+ end
+
+end
diff --git a/spec/rubyspec/core/objectspace/remove_finalizer_spec.rb b/spec/rubyspec/core/objectspace/remove_finalizer_spec.rb
new file mode 100644
index 0000000000..3053c31511
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/remove_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.remove_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb b/spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb
new file mode 100644
index 0000000000..98f521db98
--- /dev/null
+++ b/spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "ObjectSpace.undefine_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end