summaryrefslogtreecommitdiff
path: root/spec/ruby/core/hash
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/hash')
-rw-r--r--spec/ruby/core/hash/any_spec.rb4
-rw-r--r--spec/ruby/core/hash/assoc_spec.rb6
-rw-r--r--spec/ruby/core/hash/clear_spec.rb6
-rw-r--r--spec/ruby/core/hash/compact_spec.rb28
-rw-r--r--spec/ruby/core/hash/compare_by_identity_spec.rb25
-rw-r--r--spec/ruby/core/hash/constructor_spec.rb54
-rw-r--r--spec/ruby/core/hash/deconstruct_keys_spec.rb23
-rw-r--r--spec/ruby/core/hash/default_proc_spec.rb20
-rw-r--r--spec/ruby/core/hash/default_spec.rb8
-rw-r--r--spec/ruby/core/hash/delete_if_spec.rb6
-rw-r--r--spec/ruby/core/hash/delete_spec.rb22
-rw-r--r--spec/ruby/core/hash/dig_spec.rb6
-rw-r--r--spec/ruby/core/hash/element_reference_spec.rb18
-rw-r--r--spec/ruby/core/hash/empty_spec.rb10
-rw-r--r--spec/ruby/core/hash/except_spec.rb42
-rw-r--r--spec/ruby/core/hash/fetch_spec.rb14
-rw-r--r--spec/ruby/core/hash/fetch_values_spec.rb6
-rw-r--r--spec/ruby/core/hash/filter_spec.rb12
-rw-r--r--spec/ruby/core/hash/fixtures/name.rb30
-rw-r--r--spec/ruby/core/hash/flatten_spec.rb2
-rw-r--r--spec/ruby/core/hash/hash_spec.rb7
-rw-r--r--spec/ruby/core/hash/index_spec.rb7
-rw-r--r--spec/ruby/core/hash/initialize_spec.rb18
-rw-r--r--spec/ruby/core/hash/invert_spec.rb21
-rw-r--r--spec/ruby/core/hash/keep_if_spec.rb6
-rw-r--r--spec/ruby/core/hash/merge_spec.rb45
-rw-r--r--spec/ruby/core/hash/new_spec.rb37
-rw-r--r--spec/ruby/core/hash/rehash_spec.rb77
-rw-r--r--spec/ruby/core/hash/reject_spec.rb34
-rw-r--r--spec/ruby/core/hash/replace_spec.rb76
-rw-r--r--spec/ruby/core/hash/ruby2_keywords_hash_spec.rb83
-rw-r--r--spec/ruby/core/hash/shared/comparison.rb6
-rw-r--r--spec/ruby/core/hash/shared/each.rb32
-rw-r--r--spec/ruby/core/hash/shared/eql.rb14
-rw-r--r--spec/ruby/core/hash/shared/equal.rb90
-rw-r--r--spec/ruby/core/hash/shared/index.rb22
-rw-r--r--spec/ruby/core/hash/shared/replace.rb51
-rw-r--r--spec/ruby/core/hash/shared/select.rb29
-rw-r--r--spec/ruby/core/hash/shared/store.rb39
-rw-r--r--spec/ruby/core/hash/shared/to_s.rb67
-rw-r--r--spec/ruby/core/hash/shared/update.rb30
-rw-r--r--spec/ruby/core/hash/shift_spec.rb19
-rw-r--r--spec/ruby/core/hash/slice_spec.rb103
-rw-r--r--spec/ruby/core/hash/to_a_spec.rb8
-rw-r--r--spec/ruby/core/hash/to_h_spec.rb110
-rw-r--r--spec/ruby/core/hash/to_proc_spec.rb14
-rw-r--r--spec/ruby/core/hash/transform_keys_spec.rb222
-rw-r--r--spec/ruby/core/hash/transform_values_spec.rb29
-rw-r--r--spec/ruby/core/hash/try_convert_spec.rb4
49 files changed, 1051 insertions, 591 deletions
diff --git a/spec/ruby/core/hash/any_spec.rb b/spec/ruby/core/hash/any_spec.rb
index bd33e8cd8f..c26dfabde6 100644
--- a/spec/ruby/core/hash/any_spec.rb
+++ b/spec/ruby/core/hash/any_spec.rb
@@ -4,10 +4,10 @@ describe "Hash#any?" do
describe 'with no block given' do
it "checks if there are any members of a Hash" do
empty_hash = {}
- empty_hash.any?.should == false
+ empty_hash.should_not.any?
hash_with_members = { 'key' => 'value' }
- hash_with_members.any?.should == true
+ hash_with_members.should.any?
end
end
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/clear_spec.rb b/spec/ruby/core/hash/clear_spec.rb
index 706fe57e1c..cf05e36ac9 100644
--- a/spec/ruby/core/hash/clear_spec.rb
+++ b/spec/ruby/core/hash/clear_spec.rb
@@ -25,8 +25,8 @@ describe "Hash#clear" do
h.default_proc.should_not == nil
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.clear }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.clear }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.clear }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.clear }.should raise_error(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/compact_spec.rb b/spec/ruby/core/hash/compact_spec.rb
index ec9d4b5d79..13371bce43 100644
--- a/spec/ruby/core/hash/compact_spec.rb
+++ b/spec/ruby/core/hash/compact_spec.rb
@@ -18,6 +18,30 @@ describe "Hash#compact" do
@hash.compact
@hash.should == @initial_pairs
end
+
+ ruby_version_is '3.3' do
+ it "retains the default value" do
+ hash = Hash.new(1)
+ hash.compact.default.should == 1
+ hash[:a] = 1
+ hash.compact.default.should == 1
+ end
+
+ it "retains the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ hash = Hash.new(&pr)
+ hash.compact.default_proc.should == pr
+ hash[:a] = 1
+ hash.compact.default_proc.should == pr
+ end
+
+ it "retains compare_by_identity flag" do
+ hash = {}.compare_by_identity
+ hash.compact.compare_by_identity?.should == true
+ hash[:a] = 1
+ hash.compact.compare_by_identity?.should == true
+ end
+ end
end
describe "Hash#compact!" do
@@ -51,8 +75,8 @@ describe "Hash#compact!" do
@hash.freeze
end
- it "keeps pairs and raises a #{frozen_error_class}" do
- ->{ @hash.compact! }.should raise_error(frozen_error_class)
+ it "keeps pairs and raises a FrozenError" do
+ ->{ @hash.compact! }.should raise_error(FrozenError)
@hash.should == @initial_pairs
end
end
diff --git a/spec/ruby/core/hash/compare_by_identity_spec.rb b/spec/ruby/core/hash/compare_by_identity_spec.rb
index 33db59124e..2975526a97 100644
--- a/spec/ruby/core/hash/compare_by_identity_spec.rb
+++ b/spec/ruby/core/hash/compare_by_identity_spec.rb
@@ -33,7 +33,7 @@ describe "Hash#compare_by_identity" do
it "has no effect on an already compare_by_identity hash" do
@idh[:foo] = :bar
@idh.compare_by_identity.should equal @idh
- @idh.compare_by_identity?.should == true
+ @idh.should.compare_by_identity?
@idh[:foo].should == :bar
end
@@ -80,24 +80,26 @@ describe "Hash#compare_by_identity" do
@h[o].should == :o
end
- it "raises a #{frozen_error_class} on frozen hashes" do
+ it "raises a FrozenError on frozen hashes" do
@h = @h.freeze
- lambda { @h.compare_by_identity }.should raise_error(frozen_error_class)
+ -> { @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 14674a018b..0f97f7b40e 100644
--- a/spec/ruby/core/hash/constructor_spec.rb
+++ b/spec/ruby/core/hash/constructor_spec.rb
@@ -42,30 +42,17 @@ describe "Hash.[]" do
Hash[ary].should == { a: :b }
end
- ruby_version_is "" ... "2.7" do
- it "ignores elements that are not arrays" do
- -> {
- Hash[[:a]].should == {}
- }.should complain(/ignoring wrong elements/)
- -> {
- Hash[[:nil]].should == {}
- }.should complain(/ignoring wrong elements/)
- end
- end
-
- ruby_version_is "2.7" do
- it "raises for elements that are not arrays" do
- -> {
- Hash[[:a]].should == {}
- }.should raise_error(ArgumentError)
- -> {
- Hash[[:nil]].should == {}
- }.should raise_error(ArgumentError)
- end
+ it "raises for elements that are not arrays" do
+ -> {
+ Hash[[:a]].should == {}
+ }.should raise_error(ArgumentError)
+ -> {
+ Hash[[:nil]].should == {}
+ }.should raise_error(ArgumentError)
end
it "raises an ArgumentError for arrays of more than 2 elements" do
- lambda{ Hash[[[:a, :b, :c]]].should == {} }.should raise_error(ArgumentError)
+ ->{ Hash[[[:a, :b, :c]]].should == {} }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed a list of value-invalid-pairs in an array" do
@@ -89,8 +76,8 @@ describe "Hash.[]" do
end
it "raises an ArgumentError when passed an odd number of arguments" do
- lambda { Hash[1, 2, 3] }.should raise_error(ArgumentError)
- lambda { Hash[1, 2, { 3 => 4 }] }.should raise_error(ArgumentError)
+ -> { Hash[1, 2, 3] }.should raise_error(ArgumentError)
+ -> { Hash[1, 2, { 3 => 4 }] }.should raise_error(ArgumentError)
end
it "calls to_hash" do
@@ -116,8 +103,27 @@ describe "Hash.[]" do
HashSpecs::MyInitializerHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyInitializerHash)
end
- it "removes the default_proc" 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 "does not retain the default_proc" do
hash = Hash.new { |h, k| h[k] = [] }
Hash[hash].default_proc.should be_nil
+ hash[:a] = 1
+ Hash[hash].default_proc.should be_nil
+ end
+
+ ruby_version_is '3.3' do
+ it "does not retain compare_by_identity flag" do
+ hash = { a: 1 }.compare_by_identity
+ Hash[hash].compare_by_identity?.should == false
+
+ hash = {}.compare_by_identity
+ Hash[hash].compare_by_identity?.should == false
+ end
end
end
diff --git a/spec/ruby/core/hash/deconstruct_keys_spec.rb b/spec/ruby/core/hash/deconstruct_keys_spec.rb
new file mode 100644
index 0000000000..bbcd8932e5
--- /dev/null
+++ b/spec/ruby/core/hash/deconstruct_keys_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "Hash#deconstruct_keys" do
+ it "returns self" do
+ hash = {a: 1, b: 2}
+
+ hash.deconstruct_keys([:a, :b]).should equal hash
+ end
+
+ it "requires one argument" do
+ -> {
+ {a: 1}.deconstruct_keys
+ }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ end
+
+ it "ignores argument" do
+ hash = {a: 1, b: 2}
+
+ hash.deconstruct_keys([:a]).should == {a: 1, b: 2}
+ hash.deconstruct_keys(0 ).should == {a: 1, b: 2}
+ hash.deconstruct_keys('' ).should == {a: 1, b: 2}
+ end
+end
diff --git a/spec/ruby/core/hash/default_proc_spec.rb b/spec/ruby/core/hash/default_proc_spec.rb
index 2254609607..f4e4803632 100644
--- a/spec/ruby/core/hash/default_proc_spec.rb
+++ b/spec/ruby/core/hash/default_proc_spec.rb
@@ -37,7 +37,7 @@ describe "Hash#default_proc=" do
end
it "raises an error if passed stuff not convertible to procs" do
- lambda{{}.default_proc = 42}.should raise_error(TypeError)
+ ->{{}.default_proc = 42}.should raise_error(TypeError)
end
it "returns the passed Proc" do
@@ -58,23 +58,23 @@ describe "Hash#default_proc=" do
it "accepts a lambda with an arity of 2" do
h = {}
- lambda do
- h.default_proc = lambda {|a,b| }
+ -> do
+ h.default_proc = -> a, b { }
end.should_not raise_error(TypeError)
end
it "raises a TypeError if passed a lambda with an arity other than 2" do
h = {}
- lambda do
- h.default_proc = lambda {|a| }
+ -> do
+ h.default_proc = -> a { }
end.should raise_error(TypeError)
- lambda do
- h.default_proc = lambda {|a,b,c| }
+ -> do
+ h.default_proc = -> a, b, c { }
end.should raise_error(TypeError)
end
- it "raises a #{frozen_error_class} if self is frozen" do
- lambda { {}.freeze.default_proc = Proc.new {} }.should raise_error(frozen_error_class)
- lambda { {}.freeze.default_proc = nil }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if self is frozen" do
+ -> { {}.freeze.default_proc = Proc.new {} }.should raise_error(FrozenError)
+ -> { {}.freeze.default_proc = nil }.should raise_error(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/default_spec.rb b/spec/ruby/core/hash/default_spec.rb
index afc4f9780f..d8b62ea196 100644
--- a/spec/ruby/core/hash/default_spec.rb
+++ b/spec/ruby/core/hash/default_spec.rb
@@ -30,7 +30,7 @@ describe "Hash#default=" do
end
it "unsets the default proc" do
- [99, nil, lambda { 6 }].each do |default|
+ [99, nil, -> { 6 }].each do |default|
h = Hash.new { 5 }
h.default_proc.should_not == nil
h.default = default
@@ -39,8 +39,8 @@ describe "Hash#default=" do
end
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.default = nil }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.default = nil }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.default = nil }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.default = nil }.should raise_error(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/delete_if_spec.rb b/spec/ruby/core/hash/delete_if_spec.rb
index 58fba1ff80..c9e670ffc3 100644
--- a/spec/ruby/core/hash/delete_if_spec.rb
+++ b/spec/ruby/core/hash/delete_if_spec.rb
@@ -34,9 +34,9 @@ describe "Hash#delete_if" do
each_pairs.should == delete_pairs
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.delete_if { false } }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.delete_if { true } }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.delete_if { false } }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.delete_if { true } }.should raise_error(FrozenError)
end
it_behaves_like :hash_iteration_no_block, :delete_if
diff --git a/spec/ruby/core/hash/delete_spec.rb b/spec/ruby/core/hash/delete_spec.rb
index 6f0079dafa..3e3479c69c 100644
--- a/spec/ruby/core/hash/delete_spec.rb
+++ b/spec/ruby/core/hash/delete_spec.rb
@@ -24,7 +24,7 @@ describe "Hash#delete" do
it "allows removing a key while iterating" do
h = { a: 1, b: 2 }
visited = []
- h.each_pair { |k,v|
+ h.each_pair { |k, v|
visited << k
h.delete(k)
}
@@ -32,13 +32,27 @@ describe "Hash#delete" do
h.should == {}
end
+ it "allows removing a key while iterating for big hashes" do
+ h = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10,
+ k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20,
+ u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 }
+ visited = []
+ h.each_pair { |k, v|
+ visited << k
+ h.delete(k)
+ }
+ visited.should == [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m,
+ :n, :o, :p, :q, :r, :s, :t, :u, :v, :w, :x, :y, :z]
+ h.should == {}
+ end
+
it "accepts keys with private #hash method" do
key = HashSpecs::KeyWithPrivateHash.new
{ key => 5 }.delete(key).should == 5
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.delete("foo") }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.delete("foo") }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.delete("foo") }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.delete("foo") }.should raise_error(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/dig_spec.rb b/spec/ruby/core/hash/dig_spec.rb
index dcba049ac4..aa0ecadd2f 100644
--- a/spec/ruby/core/hash/dig_spec.rb
+++ b/spec/ruby/core/hash/dig_spec.rb
@@ -28,7 +28,7 @@ describe "Hash#dig" do
end
it "raises an ArgumentError if no arguments provided" do
- lambda { { the: 'borg' }.dig() }.should raise_error(ArgumentError)
+ -> { { the: 'borg' }.dig() }.should raise_error(ArgumentError)
end
it "handles type-mixed deep digging" do
@@ -47,8 +47,8 @@ describe "Hash#dig" do
it "raises TypeError if an intermediate element does not respond to #dig" do
h = {}
h[:foo] = [ { bar: [ 1 ] }, [ nil, 'str' ] ]
- lambda { h.dig(:foo, 0, :bar, 0, 0) }.should raise_error(TypeError)
- lambda { h.dig(:foo, 1, 1, 0) }.should raise_error(TypeError)
+ -> { h.dig(:foo, 0, :bar, 0, 0) }.should raise_error(TypeError)
+ -> { h.dig(:foo, 1, 1, 0) }.should raise_error(TypeError)
end
it "calls #dig on the result of #[] with the remaining arguments" do
diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb
index 2eb65d3789..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]
@@ -117,4 +117,18 @@ describe "Hash#[]" do
key = HashSpecs::KeyWithPrivateHash.new
{ key => 42 }[key].should == 42
end
+
+ it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do
+ code = <<-EOC
+ load '#{fixture __FILE__, "name.rb"}'
+ hash = { true => 42, false => 42, 1 => 42, 2.0 => 42, "hello" => 42, :ok => 42 }
+ [true, false, 1, 2.0, "hello", :ok].each do |value|
+ raise "incorrect value" unless hash[value] == 42
+ end
+ puts "Ok."
+ EOC
+ result = ruby_exe(code, args: "2>&1")
+ result.should == "Ok.\n"
+ end
+
end
diff --git a/spec/ruby/core/hash/empty_spec.rb b/spec/ruby/core/hash/empty_spec.rb
index e9be44bab0..881e1cc34b 100644
--- a/spec/ruby/core/hash/empty_spec.rb
+++ b/spec/ruby/core/hash/empty_spec.rb
@@ -3,13 +3,13 @@ require_relative 'fixtures/classes'
describe "Hash#empty?" do
it "returns true if the hash has no entries" do
- {}.empty?.should == true
- { 1 => 1 }.empty?.should == false
+ {}.should.empty?
+ { 1 => 1 }.should_not.empty?
end
it "returns true if the hash has no entries and has a default value" do
- Hash.new(5).empty?.should == true
- Hash.new { 5 }.empty?.should == true
- Hash.new { |hsh, k| hsh[k] = k }.empty?.should == true
+ Hash.new(5).should.empty?
+ Hash.new { 5 }.should.empty?
+ Hash.new { |hsh, k| hsh[k] = k }.should.empty?
end
end
diff --git a/spec/ruby/core/hash/except_spec.rb b/spec/ruby/core/hash/except_spec.rb
new file mode 100644
index 0000000000..026e454b13
--- /dev/null
+++ b/spec/ruby/core/hash/except_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "Hash#except" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
+
+ it "returns a new duplicate hash without arguments" do
+ ret = @hash.except
+ ret.should_not equal(@hash)
+ ret.should == @hash
+ end
+
+ it "returns a hash without the requested subset" do
+ @hash.except(:c, :a).should == { b: 2 }
+ end
+
+ it "ignores keys not present in the original hash" do
+ @hash.except(:a, :chunky_bacon).should == { b: 2, c: 3 }
+ end
+
+ 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/fetch_spec.rb b/spec/ruby/core/hash/fetch_spec.rb
index 197832e311..6e0d207224 100644
--- a/spec/ruby/core/hash/fetch_spec.rb
+++ b/spec/ruby/core/hash/fetch_spec.rb
@@ -4,10 +4,10 @@ require_relative '../../shared/hash/key_error'
describe "Hash#fetch" do
context "when the key is not found" do
- it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, Hash.new(a: 5)
- it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, {}
- it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, Hash.new { 5 }
- it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, Hash.new(5)
+ it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new({ a: 5 })
+ it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, {}
+ it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new { 5 }
+ it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new(5)
it "formats the object with #inspect in the KeyError message" do
-> {
@@ -31,14 +31,14 @@ describe "Hash#fetch" do
end
it "gives precedence to the default block over the default argument when passed both" do
- lambda {
+ -> {
@result = {}.fetch(9, :foo) { |i| i * i }
}.should complain(/block supersedes default value argument/)
@result.should == 81
end
it "raises an ArgumentError when not passed one or two arguments" do
- lambda { {}.fetch() }.should raise_error(ArgumentError)
- lambda { {}.fetch(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { {}.fetch() }.should raise_error(ArgumentError)
+ -> { {}.fetch(1, 2, 3) }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/core/hash/fetch_values_spec.rb b/spec/ruby/core/hash/fetch_values_spec.rb
index a4af153bf7..0cd48565af 100644
--- a/spec/ruby/core/hash/fetch_values_spec.rb
+++ b/spec/ruby/core/hash/fetch_values_spec.rb
@@ -12,10 +12,14 @@ describe "Hash#fetch_values" do
@hash.fetch_values(:a).should == [1]
@hash.fetch_values(:a, :c).should == [1, 3]
end
+
+ it "returns the values for keys ordered in the order of the requested keys" do
+ @hash.fetch_values(:c, :a).should == [3, 1]
+ end
end
describe "with unmatched keys" do
- it_behaves_like :key_error, ->(obj, key) { obj.fetch_values(key) }, Hash.new(a: 5)
+ it_behaves_like :key_error, -> obj, key { obj.fetch_values(key) }, Hash.new({ a: 5 })
it "returns the default value from block" do
@hash.fetch_values(:z) { |key| "`#{key}' is not found" }.should == ["`z' is not found"]
diff --git a/spec/ruby/core/hash/filter_spec.rb b/spec/ruby/core/hash/filter_spec.rb
index 4382c94e62..7dabe44984 100644
--- a/spec/ruby/core/hash/filter_spec.rb
+++ b/spec/ruby/core/hash/filter_spec.rb
@@ -1,12 +1,10 @@
require_relative '../../spec_helper'
require_relative 'shared/select'
-ruby_version_is "2.6" do
- describe "Hash#filter" do
- it_behaves_like :hash_select, :filter
- end
+describe "Hash#filter" do
+ it_behaves_like :hash_select, :filter
+end
- describe "Hash#filter!" do
- it_behaves_like :hash_select!, :filter!
- end
+describe "Hash#filter!" do
+ it_behaves_like :hash_select!, :filter!
end
diff --git a/spec/ruby/core/hash/fixtures/name.rb b/spec/ruby/core/hash/fixtures/name.rb
new file mode 100644
index 0000000000..b203bf6ae4
--- /dev/null
+++ b/spec/ruby/core/hash/fixtures/name.rb
@@ -0,0 +1,30 @@
+class TrueClass
+ def hash
+ raise "TrueClass#hash should not be called"
+ end
+end
+class FalseClass
+ def hash
+ raise "FalseClass#hash should not be called"
+ end
+end
+class Integer
+ def hash
+ raise "Integer#hash should not be called"
+ end
+end
+class Float
+ def hash
+ raise "Float#hash should not be called"
+ end
+end
+class String
+ def hash
+ raise "String#hash should not be called"
+ end
+end
+class Symbol
+ def hash
+ raise "Symbol#hash should not be called"
+ end
+end
diff --git a/spec/ruby/core/hash/flatten_spec.rb b/spec/ruby/core/hash/flatten_spec.rb
index 5a7ddd8baa..825da15bfc 100644
--- a/spec/ruby/core/hash/flatten_spec.rb
+++ b/spec/ruby/core/hash/flatten_spec.rb
@@ -55,7 +55,7 @@ describe "Hash#flatten" do
end
it "raises a TypeError if given a non-Integer argument" do
- lambda do
+ -> do
@h.flatten(Object.new)
end.should raise_error(TypeError)
end
diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb
index 3649d4d8de..cd67f7a652 100644
--- a/spec/ruby/core/hash/hash_spec.rb
+++ b/spec/ruby/core/hash/hash_spec.rb
@@ -41,4 +41,11 @@ describe "Hash#hash" do
h.hash.should == {x: [h]}.hash
# Like above, because h.eql?(x: [h])
end
+
+ it "allows omitting values" do
+ a = 1
+ b = 2
+
+ {a:, b:}.should == { a: 1, b: 2 }
+ end
end
diff --git a/spec/ruby/core/hash/index_spec.rb b/spec/ruby/core/hash/index_spec.rb
deleted file mode 100644
index 2b52c69949..0000000000
--- a/spec/ruby/core/hash/index_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/index'
-
-describe "Hash#index" do
- it_behaves_like :hash_index, :index
-end
diff --git a/spec/ruby/core/hash/initialize_spec.rb b/spec/ruby/core/hash/initialize_spec.rb
index 344571631a..d13496ba3b 100644
--- a/spec/ruby/core/hash/initialize_spec.rb
+++ b/spec/ruby/core/hash/initialize_spec.rb
@@ -45,17 +45,17 @@ describe "Hash#initialize" do
h.send(:initialize).should equal(h)
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- block = lambda { HashSpecs.frozen_hash.instance_eval { initialize() }}
- block.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ block = -> { HashSpecs.frozen_hash.instance_eval { initialize() }}
+ block.should raise_error(FrozenError)
- block = lambda { HashSpecs.frozen_hash.instance_eval { initialize(nil) } }
- block.should raise_error(frozen_error_class)
+ block = -> { HashSpecs.frozen_hash.instance_eval { initialize(nil) } }
+ block.should raise_error(FrozenError)
- block = lambda { HashSpecs.frozen_hash.instance_eval { initialize(5) } }
- block.should raise_error(frozen_error_class)
+ block = -> { HashSpecs.frozen_hash.instance_eval { initialize(5) } }
+ block.should raise_error(FrozenError)
- block = lambda { HashSpecs.frozen_hash.instance_eval { initialize { 5 } } }
- block.should raise_error(frozen_error_class)
+ block = -> { HashSpecs.frozen_hash.instance_eval { initialize { 5 } } }
+ block.should raise_error(FrozenError)
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/keep_if_spec.rb b/spec/ruby/core/hash/keep_if_spec.rb
index 80f7fbbf64..d50d969467 100644
--- a/spec/ruby/core/hash/keep_if_spec.rb
+++ b/spec/ruby/core/hash/keep_if_spec.rb
@@ -27,9 +27,9 @@ describe "Hash#keep_if" do
h.keep_if { true }.should equal(h)
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.keep_if { true } }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.keep_if { false } }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.keep_if { true } }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.keep_if { false } }.should raise_error(FrozenError)
end
it_behaves_like :hash_iteration_no_block, :keep_if
diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb
index 77e5c42071..6710d121ef 100644
--- a/spec/ruby/core/hash/merge_spec.rb
+++ b/spec/ruby/core/hash/merge_spec.rb
@@ -28,7 +28,7 @@ describe "Hash#merge" do
r = h1.merge(h2) { |k,x,y| "#{k}:#{x+2*y}" }
r.should == { a: "a:-2", b: "b:9", c: -3, d: 5 }
- lambda {
+ -> {
h1.merge(h2) { |k, x, y| raise(IndexError) }
}.should raise_error(IndexError)
@@ -81,19 +81,40 @@ describe "Hash#merge" do
merge_pairs.should == expected_pairs
end
- ruby_version_is "2.6" do
- it "accepts multiple hashes" do
- result = { a: 1 }.merge({ b: 2 }, { c: 3 }, { d: 4 })
- result.should == { a: 1, b: 2, c: 3, d: 4 }
- end
+ it "accepts multiple hashes" do
+ result = { a: 1 }.merge({ b: 2 }, { c: 3 }, { d: 4 })
+ result.should == { a: 1, b: 2, c: 3, d: 4 }
+ end
+
+ it "accepts zero arguments and returns a copy of self" do
+ hash = { a: 1 }
+ merged = hash.merge
- it "accepts zero arguments and returns a copy of self" do
- hash = { a: 1 }
- merged = hash.merge
+ 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
- merged.should eql(hash)
- merged.should_not equal(hash)
- 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
diff --git a/spec/ruby/core/hash/new_spec.rb b/spec/ruby/core/hash/new_spec.rb
index c21266777f..5ae3e1f98d 100644
--- a/spec/ruby/core/hash/new_spec.rb
+++ b/spec/ruby/core/hash/new_spec.rb
@@ -26,11 +26,42 @@ describe "Hash.new" do
end
it "raises an ArgumentError if more than one argument is passed" do
- lambda { Hash.new(5,6) }.should raise_error(ArgumentError)
+ -> { Hash.new(5,6) }.should raise_error(ArgumentError)
end
it "raises an ArgumentError if passed both default argument and default block" do
- lambda { Hash.new(5) { 0 } }.should raise_error(ArgumentError)
- lambda { Hash.new(nil) { 0 } }.should raise_error(ArgumentError)
+ -> { Hash.new(5) { 0 } }.should raise_error(ArgumentError)
+ -> { Hash.new(nil) { 0 } }.should raise_error(ArgumentError)
+ end
+
+ 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"))
+ )
+
+ -> { Hash.new(1, unknown: true) }.should raise_error(ArgumentError)
+ -> { Hash.new(unknown: true) { 0 } }.should raise_error(ArgumentError)
+
+ 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/rehash_spec.rb b/spec/ruby/core/hash/rehash_spec.rb
index 32ba3fcfb9..db3e91b166 100644
--- a/spec/ruby/core/hash/rehash_spec.rb
+++ b/spec/ruby/core/hash/rehash_spec.rb
@@ -2,21 +2,30 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Hash#rehash" do
- it "reorganizes the hash by recomputing all key hash codes" do
- k1 = [1]
- k2 = [2]
+ it "reorganizes the Hash by recomputing all key hash codes" do
+ k1 = Object.new
+ k2 = Object.new
+ def k1.hash; 0; end
+ def k2.hash; 1; end
+
h = {}
- h[k1] = 0
- h[k2] = 1
+ h[k1] = :v1
+ h[k2] = :v2
+
+ def k1.hash; 1; end
- k1 << 2
+ # The key should no longer be found as the #hash changed.
+ # Hash values 0 and 1 should not conflict, even with 1-bit stored hash.
h.key?(k1).should == false
+
h.keys.include?(k1).should == true
h.rehash.should equal(h)
h.key?(k1).should == true
- h[k1].should == 0
+ h[k1].should == :v1
+ end
+ it "calls #hash for each key" do
k1 = mock('k1')
k2 = mock('k2')
v1 = mock('v1')
@@ -50,8 +59,56 @@ describe "Hash#rehash" do
h.keys.should == [a]
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.rehash }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.rehash }.should raise_error(frozen_error_class)
+ it "removes duplicate keys for large hashes" do
+ a = [1,2]
+ b = [1]
+
+ h = {}
+ h[a] = true
+ h[b] = true
+ 100.times { |n| h[n] = true }
+ b << 2
+ h.size.should == 102
+ h.keys.should.include? a
+ h.keys.should.include? b
+ h.rehash
+ h.size.should == 101
+ h.keys.should.include? a
+ h.keys.should_not.include? [1]
+ end
+
+ it "iterates keys in insertion order" do
+ key = Class.new do
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def hash
+ 123
+ end
+ end
+
+ a, b, c, d = key.new('a'), key.new('b'), key.new('c'), key.new('d')
+ h = { a => 1, b => 2, c => 3, d => 4 }
+ h.size.should == 4
+
+ key.class_exec do
+ def eql?(other)
+ true
+ end
+ end
+
+ h.rehash
+ h.size.should == 1
+ k, v = h.first
+ k.name.should == 'a'
+ v.should == 4
+ end
+
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.rehash }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.rehash }.should raise_error(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/reject_spec.rb b/spec/ruby/core/hash/reject_spec.rb
index a11e80a34a..8381fc7fc1 100644
--- a/spec/ruby/core/hash/reject_spec.rb
+++ b/spec/ruby/core/hash/reject_spec.rb
@@ -31,11 +31,6 @@ describe "Hash#reject" do
HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should be_kind_of(Hash)
HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should be_kind_of(Hash)
end
-
- it "does not taint the resulting hash" do
- h = { a: 1 }.taint
- h.reject {false}.tainted?.should == false
- end
end
it "processes entries with the same order as reject!" do
@@ -49,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
@@ -87,12 +103,12 @@ describe "Hash#reject!" do
reject_bang_pairs.should == delete_if_pairs
end
- it "raises a #{frozen_error_class} if called on a frozen instance that is modified" do
- lambda { HashSpecs.empty_frozen_hash.reject! { true } }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance that is modified" do
+ -> { HashSpecs.empty_frozen_hash.reject! { true } }.should raise_error(FrozenError)
end
- it "raises a #{frozen_error_class} if called on a frozen instance that would not be modified" do
- lambda { HashSpecs.frozen_hash.reject! { false } }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance that would not be modified" do
+ -> { HashSpecs.frozen_hash.reject! { false } }.should raise_error(FrozenError)
end
it_behaves_like :hash_iteration_no_block, :reject!
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/ruby2_keywords_hash_spec.rb b/spec/ruby/core/hash/ruby2_keywords_hash_spec.rb
new file mode 100644
index 0000000000..7dbb9c0a98
--- /dev/null
+++ b/spec/ruby/core/hash/ruby2_keywords_hash_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash.ruby2_keywords_hash?" do
+ it "returns false if the Hash is not a keywords Hash" do
+ Hash.ruby2_keywords_hash?({}).should == false
+ end
+
+ it "returns true if the Hash is a keywords Hash marked by Module#ruby2_keywords" do
+ obj = Class.new {
+ ruby2_keywords def m(*args)
+ args.last
+ end
+ }.new
+ Hash.ruby2_keywords_hash?(obj.m(a: 1)).should == true
+ end
+
+ it "raises TypeError for non-Hash" do
+ -> { Hash.ruby2_keywords_hash?(nil) }.should raise_error(TypeError)
+ end
+end
+
+describe "Hash.ruby2_keywords_hash" do
+ it "returns a copy of a Hash and marks the copy as a keywords Hash" do
+ h = {a: 1}.freeze
+ kw = Hash.ruby2_keywords_hash(h)
+ Hash.ruby2_keywords_hash?(h).should == false
+ Hash.ruby2_keywords_hash?(kw).should == true
+ kw.should == h
+ end
+
+ it "returns an instance of the subclass if called on an instance of a subclass of Hash" do
+ h = HashSpecs::MyHash.new
+ h[:a] = 1
+ kw = Hash.ruby2_keywords_hash(h)
+ kw.class.should == HashSpecs::MyHash
+ Hash.ruby2_keywords_hash?(h).should == false
+ Hash.ruby2_keywords_hash?(kw).should == true
+ kw.should == h
+ end
+
+ it "copies instance variables" do
+ h = {a: 1}
+ h.instance_variable_set(:@foo, 42)
+ kw = Hash.ruby2_keywords_hash(h)
+ kw.instance_variable_get(:@foo).should == 42
+ end
+
+ it "copies the hash internals" do
+ h = {a: 1}
+ kw = Hash.ruby2_keywords_hash(h)
+ h[:a] = 2
+ kw[:a].should == 1
+ end
+
+ it "raises TypeError for non-Hash" do
+ -> { Hash.ruby2_keywords_hash(nil) }.should raise_error(TypeError)
+ end
+
+ it "retains the default value" do
+ hash = Hash.new(1)
+ Hash.ruby2_keywords_hash(hash).default.should == 1
+ hash[:a] = 1
+ Hash.ruby2_keywords_hash(hash).default.should == 1
+ end
+
+ it "retains the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ hash = Hash.new(&pr)
+ Hash.ruby2_keywords_hash(hash).default_proc.should == pr
+ hash[:a] = 1
+ Hash.ruby2_keywords_hash(hash).default_proc.should == pr
+ end
+
+ ruby_version_is '3.3' do
+ it "retains compare_by_identity_flag" do
+ hash = {}.compare_by_identity
+ Hash.ruby2_keywords_hash(hash).compare_by_identity?.should == true
+ hash[:a] = 1
+ Hash.ruby2_keywords_hash(hash).compare_by_identity?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/shared/comparison.rb b/spec/ruby/core/hash/shared/comparison.rb
index bbb9bfd6ad..07564e4cec 100644
--- a/spec/ruby/core/hash/shared/comparison.rb
+++ b/spec/ruby/core/hash/shared/comparison.rb
@@ -1,8 +1,8 @@
describe :hash_comparison, shared: true do
it "raises a TypeError if the right operand is not a hash" do
- lambda { { a: 1 }.send(@method, 1) }.should raise_error(TypeError)
- lambda { { a: 1 }.send(@method, nil) }.should raise_error(TypeError)
- lambda { { a: 1 }.send(@method, []) }.should raise_error(TypeError)
+ -> { { a: 1 }.send(@method, 1) }.should raise_error(TypeError)
+ -> { { a: 1 }.send(@method, nil) }.should raise_error(TypeError)
+ -> { { a: 1 }.send(@method, []) }.should raise_error(TypeError)
end
it "returns false if both hashes have the same keys but different values" do
diff --git a/spec/ruby/core/hash/shared/each.rb b/spec/ruby/core/hash/shared/each.rb
index d1f2e5f672..f9839ff58f 100644
--- a/spec/ruby/core/hash/shared/each.rb
+++ b/spec/ruby/core/hash/shared/each.rb
@@ -21,19 +21,39 @@ describe :hash_each, shared: true do
ary.sort.should == ["a", "b", "c"]
end
- it "yields 2 values and not an Array of 2 elements when given a callable of arity 2" do
+ it "always yields an Array of 2 elements, even when given a callable of arity 2" do
obj = Object.new
def obj.foo(key, value)
- ScratchPad << key << value
+ end
+
+ -> {
+ { "a" => 1 }.send(@method, &obj.method(:foo))
+ }.should raise_error(ArgumentError)
+
+ -> {
+ { "a" => 1 }.send(@method, &-> key, value { })
+ }.should raise_error(ArgumentError)
+ end
+
+ it "yields an Array of 2 elements when given a callable of arity 1" do
+ obj = Object.new
+ def obj.foo(key_value)
+ ScratchPad << key_value
end
ScratchPad.record([])
{ "a" => 1 }.send(@method, &obj.method(:foo))
- ScratchPad.recorded.should == ["a", 1]
+ ScratchPad.recorded.should == [["a", 1]]
+ end
- ScratchPad.record([])
- { "a" => 1 }.send(@method, &-> key, value { ScratchPad << key << value })
- ScratchPad.recorded.should == ["a", 1]
+ it "raises an error for a Hash when an arity enforcing callable of arity >2 is passed in" do
+ obj = Object.new
+ def obj.foo(key, value, extra)
+ end
+
+ -> {
+ { "a" => 1 }.send(@method, &obj.method(:foo))
+ }.should raise_error(ArgumentError)
end
it "uses the same order as keys() and values()" do
diff --git a/spec/ruby/core/hash/shared/eql.rb b/spec/ruby/core/hash/shared/eql.rb
index 1aed5f51fb..68db49f76d 100644
--- a/spec/ruby/core/hash/shared/eql.rb
+++ b/spec/ruby/core/hash/shared/eql.rb
@@ -118,7 +118,7 @@ describe :hash_eql_additional, shared: true do
{ 1.0 => "x" }.send(@method, { 1 => "x" }).should be_false
end
- it "returns true iff other Hash has the same number of keys and each key-value pair matches" do
+ it "returns true if and only if other Hash has the same number of keys and each key-value pair matches" do
a = { a: 5 }
b = {}
a.send(@method, b).should be_false
@@ -155,12 +155,8 @@ describe :hash_eql_additional, shared: true do
obj = mock('0')
obj.should_receive(:hash).at_least(1).and_return(0)
- # It's undefined whether the impl does a[0].eql?(a[1]) or
- # a[1].eql?(a[0]) so we taint both.
def obj.eql?(o)
return true if self.equal?(o)
- taint
- o.taint
false
end
@@ -168,18 +164,12 @@ describe :hash_eql_additional, shared: true do
end
{ a[0] => 1 }.send(@method, { a[1] => 1 }).should be_false
- a[0].tainted?.should be_true
- a[1].tainted?.should be_true
a = Array.new(2) do
obj = mock('0')
obj.should_receive(:hash).at_least(1).and_return(0)
def obj.eql?(o)
- # It's undefined whether the impl does a[0].send(@method, a[1]) or
- # a[1].send(@method, a[0]) so we taint both.
- taint
- o.taint
true
end
@@ -187,8 +177,6 @@ describe :hash_eql_additional, shared: true do
end
{ a[0] => 1 }.send(@method, { a[1] => 1 }).should be_true
- a[0].tainted?.should be_true
- a[1].tainted?.should be_true
end
it "compares the values in self to values in other hash" do
diff --git a/spec/ruby/core/hash/shared/equal.rb b/spec/ruby/core/hash/shared/equal.rb
deleted file mode 100644
index 43606437fe..0000000000
--- a/spec/ruby/core/hash/shared/equal.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-describe :hash_equal, shared: true do
- it "does not compare values when keys don't match" do
- value = mock('x')
- value.should_not_receive(:==)
- value.should_not_receive(:eql?)
- { 1 => value }.send(@method, { 2 => value }).should be_false
- end
-
- it "returns false when the numbers of keys differ without comparing any elements" do
- obj = mock('x')
- h = { obj => obj }
-
- obj.should_not_receive(:==)
- obj.should_not_receive(:eql?)
-
- {}.send(@method, h).should be_false
- h.send(@method, {}).should be_false
- end
-
- it "first compares keys via hash" do
- x = mock('x')
- x.should_receive(:hash).and_return(0)
- y = mock('y')
- y.should_receive(:hash).and_return(0)
-
- { x => 1 }.send(@method, { y => 1 }).should be_false
- end
-
- it "does not compare keys with different hash codes via eql?" do
- x = mock('x')
- y = mock('y')
- x.should_not_receive(:eql?)
- y.should_not_receive(:eql?)
-
- x.should_receive(:hash).and_return(0)
- y.should_receive(:hash).and_return(1)
-
- def x.hash() 0 end
- def y.hash() 1 end
-
- { x => 1 }.send(@method, { y => 1 }).should be_false
- end
-
- it "computes equality for recursive hashes" do
- h = {}
- h[:a] = h
- h.send(@method, h[:a]).should be_true
- (h == h[:a]).should be_true
- end
-
- it "computes equality for complex recursive hashes" do
- a, b = {}, {}
- a.merge! self: a, other: b
- b.merge! self: b, other: a
- a.send(@method, b).should be_true # they both have the same structure!
-
- c = {}
- c.merge! other: c, self: c
- c.send(@method, a).should be_true # subtle, but they both have the same structure!
- a[:delta] = c[:delta] = a
- c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
- c[:delta] = 42
- c.send(@method, a).should be_false
- a[:delta] = 42
- c.send(@method, a).should be_false
- b[:delta] = 42
- c.send(@method, a).should be_true
- end
-
- it "computes equality for recursive hashes & arrays" do
- x, y, z = [], [], []
- a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42}
- x << a
- y << c
- z << b
- b.send(@method, c).should be_true # they clearly have the same structure!
- y.send(@method, z).should be_true
- a.send(@method, b).should be_true # subtle, but they both have the same structure!
- x.send(@method, y).should be_true
- y << x
- y.send(@method, z).should be_false
- z << x
- y.send(@method, z).should be_true
-
- a[:foo], a[:bar] = a[:bar], a[:foo]
- a.send(@method, b).should be_false
- b[:bar] = b[:foo]
- b.send(@method, c).should be_false
- end
-end
diff --git a/spec/ruby/core/hash/shared/index.rb b/spec/ruby/core/hash/shared/index.rb
index 4858ba85f5..7f6a186464 100644
--- a/spec/ruby/core/hash/shared/index.rb
+++ b/spec/ruby/core/hash/shared/index.rb
@@ -3,25 +3,35 @@ require_relative '../fixtures/classes'
describe :hash_index, shared: true do
it "returns the corresponding key for value" do
- { 2 => 'a', 1 => 'b' }.send(@method, 'b').should == 1
+ suppress_warning do # for Hash#index
+ { 2 => 'a', 1 => 'b' }.send(@method, 'b').should == 1
+ end
end
it "returns nil if the value is not found" do
- { a: -1, b: 3.14, c: 2.718 }.send(@method, 1).should be_nil
+ suppress_warning do # for Hash#index
+ { a: -1, b: 3.14, c: 2.718 }.send(@method, 1).should be_nil
+ end
end
it "doesn't return default value if the value is not found" do
- Hash.new(5).send(@method, 5).should be_nil
+ suppress_warning do # for Hash#index
+ Hash.new(5).send(@method, 5).should be_nil
+ end
end
it "compares values using ==" do
- { 1 => 0 }.send(@method, 0.0).should == 1
- { 1 => 0.0 }.send(@method, 0).should == 1
+ suppress_warning do # for Hash#index
+ { 1 => 0 }.send(@method, 0.0).should == 1
+ { 1 => 0.0 }.send(@method, 0).should == 1
+ end
needle = mock('needle')
inhash = mock('inhash')
inhash.should_receive(:==).with(needle).and_return(true)
- { 1 => inhash }.send(@method, needle).should == 1
+ suppress_warning do # for Hash#index
+ { 1 => inhash }.send(@method, needle).should == 1
+ end
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 eb51130781..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(lambda { raise "Should not invoke lambda" })
- hash_a.send(@method, hash_b)
- hash_a.default.should == hash_b.default
- end
-
- it "raises a #{frozen_error_class} if called on a frozen instance that would not be modified" do
- lambda do
- HashSpecs.frozen_hash.send(@method, HashSpecs.frozen_hash)
- end.should raise_error(frozen_error_class)
- end
-
- it "raises a #{frozen_error_class} if called on a frozen instance that is modified" do
- lambda do
- HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
- end.should raise_error(frozen_error_class)
- end
-end
diff --git a/spec/ruby/core/hash/shared/select.rb b/spec/ruby/core/hash/shared/select.rb
index cc1a54da25..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
@@ -74,12 +95,12 @@ describe :hash_select!, shared: true do
{ a: 1 }.send(@method) { |k,v| v <= 1 }.should == nil
end
- it "raises a #{frozen_error_class} if called on an empty frozen instance" do
- lambda { HashSpecs.empty_frozen_hash.send(@method) { false } }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on an empty frozen instance" do
+ -> { HashSpecs.empty_frozen_hash.send(@method) { false } }.should raise_error(FrozenError)
end
- it "raises a #{frozen_error_class} if called on a frozen instance that would not be modified" do
- lambda { HashSpecs.frozen_hash.send(@method) { true } }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance that would not be modified" do
+ -> { HashSpecs.frozen_hash.send(@method) { true } }.should raise_error(FrozenError)
end
it_should_behave_like :hash_iteration_no_block
diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb
index de61ed1ad1..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)
@@ -36,7 +36,7 @@ describe :hash_store, shared: true do
h[key].should == "foo"
end
- it " accepts keys with a Bignum hash" do
+ it " accepts keys with an Integer hash" do
o = mock(hash: 1 << 100)
h = {}
h[o] = 1
@@ -44,13 +44,13 @@ 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"
h.should == { "foo" => 0 }
- h.keys[0].frozen?.should == true
+ h.keys[0].should.frozen?
end
it "doesn't duplicate and freeze already frozen string keys" do
@@ -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
@@ -86,13 +86,30 @@ describe :hash_store, shared: true do
h.keys.last.should_not equal(key2)
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.send(@method, 1, 2) }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.send(@method, 1, 2) }.should raise_error(FrozenError)
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
+ code = <<-EOC
+ load '#{fixture __FILE__, "name.rb"}'
+ hash = {}
+ [true, false, 1, 2.0, "hello", :ok].each do |value|
+ hash[value] = 42
+ raise "incorrect value" unless hash[value] == 42
+ hash[value] = 43
+ raise "incorrect value" unless hash[value] == 43
+ end
+ puts "OK"
+ puts hash.size
+ EOC
+ result = ruby_exe(code, args: "2>&1")
+ result.should == "OK\n6\n"
end
end
diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb
index 88333e0f42..e116b8878b 100644
--- a/spec/ruby/core/hash/shared/to_s.rb
+++ b/spec/ruby/core/hash/shared/to_s.rb
@@ -2,17 +2,10 @@ require_relative '../../../spec_helper'
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
@@ -20,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
@@ -52,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
@@ -61,36 +54,40 @@ describe :hash_to_s, shared: true do
obj.should_receive(:inspect).and_return(obj)
obj.should_receive(:to_s).and_raise(Exception)
- lambda { { a: obj }.send(@method) }.should raise_error(Exception)
+ -> { { a: obj }.send(@method) }.should raise_error(Exception)
end
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 "returns a tainted string if self is tainted and not empty" do
- {}.taint.send(@method).tainted?.should be_false
- { nil => nil }.taint.send(@method).tainted?.should be_true
+ 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))
+ expected = ruby_version_is("3.4") ? '{a: "utf_16be \u3042"}' : '{:a=>"utf_16be \u3042"}'
+ {a: utf_16be}.send(@method).should == expected
end
- it "returns an untrusted string if self is untrusted and not empty" do
- {}.untrust.send(@method).untrusted?.should be_false
- { nil => nil }.untrust.send(@method).untrusted?.should be_true
+ it "works for keys and values whose #inspect return a frozen String" do
+ expected = ruby_version_is("3.4") ? "{true => false}" : "{true=>false}"
+ { true => false }.to_s.should == expected
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"}'
+ 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/shared/update.rb b/spec/ruby/core/hash/shared/update.rb
index e808add5c0..1b0eb809bf 100644
--- a/spec/ruby/core/hash/shared/update.rb
+++ b/spec/ruby/core/hash/shared/update.rb
@@ -34,10 +34,10 @@ describe :hash_update, shared: true do
merge_bang_pairs.should == merge_pairs
end
- it "raises a #{frozen_error_class} on a frozen instance that is modified" do
- lambda do
+ it "raises a FrozenError on a frozen instance that is modified" do
+ -> do
HashSpecs.frozen_hash.send(@method, 1 => 2)
- end.should raise_error(frozen_error_class)
+ end.should raise_error(FrozenError)
end
it "checks frozen status before coercing an object with #to_hash" do
@@ -47,14 +47,14 @@ describe :hash_update, shared: true do
def obj.to_hash() raise Exception, "should not receive #to_hash" end
obj.freeze
- lambda { HashSpecs.frozen_hash.send(@method, obj) }.should raise_error(frozen_error_class)
+ -> { HashSpecs.frozen_hash.send(@method, obj) }.should raise_error(FrozenError)
end
# see redmine #1571
- it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
- lambda do
+ it "raises a FrozenError on a frozen instance that would not be modified" do
+ -> do
HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
- end.should raise_error(frozen_error_class)
+ end.should raise_error(FrozenError)
end
it "does not raise an exception if changing the value of an existing key during iteration" do
@@ -64,15 +64,13 @@ describe :hash_update, shared: true do
hash.should == {1 => :foo, 3 => :bar, 5 => 6}
end
- ruby_version_is "2.6" do
- it "accepts multiple hashes" do
- result = { a: 1 }.send(@method, { b: 2 }, { c: 3 }, { d: 4 })
- result.should == { a: 1, b: 2, c: 3, d: 4 }
- end
+ it "accepts multiple hashes" do
+ result = { a: 1 }.send(@method, { b: 2 }, { c: 3 }, { d: 4 })
+ result.should == { a: 1, b: 2, c: 3, d: 4 }
+ end
- it "accepts zero arguments" do
- hash = { a: 1 }
- hash.send(@method).should eql(hash)
- end
+ it "accepts zero arguments" do
+ hash = { a: 1 }
+ hash.send(@method).should eql(hash)
end
end
diff --git a/spec/ruby/core/hash/shift_spec.rb b/spec/ruby/core/hash/shift_spec.rb
index 47c9ac1821..3f31b9864c 100644
--- a/spec/ruby/core/hash/shift_spec.rb
+++ b/spec/ruby/core/hash/shift_spec.rb
@@ -30,23 +30,22 @@ describe "Hash#shift" do
h.should == {}
end
- it "calls #default with nil if the Hash is empty" do
+ it "returns nil if the Hash is empty" do
h = {}
def h.default(key)
- key.should == nil
- :foo
+ raise
end
- h.shift.should == :foo
+ h.shift.should == nil
end
it "returns nil from an empty hash" do
{}.shift.should == nil
end
- it "returns (computed) default for empty hashes" do
- Hash.new(5).shift.should == 5
+ 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 == [h, nil]
+ h.shift.should == nil
end
it "preserves Hash invariants when removing the last item" do
@@ -57,9 +56,9 @@ describe "Hash#shift" do
h.should == {:c => 3}
end
- it "raises a #{frozen_error_class} if called on a frozen instance" do
- lambda { HashSpecs.frozen_hash.shift }.should raise_error(frozen_error_class)
- lambda { HashSpecs.empty_frozen_hash.shift }.should raise_error(frozen_error_class)
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash.shift }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.shift }.should raise_error(FrozenError)
end
it "works when the hash is at capacity" do
diff --git a/spec/ruby/core/hash/slice_spec.rb b/spec/ruby/core/hash/slice_spec.rb
index f7717c9404..4fcc01f9a6 100644
--- a/spec/ruby/core/hash/slice_spec.rb
+++ b/spec/ruby/core/hash/slice_spec.rb
@@ -1,55 +1,74 @@
require_relative '../../spec_helper'
-ruby_version_is "2.5" do
- describe "Hash#slice" do
- before :each do
- @hash = { a: 1, b: 2, c: 3 }
- end
+describe "Hash#slice" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
- it "returns a new empty hash without arguments" do
- ret = @hash.slice
- ret.should_not equal(@hash)
- ret.should be_an_instance_of(Hash)
- ret.should == {}
- end
+ it "returns a new empty hash without arguments" do
+ ret = @hash.slice
+ ret.should_not equal(@hash)
+ ret.should be_an_instance_of(Hash)
+ ret.should == {}
+ end
- it "returns the requested subset" do
- @hash.slice(:c, :a).should == { c: 3, a: 1 }
- end
+ it "returns the requested subset" do
+ @hash.slice(:c, :a).should == { c: 3, a: 1 }
+ end
- it "returns a hash ordered in the order of the requested keys" do
- @hash.slice(:c, :a).keys.should == [:c, :a]
- end
+ it "returns a hash ordered in the order of the requested keys" do
+ @hash.slice(:c, :a).keys.should == [:c, :a]
+ end
- it "returns only the keys of the original hash" do
- @hash.slice(:a, :chunky_bacon).should == { a: 1 }
- end
+ it "returns only the keys of the original hash" do
+ @hash.slice(:a, :chunky_bacon).should == { a: 1 }
+ end
- it "returns a Hash instance, even on subclasses" do
- klass = Class.new(Hash)
- h = klass.new
- h[:bar] = 12
- h[:foo] = 42
- r = h.slice(:foo)
- r.should == {foo: 42}
- r.class.should == Hash
- end
+ it "returns a Hash instance, even on subclasses" do
+ klass = Class.new(Hash)
+ h = klass.new
+ h[:bar] = 12
+ h[:foo] = 42
+ r = h.slice(:foo)
+ r.should == {foo: 42}
+ r.class.should == Hash
+ end
- it "uses the regular Hash#[] method, even on subclasses that override it" do
- ScratchPad.record []
- klass = Class.new(Hash) do
- def [](value)
- ScratchPad << :used_subclassed_operator
- super
- end
+ it "uses the regular Hash#[] method, even on subclasses that override it" do
+ ScratchPad.record []
+ klass = Class.new(Hash) do
+ def [](value)
+ ScratchPad << :used_subclassed_operator
+ super
end
+ end
- h = klass.new
- h[:bar] = 12
- h[:foo] = 42
- h.slice(:foo)
+ h = klass.new
+ h[:bar] = 12
+ h[:foo] = 42
+ h.slice(:foo)
- ScratchPad.recorded.should == []
- end
+ 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_a_spec.rb b/spec/ruby/core/hash/to_a_spec.rb
index 33ad7cdec9..5baf677929 100644
--- a/spec/ruby/core/hash/to_a_spec.rb
+++ b/spec/ruby/core/hash/to_a_spec.rb
@@ -26,12 +26,4 @@ describe "Hash#to_a" do
ent.should be_kind_of(Array)
ent.should == pairs
end
-
- it "returns a tainted array if self is tainted" do
- {}.taint.to_a.tainted?.should be_true
- end
-
- it "returns an untrusted array if self is untrusted" do
- {}.untrust.to_a.untrusted?.should be_true
- end
end
diff --git a/spec/ruby/core/hash/to_h_spec.rb b/spec/ruby/core/hash/to_h_spec.rb
index d6eaac9f33..f84fd7b503 100644
--- a/spec/ruby/core/hash/to_h_spec.rb
+++ b/spec/ruby/core/hash/to_h_spec.rb
@@ -19,56 +19,88 @@ 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
- ruby_version_is "2.6" do
- context "with block" do
- it "converts [key, value] pairs returned by the block to a hash" do
- { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 }
- 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] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
-
- -> do
- { a: 1, b: 2 }.to_h { |k, v| [k] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
- end
-
- it "raises TypeError if block returns something other than Array" do
- -> do
- { a: 1, b: 2 }.to_h { |k, v| "not-array" }
- end.should raise_error(TypeError, /wrong element type String/)
- end
-
- it "coerces returned pair to Array with #to_ary" do
- x = mock('x')
- x.stub!(:to_ary).and_return([:b, 'b'])
-
- { a: 1 }.to_h { |k| x }.should == { :b => 'b' }
- end
-
- it "does not coerce returned pair to Array with #to_a" do
- x = mock('x')
- x.stub!(:to_a).and_return([:b, 'b'])
-
- -> do
- { a: 1 }.to_h { |k| x }
- end.should raise_error(TypeError, /wrong element type MockObject/)
- end
+ context "with block" do
+ it "converts [key, value] pairs returned by the block to a hash" 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] }
+ end.should raise_error(ArgumentError, /element has wrong array length/)
+
+ -> do
+ { a: 1, b: 2 }.to_h { |k, v| [k] }
+ end.should raise_error(ArgumentError, /element has wrong array length/)
+ end
+
+ it "raises TypeError if block returns something other than Array" do
+ -> do
+ { a: 1, b: 2 }.to_h { |k, v| "not-array" }
+ end.should raise_error(TypeError, /wrong element type String/)
+ end
+
+ it "coerces returned pair to Array with #to_ary" do
+ x = mock('x')
+ x.stub!(:to_ary).and_return([:b, 'b'])
+
+ { a: 1 }.to_h { |k| x }.should == { :b => 'b' }
+ end
+
+ it "does not coerce returned pair to Array with #to_a" do
+ x = mock('x')
+ x.stub!(:to_a).and_return([:b, 'b'])
+
+ -> 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/to_proc_spec.rb b/spec/ruby/core/hash/to_proc_spec.rb
index ca55604043..9dbc79e5eb 100644
--- a/spec/ruby/core/hash/to_proc_spec.rb
+++ b/spec/ruby/core/hash/to_proc_spec.rb
@@ -19,16 +19,20 @@ describe "Hash#to_proc" do
@proc = @hash.to_proc
end
- it "is not a lambda" do
- @proc.lambda?.should == false
+ it "is a lambda" do
+ @proc.should.lambda?
+ end
+
+ it "has an arity of 1" do
+ @proc.arity.should == 1
end
it "raises ArgumentError if not passed exactly one argument" do
- lambda {
+ -> {
@proc.call
}.should raise_error(ArgumentError)
- lambda {
+ -> {
@proc.call 1, 2
}.should raise_error(ArgumentError)
end
@@ -81,7 +85,7 @@ describe "Hash#to_proc" do
end
it "raises an ArgumentError when calling #call on the Proc with no arguments" do
- lambda { @hash.to_proc.call }.should raise_error(ArgumentError)
+ -> { @hash.to_proc.call }.should raise_error(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb
index 32ac89b765..e2eeab1813 100644
--- a/spec/ruby/core/hash/transform_keys_spec.rb
+++ b/spec/ruby/core/hash/transform_keys_spec.rb
@@ -1,131 +1,149 @@
require_relative '../../spec_helper'
-ruby_version_is "2.5" do
- describe "Hash#transform_keys" do
- before :each do
- @hash = { a: 1, b: 2, c: 3 }
- end
+describe "Hash#transform_keys" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
- it "returns new hash" do
- ret = @hash.transform_keys(&:succ)
- ret.should_not equal(@hash)
- ret.should be_an_instance_of(Hash)
- end
+ it "returns new hash" do
+ ret = @hash.transform_keys(&:succ)
+ ret.should_not equal(@hash)
+ ret.should be_an_instance_of(Hash)
+ end
- it "sets the result as transformed keys with the given block" do
- @hash.transform_keys(&:succ).should == { b: 1, c: 2, d: 3 }
- end
+ it "sets the result as transformed keys with the given block" do
+ @hash.transform_keys(&:succ).should == { b: 1, c: 2, d: 3 }
+ end
- it "keeps last pair if new keys conflict" do
- @hash.transform_keys { |_| :a }.should == { a: 3 }
- end
+ it "keeps last pair if new keys conflict" do
+ @hash.transform_keys { |_| :a }.should == { a: 3 }
+ end
- it "makes both hashes to share values" do
- value = [1, 2, 3]
- new_hash = { a: value }.transform_keys(&:upcase)
- new_hash[:A].should equal(value)
- end
+ it "makes both hashes to share values" do
+ value = [1, 2, 3]
+ new_hash = { a: value }.transform_keys(&:upcase)
+ new_hash[:A].should equal(value)
+ end
- context "when no block is given" do
- it "returns a sized Enumerator" do
- enumerator = @hash.transform_keys
- enumerator.should be_an_instance_of(Enumerator)
- enumerator.size.should == @hash.size
- enumerator.each(&:succ).should == { b: 1, c: 2, d: 3 }
- end
+ context "when no block is given" do
+ it "returns a sized Enumerator" do
+ enumerator = @hash.transform_keys
+ enumerator.should be_an_instance_of(Enumerator)
+ enumerator.size.should == @hash.size
+ enumerator.each(&:succ).should == { b: 1, c: 2, d: 3 }
end
+ end
- it "returns a Hash instance, even on subclasses" do
- klass = Class.new(Hash)
- h = klass.new
- h[:foo] = 42
- r = h.transform_keys{|v| :"x#{v}"}
- r.keys.should == [:xfoo]
- r.class.should == Hash
- end
+ it "returns a Hash instance, even on subclasses" do
+ klass = Class.new(Hash)
+ h = klass.new
+ h[:foo] = 42
+ r = h.transform_keys{|v| :"x#{v}"}
+ r.keys.should == [:xfoo]
+ r.class.should == Hash
end
- describe "Hash#transform_keys!" do
- before :each do
- @hash = { a: 1, b: 2, c: 3, d: 4 }
- @initial_pairs = @hash.dup
- end
+ it "allows a hash argument" do
+ @hash.transform_keys({ a: :A, b: :B, c: :C }).should == { A: 1, B: 2, C: 3 }
+ end
- it "returns self" do
- @hash.transform_keys!(&:succ).should equal(@hash)
- end
+ it "allows a partial transformation of keys when using a hash argument" do
+ @hash.transform_keys({ a: :A, c: :C }).should == { A: 1, b: 2, C: 3 }
+ end
- it "updates self as transformed values with the given block" do
- @hash.transform_keys!(&:to_s)
- @hash.should == { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 }
- end
+ 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
- # https://bugs.ruby-lang.org/issues/14380
- ruby_version_is ""..."2.5.1" do
- it "does not prevent conflicts between new keys and old ones" do
- @hash.transform_keys!(&:succ)
- @hash.should == { e: 1 }
- end
- 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
- ruby_version_is "2.5.1" do
- it "prevents conflicts between new keys and old ones" do
- @hash.transform_keys!(&:succ)
- @hash.should == { b: 1, c: 2, d: 3, e: 4 }
- end
- 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
- ruby_version_is ""..."2.5.1" do
- it "partially modifies the contents if we broke from the block" do
- @hash.transform_keys! do |v|
- break if v == :c
- v.succ
- end
- @hash.should == { c: 1, d: 4 }
- end
- 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
- ruby_version_is "2.5.1" do
- it "returns the processed keys if we broke from the block" do
- @hash.transform_keys! do |v|
- break if v == :c
- v.succ
- end
- @hash.should == { b: 1, c: 2 }
- end
+describe "Hash#transform_keys!" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3, d: 4 }
+ @initial_pairs = @hash.dup
+ end
+
+ it "returns self" do
+ @hash.transform_keys!(&:succ).should equal(@hash)
+ end
+
+ it "updates self as transformed values with the given block" do
+ @hash.transform_keys!(&:to_s)
+ @hash.should == { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 }
+ end
+
+ it "prevents conflicts between new keys and old ones" do
+ @hash.transform_keys!(&:succ)
+ @hash.should == { b: 1, c: 2, d: 3, e: 4 }
+ end
+
+ 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
+ @hash.transform_keys! { |_| :a }.should == { a: 4 }
+ end
- it "keeps later pair if new keys conflict" do
- @hash.transform_keys! { |_| :a }.should == { a: 4 }
+ context "when no block is given" do
+ it "returns a sized Enumerator" do
+ enumerator = @hash.transform_keys!
+ enumerator.should be_an_instance_of(Enumerator)
+ enumerator.size.should == @hash.size
+ enumerator.each(&:upcase).should == { A: 1, B: 2, C: 3, D: 4 }
end
+ end
- context "when no block is given" do
- it "returns a sized Enumerator" do
- enumerator = @hash.transform_keys!
- enumerator.should be_an_instance_of(Enumerator)
- enumerator.size.should == @hash.size
- enumerator.each(&:upcase).should == { A: 1, B: 2, C: 3, D: 4 }
- end
+ it "allows a hash argument" do
+ @hash.transform_keys!({ a: :A, b: :B, c: :C, d: :D })
+ @hash.should == { A: 1, B: 2, C: 3, D: 4 }
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @hash.freeze
end
- describe "on frozen instance" do
- before :each do
- @hash.freeze
- end
+ it "raises a FrozenError on an empty hash" do
+ ->{ {}.freeze.transform_keys!(&:upcase) }.should raise_error(FrozenError)
+ end
- it "raises a #{frozen_error_class} on an empty hash" do
- ->{ {}.freeze.transform_keys!(&:upcase) }.should raise_error(frozen_error_class)
- end
+ it "keeps pairs and raises a FrozenError" do
+ ->{ @hash.transform_keys!(&:upcase) }.should raise_error(FrozenError)
+ @hash.should == @initial_pairs
+ end
- it "keeps pairs and raises a #{frozen_error_class}" do
- ->{ @hash.transform_keys!(&:upcase) }.should raise_error(frozen_error_class)
- @hash.should == @initial_pairs
- end
+ it "raises a FrozenError on hash argument" do
+ ->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should raise_error(FrozenError)
+ end
- context "when no block is given" do
- it "does not raise an exception" do
- @hash.transform_keys!.should be_an_instance_of(Enumerator)
- end
+ context "when no block is given" do
+ it "does not raise an exception" do
+ @hash.transform_keys!.should be_an_instance_of(Enumerator)
end
end
end
diff --git a/spec/ruby/core/hash/transform_values_spec.rb b/spec/ruby/core/hash/transform_values_spec.rb
index 8b53b7a522..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
@@ -79,12 +100,12 @@ describe "Hash#transform_values!" do
@hash.freeze
end
- it "raises a #{frozen_error_class} on an empty hash" do
- ->{ {}.freeze.transform_values!(&:succ) }.should raise_error(frozen_error_class)
+ it "raises a FrozenError on an empty hash" do
+ ->{ {}.freeze.transform_values!(&:succ) }.should raise_error(FrozenError)
end
- it "keeps pairs and raises a #{frozen_error_class}" do
- ->{ @hash.transform_values!(&:succ) }.should raise_error(frozen_error_class)
+ it "keeps pairs and raises a FrozenError" do
+ ->{ @hash.transform_values!(&:succ) }.should raise_error(FrozenError)
@hash.should == @initial_pairs
end
diff --git a/spec/ruby/core/hash/try_convert_spec.rb b/spec/ruby/core/hash/try_convert_spec.rb
index 50bb59816c..d359ae49d8 100644
--- a/spec/ruby/core/hash/try_convert_spec.rb
+++ b/spec/ruby/core/hash/try_convert_spec.rb
@@ -39,12 +39,12 @@ describe "Hash.try_convert" do
it "sends #to_hash to the argument and raises TypeError if it's not a kind of Hash" do
obj = mock("to_hash")
obj.should_receive(:to_hash).and_return(Object.new)
- lambda { Hash.try_convert obj }.should raise_error(TypeError)
+ -> { Hash.try_convert obj }.should raise_error(TypeError, "can't convert MockObject to Hash (MockObject#to_hash gives Object)")
end
it "does not rescue exceptions raised by #to_hash" do
obj = mock("to_hash")
obj.should_receive(:to_hash).and_raise(RuntimeError)
- lambda { Hash.try_convert obj }.should raise_error(RuntimeError)
+ -> { Hash.try_convert obj }.should raise_error(RuntimeError)
end
end