diff options
Diffstat (limited to 'spec/ruby/core/hash')
21 files changed, 379 insertions, 170 deletions
diff --git a/spec/ruby/core/hash/assoc_spec.rb b/spec/ruby/core/hash/assoc_spec.rb index 64442918d1..62b2a11b30 100644 --- a/spec/ruby/core/hash/assoc_spec.rb +++ b/spec/ruby/core/hash/assoc_spec.rb @@ -22,11 +22,11 @@ describe "Hash#assoc" do end it "only returns the first matching key-value pair for identity hashes" do - # Avoid literal String keys in Hash#[]= due to https://bugs.ruby-lang.org/issues/12855 + # Avoid literal String keys since string literals can be frozen and interned e.g. with --enable-frozen-string-literal h = {}.compare_by_identity - k1 = 'pear' + k1 = 'pear'.dup h[k1] = :red - k2 = 'pear' + k2 = 'pear'.dup h[k2] = :green h.size.should == 2 h.keys.grep(/pear/).size.should == 2 diff --git a/spec/ruby/core/hash/compact_spec.rb b/spec/ruby/core/hash/compact_spec.rb index 76aa43949d..13371bce43 100644 --- a/spec/ruby/core/hash/compact_spec.rb +++ b/spec/ruby/core/hash/compact_spec.rb @@ -35,7 +35,7 @@ describe "Hash#compact" do hash.compact.default_proc.should == pr end - it "retains compare_by_identity_flag" do + it "retains compare_by_identity flag" do hash = {}.compare_by_identity hash.compact.compare_by_identity?.should == true hash[:a] = 1 diff --git a/spec/ruby/core/hash/compare_by_identity_spec.rb b/spec/ruby/core/hash/compare_by_identity_spec.rb index 874cd46eb7..2975526a97 100644 --- a/spec/ruby/core/hash/compare_by_identity_spec.rb +++ b/spec/ruby/core/hash/compare_by_identity_spec.rb @@ -85,19 +85,21 @@ describe "Hash#compare_by_identity" do -> { @h.compare_by_identity }.should raise_error(FrozenError) end - # Behaviour confirmed in bug #1871 + # Behaviour confirmed in https://bugs.ruby-lang.org/issues/1871 it "persists over #dups" do - @idh['foo'] = :bar - @idh['foo'] = :glark + @idh['foo'.dup] = :bar + @idh['foo'.dup] = :glark @idh.dup.should == @idh @idh.dup.size.should == @idh.size + @idh.dup.should.compare_by_identity? end it "persists over #clones" do - @idh['foo'] = :bar - @idh['foo'] = :glark + @idh['foo'.dup] = :bar + @idh['foo'.dup] = :glark @idh.clone.should == @idh @idh.clone.size.should == @idh.size + @idh.dup.should.compare_by_identity? end it "does not copy string keys" do @@ -108,9 +110,16 @@ describe "Hash#compare_by_identity" do @idh.keys.first.should equal foo end + # Check `#[]=` call with a String literal. + # Don't use `#+` because with `#+` it's no longer a String literal. + # + # See https://bugs.ruby-lang.org/issues/12855 it "gives different identity for string literals" do + eval <<~RUBY + # frozen_string_literal: false @idh['foo'] = 1 @idh['foo'] = 2 + RUBY @idh.values.should == [1, 2] @idh.size.should == 2 end diff --git a/spec/ruby/core/hash/constructor_spec.rb b/spec/ruby/core/hash/constructor_spec.rb index 8d29773909..0f97f7b40e 100644 --- a/spec/ruby/core/hash/constructor_spec.rb +++ b/spec/ruby/core/hash/constructor_spec.rb @@ -103,14 +103,14 @@ describe "Hash.[]" do HashSpecs::MyInitializerHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyInitializerHash) end - it "removes the default value" do + it "does not retain the default value" do hash = Hash.new(1) Hash[hash].default.should be_nil hash[:a] = 1 Hash[hash].default.should be_nil end - it "removes the default_proc" do + it "does not retain the default_proc" do hash = Hash.new { |h, k| h[k] = [] } Hash[hash].default_proc.should be_nil hash[:a] = 1 @@ -118,10 +118,11 @@ describe "Hash.[]" do end ruby_version_is '3.3' do - it "does not retain compare_by_identity_flag" do - hash = {}.compare_by_identity + it "does not retain compare_by_identity flag" do + hash = { a: 1 }.compare_by_identity Hash[hash].compare_by_identity?.should == false - hash[:a] = 1 + + hash = {}.compare_by_identity Hash[hash].compare_by_identity?.should == false end end diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb index e271f37ea6..d5859cb342 100644 --- a/spec/ruby/core/hash/element_reference_spec.rb +++ b/spec/ruby/core/hash/element_reference_spec.rb @@ -12,7 +12,7 @@ describe "Hash#[]" do h[[]].should == "baz" end - it "returns nil as default default value" do + it "returns nil as default value" do { 0 => 0 }[5].should == nil end @@ -30,7 +30,7 @@ describe "Hash#[]" do end it "does not create copies of the immediate default value" do - str = "foo" + str = +"foo" h = Hash.new(str) a = h[:a] b = h[:b] diff --git a/spec/ruby/core/hash/except_spec.rb b/spec/ruby/core/hash/except_spec.rb index ac84f9975c..026e454b13 100644 --- a/spec/ruby/core/hash/except_spec.rb +++ b/spec/ruby/core/hash/except_spec.rb @@ -19,14 +19,24 @@ describe "Hash#except" do @hash.except(:a, :chunky_bacon).should == { b: 2, c: 3 } end - it "always returns a Hash without a default" do - klass = Class.new(Hash) - h = klass.new(:default) - h[:bar] = 12 - h[:foo] = 42 - r = h.except(:foo) - r.should == {bar: 12} - r.class.should == Hash - r.default.should == nil + it "does not retain the default value" do + h = Hash.new(1) + h.except(:a).default.should be_nil + h[:a] = 1 + h.except(:a).default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.except(:a).default_proc.should be_nil + h[:a] = 1 + h.except(:a).default_proc.should be_nil + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.except(:a) + h2.compare_by_identity?.should == true end end diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb index 19eb806dc4..cd67f7a652 100644 --- a/spec/ruby/core/hash/hash_spec.rb +++ b/spec/ruby/core/hash/hash_spec.rb @@ -42,12 +42,10 @@ describe "Hash#hash" do # Like above, because h.eql?(x: [h]) end - ruby_version_is "3.1" do - it "allows omitting values" do - a = 1 - b = 2 + it "allows omitting values" do + a = 1 + b = 2 - eval('{a:, b:}.should == { a: 1, b: 2 }') - end + {a:, b:}.should == { a: 1, b: 2 } end end diff --git a/spec/ruby/core/hash/invert_spec.rb b/spec/ruby/core/hash/invert_spec.rb index 73377a9e97..c06e15ff7c 100644 --- a/spec/ruby/core/hash/invert_spec.rb +++ b/spec/ruby/core/hash/invert_spec.rb @@ -24,4 +24,25 @@ describe "Hash#invert" do HashSpecs::MyHash[1 => 2, 3 => 4].invert.class.should == Hash HashSpecs::MyHash[].invert.class.should == Hash end + + it "does not retain the default value" do + h = Hash.new(1) + h.invert.default.should be_nil + h[:a] = 1 + h.invert.default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.invert.default_proc.should be_nil + h[:a] = 1 + h.invert.default_proc.should be_nil + end + + it "does not retain compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.invert + h2.compare_by_identity?.should == false + end end diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb index 5521864297..6710d121ef 100644 --- a/spec/ruby/core/hash/merge_spec.rb +++ b/spec/ruby/core/hash/merge_spec.rb @@ -93,6 +93,29 @@ describe "Hash#merge" do merged.should eql(hash) merged.should_not equal(hash) end + + it "retains the default value" do + h = Hash.new(1) + h.merge(b: 1, d: 2).default.should == 1 + end + + it "retains the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.merge(b: 1, d: 2).default_proc.should == pr + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.merge(b: 1, d: 2) + h2.compare_by_identity?.should == true + end + + it "ignores compare_by_identity flag of an argument" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = { b: 1, d: 2 }.merge(h) + h2.compare_by_identity?.should == false + end end describe "Hash#merge!" do diff --git a/spec/ruby/core/hash/new_spec.rb b/spec/ruby/core/hash/new_spec.rb index 6279815fd6..5ae3e1f98d 100644 --- a/spec/ruby/core/hash/new_spec.rb +++ b/spec/ruby/core/hash/new_spec.rb @@ -34,7 +34,7 @@ describe "Hash.new" do -> { Hash.new(nil) { 0 } }.should raise_error(ArgumentError) end - ruby_version_is "3.3" do + ruby_version_is "3.3"..."3.4" do it "emits a deprecation warning if keyword arguments are passed" do -> { Hash.new(unknown: true) }.should complain( Regexp.new(Regexp.escape("Calling Hash.new with keyword arguments is deprecated and will be removed in Ruby 3.4; use Hash.new({ key: value }) instead")) @@ -46,4 +46,22 @@ describe "Hash.new" do Hash.new({ unknown: true }).default.should == { unknown: true } end end + + ruby_version_is "3.4" do + it "accepts a capacity: argument" do + Hash.new(5, capacity: 42).default.should == 5 + Hash.new(capacity: 42).default.should == nil + (Hash.new(capacity: 42) { 1 }).default_proc.should_not == nil + end + + it "ignores negative capacity" do + -> { Hash.new(capacity: -42) }.should_not raise_error + end + + it "raises an error if unknown keyword arguments are passed" do + -> { Hash.new(unknown: true) }.should raise_error(ArgumentError) + -> { Hash.new(1, unknown: true) }.should raise_error(ArgumentError) + -> { Hash.new(unknown: true) { 0 } }.should raise_error(ArgumentError) + end + end end diff --git a/spec/ruby/core/hash/reject_spec.rb b/spec/ruby/core/hash/reject_spec.rb index dd8e817237..8381fc7fc1 100644 --- a/spec/ruby/core/hash/reject_spec.rb +++ b/spec/ruby/core/hash/reject_spec.rb @@ -44,6 +44,27 @@ describe "Hash#reject" do reject_pairs.should == reject_bang_pairs end + it "does not retain the default value" do + h = Hash.new(1) + h.reject { false }.default.should be_nil + h[:a] = 1 + h.reject { false }.default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.reject { false }.default_proc.should be_nil + h[:a] = 1 + h.reject { false }.default_proc.should be_nil + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.reject { |k, _| k == :a } + h2.compare_by_identity?.should == true + end + it_behaves_like :hash_iteration_no_block, :reject it_behaves_like :enumeratorized_with_origin_size, :reject, { 1 => 2, 3 => 4, 5 => 6 } end diff --git a/spec/ruby/core/hash/replace_spec.rb b/spec/ruby/core/hash/replace_spec.rb index 92b2118fd3..db30145e1a 100644 --- a/spec/ruby/core/hash/replace_spec.rb +++ b/spec/ruby/core/hash/replace_spec.rb @@ -1,7 +1,79 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/replace' describe "Hash#replace" do - it_behaves_like :hash_replace, :replace + it "replaces the contents of self with other" do + h = { a: 1, b: 2 } + h.replace(c: -1, d: -2).should equal(h) + h.should == { c: -1, d: -2 } + end + + it "tries to convert the passed argument to a hash using #to_hash" do + obj = mock('{1=>2,3=>4}') + obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 }) + + h = {} + h.replace(obj) + h.should == { 1 => 2, 3 => 4 } + end + + it "calls to_hash on hash subclasses" do + h = {} + h.replace(HashSpecs::ToHashHash[1 => 2]) + h.should == { 1 => 2 } + end + + it "does not retain the default value" do + hash = Hash.new(1) + hash.replace(b: 2).default.should be_nil + end + + it "transfers the default value of an argument" do + hash = Hash.new(1) + { a: 1 }.replace(hash).default.should == 1 + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + hash = Hash.new(&pr) + hash.replace(b: 2).default_proc.should be_nil + end + + it "transfers the default_proc of an argument" do + pr = proc { |h, k| h[k] = [] } + hash = Hash.new(&pr) + { a: 1 }.replace(hash).default_proc.should == pr + end + + it "does not call the default_proc of an argument" do + hash_a = Hash.new { |h, k| k * 5 } + hash_b = Hash.new(-> { raise "Should not invoke lambda" }) + hash_a.replace(hash_b) + hash_a.default.should == hash_b.default + end + + it "transfers compare_by_identity flag of an argument" do + h = { a: 1, c: 3 } + h2 = { b: 2, d: 4 }.compare_by_identity + h.replace(h2) + h.compare_by_identity?.should == true + end + + it "does not retain compare_by_identity flag" do + h = { a: 1, c: 3 }.compare_by_identity + h.replace(b: 2, d: 4) + h.compare_by_identity?.should == false + end + + it "raises a FrozenError if called on a frozen instance that would not be modified" do + -> do + HashSpecs.frozen_hash.replace(HashSpecs.frozen_hash) + end.should raise_error(FrozenError) + end + + it "raises a FrozenError if called on a frozen instance that is modified" do + -> do + HashSpecs.frozen_hash.replace(HashSpecs.empty_frozen_hash) + end.should raise_error(FrozenError) + end end diff --git a/spec/ruby/core/hash/shared/replace.rb b/spec/ruby/core/hash/shared/replace.rb deleted file mode 100644 index bea64384bb..0000000000 --- a/spec/ruby/core/hash/shared/replace.rb +++ /dev/null @@ -1,51 +0,0 @@ -describe :hash_replace, shared: true do - it "replaces the contents of self with other" do - h = { a: 1, b: 2 } - h.send(@method, c: -1, d: -2).should equal(h) - h.should == { c: -1, d: -2 } - end - - it "tries to convert the passed argument to a hash using #to_hash" do - obj = mock('{1=>2,3=>4}') - obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 }) - - h = {} - h.send(@method, obj) - h.should == { 1 => 2, 3 => 4 } - end - - it "calls to_hash on hash subclasses" do - h = {} - h.send(@method, HashSpecs::ToHashHash[1 => 2]) - h.should == { 1 => 2 } - end - - it "does not transfer default values" do - hash_a = {} - hash_b = Hash.new(5) - hash_a.send(@method, hash_b) - hash_a.default.should == 5 - - hash_a = {} - hash_b = Hash.new { |h, k| k * 2 } - hash_a.send(@method, hash_b) - hash_a.default(5).should == 10 - - hash_a = Hash.new { |h, k| k * 5 } - hash_b = Hash.new(-> { raise "Should not invoke lambda" }) - hash_a.send(@method, hash_b) - hash_a.default.should == hash_b.default - end - - it "raises a FrozenError if called on a frozen instance that would not be modified" do - -> do - HashSpecs.frozen_hash.send(@method, HashSpecs.frozen_hash) - end.should raise_error(FrozenError) - end - - it "raises a FrozenError if called on a frozen instance that is modified" do - -> do - HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash) - end.should raise_error(FrozenError) - end -end diff --git a/spec/ruby/core/hash/shared/select.rb b/spec/ruby/core/hash/shared/select.rb index 5170af50d6..fbeff07330 100644 --- a/spec/ruby/core/hash/shared/select.rb +++ b/spec/ruby/core/hash/shared/select.rb @@ -40,6 +40,27 @@ describe :hash_select, shared: true do @empty.send(@method).should be_an_instance_of(Enumerator) end + it "does not retain the default value" do + h = Hash.new(1) + h.send(@method) { true }.default.should be_nil + h[:a] = 1 + h.send(@method) { true }.default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.send(@method) { true }.default_proc.should be_nil + h[:a] = 1 + h.send(@method) { true }.default_proc.should be_nil + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.send(@method) { |k, _| k == :a } + h2.compare_by_identity?.should == true + end + it_should_behave_like :hash_iteration_no_block before :each do diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb index b823ea45ca..72a462a42f 100644 --- a/spec/ruby/core/hash/shared/store.rb +++ b/spec/ruby/core/hash/shared/store.rb @@ -9,7 +9,7 @@ describe :hash_store, shared: true do it "duplicates string keys using dup semantics" do # dup doesn't copy singleton methods - key = "foo" + key = +"foo" def key.reverse() "bar" end h = {} h.send(@method, key, 0) @@ -44,7 +44,7 @@ describe :hash_store, shared: true do end it "duplicates and freezes string keys" do - key = "foo" + key = +"foo" h = {} h.send(@method, key, 0) key << "bar" @@ -75,8 +75,8 @@ describe :hash_store, shared: true do it "keeps the existing String key in the hash if there is a matching one" do h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } - key1 = "foo" - key2 = "foo" + key1 = "foo".dup + key2 = "foo".dup key1.should_not equal(key2) h[key1] = 41 frozen_key = h.keys.last @@ -91,9 +91,9 @@ describe :hash_store, shared: true do end it "does not raise an exception if changing the value of an existing key during iteration" do - hash = {1 => 2, 3 => 4, 5 => 6} - hash.each { hash.send(@method, 1, :foo) } - hash.should == {1 => :foo, 3 => 4, 5 => 6} + hash = {1 => 2, 3 => 4, 5 => 6} + hash.each { hash.send(@method, 1, :foo) } + hash.should == {1 => :foo, 3 => 4, 5 => 6} end it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb index 2db3a96583..e116b8878b 100644 --- a/spec/ruby/core/hash/shared/to_s.rb +++ b/spec/ruby/core/hash/shared/to_s.rb @@ -4,14 +4,8 @@ require_relative '../fixtures/classes' describe :hash_to_s, shared: true do it "returns a string representation with same order as each()" do h = { a: [1, 2], b: -2, d: -6, nil => nil } - - pairs = [] - h.each do |key, value| - pairs << key.inspect + '=>' + value.inspect - end - - str = '{' + pairs.join(', ') + '}' - h.send(@method).should == str + expected = ruby_version_is("3.4") ? "{a: [1, 2], b: -2, d: -6, nil => nil}" : "{:a=>[1, 2], :b=>-2, :d=>-6, nil=>nil}" + h.send(@method).should == expected end it "calls #inspect on keys and values" do @@ -19,31 +13,31 @@ describe :hash_to_s, shared: true do val = mock('val') key.should_receive(:inspect).and_return('key') val.should_receive(:inspect).and_return('val') - - { key => val }.send(@method).should == '{key=>val}' + expected = ruby_version_is("3.4") ? "{key => val}" : "{key=>val}" + { key => val }.send(@method).should == expected end it "does not call #to_s on a String returned from #inspect" do - str = "abc" + str = +"abc" str.should_not_receive(:to_s) - - { a: str }.send(@method).should == '{:a=>"abc"}' + expected = ruby_version_is("3.4") ? '{a: "abc"}' : '{:a=>"abc"}' + { a: str }.send(@method).should == expected end it "calls #to_s on the object returned from #inspect if the Object isn't a String" do obj = mock("Hash#inspect/to_s calls #to_s") obj.should_receive(:inspect).and_return(obj) obj.should_receive(:to_s).and_return("abc") - - { a: obj }.send(@method).should == "{:a=>abc}" + expected = ruby_version_is("3.4") ? "{a: abc}" : "{:a=>abc}" + { a: obj }.send(@method).should == expected end it "does not call #to_str on the object returned from #inspect when it is not a String" do obj = mock("Hash#inspect/to_s does not call #to_str") obj.should_receive(:inspect).and_return(obj) obj.should_not_receive(:to_str) - - { a: obj }.send(@method).should =~ /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/ + expected_pattern = ruby_version_is("3.4") ? /^\{a: #<MockObject:0x[0-9a-f]+>\}$/ : /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/ + { a: obj }.send(@method).should =~ expected_pattern end it "does not call #to_str on the object returned from #to_s when it is not a String" do @@ -51,8 +45,8 @@ describe :hash_to_s, shared: true do obj.should_receive(:inspect).and_return(obj) obj.should_receive(:to_s).and_return(obj) obj.should_not_receive(:to_str) - - { a: obj }.send(@method).should =~ /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/ + expected_pattern = ruby_version_is("3.4") ? /^\{a: #<MockObject:0x[0-9a-f]+>\}$/ : /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/ + { a: obj }.send(@method).should =~ expected_pattern end it "does not swallow exceptions raised by #to_s" do @@ -66,24 +60,34 @@ describe :hash_to_s, shared: true do it "handles hashes with recursive values" do x = {} x[0] = x - x.send(@method).should == '{0=>{...}}' + expected = ruby_version_is("3.4") ? '{0 => {...}}' : '{0=>{...}}' + x.send(@method).should == expected x = {} y = {} x[0] = y y[1] = x - x.send(@method).should == "{0=>{1=>{...}}}" - y.send(@method).should == "{1=>{0=>{...}}}" + expected_x = ruby_version_is("3.4") ? '{0 => {1 => {...}}}' : '{0=>{1=>{...}}}' + expected_y = ruby_version_is("3.4") ? '{1 => {0 => {...}}}' : '{1=>{0=>{...}}}' + x.send(@method).should == expected_x + y.send(@method).should == expected_y end it "does not raise if inspected result is not default external encoding" do utf_16be = mock("utf_16be") - utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE)) - - {a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}' + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) + expected = ruby_version_is("3.4") ? '{a: "utf_16be \u3042"}' : '{:a=>"utf_16be \u3042"}' + {a: utf_16be}.send(@method).should == expected end it "works for keys and values whose #inspect return a frozen String" do - { true => false }.to_s.should == "{true=>false}" + expected = ruby_version_is("3.4") ? "{true => false}" : "{true=>false}" + { true => false }.to_s.should == expected + end + + ruby_version_is "3.4" do + it "adds quotes to symbol keys that are not valid symbol literals" do + { "needs-quotes": 1 }.send(@method).should == '{"needs-quotes": 1}' + end end end diff --git a/spec/ruby/core/hash/shift_spec.rb b/spec/ruby/core/hash/shift_spec.rb index ea36488a04..3f31b9864c 100644 --- a/spec/ruby/core/hash/shift_spec.rb +++ b/spec/ruby/core/hash/shift_spec.rb @@ -30,45 +30,22 @@ describe "Hash#shift" do h.should == {} end - ruby_version_is '3.2' do - it "returns nil if the Hash is empty" do - h = {} - def h.default(key) - raise - end - h.shift.should == nil - end - end - - ruby_version_is ''...'3.2' do - it "calls #default with nil if the Hash is empty" do - h = {} - def h.default(key) - key.should == nil - :foo - end - h.shift.should == :foo + it "returns nil if the Hash is empty" do + h = {} + def h.default(key) + raise end + h.shift.should == nil end it "returns nil from an empty hash" do {}.shift.should == nil end - ruby_version_is '3.2' do - it "returns nil for empty hashes with defaults and default procs" do - Hash.new(5).shift.should == nil - h = Hash.new { |*args| args } - h.shift.should == nil - end - end - - ruby_version_is ''...'3.2' do - it "returns (computed) default for empty hashes" do - Hash.new(5).shift.should == 5 - h = Hash.new { |*args| args } - h.shift.should == [h, nil] - end + it "returns nil for empty hashes with defaults and default procs" do + Hash.new(5).shift.should == nil + h = Hash.new { |*args| args } + h.shift.should == nil end it "preserves Hash invariants when removing the last item" do diff --git a/spec/ruby/core/hash/slice_spec.rb b/spec/ruby/core/hash/slice_spec.rb index e3046d83d7..4fcc01f9a6 100644 --- a/spec/ruby/core/hash/slice_spec.rb +++ b/spec/ruby/core/hash/slice_spec.rb @@ -50,4 +50,25 @@ describe "Hash#slice" do ScratchPad.recorded.should == [] end + + it "does not retain the default value" do + h = Hash.new(1) + h.slice(:a).default.should be_nil + h[:a] = 1 + h.slice(:a).default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.slice(:a).default_proc.should be_nil + h[:a] = 1 + h.slice(:a).default_proc.should be_nil + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.slice(:a) + h2.compare_by_identity?.should == true + end end diff --git a/spec/ruby/core/hash/to_h_spec.rb b/spec/ruby/core/hash/to_h_spec.rb index 75ebce68b1..f84fd7b503 100644 --- a/spec/ruby/core/hash/to_h_spec.rb +++ b/spec/ruby/core/hash/to_h_spec.rb @@ -19,17 +19,22 @@ describe "Hash#to_h" do @h[:foo].should == :bar end - it "copies the default" do + it "retains the default" do @h.default = 42 @h.to_h.default.should == 42 @h[:hello].should == 42 end - it "copies the default_proc" do + it "retains the default_proc" do @h.default_proc = prc = Proc.new{ |h, k| h[k] = 2 * k } @h.to_h.default_proc.should == prc @h[42].should == 84 end + + it "retains compare_by_identity flag" do + @h.compare_by_identity + @h.to_h.compare_by_identity?.should == true + end end context "with block" do @@ -37,6 +42,16 @@ describe "Hash#to_h" do { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 } end + it "passes to a block each pair's key and value as separate arguments" do + ScratchPad.record [] + { a: 1, b: 2 }.to_h { |k, v| ScratchPad << [k, v]; [k, v] } + ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]] + + ScratchPad.record [] + { a: 1, b: 2 }.to_h { |*args| ScratchPad << args; [args[0], args[1]] } + ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]] + end + it "raises ArgumentError if block returns longer or shorter array" do -> do { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v, 1] } @@ -68,5 +83,24 @@ describe "Hash#to_h" do { a: 1 }.to_h { |k| x } end.should raise_error(TypeError, /wrong element type MockObject/) end + + it "does not retain the default value" do + h = Hash.new(1) + h2 = h.to_h { |k, v| [k.to_s, v*v]} + h2.default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h2 = h.to_h { |k, v| [k.to_s, v*v]} + h2.default_proc.should be_nil + end + + it "does not retain compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.to_h { |k, v| [k.to_s, v*v]} + h2.compare_by_identity?.should == false + end end end diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb index 2fbb17a8e2..e2eeab1813 100644 --- a/spec/ruby/core/hash/transform_keys_spec.rb +++ b/spec/ruby/core/hash/transform_keys_spec.rb @@ -54,6 +54,27 @@ describe "Hash#transform_keys" do it "allows a combination of hash and block argument" do @hash.transform_keys({ a: :A }, &:to_s).should == { A: 1, 'b' => 2, 'c' => 3 } end + + it "does not retain the default value" do + h = Hash.new(1) + h.transform_keys(&:succ).default.should be_nil + h[:a] = 1 + h.transform_keys(&:succ).default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.transform_values(&:succ).default_proc.should be_nil + h[:a] = 1 + h.transform_values(&:succ).default_proc.should be_nil + end + + it "does not retain compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.transform_keys(&:succ) + h2.compare_by_identity?.should == false + end end describe "Hash#transform_keys!" do @@ -76,24 +97,12 @@ describe "Hash#transform_keys!" do @hash.should == { b: 1, c: 2, d: 3, e: 4 } end - ruby_version_is ""..."3.0.2" do # https://bugs.ruby-lang.org/issues/17735 - it "returns the processed keys if we break from the block" do - @hash.transform_keys! do |v| - break if v == :c - v.succ - end - @hash.should == { b: 1, c: 2 } - end - end - - ruby_version_is "3.0.2" do - it "returns the processed keys and non evaluated keys if we break from the block" do - @hash.transform_keys! do |v| - break if v == :c - v.succ - end - @hash.should == { b: 1, c: 2, d: 4 } + it "returns the processed keys and non evaluated keys if we break from the block" do + @hash.transform_keys! do |v| + break if v == :c + v.succ end + @hash.should == { b: 1, c: 2, d: 4 } end it "keeps later pair if new keys conflict" do @@ -129,7 +138,7 @@ describe "Hash#transform_keys!" do end it "raises a FrozenError on hash argument" do - ->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should raise_error(FrozenError) + ->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should raise_error(FrozenError) end context "when no block is given" do diff --git a/spec/ruby/core/hash/transform_values_spec.rb b/spec/ruby/core/hash/transform_values_spec.rb index acb469416a..4a0ae8a5a5 100644 --- a/spec/ruby/core/hash/transform_values_spec.rb +++ b/spec/ruby/core/hash/transform_values_spec.rb @@ -39,6 +39,27 @@ describe "Hash#transform_values" do r[:foo].should == 84 r.class.should == Hash end + + it "does not retain the default value" do + h = Hash.new(1) + h.transform_values(&:succ).default.should be_nil + h[:a] = 1 + h.transform_values(&:succ).default.should be_nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.transform_values(&:succ).default_proc.should be_nil + h[:a] = 1 + h.transform_values(&:succ).default_proc.should be_nil + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.transform_values(&:succ) + h2.compare_by_identity?.should == true + end end describe "Hash#transform_values!" do |
