diff options
Diffstat (limited to 'spec/ruby/core/objectspace')
19 files changed, 477 insertions, 283 deletions
diff --git a/spec/ruby/core/objectspace/_id2ref_spec.rb b/spec/ruby/core/objectspace/_id2ref_spec.rb index c088ae2743..a9fd526b7d 100644 --- a/spec/ruby/core/objectspace/_id2ref_spec.rb +++ b/spec/ruby/core/objectspace/_id2ref_spec.rb @@ -1,52 +1,65 @@ require_relative '../../spec_helper' -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 +ruby_version_is "4.0" do + describe "ObjectSpace._id2ref" do + it "is deprecated" do + id = nil.object_id + -> { + ObjectSpace._id2ref(id) + }.should complain(/warning: ObjectSpace\._id2ref is deprecated/) + end end +end - it "retrieves true by object_id" do - ObjectSpace._id2ref(true.object_id).should == true - end +ruby_version_is ""..."4.0" do + 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 false by object_id" do - ObjectSpace._id2ref(false.object_id).should == false - end + it "retrieves true by object_id" do + ObjectSpace._id2ref(true.object_id).should == true + end - it "retrieves nil by object_id" do - ObjectSpace._id2ref(nil.object_id).should == nil - end + it "retrieves false by object_id" do + ObjectSpace._id2ref(false.object_id).should == false + end - it "retrieves a small Integer by object_id" do - ObjectSpace._id2ref(1.object_id).should == 1 - ObjectSpace._id2ref((-42).object_id).should == -42 - end + it "retrieves nil by object_id" do + ObjectSpace._id2ref(nil.object_id).should == nil + end - it "retrieves a large Integer by object_id" do - obj = 1 << 88 - ObjectSpace._id2ref(obj.object_id).should.equal?(obj) - end + it "retrieves a small Integer by object_id" do + ObjectSpace._id2ref(1.object_id).should == 1 + ObjectSpace._id2ref((-42).object_id).should == -42 + end - it "retrieves a Symbol by object_id" do - ObjectSpace._id2ref(:sym.object_id).should.equal?(:sym) - end + it "retrieves a large Integer by object_id" do + obj = 1 << 88 + ObjectSpace._id2ref(obj.object_id).should.equal?(obj) + end - it "retrieves a String by object_id" do - obj = "str" - ObjectSpace._id2ref(obj.object_id).should.equal?(obj) - end + it "retrieves a Symbol by object_id" do + ObjectSpace._id2ref(:sym.object_id).should.equal?(:sym) + end - it "retrieves a frozen literal String by object_id" do - ObjectSpace._id2ref("frozen string literal _id2ref".freeze.object_id).should.equal?("frozen string literal _id2ref".freeze) - end + it "retrieves a String by object_id" do + obj = "str" + ObjectSpace._id2ref(obj.object_id).should.equal?(obj) + end - it "retrieves an Encoding by object_id" do - ObjectSpace._id2ref(Encoding::UTF_8.object_id).should.equal?(Encoding::UTF_8) - end + it "retrieves a frozen literal String by object_id" do + ObjectSpace._id2ref("frozen string literal _id2ref".freeze.object_id).should.equal?("frozen string literal _id2ref".freeze) + end + + it "retrieves an Encoding by object_id" do + ObjectSpace._id2ref(Encoding::UTF_8.object_id).should.equal?(Encoding::UTF_8) + end - it 'raises RangeError when an object could not be found' do - proc { ObjectSpace._id2ref(1 << 60) }.should raise_error(RangeError) + it 'raises RangeError when an object could not be found' do + proc { ObjectSpace._id2ref(1 << 60) }.should.raise(RangeError) + end end end diff --git a/spec/ruby/core/objectspace/add_finalizer_spec.rb b/spec/ruby/core/objectspace/add_finalizer_spec.rb deleted file mode 100644 index 3540ac0413..0000000000 --- a/spec/ruby/core/objectspace/add_finalizer_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.add_finalizer" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/call_finalizer_spec.rb b/spec/ruby/core/objectspace/call_finalizer_spec.rb deleted file mode 100644 index 6dce92ddd6..0000000000 --- a/spec/ruby/core/objectspace/call_finalizer_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.call_finalizer" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb index 6be83e518e..5441cb4a21 100644 --- a/spec/ruby/core/objectspace/define_finalizer_spec.rb +++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb @@ -13,7 +13,7 @@ describe "ObjectSpace.define_finalizer" do it "raises an ArgumentError if the action does not respond to call" do -> { ObjectSpace.define_finalizer(Object.new, mock("ObjectSpace.define_finalizer no #call")) - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "accepts an object and a proc" do @@ -42,7 +42,7 @@ describe "ObjectSpace.define_finalizer" do it "raises ArgumentError trying to define a finalizer on a non-reference" do -> { ObjectSpace.define_finalizer(:blah) { 1 } - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end # see [ruby-core:24095] @@ -52,12 +52,12 @@ describe "ObjectSpace.define_finalizer" do Proc.new { puts "finalizer run" } end handler = scoped - obj = "Test" + obj = +"Test" ObjectSpace.define_finalizer(obj, handler) exit 0 RUBY - ruby_exe(code, :args => "2>&1").should include("finalizer run\n") + ruby_exe(code, :args => "2>&1").should.include?("finalizer run\n") end it "warns if the finalizer has the object as the receiver" do @@ -73,7 +73,7 @@ describe "ObjectSpace.define_finalizer" do exit 0 RUBY - ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n") + ruby_exe(code, :args => "2>&1").should.include?("warning: finalizer references object to be finalized\n") end it "warns if the finalizer is a method bound to the receiver" do @@ -90,7 +90,7 @@ describe "ObjectSpace.define_finalizer" do exit 0 RUBY - ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n") + ruby_exe(code, :args => "2>&1").should.include?("warning: finalizer references object to be finalized\n") end it "warns if the finalizer was a block in the receiver" do @@ -106,18 +106,18 @@ describe "ObjectSpace.define_finalizer" do exit 0 RUBY - ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n") + ruby_exe(code, :args => "2>&1").should.include?("warning: finalizer references object to be finalized\n") end it "calls a finalizer at exit even if it is self-referencing" do code = <<-RUBY - obj = "Test" + obj = +"Test" handler = Proc.new { puts "finalizer run" } ObjectSpace.define_finalizer(obj, handler) exit 0 RUBY - ruby_exe(code).should include("finalizer run\n") + ruby_exe(code).should.include?("finalizer run\n") end it "calls a finalizer at exit even if it is indirectly self-referencing" do @@ -136,14 +136,14 @@ describe "ObjectSpace.define_finalizer" do exit 0 RUBY - ruby_exe(code, :args => "2>&1").should include("finalizer run\n") + ruby_exe(code, :args => "2>&1").should.include?("finalizer run\n") end it "calls a finalizer defined in a finalizer running at exit" do code = <<-RUBY - obj = "Test" + obj = +"Test" handler = Proc.new do - obj2 = "Test" + obj2 = +"Test" handler2 = Proc.new { puts "finalizer 2 run" } ObjectSpace.define_finalizer(obj2, handler2) exit 0 @@ -152,11 +152,11 @@ describe "ObjectSpace.define_finalizer" do exit 0 RUBY - ruby_exe(code, :args => "2>&1").should include("finalizer 2 run\n") + ruby_exe(code, :args => "2>&1").should.include?("finalizer 2 run\n") end it "allows multiple finalizers with different 'callables' to be defined" do - code = <<-RUBY + code = <<-'RUBY' obj = Object.new ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized1\n" }) @@ -168,25 +168,50 @@ describe "ObjectSpace.define_finalizer" do ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"] end - ruby_version_is "3.1" do - describe "when $VERBOSE is not nil" do - it "warns if an exception is raised in finalizer" do - code = <<-RUBY - ObjectSpace.define_finalizer(Object.new) { raise "finalizing" } - RUBY + it "defines same finalizer only once" do + code = <<~RUBY + obj = Object.new + p = proc { |id| print "ok" } + ObjectSpace.define_finalizer(obj, p.dup) + ObjectSpace.define_finalizer(obj, p.dup) + RUBY - ruby_exe(code, args: "2>&1").should include("warning: Exception in finalizer", "finalizing") - end + ruby_exe(code).should == "ok" + end + + it "returns the defined finalizer" do + obj = Object.new + p = proc { |id| } + p2 = p.dup + + ret = ObjectSpace.define_finalizer(obj, p) + ret.should == [0, p] + ret[1].should.equal?(p) + + ret = ObjectSpace.define_finalizer(obj, p2) + ret.should == [0, p] + ret[1].should.equal?(p) + end + + describe "when $VERBOSE is not nil" do + it "warns if an exception is raised in finalizer" do + code = <<-RUBY + ObjectSpace.define_finalizer(Object.new) { raise "finalizing" } + RUBY + + out = ruby_exe(code, args: "2>&1") + out.should.include?("warning: Exception in finalizer") + out.should.include?("finalizing") end + end - describe "when $VERBOSE is nil" do - it "does not warn even if an exception is raised in finalizer" do - code = <<-RUBY - ObjectSpace.define_finalizer(Object.new) { raise "finalizing" } - RUBY + describe "when $VERBOSE is nil" do + it "does not warn even if an exception is raised in finalizer" do + code = <<-RUBY + ObjectSpace.define_finalizer(Object.new) { raise "finalizing" } + RUBY - ruby_exe(code, args: "2>&1", options: "-W0").should == "" - end + ruby_exe(code, args: "2>&1", options: "-W0").should == "" end end end diff --git a/spec/ruby/core/objectspace/each_object_spec.rb b/spec/ruby/core/objectspace/each_object_spec.rb index 09a582afaf..aee0fd629c 100644 --- a/spec/ruby/core/objectspace/each_object_spec.rb +++ b/spec/ruby/core/objectspace/each_object_spec.rb @@ -39,7 +39,7 @@ describe "ObjectSpace.each_object" do new_obj = klass.new counter = ObjectSpace.each_object(klass) - counter.should be_an_instance_of(Enumerator) + counter.should.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 @@ -47,20 +47,20 @@ describe "ObjectSpace.each_object" do 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) + 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) + 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) + 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) + ObjectSpaceFixtures.to_be_found_symbols.should.include?(:local) end it "finds an object stored in a local variable captured in a block explicitly" do @@ -69,7 +69,7 @@ describe "ObjectSpace.each_object" do Proc.new { local_in_block } }.call - ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_explicit) + 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 @@ -78,11 +78,11 @@ describe "ObjectSpace.each_object" do Proc.new { } }.call - ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_implicit) + 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) + 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 @@ -91,7 +91,7 @@ describe "ObjectSpace.each_object" do Proc.new { }.binding }.call - ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_proc_binding) + 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 @@ -100,45 +100,45 @@ describe "ObjectSpace.each_object" do binding }.call - ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_kernel_binding) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + ObjectSpaceFixtures.to_be_found_symbols.should.include?(:fiber_local) end it "finds an object captured in an at_exit handler" do @@ -150,7 +150,7 @@ describe "ObjectSpace.each_object" do end }.call - ObjectSpaceFixtures.to_be_found_symbols.should include(:at_exit) + ObjectSpaceFixtures.to_be_found_symbols.should.include?(:at_exit) end it "finds an object captured in finalizer" do @@ -164,9 +164,9 @@ describe "ObjectSpace.each_object" do }) }.call - ObjectSpaceFixtures.to_be_found_symbols.should include(:finalizer) + ObjectSpaceFixtures.to_be_found_symbols.should.include?(:finalizer) - alive.should_not be_nil + alive.should_not == nil end describe "on singleton classes" do @@ -185,8 +185,8 @@ describe "ObjectSpace.each_object" do end it "walks singleton classes" do - @sclass.should be_kind_of(@meta) - ObjectSpace.each_object(@meta).to_a.should include(@sclass) + @sclass.should.is_a?(@meta) + ObjectSpace.each_object(@meta).to_a.should.include?(@sclass) end end @@ -202,7 +202,7 @@ describe "ObjectSpace.each_object" do expected = [ a, b, c, d ] expected << c_sclass - c_sclass.should be_kind_of(a.singleton_class) + c_sclass.should.is_a?(a.singleton_class) b.extend Enumerable # included modules should not be walked diff --git a/spec/ruby/core/objectspace/finalizers_spec.rb b/spec/ruby/core/objectspace/finalizers_spec.rb deleted file mode 100644 index e7f20fc8a0..0000000000 --- a/spec/ruby/core/objectspace/finalizers_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.finalizers" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/garbage_collect_spec.rb b/spec/ruby/core/objectspace/garbage_collect_spec.rb index 521eaa8785..d2db22e0aa 100644 --- a/spec/ruby/core/objectspace/garbage_collect_spec.rb +++ b/spec/ruby/core/objectspace/garbage_collect_spec.rb @@ -3,7 +3,7 @@ require_relative '../../spec_helper' describe "ObjectSpace.garbage_collect" do it "can be invoked without any exceptions" do - -> { ObjectSpace.garbage_collect }.should_not raise_error + -> { ObjectSpace.garbage_collect }.should_not.raise end it "accepts keyword arguments" do @@ -11,7 +11,7 @@ describe "ObjectSpace.garbage_collect" do end it "ignores the supplied block" do - -> { ObjectSpace.garbage_collect {} }.should_not raise_error + -> { ObjectSpace.garbage_collect {} }.should_not.raise end it "always returns nil" do diff --git a/spec/ruby/core/objectspace/remove_finalizer_spec.rb b/spec/ruby/core/objectspace/remove_finalizer_spec.rb deleted file mode 100644 index 0b2b8cf16b..0000000000 --- a/spec/ruby/core/objectspace/remove_finalizer_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.remove_finalizer" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/undefine_finalizer_spec.rb b/spec/ruby/core/objectspace/undefine_finalizer_spec.rb index 11d43121f8..98ffc6a986 100644 --- a/spec/ruby/core/objectspace/undefine_finalizer_spec.rb +++ b/spec/ruby/core/objectspace/undefine_finalizer_spec.rb @@ -1,5 +1,33 @@ require_relative '../../spec_helper' describe "ObjectSpace.undefine_finalizer" do - it "needs to be reviewed for spec completeness" + it "removes finalizers for an object" do + code = <<~RUBY + obj = Object.new + ObjectSpace.define_finalizer(obj, proc { |id| puts "hello" }) + ObjectSpace.undefine_finalizer(obj) + RUBY + + ruby_exe(code).should.empty? + end + + it "should not remove finalizers for a frozen object" do + code = <<~RUBY + obj = Object.new + ObjectSpace.define_finalizer(obj, proc { |id| print "ok" }) + obj.freeze + begin + ObjectSpace.undefine_finalizer(obj) + rescue + end + RUBY + + ruby_exe(code).should == "ok" + end + + it "should raise when removing finalizers for a frozen object" do + obj = Object.new + obj.freeze + -> { ObjectSpace.undefine_finalizer(obj) }.should.raise(FrozenError) + end end diff --git a/spec/ruby/core/objectspace/weakkeymap/clear_spec.rb b/spec/ruby/core/objectspace/weakkeymap/clear_spec.rb new file mode 100644 index 0000000000..b1804ec9b0 --- /dev/null +++ b/spec/ruby/core/objectspace/weakkeymap/clear_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../../spec_helper' + +describe "ObjectSpace::WeakKeyMap#clear" do + it "removes all the entries" do + m = ObjectSpace::WeakKeyMap.new + + key = Object.new + value = Object.new + m[key] = value + + key2 = Object.new + value2 = Object.new + m[key2] = value2 + + m.clear + + m.key?(key).should == false + m.key?(key2).should == false + end + + it "returns self" do + m = ObjectSpace::WeakKeyMap.new + m.clear.should.equal?(m) + end +end diff --git a/spec/ruby/core/objectspace/weakkeymap/delete_spec.rb b/spec/ruby/core/objectspace/weakkeymap/delete_spec.rb index 6e534b8ea8..ad32c2c75e 100644 --- a/spec/ruby/core/objectspace/weakkeymap/delete_spec.rb +++ b/spec/ruby/core/objectspace/weakkeymap/delete_spec.rb @@ -1,40 +1,49 @@ require_relative '../../../spec_helper' -ruby_version_is '3.3' do - describe "ObjectSpace::WeakKeyMap#delete" do - it "removes the entry and returns the deleted value" do - m = ObjectSpace::WeakKeyMap.new - key = Object.new - value = Object.new - m[key] = value - - m.delete(key).should == value - m.key?(key).should == false - end +describe "ObjectSpace::WeakKeyMap#delete" do + it "removes the entry and returns the deleted value" do + m = ObjectSpace::WeakKeyMap.new + key = Object.new + value = Object.new + m[key] = value - it "uses equality semantic" do - m = ObjectSpace::WeakKeyMap.new - key = "foo".upcase - value = Object.new - m[key] = value + m.delete(key).should == value + m.key?(key).should == false + end - m.delete("foo".upcase).should == value - m.key?(key).should == false - end + it "uses equality semantic" do + m = ObjectSpace::WeakKeyMap.new + key = "foo".upcase + value = Object.new + m[key] = value - it "calls supplied block if the key is not found" do - key = Object.new - m = ObjectSpace::WeakKeyMap.new - return_value = m.delete(key) do |yielded_key| - yielded_key.should == key - 5 - end - return_value.should == 5 - end + m.delete("foo".upcase).should == value + m.key?(key).should == false + end - it "returns nil if the key is not found when no block is given" do - m = ObjectSpace::WeakMap.new - m.delete(Object.new).should == nil + it "calls supplied block if the key is not found" do + key = Object.new + m = ObjectSpace::WeakKeyMap.new + return_value = m.delete(key) do |yielded_key| + yielded_key.should == key + 5 end + return_value.should == 5 + end + + it "returns nil if the key is not found when no block is given" do + m = ObjectSpace::WeakKeyMap.new + m.delete(Object.new).should == nil + end + + it "returns nil when a key cannot be garbage collected" do + map = ObjectSpace::WeakKeyMap.new + + map.delete(1).should == nil + map.delete(1.0).should == nil + map.delete(:a).should == nil + map.delete(true).should == nil + map.delete(false).should == nil + map.delete(nil).should == nil end end diff --git a/spec/ruby/core/objectspace/weakkeymap/element_reference_spec.rb b/spec/ruby/core/objectspace/weakkeymap/element_reference_spec.rb index 862480cd02..53eff79c40 100644 --- a/spec/ruby/core/objectspace/weakkeymap/element_reference_spec.rb +++ b/spec/ruby/core/objectspace/weakkeymap/element_reference_spec.rb @@ -1,26 +1,105 @@ require_relative '../../../spec_helper' +require_relative 'fixtures/classes' -ruby_version_is "3.3" do - describe "ObjectSpace::WeakKeyMap#[]" do - it "is faithful to the map's content" do - map = ObjectSpace::WeakKeyMap.new - key1, key2 = %w[a b].map(&:upcase) - ref1, ref2 = %w[x y] - map[key1] = ref1 - map[key1].should == ref1 - map[key1] = ref1 - map[key1].should == ref1 - map[key2] = ref2 - map[key1].should == ref1 - map[key2].should == ref2 - end - - it "matches using equality semantics" do - map = ObjectSpace::WeakKeyMap.new - key1, key2 = %w[a a].map(&:upcase) - ref = "x" - map[key1] = ref - map[key2].should == ref - end +describe "ObjectSpace::WeakKeyMap#[]" do + it "is faithful to the map's content" do + map = ObjectSpace::WeakKeyMap.new + key1, key2 = %w[a b].map(&:upcase) + ref1, ref2 = %w[x y] + map[key1] = ref1 + map[key1].should == ref1 + map[key1] = ref1 + map[key1].should == ref1 + map[key2] = ref2 + map[key1].should == ref1 + map[key2].should == ref2 + end + + it "compares keys with #eql? semantics" do + map = ObjectSpace::WeakKeyMap.new + key = [1.0] + map[key] = "x" + map[[1]].should == nil + map[[1.0]].should == "x" + key.should == [1.0] # keep the key alive until here to keep the map entry + + map = ObjectSpace::WeakKeyMap.new + key = [1] + map[key] = "x" + map[[1.0]].should == nil + map[[1]].should == "x" + key.should == [1] # keep the key alive until here to keep the map entry + + map = ObjectSpace::WeakKeyMap.new + key1, key2 = %w[a a].map(&:upcase) + ref = "x" + map[key1] = ref + map[key2].should == ref + end + + it "compares key via #hash first" do + x = mock('0') + x.should_receive(:hash).and_return(0) + + map = ObjectSpace::WeakKeyMap.new + key = 'foo' + map[key] = :bar + map[x].should == nil + end + + it "does not compare keys with different #hash values via #eql?" do + x = mock('x') + x.should_not_receive(:eql?) + x.stub!(:hash).and_return(0) + + y = mock('y') + y.should_not_receive(:eql?) + y.stub!(:hash).and_return(1) + + map = ObjectSpace::WeakKeyMap.new + map[y] = 1 + map[x].should == nil + end + + it "compares keys with the same #hash value via #eql?" do + x = mock('x') + x.should_receive(:eql?).and_return(true) + x.stub!(:hash).and_return(42) + + y = mock('y') + y.should_not_receive(:eql?) + y.stub!(:hash).and_return(42) + + map = ObjectSpace::WeakKeyMap.new + map[y] = 1 + map[x].should == 1 + end + + it "finds a value via an identical key even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + map = ObjectSpace::WeakKeyMap.new + map[x] = :x + map[x].should == :x + end + + it "supports keys with private #hash method" do + key = WeakKeyMapSpecs::KeyWithPrivateHash.new + map = ObjectSpace::WeakKeyMap.new + map[key] = 42 + map[key].should == 42 + end + + it "returns nil and does not raise error when a key cannot be garbage collected" do + map = ObjectSpace::WeakKeyMap.new + + map[1].should == nil + map[1.0].should == nil + map[:a].should == nil + map[true].should == nil + map[false].should == nil + map[nil].should == nil end end diff --git a/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb b/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb index 689509d820..cf59aebc6f 100644 --- a/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb +++ b/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb @@ -1,71 +1,80 @@ require_relative '../../../spec_helper' -ruby_version_is "3.3" do - describe "ObjectSpace::WeakKeyMap#[]=" do - def should_accept(map, key, value) - (map[key] = value).should == value - map.should.key?(key) - map[key].should == value - end +describe "ObjectSpace::WeakKeyMap#[]=" do + def should_accept(map, key, value) + (map[key] = value).should == value + map.should.key?(key) + map[key].should == value + end - def should_not_accept(map, key, value) - -> { map[key] = value }.should raise_error(ArgumentError) - end + it "is correct" do + map = ObjectSpace::WeakKeyMap.new + key1, key2 = %w[a b].map(&:upcase) + ref1, ref2 = %w[x y] + should_accept(map, key1, ref1) + should_accept(map, key1, ref1) + should_accept(map, key2, ref2) + map[key1].should == ref1 + end - it "is correct" do - map = ObjectSpace::WeakKeyMap.new - key1, key2 = %w[a b].map(&:upcase) - ref1, ref2 = %w[x y] - should_accept(map, key1, ref1) - should_accept(map, key1, ref1) - should_accept(map, key2, ref2) - map[key1].should == ref1 - end + it "requires the keys to implement #hash" do + map = ObjectSpace::WeakKeyMap.new + -> { map[BasicObject.new] = 1 }.should.raise(NoMethodError, /undefined method [`']hash' for an instance of BasicObject/) + end - it "requires the keys to implement #hash" do - map = ObjectSpace::WeakKeyMap.new - -> { map[BasicObject.new] = 1 }.should raise_error(NoMethodError, "undefined method `hash' for an instance of BasicObject") - end + it "accepts frozen keys or values" do + map = ObjectSpace::WeakKeyMap.new + x = Object.new + should_accept(map, x, true) + should_accept(map, x, false) + should_accept(map, x, 42) + should_accept(map, x, :foo) - it "accepts frozen keys or values" do - map = ObjectSpace::WeakKeyMap.new - x = Object.new - should_accept(map, x, true) - should_accept(map, x, false) - should_accept(map, x, 42) - should_accept(map, x, :foo) + y = Object.new.freeze + should_accept(map, x, y) + should_accept(map, y, x) + end + + it "does not duplicate and freeze String keys (like Hash#[]= does)" do + map = ObjectSpace::WeakKeyMap.new + key = +"a" + map[key] = 1 - y = Object.new.freeze - should_accept(map, x, y) - should_accept(map, y, x) + map.getkey("a").should.equal? key + map.getkey("a").should_not.frozen? + + key.should == "a" # keep the key alive until here to keep the map entry + end + + context "a key cannot be garbage collected" do + it "raises ArgumentError when Integer is used as a key" do + map = ObjectSpace::WeakKeyMap.new + -> { map[1] = "x" }.should.raise(ArgumentError, /WeakKeyMap (keys )?must be garbage collectable/) end - it "rejects symbols as keys" do + it "raises ArgumentError when Float is used as a key" do map = ObjectSpace::WeakKeyMap.new - should_not_accept(map, :foo, true) - should_not_accept(map, rand.to_s.to_sym, true) + -> { map[1.0] = "x" }.should.raise(ArgumentError, /WeakKeyMap (keys )?must be garbage collectable/) end - it "rejects integers as keys" do + it "raises ArgumentError when Symbol is used as a key" do map = ObjectSpace::WeakKeyMap.new - should_not_accept(map, 42, true) - should_not_accept(map, 2 ** 68, true) + -> { map[:a] = "x" }.should.raise(ArgumentError, /WeakKeyMap (keys )?must be garbage collectable/) end - it "rejects floats as keys" do + it "raises ArgumentError when true is used as a key" do map = ObjectSpace::WeakKeyMap.new - should_not_accept(map, 4.2, true) + -> { map[true] = "x" }.should.raise(ArgumentError, /WeakKeyMap (keys )?must be garbage collectable/) end - it "rejects booleans as keys" do + it "raises ArgumentError when false is used as a key" do map = ObjectSpace::WeakKeyMap.new - should_not_accept(map, true, true) - should_not_accept(map, false, true) + -> { map[false] = "x" }.should.raise(ArgumentError, /WeakKeyMap (keys )?must be garbage collectable/) end - it "rejects nil as keys" do + it "raises ArgumentError when nil is used as a key" do map = ObjectSpace::WeakKeyMap.new - should_not_accept(map, nil, true) + -> { map[nil] = "x" }.should.raise(ArgumentError, /WeakKeyMap (keys )?must be garbage collectable/) end end end diff --git a/spec/ruby/core/objectspace/weakkeymap/fixtures/classes.rb b/spec/ruby/core/objectspace/weakkeymap/fixtures/classes.rb new file mode 100644 index 0000000000..0fd04551b5 --- /dev/null +++ b/spec/ruby/core/objectspace/weakkeymap/fixtures/classes.rb @@ -0,0 +1,5 @@ +module WeakKeyMapSpecs + class KeyWithPrivateHash + private :hash + end +end diff --git a/spec/ruby/core/objectspace/weakkeymap/getkey_spec.rb b/spec/ruby/core/objectspace/weakkeymap/getkey_spec.rb index 3af0186f27..798a2e2cda 100644 --- a/spec/ruby/core/objectspace/weakkeymap/getkey_spec.rb +++ b/spec/ruby/core/objectspace/weakkeymap/getkey_spec.rb @@ -1,14 +1,26 @@ require_relative '../../../spec_helper' -ruby_version_is "3.3" do - describe "ObjectSpace::WeakKeyMap#getkey" do - it "returns the existing equal key" do - map = ObjectSpace::WeakKeyMap.new - key1, key2 = %w[a a].map(&:upcase) - - map[key1] = true - map.getkey(key2).should equal(key1) - map.getkey("X").should == nil - end +describe "ObjectSpace::WeakKeyMap#getkey" do + it "returns the existing equal key" do + map = ObjectSpace::WeakKeyMap.new + key1, key2 = %w[a a].map(&:upcase) + + map[key1] = true + map.getkey(key2).should.equal?(key1) + map.getkey("X").should == nil + + key1.should == "A" # keep the key alive until here to keep the map entry + key2.should == "A" # keep the key alive until here to keep the map entry + end + + it "returns nil when a key cannot be garbage collected" do + map = ObjectSpace::WeakKeyMap.new + + map.getkey(1).should == nil + map.getkey(1.0).should == nil + map.getkey(:a).should == nil + map.getkey(true).should == nil + map.getkey(false).should == nil + map.getkey(nil).should == nil end end diff --git a/spec/ruby/core/objectspace/weakkeymap/inspect_spec.rb b/spec/ruby/core/objectspace/weakkeymap/inspect_spec.rb index 557fbc8733..b6bb469158 100644 --- a/spec/ruby/core/objectspace/weakkeymap/inspect_spec.rb +++ b/spec/ruby/core/objectspace/weakkeymap/inspect_spec.rb @@ -1,17 +1,19 @@ require_relative '../../../spec_helper' -ruby_version_is "3.3" do - describe "ObjectSpace::WeakKeyMap#inspect" do - it "only displays size in output" do - map = ObjectSpace::WeakKeyMap.new - key1, key2, key3 = "foo", "bar", "bar" - map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=0>\z/ - map[key1] = 1 - map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=1>\z/ - map[key2] = 2 - map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=2>\z/ - map[key3] = 3 - map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=2>\z/ - end +describe "ObjectSpace::WeakKeyMap#inspect" do + it "only displays size in output" do + map = ObjectSpace::WeakKeyMap.new + key1, key2, key3 = "foo", "bar", "bar" + map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=0>\z/ + map[key1] = 1 + map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=1>\z/ + map[key2] = 2 + map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=2>\z/ + map[key3] = 3 + map.inspect.should =~ /\A\#<ObjectSpace::WeakKeyMap:0x\h+ size=2>\z/ + + key1.should == "foo" # keep the key alive until here to keep the map entry + key2.should == "bar" # keep the key alive until here to keep the map entry + key3.should == "bar" # keep the key alive until here to keep the map entry end end diff --git a/spec/ruby/core/objectspace/weakkeymap/key_spec.rb b/spec/ruby/core/objectspace/weakkeymap/key_spec.rb index 2af9c2b8e7..e0b6866671 100644 --- a/spec/ruby/core/objectspace/weakkeymap/key_spec.rb +++ b/spec/ruby/core/objectspace/weakkeymap/key_spec.rb @@ -1,33 +1,42 @@ require_relative '../../../spec_helper' -ruby_version_is "3.3" do - describe "ObjectSpace::WeakKeyMap#key?" do - it "recognizes keys in use" do - map = ObjectSpace::WeakKeyMap.new - key1, key2 = %w[a b].map(&:upcase) - ref1, ref2 = %w[x y] +describe "ObjectSpace::WeakKeyMap#key?" do + it "recognizes keys in use" do + map = ObjectSpace::WeakKeyMap.new + key1, key2 = %w[a b].map(&:upcase) + ref1, ref2 = %w[x y] - map[key1] = ref1 - map.key?(key1).should == true - map[key1] = ref1 - map.key?(key1).should == true - map[key2] = ref2 - map.key?(key2).should == true - end + map[key1] = ref1 + map.key?(key1).should == true + map[key1] = ref1 + map.key?(key1).should == true + map[key2] = ref2 + map.key?(key2).should == true + end + + it "matches using equality semantics" do + map = ObjectSpace::WeakKeyMap.new + key1, key2 = %w[a a].map(&:upcase) + ref = "x" + map[key1] = ref + map.key?(key2).should == true + end + + it "reports true if the pair exists and the value is nil" do + map = ObjectSpace::WeakKeyMap.new + key = Object.new + map[key] = nil + map.key?(key).should == true + end - it "matches using equality semantics" do - map = ObjectSpace::WeakKeyMap.new - key1, key2 = %w[a a].map(&:upcase) - ref = "x" - map[key1] = ref - map.key?(key2).should == true - end + it "returns false when a key cannot be garbage collected" do + map = ObjectSpace::WeakKeyMap.new - it "reports true if the pair exists and the value is nil" do - map = ObjectSpace::WeakKeyMap.new - key = Object.new - map[key] = nil - map.key?(key).should == true - end + map.key?(1).should == false + map.key?(1.0).should == false + map.key?(:a).should == false + map.key?(true).should == false + map.key?(false).should == false + map.key?(nil).should == false end end diff --git a/spec/ruby/core/objectspace/weakmap/delete_spec.rb b/spec/ruby/core/objectspace/weakmap/delete_spec.rb index 302de264fb..03beebbb83 100644 --- a/spec/ruby/core/objectspace/weakmap/delete_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/delete_spec.rb @@ -1,30 +1,28 @@ require_relative '../../../spec_helper' -ruby_version_is '3.3' do - describe "ObjectSpace::WeakMap#delete" do - it "removes the entry and returns the deleted value" do - m = ObjectSpace::WeakMap.new - key = Object.new - value = Object.new - m[key] = value +describe "ObjectSpace::WeakMap#delete" do + it "removes the entry and returns the deleted value" do + m = ObjectSpace::WeakMap.new + key = Object.new + value = Object.new + m[key] = value - m.delete(key).should == value - m.key?(key).should == false - end + m.delete(key).should == value + m.key?(key).should == false + end - it "calls supplied block if the key is not found" do - key = Object.new - m = ObjectSpace::WeakMap.new - return_value = m.delete(key) do |yielded_key| - yielded_key.should == key - 5 - end - return_value.should == 5 + it "calls supplied block if the key is not found" do + key = Object.new + m = ObjectSpace::WeakMap.new + return_value = m.delete(key) do |yielded_key| + yielded_key.should == key + 5 end + return_value.should == 5 + end - it "returns nil if the key is not found when no block is given" do - m = ObjectSpace::WeakMap.new - m.delete(Object.new).should == nil - end + it "returns nil if the key is not found when no block is given" do + m = ObjectSpace::WeakMap.new + m.delete(Object.new).should == nil end end diff --git a/spec/ruby/core/objectspace/weakmap/shared/each.rb b/spec/ruby/core/objectspace/weakmap/shared/each.rb index 3d43a19347..771c416dde 100644 --- a/spec/ruby/core/objectspace/weakmap/shared/each.rb +++ b/spec/ruby/core/objectspace/weakmap/shared/each.rb @@ -5,6 +5,6 @@ describe :weakmap_each, shared: true do ref = "x" map.send(@method).should == map map[key] = ref - -> { map.send(@method) }.should raise_error(LocalJumpError) + -> { map.send(@method) }.should.raise(LocalJumpError) end end |
