mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
70b60d24b9
opt_aref_with is an optimized instruction for accessing a Hash using a non-frozen string key (ie. from a file without frozen_string_literal). It attempts to avoid allocating the string, and instead silently using a frozen string (hash string keys are always fstrings). Because this is just an optimization, it should be invisible to the user. However, previously this optimization was could be seen via hashes with default procs. For example, previously: h = Hash.new { |h, k| k.frozen? } str = "foo" h[str] # false h["foo"] # true when optimizations enabled This commit checks that the Hash doesn't have a default proc when using opt_aref_with.
2229 lines
52 KiB
Ruby
2229 lines
52 KiB
Ruby
# -*- coding: us-ascii -*-
|
|
# frozen_string_literal: false
|
|
require 'test/unit'
|
|
EnvUtil.suppress_warning {require 'continuation'}
|
|
|
|
class TestHash < Test::Unit::TestCase
|
|
|
|
def test_hash
|
|
x = @cls[1=>2, 2=>4, 3=>6]
|
|
y = @cls[1=>2, 2=>4, 3=>6] # y = {1, 2, 2, 4, 3, 6} # 1.9 doesn't support
|
|
|
|
assert_equal(2, x[1])
|
|
|
|
assert(begin
|
|
for k,v in y
|
|
raise if k*2 != v
|
|
end
|
|
true
|
|
rescue
|
|
false
|
|
end)
|
|
|
|
assert_equal(3, x.length)
|
|
assert_send([x, :has_key?, 1])
|
|
assert_send([x, :has_value?, 4])
|
|
assert_equal([4,6], x.values_at(2,3))
|
|
assert_equal({1=>2, 2=>4, 3=>6}, x)
|
|
|
|
z = y.keys.join(":")
|
|
assert_equal("1:2:3", z)
|
|
|
|
z = y.values.join(":")
|
|
assert_equal("2:4:6", z)
|
|
assert_equal(x, y)
|
|
|
|
y.shift
|
|
assert_equal(2, y.length)
|
|
|
|
z = [1,2]
|
|
y[z] = 256
|
|
assert_equal(256, y[z])
|
|
|
|
x = Hash.new(0)
|
|
x[1] = 1
|
|
assert_equal(1, x[1])
|
|
assert_equal(0, x[2])
|
|
|
|
x = Hash.new([])
|
|
assert_equal([], x[22])
|
|
assert_same(x[22], x[22])
|
|
|
|
x = Hash.new{[]}
|
|
assert_equal([], x[22])
|
|
assert_not_same(x[22], x[22])
|
|
|
|
x = Hash.new{|h,kk| z = kk; h[kk] = kk*2}
|
|
z = 0
|
|
assert_equal(44, x[22])
|
|
assert_equal(22, z)
|
|
z = 0
|
|
assert_equal(44, x[22])
|
|
assert_equal(0, z)
|
|
x.default = 5
|
|
assert_equal(5, x[23])
|
|
|
|
x = Hash.new
|
|
def x.default(k)
|
|
$z = k
|
|
self[k] = k*2
|
|
end
|
|
$z = 0
|
|
assert_equal(44, x[22])
|
|
assert_equal(22, $z)
|
|
$z = 0
|
|
assert_equal(44, x[22])
|
|
assert_equal(0, $z)
|
|
end
|
|
|
|
# From rubicon
|
|
|
|
def setup
|
|
@cls ||= Hash
|
|
@h = @cls[
|
|
1 => 'one', 2 => 'two', 3 => 'three',
|
|
self => 'self', true => 'true', nil => 'nil',
|
|
'nil' => nil
|
|
]
|
|
@verbose = $VERBOSE
|
|
end
|
|
|
|
def teardown
|
|
$VERBOSE = @verbose
|
|
end
|
|
|
|
def test_bad_initialize_copy
|
|
h = Class.new(Hash) {
|
|
def initialize_copy(h)
|
|
super(Object.new)
|
|
end
|
|
}.new
|
|
assert_raise(TypeError) { h.dup }
|
|
end
|
|
|
|
def test_clear_initialize_copy
|
|
h = @cls[1=>2]
|
|
h.instance_eval {initialize_copy({})}
|
|
assert_empty(h)
|
|
end
|
|
|
|
def test_self_initialize_copy
|
|
h = @cls[1=>2]
|
|
h.instance_eval {initialize_copy(h)}
|
|
assert_equal(2, h[1])
|
|
end
|
|
|
|
def test_dup_will_not_rehash
|
|
assert_hash_does_not_rehash(&:dup)
|
|
end
|
|
|
|
def assert_hash_does_not_rehash
|
|
obj = Object.new
|
|
class << obj
|
|
attr_accessor :hash_calls
|
|
def hash
|
|
@hash_calls += 1
|
|
super
|
|
end
|
|
end
|
|
obj.hash_calls = 0
|
|
hash = {obj => 42}
|
|
assert_equal(1, obj.hash_calls)
|
|
yield hash
|
|
assert_equal(1, obj.hash_calls)
|
|
end
|
|
|
|
def test_select_reject_will_not_rehash
|
|
assert_hash_does_not_rehash do |hash|
|
|
hash.select { true }
|
|
end
|
|
assert_hash_does_not_rehash do |hash|
|
|
hash.reject { false }
|
|
end
|
|
end
|
|
|
|
def test_s_AREF_from_hash
|
|
h = @cls["a" => 100, "b" => 200]
|
|
assert_equal(100, h['a'])
|
|
assert_equal(200, h['b'])
|
|
assert_nil(h['c'])
|
|
|
|
h = @cls.[]("a" => 100, "b" => 200)
|
|
assert_equal(100, h['a'])
|
|
assert_equal(200, h['b'])
|
|
assert_nil(h['c'])
|
|
|
|
h = @cls[Hash.new(42)]
|
|
assert_nil(h['a'])
|
|
|
|
h = @cls[Hash.new {42}]
|
|
assert_nil(h['a'])
|
|
end
|
|
|
|
def test_s_AREF_from_list
|
|
h = @cls["a", 100, "b", 200]
|
|
assert_equal(100, h['a'])
|
|
assert_equal(200, h['b'])
|
|
assert_nil(h['c'])
|
|
end
|
|
|
|
def test_s_AREF_from_pairs
|
|
h = @cls[[["a", 100], ["b", 200]]]
|
|
assert_equal(100, h['a'])
|
|
assert_equal(200, h['b'])
|
|
assert_nil(h['c'])
|
|
|
|
h = @cls[[["a", 100], ["b"], ["c", 300]]]
|
|
assert_equal(100, h['a'])
|
|
assert_equal(nil, h['b'])
|
|
assert_equal(300, h['c'])
|
|
|
|
assert_raise(ArgumentError) do
|
|
@cls[[["a", 100], "b", ["c", 300]]]
|
|
end
|
|
end
|
|
|
|
def test_s_AREF_duplicated_key
|
|
alist = [["a", 100], ["b", 200], ["a", 300], ["a", 400]]
|
|
h = @cls[alist]
|
|
assert_equal(2, h.size)
|
|
assert_equal(400, h['a'])
|
|
assert_equal(200, h['b'])
|
|
assert_nil(h['c'])
|
|
assert_equal(nil, h.key('300'))
|
|
end
|
|
|
|
def test_s_AREF_frozen_key_id
|
|
key = "a".freeze
|
|
h = @cls[key, 100]
|
|
assert_equal(100, h['a'])
|
|
assert_same(key, *h.keys)
|
|
end
|
|
|
|
def test_s_AREF_key_tampering
|
|
key = "a".dup
|
|
h = @cls[key, 100]
|
|
key.upcase!
|
|
assert_equal(100, h['a'])
|
|
end
|
|
|
|
def test_s_new
|
|
h = @cls.new
|
|
assert_instance_of(@cls, h)
|
|
assert_nil(h.default)
|
|
assert_nil(h['spurious'])
|
|
|
|
h = @cls.new('default')
|
|
assert_instance_of(@cls, h)
|
|
assert_equal('default', h.default)
|
|
assert_equal('default', h['spurious'])
|
|
end
|
|
|
|
def test_try_convert
|
|
assert_equal({1=>2}, Hash.try_convert({1=>2}))
|
|
assert_equal(nil, Hash.try_convert("1=>2"))
|
|
o = Object.new
|
|
def o.to_hash; {3=>4} end
|
|
assert_equal({3=>4}, Hash.try_convert(o))
|
|
end
|
|
|
|
def test_AREF # '[]'
|
|
t = Time.now
|
|
h = @cls[
|
|
1 => 'one', 2 => 'two', 3 => 'three',
|
|
self => 'self', t => 'time', nil => 'nil',
|
|
'nil' => nil
|
|
]
|
|
|
|
assert_equal('one', h[1])
|
|
assert_equal('two', h[2])
|
|
assert_equal('three', h[3])
|
|
assert_equal('self', h[self])
|
|
assert_equal('time', h[t])
|
|
assert_equal('nil', h[nil])
|
|
assert_equal(nil, h['nil'])
|
|
assert_equal(nil, h['koala'])
|
|
|
|
h1 = h.dup
|
|
h1.default = :default
|
|
|
|
assert_equal('one', h1[1])
|
|
assert_equal('two', h1[2])
|
|
assert_equal('three', h1[3])
|
|
assert_equal('self', h1[self])
|
|
assert_equal('time', h1[t])
|
|
assert_equal('nil', h1[nil])
|
|
assert_equal(nil, h1['nil'])
|
|
assert_equal(:default, h1['koala'])
|
|
|
|
|
|
end
|
|
|
|
def test_ASET # '[]='
|
|
t = Time.now
|
|
h = @cls.new
|
|
h[1] = 'one'
|
|
h[2] = 'two'
|
|
h[3] = 'three'
|
|
h[self] = 'self'
|
|
h[t] = 'time'
|
|
h[nil] = 'nil'
|
|
h['nil'] = nil
|
|
assert_equal('one', h[1])
|
|
assert_equal('two', h[2])
|
|
assert_equal('three', h[3])
|
|
assert_equal('self', h[self])
|
|
assert_equal('time', h[t])
|
|
assert_equal('nil', h[nil])
|
|
assert_equal(nil, h['nil'])
|
|
assert_equal(nil, h['koala'])
|
|
|
|
h[1] = 1
|
|
h[nil] = 99
|
|
h['nil'] = nil
|
|
z = [1,2]
|
|
h[z] = 256
|
|
assert_equal(1, h[1])
|
|
assert_equal('two', h[2])
|
|
assert_equal('three', h[3])
|
|
assert_equal('self', h[self])
|
|
assert_equal('time', h[t])
|
|
assert_equal(99, h[nil])
|
|
assert_equal(nil, h['nil'])
|
|
assert_equal(nil, h['koala'])
|
|
assert_equal(256, h[z])
|
|
end
|
|
|
|
def test_AREF_fstring_key
|
|
# warmup ObjectSpace.count_objects
|
|
ObjectSpace.count_objects
|
|
|
|
h = {"abc" => 1}
|
|
before = ObjectSpace.count_objects[:T_STRING]
|
|
5.times{ h["abc"] }
|
|
assert_equal before, ObjectSpace.count_objects[:T_STRING]
|
|
end
|
|
|
|
def test_AREF_fstring_key_default_proc
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
h = Hash.new do |h, k|
|
|
k.frozen?
|
|
end
|
|
|
|
str = "foo"
|
|
refute str.frozen? # assumes this file is frozen_string_literal: false
|
|
refute h[str]
|
|
refute h["foo"]
|
|
end;
|
|
end
|
|
|
|
def test_ASET_fstring_key
|
|
a, b = {}, {}
|
|
assert_equal 1, a["abc"] = 1
|
|
assert_equal 1, b["abc"] = 1
|
|
assert_same a.keys[0], b.keys[0]
|
|
end
|
|
|
|
def test_ASET_fstring_non_literal_key
|
|
underscore = "_"
|
|
non_literal_strings = Proc.new{ ["abc#{underscore}def", "abc" * 5, "abc" + "def", "" << "ghi" << "jkl"] }
|
|
|
|
a, b = {}, {}
|
|
non_literal_strings.call.each do |string|
|
|
assert_equal 1, a[string] = 1
|
|
end
|
|
|
|
non_literal_strings.call.each do |string|
|
|
assert_equal 1, b[string] = 1
|
|
end
|
|
|
|
[a.keys, b.keys].transpose.each do |key_a, key_b|
|
|
assert_same key_a, key_b
|
|
end
|
|
end
|
|
|
|
def test_hash_aset_fstring_identity
|
|
h = {}.compare_by_identity
|
|
h['abc'] = 1
|
|
h['abc'] = 2
|
|
assert_equal 2, h.size, '[ruby-core:78783] [Bug #12855]'
|
|
end
|
|
|
|
def test_hash_aref_fstring_identity
|
|
h = {}.compare_by_identity
|
|
h['abc'] = 1
|
|
assert_nil h['abc'], '[ruby-core:78783] [Bug #12855]'
|
|
end
|
|
|
|
def test_NEWHASH_fstring_key
|
|
a = {"ABC" => :t}
|
|
b = {"ABC" => :t}
|
|
assert_same a.keys[0], b.keys[0]
|
|
assert_same "ABC".freeze, a.keys[0]
|
|
var = +'ABC'
|
|
c = { var => :t }
|
|
assert_same "ABC".freeze, c.keys[0]
|
|
end
|
|
|
|
def test_EQUAL # '=='
|
|
h1 = @cls[ "a" => 1, "c" => 2 ]
|
|
h2 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
|
|
h3 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
|
|
h4 = @cls[ ]
|
|
assert_equal(h1, h1)
|
|
assert_equal(h2, h2)
|
|
assert_equal(h3, h3)
|
|
assert_equal(h4, h4)
|
|
assert_not_equal(h1, h2)
|
|
assert_equal(h2, h3)
|
|
assert_not_equal(h3, h4)
|
|
end
|
|
|
|
def test_clear
|
|
assert_operator(@h.size, :>, 0)
|
|
@h.clear
|
|
assert_equal(0, @h.size)
|
|
assert_nil(@h[1])
|
|
end
|
|
|
|
def test_clone
|
|
for frozen in [ false, true ]
|
|
a = @h.clone
|
|
a.freeze if frozen
|
|
b = a.clone
|
|
|
|
assert_equal(a, b)
|
|
assert_not_same(a, b)
|
|
assert_equal(a.frozen?, b.frozen?)
|
|
end
|
|
end
|
|
|
|
def test_default
|
|
assert_nil(@h.default)
|
|
h = @cls.new(:xyzzy)
|
|
assert_equal(:xyzzy, h.default)
|
|
end
|
|
|
|
def test_default=
|
|
assert_nil(@h.default)
|
|
@h.default = :xyzzy
|
|
assert_equal(:xyzzy, @h.default)
|
|
end
|
|
|
|
def test_delete
|
|
h1 = @cls[ 1 => 'one', 2 => 'two', true => 'true' ]
|
|
h2 = @cls[ 1 => 'one', 2 => 'two' ]
|
|
h3 = @cls[ 2 => 'two' ]
|
|
|
|
assert_equal('true', h1.delete(true))
|
|
assert_equal(h2, h1)
|
|
|
|
assert_equal('one', h1.delete(1))
|
|
assert_equal(h3, h1)
|
|
|
|
assert_equal('two', h1.delete(2))
|
|
assert_equal(@cls[], h1)
|
|
|
|
assert_nil(h1.delete(99))
|
|
assert_equal(@cls[], h1)
|
|
|
|
assert_equal('default 99', h1.delete(99) {|i| "default #{i}" })
|
|
end
|
|
|
|
def test_delete_if
|
|
base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
|
|
h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
|
|
h2 = @cls[ 2 => false, 'cat' => 99 ]
|
|
h3 = @cls[ 2 => false ]
|
|
|
|
h = base.dup
|
|
assert_equal(h, h.delete_if { false })
|
|
assert_equal(@cls[], h.delete_if { true })
|
|
|
|
h = base.dup
|
|
assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) })
|
|
assert_equal(h1, h)
|
|
|
|
h = base.dup
|
|
assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) })
|
|
assert_equal(h2, h)
|
|
|
|
h = base.dup
|
|
assert_equal(h3, h.delete_if {|k,v| v })
|
|
assert_equal(h3, h)
|
|
|
|
h = base.dup
|
|
n = 0
|
|
h.delete_if {|*a|
|
|
n += 1
|
|
assert_equal(2, a.size)
|
|
assert_equal(base[a[0]], a[1])
|
|
h.shift
|
|
true
|
|
}
|
|
assert_equal(base.size, n)
|
|
|
|
h = base.dup
|
|
assert_raise(FrozenError) do
|
|
h.delete_if do
|
|
h.freeze
|
|
true
|
|
end
|
|
end
|
|
assert_equal(base.dup, h)
|
|
end
|
|
|
|
def test_keep_if
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal({1=>2,3=>4,5=>6}, h.keep_if{true})
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_raise(FrozenError) do
|
|
h.keep_if do
|
|
h.freeze
|
|
false
|
|
end
|
|
end
|
|
assert_equal(@cls[1=>2,3=>4,5=>6], h)
|
|
end
|
|
|
|
def test_compact
|
|
h = @cls[a: 1, b: nil, c: false, d: true, e: nil]
|
|
assert_equal({a: 1, c: false, d: true}, h.compact)
|
|
assert_equal({a: 1, b: nil, c: false, d: true, e: nil}, h)
|
|
assert_same(h, h.compact!)
|
|
assert_equal({a: 1, c: false, d: true}, h)
|
|
assert_nil(h.compact!)
|
|
end
|
|
|
|
def test_dup
|
|
for frozen in [ false, true ]
|
|
a = @h.dup
|
|
a.freeze if frozen
|
|
b = a.dup
|
|
|
|
assert_equal(a, b)
|
|
assert_not_same(a, b)
|
|
assert_equal(false, b.frozen?)
|
|
end
|
|
end
|
|
|
|
def test_dup_equality
|
|
h = @cls['k' => 'v']
|
|
assert_equal(h, h.dup)
|
|
h1 = @cls[h => 1]
|
|
assert_equal(h1, h1.dup)
|
|
h[1] = 2
|
|
h1.rehash
|
|
assert_equal(h1, h1.dup)
|
|
end
|
|
|
|
def test_each
|
|
count = 0
|
|
@cls[].each { |k, v| count + 1 }
|
|
assert_equal(0, count)
|
|
|
|
h = @h
|
|
h.each do |k, v|
|
|
assert_equal(v, h.delete(k))
|
|
end
|
|
assert_equal(@cls[], h)
|
|
|
|
h = @cls[]
|
|
h[1] = 1
|
|
h[2] = 2
|
|
assert_equal([[1,1],[2,2]], h.each.to_a)
|
|
end
|
|
|
|
def test_each_key
|
|
count = 0
|
|
@cls[].each_key { |k| count + 1 }
|
|
assert_equal(0, count)
|
|
|
|
h = @h
|
|
h.each_key do |k|
|
|
h.delete(k)
|
|
end
|
|
assert_equal(@cls[], h)
|
|
end
|
|
|
|
def test_each_pair
|
|
count = 0
|
|
@cls[].each_pair { |k, v| count + 1 }
|
|
assert_equal(0, count)
|
|
|
|
h = @h
|
|
h.each_pair do |k, v|
|
|
assert_equal(v, h.delete(k))
|
|
end
|
|
assert_equal(@cls[], h)
|
|
end
|
|
|
|
def test_each_value
|
|
res = []
|
|
@cls[].each_value { |v| res << v }
|
|
assert_equal(0, [].length)
|
|
|
|
@h.each_value { |v| res << v }
|
|
assert_equal(0, [].length)
|
|
|
|
expected = []
|
|
@h.each { |k, v| expected << v }
|
|
|
|
assert_equal([], expected - res)
|
|
assert_equal([], res - expected)
|
|
end
|
|
|
|
def test_empty?
|
|
assert_empty(@cls[])
|
|
assert_not_empty(@h)
|
|
end
|
|
|
|
def test_fetch
|
|
assert_equal('gumbygumby', @h.fetch('gumby') {|k| k * 2 })
|
|
assert_equal('pokey', @h.fetch('gumby', 'pokey'))
|
|
|
|
assert_equal('one', @h.fetch(1))
|
|
assert_equal(nil, @h.fetch('nil'))
|
|
assert_equal('nil', @h.fetch(nil))
|
|
end
|
|
|
|
def test_fetch_error
|
|
assert_raise(KeyError) { @cls[].fetch(1) }
|
|
assert_raise(KeyError) { @h.fetch('gumby') }
|
|
e = assert_raise(KeyError) { @h.fetch('gumby'*20) }
|
|
assert_match(/key not found: "gumbygumby/, e.message)
|
|
assert_match(/\.\.\.\z/, e.message)
|
|
assert_same(@h, e.receiver)
|
|
assert_equal('gumby'*20, e.key)
|
|
end
|
|
|
|
def test_key2?
|
|
assert_not_send([@cls[], :key?, 1])
|
|
assert_not_send([@cls[], :key?, nil])
|
|
assert_send([@h, :key?, nil])
|
|
assert_send([@h, :key?, 1])
|
|
assert_not_send([@h, :key?, 'gumby'])
|
|
end
|
|
|
|
def test_value?
|
|
assert_not_send([@cls[], :value?, 1])
|
|
assert_not_send([@cls[], :value?, nil])
|
|
assert_send([@h, :value?, 'one'])
|
|
assert_send([@h, :value?, nil])
|
|
assert_not_send([@h, :value?, 'gumby'])
|
|
end
|
|
|
|
def test_include?
|
|
assert_not_send([@cls[], :include?, 1])
|
|
assert_not_send([@cls[], :include?, nil])
|
|
assert_send([@h, :include?, nil])
|
|
assert_send([@h, :include?, 1])
|
|
assert_not_send([@h, :include?, 'gumby'])
|
|
end
|
|
|
|
def test_key
|
|
assert_equal(1, @h.key('one'))
|
|
assert_equal(nil, @h.key('nil'))
|
|
assert_equal('nil', @h.key(nil))
|
|
|
|
assert_equal(nil, @h.key('gumby'))
|
|
assert_equal(nil, @cls[].key('gumby'))
|
|
end
|
|
|
|
def test_values_at
|
|
res = @h.values_at('dog', 'cat', 'horse')
|
|
assert_equal(3, res.length)
|
|
assert_equal([nil, nil, nil], res)
|
|
|
|
res = @h.values_at
|
|
assert_equal(0, res.length)
|
|
|
|
res = @h.values_at(3, 2, 1, nil)
|
|
assert_equal 4, res.length
|
|
assert_equal %w( three two one nil ), res
|
|
|
|
res = @h.values_at(3, 99, 1, nil)
|
|
assert_equal 4, res.length
|
|
assert_equal ['three', nil, 'one', 'nil'], res
|
|
end
|
|
|
|
def test_fetch_values
|
|
res = @h.fetch_values
|
|
assert_equal(0, res.length)
|
|
|
|
res = @h.fetch_values(3, 2, 1, nil)
|
|
assert_equal(4, res.length)
|
|
assert_equal %w( three two one nil ), res
|
|
|
|
e = assert_raise KeyError do
|
|
@h.fetch_values(3, 'invalid')
|
|
end
|
|
assert_same(@h, e.receiver)
|
|
assert_equal('invalid', e.key)
|
|
|
|
res = @h.fetch_values(3, 'invalid') { |k| k.upcase }
|
|
assert_equal %w( three INVALID ), res
|
|
end
|
|
|
|
def test_invert
|
|
h = @h.invert
|
|
assert_equal(1, h['one'])
|
|
assert_equal(true, h['true'])
|
|
assert_equal(nil, h['nil'])
|
|
|
|
h.each do |k, v|
|
|
assert_send([@h, :key?, v]) # not true in general, but works here
|
|
end
|
|
|
|
h = @cls[ 'a' => 1, 'b' => 2, 'c' => 1].invert
|
|
assert_equal(2, h.length)
|
|
assert_include(%w[a c], h[1])
|
|
assert_equal('b', h[2])
|
|
end
|
|
|
|
def test_key?
|
|
assert_not_send([@cls[], :key?, 1])
|
|
assert_not_send([@cls[], :key?, nil])
|
|
assert_send([@h, :key?, nil])
|
|
assert_send([@h, :key?, 1])
|
|
assert_not_send([@h, :key?, 'gumby'])
|
|
end
|
|
|
|
def test_keys
|
|
assert_equal([], @cls[].keys)
|
|
|
|
keys = @h.keys
|
|
expected = []
|
|
@h.each { |k, v| expected << k }
|
|
assert_equal([], keys - expected)
|
|
assert_equal([], expected - keys)
|
|
end
|
|
|
|
def test_length
|
|
assert_equal(0, @cls[].length)
|
|
assert_equal(7, @h.length)
|
|
end
|
|
|
|
def test_member?
|
|
assert_not_send([@cls[], :member?, 1])
|
|
assert_not_send([@cls[], :member?, nil])
|
|
assert_send([@h, :member?, nil])
|
|
assert_send([@h, :member?, 1])
|
|
assert_not_send([@h, :member?, 'gumby'])
|
|
end
|
|
|
|
def hash_hint hv
|
|
hv & 0xff
|
|
end
|
|
|
|
def test_rehash
|
|
a = [ "a", "b" ]
|
|
c = [ "c", "d" ]
|
|
h = @cls[ a => 100, c => 300 ]
|
|
assert_equal(100, h[a])
|
|
|
|
hv = a.hash
|
|
begin
|
|
a[0] << "z"
|
|
end while hash_hint(a.hash) == hash_hint(hv)
|
|
|
|
assert_nil(h[a])
|
|
h.rehash
|
|
assert_equal(100, h[a])
|
|
end
|
|
|
|
def test_reject
|
|
assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].reject {|k, v| k + v < 7 })
|
|
|
|
base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
|
|
h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
|
|
h2 = @cls[ 2 => false, 'cat' => 99 ]
|
|
h3 = @cls[ 2 => false ]
|
|
|
|
h = base.dup
|
|
assert_equal(h, h.reject { false })
|
|
assert_equal(@cls[], h.reject { true })
|
|
|
|
h = base.dup
|
|
assert_equal(h1, h.reject {|k,v| k.instance_of?(String) })
|
|
|
|
assert_equal(h2, h.reject {|k,v| v.instance_of?(String) })
|
|
|
|
assert_equal(h3, h.reject {|k,v| v })
|
|
assert_equal(base, h)
|
|
|
|
h.instance_variable_set(:@foo, :foo)
|
|
h.default = 42
|
|
h = EnvUtil.suppress_warning {h.reject {false}}
|
|
assert_instance_of(Hash, h)
|
|
assert_nil(h.default)
|
|
assert_not_send([h, :instance_variable_defined?, :@foo])
|
|
end
|
|
|
|
def test_reject_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
expected = {}.compare_by_identity
|
|
expected[str1] = 1
|
|
expected[str2] = 2
|
|
h2 = h.reject{|k,| k != 'str'}
|
|
assert_equal(expected, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
h2 = h.reject{true}
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
h2 = h.reject{true}
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
h2 = h.reject{|k,| k != 'str'}
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
end
|
|
|
|
def test_reject!
|
|
base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
|
|
h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
|
|
h2 = @cls[ 2 => false, 'cat' => 99 ]
|
|
h3 = @cls[ 2 => false ]
|
|
|
|
h = base.dup
|
|
assert_equal(nil, h.reject! { false })
|
|
assert_equal(@cls[], h.reject! { true })
|
|
|
|
h = base.dup
|
|
assert_equal(h1, h.reject! {|k,v| k.instance_of?(String) })
|
|
assert_equal(h1, h)
|
|
|
|
h = base.dup
|
|
assert_equal(h2, h.reject! {|k,v| v.instance_of?(String) })
|
|
assert_equal(h2, h)
|
|
|
|
h = base.dup
|
|
assert_equal(h3, h.reject! {|k,v| v })
|
|
assert_equal(h3, h)
|
|
|
|
h = base.dup
|
|
assert_raise(FrozenError) do
|
|
h.reject! do
|
|
h.freeze
|
|
true
|
|
end
|
|
end
|
|
assert_equal(base.dup, h)
|
|
end
|
|
|
|
def test_replace
|
|
h = @cls[ 1 => 2, 3 => 4 ]
|
|
h1 = h.replace(@cls[ 9 => 8, 7 => 6 ])
|
|
assert_equal(h, h1)
|
|
assert_equal(8, h[9])
|
|
assert_equal(6, h[7])
|
|
assert_nil(h[1])
|
|
assert_nil(h[2])
|
|
end
|
|
|
|
def test_replace_bug9230
|
|
h = @cls[]
|
|
h.replace(@cls[])
|
|
assert_empty h
|
|
|
|
h = @cls[]
|
|
h.replace(@cls[].compare_by_identity)
|
|
assert_predicate(h, :compare_by_identity?)
|
|
end
|
|
|
|
def test_replace_bug15358
|
|
h1 = {}
|
|
h2 = {a:1,b:2,c:3,d:4,e:5}
|
|
h2.replace(h1)
|
|
GC.start
|
|
assert(true)
|
|
end
|
|
|
|
def test_shift
|
|
h = @h.dup
|
|
|
|
@h.length.times {
|
|
k, v = h.shift
|
|
assert_send([@h, :key?, k])
|
|
assert_equal(@h[k], v)
|
|
}
|
|
|
|
assert_equal(0, h.length)
|
|
end
|
|
|
|
def test_size
|
|
assert_equal(0, @cls[].length)
|
|
assert_equal(7, @h.length)
|
|
end
|
|
|
|
def test_sort
|
|
h = @cls[].sort
|
|
assert_equal([], h)
|
|
|
|
h = @cls[ 1 => 1, 2 => 1 ].sort
|
|
assert_equal([[1,1], [2,1]], h)
|
|
|
|
h = @cls[ 'cat' => 'feline', 'ass' => 'asinine', 'bee' => 'beeline' ]
|
|
h1 = h.sort
|
|
assert_equal([ %w(ass asinine), %w(bee beeline), %w(cat feline)], h1)
|
|
end
|
|
|
|
def test_store
|
|
t = Time.now
|
|
h = @cls.new
|
|
h.store(1, 'one')
|
|
h.store(2, 'two')
|
|
h.store(3, 'three')
|
|
h.store(self, 'self')
|
|
h.store(t, 'time')
|
|
h.store(nil, 'nil')
|
|
h.store('nil', nil)
|
|
assert_equal('one', h[1])
|
|
assert_equal('two', h[2])
|
|
assert_equal('three', h[3])
|
|
assert_equal('self', h[self])
|
|
assert_equal('time', h[t])
|
|
assert_equal('nil', h[nil])
|
|
assert_equal(nil, h['nil'])
|
|
assert_equal(nil, h['koala'])
|
|
|
|
h.store(1, 1)
|
|
h.store(nil, 99)
|
|
h.store('nil', nil)
|
|
assert_equal(1, h[1])
|
|
assert_equal('two', h[2])
|
|
assert_equal('three', h[3])
|
|
assert_equal('self', h[self])
|
|
assert_equal('time', h[t])
|
|
assert_equal(99, h[nil])
|
|
assert_equal(nil, h['nil'])
|
|
assert_equal(nil, h['koala'])
|
|
end
|
|
|
|
def test_to_a
|
|
assert_equal([], @cls[].to_a)
|
|
assert_equal([[1,2]], @cls[ 1=>2 ].to_a)
|
|
a = @cls[ 1=>2, 3=>4, 5=>6 ].to_a
|
|
assert_equal([1,2], a.delete([1,2]))
|
|
assert_equal([3,4], a.delete([3,4]))
|
|
assert_equal([5,6], a.delete([5,6]))
|
|
assert_equal(0, a.length)
|
|
end
|
|
|
|
def test_to_hash
|
|
h = @h.to_hash
|
|
assert_equal(@h, h)
|
|
assert_instance_of(@cls, h)
|
|
end
|
|
|
|
def test_to_h
|
|
h = @h.to_h
|
|
assert_equal(@h, h)
|
|
assert_instance_of(Hash, h)
|
|
end
|
|
|
|
def test_to_h_instance_variable
|
|
@h.instance_variable_set(:@x, 42)
|
|
h = @h.to_h
|
|
if @cls == Hash
|
|
assert_equal(42, h.instance_variable_get(:@x))
|
|
else
|
|
assert_not_send([h, :instance_variable_defined?, :@x])
|
|
end
|
|
end
|
|
|
|
def test_to_h_default_value
|
|
@h.default = :foo
|
|
h = @h.to_h
|
|
assert_equal(:foo, h.default)
|
|
end
|
|
|
|
def test_to_h_default_proc
|
|
@h.default_proc = ->(_,k) {"nope#{k}"}
|
|
h = @h.to_h
|
|
assert_equal("nope42", h[42])
|
|
end
|
|
|
|
def test_to_h_block
|
|
h = @h.to_h {|k, v| [k.to_s, v.to_s]}
|
|
assert_equal({
|
|
"1"=>"one", "2"=>"two", "3"=>"three", to_s=>"self",
|
|
"true"=>"true", ""=>"nil", "nil"=>""
|
|
},
|
|
h)
|
|
assert_instance_of(Hash, h)
|
|
end
|
|
|
|
def test_nil_to_h
|
|
h = nil.to_h
|
|
assert_equal({}, h)
|
|
assert_nil(h.default)
|
|
assert_nil(h.default_proc)
|
|
end
|
|
|
|
def test_to_s
|
|
h = @cls[ 1 => 2, "cat" => "dog", 1.5 => :fred ]
|
|
assert_equal(h.inspect, h.to_s)
|
|
assert_deprecated_warning { $, = ":" }
|
|
assert_equal(h.inspect, h.to_s)
|
|
h = @cls[]
|
|
assert_equal(h.inspect, h.to_s)
|
|
ensure
|
|
$, = nil
|
|
end
|
|
|
|
def test_update
|
|
h1 = @cls[ 1 => 2, 2 => 3, 3 => 4 ]
|
|
h2 = @cls[ 2 => 'two', 4 => 'four' ]
|
|
|
|
ha = @cls[ 1 => 2, 2 => 'two', 3 => 4, 4 => 'four' ]
|
|
hb = @cls[ 1 => 2, 2 => 3, 3 => 4, 4 => 'four' ]
|
|
|
|
assert_equal(ha, h1.update(h2))
|
|
assert_equal(ha, h1)
|
|
|
|
h1 = @cls[ 1 => 2, 2 => 3, 3 => 4 ]
|
|
h2 = @cls[ 2 => 'two', 4 => 'four' ]
|
|
|
|
assert_equal(hb, h2.update(h1))
|
|
assert_equal(hb, h2)
|
|
end
|
|
|
|
def test_value2?
|
|
assert_not_send([@cls[], :value?, 1])
|
|
assert_not_send([@cls[], :value?, nil])
|
|
assert_send([@h, :value?, nil])
|
|
assert_send([@h, :value?, 'one'])
|
|
assert_not_send([@h, :value?, 'gumby'])
|
|
end
|
|
|
|
def test_values
|
|
assert_equal([], @cls[].values)
|
|
|
|
vals = @h.values
|
|
expected = []
|
|
@h.each { |k, v| expected << v }
|
|
assert_equal([], vals - expected)
|
|
assert_equal([], expected - vals)
|
|
end
|
|
|
|
def test_initialize_wrong_arguments
|
|
assert_raise(ArgumentError) do
|
|
Hash.new(0) { }
|
|
end
|
|
end
|
|
|
|
def test_create
|
|
assert_equal({1=>2, 3=>4}, @cls[[[1,2],[3,4]]])
|
|
assert_raise(ArgumentError) { @cls[0, 1, 2] }
|
|
assert_raise(ArgumentError) { @cls[[[0, 1], 2]] }
|
|
bug5406 = '[ruby-core:39945]'
|
|
assert_raise(ArgumentError, bug5406) { @cls[[[1, 2], [3, 4, 5]]] }
|
|
assert_equal({1=>2, 3=>4}, @cls[1,2,3,4])
|
|
o = Object.new
|
|
def o.to_hash() {1=>2} end
|
|
assert_equal({1=>2}, @cls[o], "[ruby-dev:34555]")
|
|
end
|
|
|
|
def test_rehash2
|
|
h = @cls[1 => 2, 3 => 4]
|
|
assert_equal(h.dup, h.rehash)
|
|
assert_raise(RuntimeError) { h.each { h.rehash } }
|
|
assert_equal({}, @cls[].rehash)
|
|
end
|
|
|
|
def test_fetch2
|
|
assert_equal(:bar, assert_warning(/block supersedes default value argument/) {@h.fetch(0, :foo) { :bar }})
|
|
end
|
|
|
|
def test_default_proc
|
|
h = @cls.new {|hh, k| hh + k + "baz" }
|
|
assert_equal("foobarbaz", h.default_proc.call("foo", "bar"))
|
|
assert_nil(h.default_proc = nil)
|
|
assert_nil(h.default_proc)
|
|
h.default_proc = ->(_,_){ true }
|
|
assert_equal(true, h[:nope])
|
|
h = @cls[]
|
|
assert_nil(h.default_proc)
|
|
end
|
|
|
|
def test_shift2
|
|
h = @cls.new {|hh, k| :foo }
|
|
h[1] = 2
|
|
assert_equal([1, 2], h.shift)
|
|
assert_nil(h.shift)
|
|
assert_nil(h.shift)
|
|
|
|
h = @cls.new(:foo)
|
|
h[1] = 2
|
|
assert_equal([1, 2], h.shift)
|
|
assert_nil(h.shift)
|
|
assert_nil(h.shift)
|
|
|
|
h =@cls[1=>2]
|
|
h.each { assert_equal([1, 2], h.shift) }
|
|
end
|
|
|
|
def test_shift_none
|
|
h = @cls.new {|hh, k| "foo"}
|
|
def h.default(k = nil)
|
|
super.upcase
|
|
end
|
|
assert_nil(h.shift)
|
|
end
|
|
|
|
def test_shift_for_empty_hash
|
|
# [ruby-dev:51159]
|
|
h = @cls[]
|
|
100.times{|n|
|
|
while h.size < n
|
|
k = Random.rand 0..1<<30
|
|
h[k] = 1
|
|
end
|
|
0 while h.shift
|
|
assert_equal({}, h)
|
|
}
|
|
end
|
|
|
|
def test_reject_bang2
|
|
assert_equal({1=>2}, @cls[1=>2,3=>4].reject! {|k, v| k + v == 7 })
|
|
assert_nil(@cls[1=>2,3=>4].reject! {|k, v| k == 5 })
|
|
assert_nil(@cls[].reject! { })
|
|
end
|
|
|
|
def test_select
|
|
assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].select {|k, v| k + v >= 7 })
|
|
|
|
base = @cls[ 1 => 'one', '2' => false, true => 'true', 'cat' => 99 ]
|
|
h1 = @cls[ '2' => false, 'cat' => 99 ]
|
|
h2 = @cls[ 1 => 'one', true => 'true' ]
|
|
h3 = @cls[ 1 => 'one', true => 'true', 'cat' => 99 ]
|
|
|
|
h = base.dup
|
|
assert_equal(h, h.select { true })
|
|
assert_equal(@cls[], h.select { false })
|
|
|
|
h = base.dup
|
|
assert_equal(h1, h.select {|k,v| k.instance_of?(String) })
|
|
|
|
assert_equal(h2, h.select {|k,v| v.instance_of?(String) })
|
|
|
|
assert_equal(h3, h.select {|k,v| v })
|
|
assert_equal(base, h)
|
|
|
|
h.instance_variable_set(:@foo, :foo)
|
|
h.default = 42
|
|
h = h.select {true}
|
|
assert_instance_of(Hash, h)
|
|
assert_nil(h.default)
|
|
assert_not_send([h, :instance_variable_defined?, :@foo])
|
|
end
|
|
|
|
def test_select_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
expected = {}.compare_by_identity
|
|
expected[str1] = 1
|
|
expected[str2] = 2
|
|
h2 = h.select{|k,| k == 'str'}
|
|
assert_equal(expected, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
h2 = h.select{false}
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
h2 = h.select{false}
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
h2 = h.select{|k,| k == 'str'}
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
end
|
|
|
|
def test_select!
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal(h, h.select! {|k, v| k + v >= 7 })
|
|
assert_equal({3=>4,5=>6}, h)
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal(nil, h.select!{true})
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_raise(FrozenError) do
|
|
h.select! do
|
|
h.freeze
|
|
false
|
|
end
|
|
end
|
|
assert_equal(@cls[1=>2,3=>4,5=>6], h)
|
|
end
|
|
|
|
def test_slice
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal({1=>2, 3=>4}, h.slice(1, 3))
|
|
assert_equal({}, h.slice(7))
|
|
assert_equal({}, h.slice)
|
|
assert_equal({}, {}.slice)
|
|
end
|
|
|
|
def test_slice_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
sliced = h.slice(str1, str2)
|
|
expected = {}.compare_by_identity
|
|
expected[str1] = 1
|
|
expected[str2] = 2
|
|
assert_equal(expected, sliced)
|
|
assert_equal(true, sliced.compare_by_identity?)
|
|
sliced = h.slice
|
|
assert_equal({}.compare_by_identity, sliced)
|
|
assert_equal(true, sliced.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
sliced= h.slice
|
|
assert_equal({}.compare_by_identity, sliced)
|
|
assert_equal(true, sliced.compare_by_identity?)
|
|
sliced = h.slice(str1, str2)
|
|
assert_equal({}.compare_by_identity, sliced)
|
|
assert_equal(true, sliced.compare_by_identity?)
|
|
end
|
|
|
|
def test_except
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal({5=>6}, h.except(1, 3))
|
|
assert_equal({1=>2,3=>4,5=>6}, h.except(7))
|
|
assert_equal({1=>2,3=>4,5=>6}, h.except)
|
|
assert_equal({}, {}.except)
|
|
end
|
|
|
|
def test_except_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
excepted = h.except(str1, str2)
|
|
assert_equal({1=>2,3=>4,5=>6}.compare_by_identity, excepted)
|
|
assert_equal(true, excepted.compare_by_identity?)
|
|
excepted = h.except
|
|
assert_equal(h, excepted)
|
|
assert_equal(true, excepted.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
excepted = h.except
|
|
assert_equal({}.compare_by_identity, excepted)
|
|
assert_equal(true, excepted.compare_by_identity?)
|
|
excepted = h.except(str1, str2)
|
|
assert_equal({}.compare_by_identity, excepted)
|
|
assert_equal(true, excepted.compare_by_identity?)
|
|
end
|
|
|
|
def test_filter
|
|
assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].filter {|k, v| k + v >= 7 })
|
|
|
|
base = @cls[ 1 => 'one', '2' => false, true => 'true', 'cat' => 99 ]
|
|
h1 = @cls[ '2' => false, 'cat' => 99 ]
|
|
h2 = @cls[ 1 => 'one', true => 'true' ]
|
|
h3 = @cls[ 1 => 'one', true => 'true', 'cat' => 99 ]
|
|
|
|
h = base.dup
|
|
assert_equal(h, h.filter { true })
|
|
assert_equal(@cls[], h.filter { false })
|
|
|
|
h = base.dup
|
|
assert_equal(h1, h.filter {|k,v| k.instance_of?(String) })
|
|
|
|
assert_equal(h2, h.filter {|k,v| v.instance_of?(String) })
|
|
|
|
assert_equal(h3, h.filter {|k,v| v })
|
|
assert_equal(base, h)
|
|
|
|
h.instance_variable_set(:@foo, :foo)
|
|
h.default = 42
|
|
h = h.filter {true}
|
|
assert_instance_of(Hash, h)
|
|
assert_nil(h.default)
|
|
assert_not_send([h, :instance_variable_defined?, :@foo])
|
|
end
|
|
|
|
def test_filter!
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal(h, h.filter! {|k, v| k + v >= 7 })
|
|
assert_equal({3=>4,5=>6}, h)
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_equal(nil, h.filter!{true})
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
assert_raise(FrozenError) do
|
|
h.filter! do
|
|
h.freeze
|
|
false
|
|
end
|
|
end
|
|
assert_equal(@cls[1=>2,3=>4,5=>6], h)
|
|
end
|
|
|
|
def test_clear2
|
|
assert_equal({}, @cls[1=>2,3=>4,5=>6].clear)
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.each { h.clear }
|
|
assert_equal({}, h)
|
|
end
|
|
|
|
def test_replace2
|
|
h1 = @cls.new { :foo }
|
|
h2 = @cls.new
|
|
h2.replace h1
|
|
assert_equal(:foo, h2[0])
|
|
|
|
assert_raise(ArgumentError) { h2.replace() }
|
|
assert_raise(TypeError) { h2.replace(1) }
|
|
h2.freeze
|
|
assert_raise(ArgumentError) { h2.replace() }
|
|
assert_raise(FrozenError) { h2.replace(h1) }
|
|
assert_raise(FrozenError) { h2.replace(42) }
|
|
end
|
|
|
|
def test_replace_memory_leak
|
|
assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}")
|
|
h = ("aa".."zz").each_with_index.to_h
|
|
10_000.times {h.dup}
|
|
begin;
|
|
500_000.times {h.dup.replace(h)}
|
|
end;
|
|
end
|
|
|
|
def test_size2
|
|
assert_equal(0, @cls[].size)
|
|
end
|
|
|
|
def test_equal2
|
|
assert_not_equal(0, @cls[])
|
|
o = Object.new
|
|
o.instance_variable_set(:@cls, @cls)
|
|
def o.to_hash; @cls[]; end
|
|
def o.==(x); true; end
|
|
assert_equal({}, o)
|
|
o.singleton_class.remove_method(:==)
|
|
def o.==(x); false; end
|
|
assert_not_equal({}, o)
|
|
|
|
h1 = @cls[1=>2]; h2 = @cls[3=>4]
|
|
assert_not_equal(h1, h2)
|
|
h1 = @cls[1=>2]; h2 = @cls[1=>4]
|
|
assert_not_equal(h1, h2)
|
|
end
|
|
|
|
def test_eql
|
|
assert_not_send([@cls[], :eql?, 0])
|
|
o = Object.new
|
|
o.instance_variable_set(:@cls, @cls)
|
|
def o.to_hash; @cls[]; end
|
|
def o.eql?(x); true; end
|
|
assert_send([@cls[], :eql?, o])
|
|
o.singleton_class.remove_method(:eql?)
|
|
def o.eql?(x); false; end
|
|
assert_not_send([@cls[], :eql?, o])
|
|
end
|
|
|
|
def test_hash2
|
|
assert_kind_of(Integer, @cls[].hash)
|
|
h = @cls[1=>2]
|
|
h.shift
|
|
assert_equal({}.hash, h.hash, '[ruby-core:38650]')
|
|
bug9231 = '[ruby-core:58993] [Bug #9231]'
|
|
assert_not_equal(0, @cls[].hash, bug9231)
|
|
end
|
|
|
|
def test_update2
|
|
h1 = @cls[1=>2, 3=>4]
|
|
h2 = {1=>3, 5=>7}
|
|
h1.update(h2) {|k, v1, v2| k + v1 + v2 }
|
|
assert_equal({1=>6, 3=>4, 5=>7}, h1)
|
|
end
|
|
|
|
def test_update3
|
|
h1 = @cls[1=>2, 3=>4]
|
|
h1.update()
|
|
assert_equal({1=>2, 3=>4}, h1)
|
|
h2 = {1=>3, 5=>7}
|
|
h3 = {1=>1, 2=>4}
|
|
h1.update(h2, h3)
|
|
assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1)
|
|
end
|
|
|
|
def test_update4
|
|
h1 = @cls[1=>2, 3=>4]
|
|
h1.update(){|k, v1, v2| k + v1 + v2 }
|
|
assert_equal({1=>2, 3=>4}, h1)
|
|
h2 = {1=>3, 5=>7}
|
|
h3 = {1=>1, 2=>4}
|
|
h1.update(h2, h3){|k, v1, v2| k + v1 + v2 }
|
|
assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1)
|
|
end
|
|
|
|
def test_update5
|
|
h = @cls[a: 1, b: 2, c: 3]
|
|
assert_raise(FrozenError) do
|
|
h.update({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
|
|
end
|
|
assert_equal(@cls[a: 10, b: 2, c: 3], h)
|
|
|
|
h = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
|
|
assert_raise(FrozenError) do
|
|
h.update({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
|
|
end
|
|
assert_equal(@cls[a: 10, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], h)
|
|
end
|
|
|
|
def test_merge
|
|
h1 = @cls[1=>2, 3=>4]
|
|
h2 = {1=>3, 5=>7}
|
|
h3 = {1=>1, 2=>4}
|
|
assert_equal({1=>2, 3=>4}, h1.merge())
|
|
assert_equal({1=>3, 3=>4, 5=>7}, h1.merge(h2))
|
|
assert_equal({1=>6, 3=>4, 5=>7}, h1.merge(h2) {|k, v1, v2| k + v1 + v2 })
|
|
assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3))
|
|
assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3) {|k, v1, v2| k + v1 + v2 })
|
|
end
|
|
|
|
def test_merge_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
expected = h.dup
|
|
expected[7] = 8
|
|
h2 = h.merge(7=>8)
|
|
assert_equal(expected, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
h2 = h.merge({})
|
|
assert_equal(h, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
h1 = @cls[7=>8]
|
|
h1.compare_by_identity
|
|
h2 = h.merge(7=>8)
|
|
assert_equal(h1, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
h2 = h.merge({})
|
|
assert_equal(h, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
end
|
|
|
|
def test_merge!
|
|
h = @cls[a: 1, b: 2, c: 3]
|
|
assert_raise(FrozenError) do
|
|
h.merge!({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
|
|
end
|
|
assert_equal(@cls[a: 10, b: 2, c: 3], h)
|
|
|
|
h = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
|
|
assert_raise(FrozenError) do
|
|
h.merge!({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
|
|
end
|
|
assert_equal(@cls[a: 10, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], h)
|
|
end
|
|
|
|
def test_assoc
|
|
assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].assoc(3))
|
|
assert_nil(@cls[1=>2, 3=>4, 5=>6].assoc(4))
|
|
assert_equal([1.0,1], @cls[1.0=>1].assoc(1))
|
|
end
|
|
|
|
def test_assoc_compare_by_identity
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
h["a"] = 1
|
|
h["a".dup] = 2
|
|
assert_equal(["a",1], h.assoc("a"))
|
|
end
|
|
|
|
def test_rassoc
|
|
assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].rassoc(4))
|
|
assert_nil({1=>2, 3=>4, 5=>6}.rassoc(3))
|
|
end
|
|
|
|
def test_flatten
|
|
assert_equal([[1], [2]], @cls[[1] => [2]].flatten)
|
|
|
|
a = @cls[1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]]
|
|
assert_equal([1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten)
|
|
assert_equal([[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0))
|
|
assert_equal([1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1))
|
|
assert_equal([1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2))
|
|
assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3))
|
|
assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(-1))
|
|
assert_raise(TypeError){ a.flatten(nil) }
|
|
end
|
|
|
|
def test_flatten_arity
|
|
a = @cls[1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]]
|
|
assert_raise(ArgumentError){ a.flatten(1, 2) }
|
|
end
|
|
|
|
def test_callcc
|
|
h = @cls[1=>2]
|
|
c = nil
|
|
f = false
|
|
h.each { callcc {|c2| c = c2 } }
|
|
unless f
|
|
f = true
|
|
c.call
|
|
end
|
|
assert_raise(RuntimeError) { h.each { h.rehash } }
|
|
|
|
h = @cls[1=>2]
|
|
c = nil
|
|
assert_raise(RuntimeError) do
|
|
h.each { callcc {|c2| c = c2 } }
|
|
h.clear
|
|
c.call
|
|
end
|
|
end
|
|
|
|
def test_callcc_iter_level
|
|
bug9105 = '[ruby-dev:47803] [Bug #9105]'
|
|
h = @cls[1=>2, 3=>4]
|
|
c = nil
|
|
f = false
|
|
h.each {callcc {|c2| c = c2}}
|
|
unless f
|
|
f = true
|
|
c.call
|
|
end
|
|
assert_nothing_raised(RuntimeError, bug9105) do
|
|
h.each {|i, j|
|
|
h.delete(i);
|
|
assert_not_equal(false, i, bug9105)
|
|
}
|
|
end
|
|
end
|
|
|
|
def test_callcc_escape
|
|
bug9105 = '[ruby-dev:47803] [Bug #9105]'
|
|
assert_nothing_raised(RuntimeError, bug9105) do
|
|
h=@cls[]
|
|
cnt=0
|
|
c = callcc {|cc|cc}
|
|
h[cnt] = true
|
|
h.each{|i|
|
|
cnt+=1
|
|
c.call if cnt == 1
|
|
}
|
|
end
|
|
end
|
|
|
|
def test_callcc_reenter
|
|
bug9105 = '[ruby-dev:47803] [Bug #9105]'
|
|
assert_nothing_raised(RuntimeError, bug9105) do
|
|
h = @cls[1=>2,3=>4]
|
|
c = nil
|
|
f = false
|
|
h.each { |i|
|
|
callcc {|c2| c = c2 } unless c
|
|
h.delete(1) if f
|
|
}
|
|
unless f
|
|
f = true
|
|
c.call
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_threaded_iter_level
|
|
bug9105 = '[ruby-dev:47807] [Bug #9105]'
|
|
h = @cls[1=>2]
|
|
2.times.map {
|
|
f = false
|
|
th = Thread.start {h.each {f = true; sleep}}
|
|
Thread.pass until f
|
|
Thread.pass until th.stop?
|
|
th
|
|
}.each {|th| th.run; th.join}
|
|
assert_nothing_raised(RuntimeError, bug9105) do
|
|
h[5] = 6
|
|
end
|
|
assert_equal(6, h[5], bug9105)
|
|
end
|
|
|
|
def test_compare_by_identity
|
|
a = "foo"
|
|
assert_not_predicate(@cls[], :compare_by_identity?)
|
|
h = @cls[a => "bar"]
|
|
assert_not_predicate(h, :compare_by_identity?)
|
|
h.compare_by_identity
|
|
assert_predicate(h, :compare_by_identity?)
|
|
#assert_equal("bar", h[a])
|
|
assert_nil(h["foo"])
|
|
|
|
bug8703 = '[ruby-core:56256] [Bug #8703] copied identhash'
|
|
h.clear
|
|
assert_predicate(h.dup, :compare_by_identity?, bug8703)
|
|
end
|
|
|
|
def test_same_key
|
|
bug9646 = '[ruby-dev:48047] [Bug #9646] Infinite loop at Hash#each'
|
|
h = @cls[a=[], 1]
|
|
a << 1
|
|
h[[]] = 2
|
|
a.clear
|
|
cnt = 0
|
|
r = h.each{ break nil if (cnt+=1) > 100 }
|
|
assert_not_nil(r,bug9646)
|
|
end
|
|
|
|
class ObjWithHash
|
|
def initialize(value, hash)
|
|
@value = value
|
|
@hash = hash
|
|
end
|
|
attr_reader :value, :hash
|
|
|
|
def eql?(other)
|
|
@value == other.value
|
|
end
|
|
end
|
|
|
|
def test_hash_hash
|
|
assert_equal({0=>2,11=>1}.hash, @cls[11=>1,0=>2].hash)
|
|
o1 = ObjWithHash.new(0,1)
|
|
o2 = ObjWithHash.new(11,1)
|
|
assert_equal({o1=>1,o2=>2}.hash, @cls[o2=>2,o1=>1].hash)
|
|
end
|
|
|
|
def test_hash_bignum_hash
|
|
x = 2<<(32-3)-1
|
|
assert_equal({x=>1}.hash, @cls[x=>1].hash)
|
|
x = 2<<(64-3)-1
|
|
assert_equal({x=>1}.hash, @cls[x=>1].hash)
|
|
|
|
o = Object.new
|
|
def o.hash; 2 << 100; end
|
|
assert_equal({o=>1}.hash, @cls[o=>1].hash)
|
|
end
|
|
|
|
def test_hash_popped
|
|
assert_nothing_raised { eval("a = 1; @cls[a => a]; a") }
|
|
end
|
|
|
|
def test_recursive_key
|
|
h = @cls[]
|
|
assert_nothing_raised { h[h] = :foo }
|
|
h.rehash
|
|
assert_equal(:foo, h[h])
|
|
end
|
|
|
|
def test_inverse_hash
|
|
feature4262 = '[ruby-core:34334]'
|
|
[@cls[1=>2], @cls[123=>"abc"]].each do |h|
|
|
assert_not_equal(h.hash, h.invert.hash, feature4262)
|
|
end
|
|
end
|
|
|
|
def test_recursive_hash_value_struct
|
|
bug9151 = '[ruby-core:58567] [Bug #9151]'
|
|
|
|
s = Struct.new(:x) {def hash; [x,""].hash; end}
|
|
a = s.new
|
|
b = s.new
|
|
a.x = b
|
|
b.x = a
|
|
assert_nothing_raised(SystemStackError, bug9151) {a.hash}
|
|
assert_nothing_raised(SystemStackError, bug9151) {b.hash}
|
|
|
|
h = @cls[]
|
|
h[[a,"hello"]] = 1
|
|
assert_equal(1, h.size)
|
|
h[[b,"world"]] = 2
|
|
assert_equal(2, h.size)
|
|
|
|
obj = Object.new
|
|
h = @cls[a => obj]
|
|
assert_same(obj, h[b])
|
|
end
|
|
|
|
def test_recursive_hash_value_array
|
|
h = @cls[]
|
|
h[[[1]]] = 1
|
|
assert_equal(1, h.size)
|
|
h[[[2]]] = 1
|
|
assert_equal(2, h.size)
|
|
|
|
a = []
|
|
a << a
|
|
|
|
h = @cls[]
|
|
h[[a, 1]] = 1
|
|
assert_equal(1, h.size)
|
|
h[[a, 2]] = 2
|
|
assert_equal(2, h.size)
|
|
h[[a, a]] = 3
|
|
assert_equal(3, h.size)
|
|
|
|
obj = Object.new
|
|
h = @cls[a => obj]
|
|
assert_same(obj, h[[[a]]])
|
|
end
|
|
|
|
def test_recursive_hash_value_array_hash
|
|
h = @cls[]
|
|
rec = [h]
|
|
h[:x] = rec
|
|
|
|
obj = Object.new
|
|
h2 = {rec => obj}
|
|
[h, {x: rec}].each do |k|
|
|
k = [k]
|
|
assert_same(obj, h2[k], ->{k.inspect})
|
|
end
|
|
end
|
|
|
|
def test_recursive_hash_value_hash_array
|
|
h = @cls[]
|
|
rec = [h]
|
|
h[:x] = rec
|
|
|
|
obj = Object.new
|
|
h2 = {h => obj}
|
|
[rec, [h]].each do |k|
|
|
k = {x: k}
|
|
assert_same(obj, h2[k], ->{k.inspect})
|
|
end
|
|
end
|
|
|
|
def test_exception_in_rehash_memory_leak
|
|
return unless @cls == Hash
|
|
|
|
bug9187 = '[ruby-core:58728] [Bug #9187]'
|
|
|
|
prepare = <<-EOS
|
|
class Foo
|
|
def initialize
|
|
@raise = false
|
|
end
|
|
|
|
def hash
|
|
raise if @raise
|
|
@raise = true
|
|
return 0
|
|
end
|
|
end
|
|
h = {Foo.new => true}
|
|
EOS
|
|
|
|
code = <<-EOS
|
|
10_0000.times do
|
|
h.rehash rescue nil
|
|
end
|
|
GC.start
|
|
EOS
|
|
|
|
assert_no_memory_leak([], prepare, code, bug9187)
|
|
end
|
|
|
|
def test_wrapper
|
|
bug9381 = '[ruby-core:59638] [Bug #9381]'
|
|
|
|
wrapper = Class.new do
|
|
def initialize(obj)
|
|
@obj = obj
|
|
end
|
|
|
|
def hash
|
|
@obj.hash
|
|
end
|
|
|
|
def eql?(other)
|
|
@obj.eql?(other)
|
|
end
|
|
end
|
|
|
|
bad = [
|
|
5, true, false, nil,
|
|
0.0, 1.72723e-77,
|
|
:foo, "dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym,
|
|
"str",
|
|
].select do |x|
|
|
hash = {x => bug9381}
|
|
hash[wrapper.new(x)] != bug9381
|
|
end
|
|
assert_empty(bad, bug9381)
|
|
end
|
|
|
|
def assert_hash_random(obj, dump = obj.inspect)
|
|
a = [obj.hash.to_s]
|
|
3.times {
|
|
assert_in_out_err(["-e", "print (#{dump}).hash"], "") do |r, e|
|
|
a += r
|
|
assert_equal([], e)
|
|
end
|
|
}
|
|
assert_not_equal([obj.hash.to_s], a.uniq)
|
|
assert_operator(a.uniq.size, :>, 2, proc {a.inspect})
|
|
end
|
|
|
|
def test_string_hash_random
|
|
assert_hash_random('abc')
|
|
end
|
|
|
|
def test_symbol_hash_random
|
|
assert_hash_random(:-)
|
|
assert_hash_random(:foo)
|
|
assert_hash_random("dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym)
|
|
end
|
|
|
|
def test_integer_hash_random
|
|
assert_hash_random(0)
|
|
assert_hash_random(+1)
|
|
assert_hash_random(-1)
|
|
assert_hash_random(+(1<<100))
|
|
assert_hash_random(-(1<<100))
|
|
end
|
|
|
|
def test_float_hash_random
|
|
assert_hash_random(0.0)
|
|
assert_hash_random(+1.0)
|
|
assert_hash_random(-1.0)
|
|
assert_hash_random(1.72723e-77)
|
|
assert_hash_random(Float::INFINITY, "Float::INFINITY")
|
|
end
|
|
|
|
def test_label_syntax
|
|
return unless @cls == Hash
|
|
|
|
feature4935 = '[ruby-core:37553] [Feature #4935]'
|
|
x = 'world'
|
|
hash = assert_nothing_raised(SyntaxError, feature4935) do
|
|
break eval(%q({foo: 1, "foo-bar": 2, "hello-#{x}": 3, 'hello-#{x}': 4, 'bar': {}}))
|
|
end
|
|
assert_equal({:foo => 1, :'foo-bar' => 2, :'hello-world' => 3, :'hello-#{x}' => 4, :bar => {}}, hash, feature4935)
|
|
x = x
|
|
end
|
|
|
|
def test_dig
|
|
h = @cls[a: @cls[b: [1, 2, 3]], c: 4]
|
|
assert_equal(1, h.dig(:a, :b, 0))
|
|
assert_nil(h.dig(:b, 1))
|
|
assert_raise(TypeError) {h.dig(:c, 1)}
|
|
o = Object.new
|
|
def o.dig(*args)
|
|
{dug: args}
|
|
end
|
|
h[:d] = o
|
|
assert_equal({dug: [:foo, :bar]}, h.dig(:d, :foo, :bar))
|
|
end
|
|
|
|
def test_dig_with_respond_to
|
|
bug12030 = '[ruby-core:73556] [Bug #12030]'
|
|
o = Object.new
|
|
def o.respond_to?(*args)
|
|
super
|
|
end
|
|
assert_raise(TypeError, bug12030) {{foo: o}.dig(:foo, :foo)}
|
|
end
|
|
|
|
def test_cmp
|
|
h1 = {a:1, b:2}
|
|
h2 = {a:1, b:2, c:3}
|
|
|
|
assert_operator(h1, :<=, h1)
|
|
assert_operator(h1, :<=, h2)
|
|
assert_not_operator(h2, :<=, h1)
|
|
assert_operator(h2, :<=, h2)
|
|
|
|
assert_operator(h1, :>=, h1)
|
|
assert_not_operator(h1, :>=, h2)
|
|
assert_operator(h2, :>=, h1)
|
|
assert_operator(h2, :>=, h2)
|
|
|
|
assert_not_operator(h1, :<, h1)
|
|
assert_operator(h1, :<, h2)
|
|
assert_not_operator(h2, :<, h1)
|
|
assert_not_operator(h2, :<, h2)
|
|
|
|
assert_not_operator(h1, :>, h1)
|
|
assert_not_operator(h1, :>, h2)
|
|
assert_operator(h2, :>, h1)
|
|
assert_not_operator(h2, :>, h2)
|
|
end
|
|
|
|
def test_cmp_samekeys
|
|
h1 = {a:1}
|
|
h2 = {a:2}
|
|
|
|
assert_operator(h1, :<=, h1)
|
|
assert_not_operator(h1, :<=, h2)
|
|
assert_not_operator(h2, :<=, h1)
|
|
assert_operator(h2, :<=, h2)
|
|
|
|
assert_operator(h1, :>=, h1)
|
|
assert_not_operator(h1, :>=, h2)
|
|
assert_not_operator(h2, :>=, h1)
|
|
assert_operator(h2, :>=, h2)
|
|
|
|
assert_not_operator(h1, :<, h1)
|
|
assert_not_operator(h1, :<, h2)
|
|
assert_not_operator(h2, :<, h1)
|
|
assert_not_operator(h2, :<, h2)
|
|
|
|
assert_not_operator(h1, :>, h1)
|
|
assert_not_operator(h1, :>, h2)
|
|
assert_not_operator(h2, :>, h1)
|
|
assert_not_operator(h2, :>, h2)
|
|
end
|
|
|
|
def test_to_proc
|
|
h = {
|
|
1 => 10,
|
|
2 => 20,
|
|
3 => 30,
|
|
}
|
|
|
|
assert_equal([10, 20, 30], [1, 2, 3].map(&h))
|
|
|
|
assert_equal(true, h.to_proc.lambda?)
|
|
end
|
|
|
|
def test_transform_keys
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
y = x.transform_keys {|k| :"#{k}!" }
|
|
assert_equal({a: 1, b: 2, c: 3}, x)
|
|
assert_equal({a!: 1, b!: 2, c!: 3}, y)
|
|
|
|
enum = x.transform_keys
|
|
assert_equal(x.size, enum.size)
|
|
assert_instance_of(Enumerator, enum)
|
|
|
|
y = x.transform_keys.with_index {|k, i| "#{k}.#{i}" }
|
|
assert_equal(%w(a.0 b.1 c.2), y.keys)
|
|
|
|
assert_equal({A: 1, B: 2, c: 3}, x.transform_keys({a: :A, b: :B, d: :D}))
|
|
assert_equal({A: 1, B: 2, "c" => 3}, x.transform_keys({a: :A, b: :B, d: :D}, &:to_s))
|
|
end
|
|
|
|
def test_transform_keys_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
h2 = h.transform_keys(&:itself)
|
|
assert_equal(Hash[h.to_a], h2)
|
|
assert_equal(false, h2.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
h2 = h.transform_keys(&:itself)
|
|
assert_equal({}, h2)
|
|
assert_equal(false, h2.compare_by_identity?)
|
|
end
|
|
|
|
def test_transform_keys_bang
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
y = x.transform_keys! {|k| :"#{k}!" }
|
|
assert_equal({a!: 1, b!: 2, c!: 3}, x)
|
|
assert_same(x, y)
|
|
|
|
enum = x.transform_keys!
|
|
assert_equal(x.size, enum.size)
|
|
assert_instance_of(Enumerator, enum)
|
|
|
|
x.transform_keys!.with_index {|k, i| "#{k}.#{i}" }
|
|
assert_equal(%w(a!.0 b!.1 c!.2), x.keys)
|
|
|
|
x = @cls[1 => :a, -1 => :b]
|
|
x.transform_keys! {|k| -k }
|
|
assert_equal([-1, :a, 1, :b], x.flatten)
|
|
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
x.transform_keys! { |k| k == :b && break }
|
|
assert_equal({false => 1, b: 2, c: 3}, x)
|
|
|
|
x = @cls[true => :a, false => :b]
|
|
x.transform_keys! {|k| !k }
|
|
assert_equal([false, :a, true, :b], x.flatten)
|
|
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
x.transform_keys!({a: :A, b: :B, d: :D})
|
|
assert_equal({A: 1, B: 2, c: 3}, x)
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
x.transform_keys!({a: :A, b: :B, d: :D}, &:to_s)
|
|
assert_equal({A: 1, B: 2, "c" => 3}, x)
|
|
end
|
|
|
|
def test_transform_values
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
x.default = 42
|
|
y = x.transform_values {|v| v ** 2 }
|
|
assert_equal([1, 4, 9], y.values_at(:a, :b, :c))
|
|
assert_not_same(x, y)
|
|
assert_nil(y.default)
|
|
|
|
x.default_proc = proc {|h, k| k}
|
|
y = x.transform_values {|v| v ** 2 }
|
|
assert_nil(y.default_proc)
|
|
assert_nil(y.default)
|
|
|
|
y = x.transform_values.with_index {|v, i| "#{v}.#{i}" }
|
|
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
|
|
end
|
|
|
|
def test_transform_values_on_identhash
|
|
h = @cls[1=>2,3=>4,5=>6]
|
|
h.compare_by_identity
|
|
str1 = +'str'
|
|
str2 = +'str'
|
|
h[str1] = 1
|
|
h[str2] = 2
|
|
h2 = h.transform_values(&:itself)
|
|
assert_equal(h, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
|
|
h = @cls[]
|
|
h.compare_by_identity
|
|
h2 = h.transform_values(&:itself)
|
|
assert_equal({}.compare_by_identity, h2)
|
|
assert_equal(true, h2.compare_by_identity?)
|
|
end
|
|
|
|
def test_transform_values_bang
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
y = x.transform_values! {|v| v ** 2 }
|
|
assert_equal([1, 4, 9], y.values_at(:a, :b, :c))
|
|
assert_same(x, y)
|
|
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
x.transform_values! { |v| v == 2 && break }
|
|
assert_equal({a: false, b: 2, c: 3}, x)
|
|
|
|
x = @cls[a: 1, b: 2, c: 3]
|
|
y = x.transform_values!.with_index {|v, i| "#{v}.#{i}" }
|
|
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
|
|
|
|
x = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
|
|
assert_raise(FrozenError) do
|
|
x.transform_values!() do |v|
|
|
x.freeze if v == 2
|
|
v.succ
|
|
end
|
|
end
|
|
assert_equal(@cls[a: 2, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], x)
|
|
end
|
|
|
|
def test_broken_hash_value
|
|
bug14218 = '[ruby-core:84395] [Bug #14218]'
|
|
|
|
assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; a < 0 && b < 0 && a + b > 0}, bug14218)
|
|
assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; 0 + a + b != 0 + b + a}, bug14218)
|
|
end
|
|
|
|
def test_reserved_hash_val
|
|
s = Struct.new(:hash)
|
|
h = {}
|
|
keys = [*0..8]
|
|
keys.each {|i| h[s.new(i)]=true}
|
|
msg = proc {h.inspect}
|
|
assert_equal(keys, h.keys.map(&:hash), msg)
|
|
end
|
|
|
|
def hrec h, n, &b
|
|
if n > 0
|
|
h.each{hrec(h, n-1, &b)}
|
|
else
|
|
yield
|
|
end
|
|
end
|
|
|
|
def test_huge_iter_level
|
|
nrec = 200
|
|
|
|
h = @cls[a: 1]
|
|
hrec(h, nrec){}
|
|
h[:c] = 3
|
|
assert_equal(3, h[:c])
|
|
|
|
h = @cls[a: 1]
|
|
h.freeze # set hidden attribute for a frozen object
|
|
hrec(h, nrec){}
|
|
assert_equal(1, h.size)
|
|
|
|
h = @cls[a: 1]
|
|
assert_raise(RuntimeError){
|
|
hrec(h, nrec){ h[:c] = 3 }
|
|
}
|
|
rescue SystemStackError
|
|
# ignore
|
|
end
|
|
|
|
class TestSubHash < TestHash
|
|
class SubHash < Hash
|
|
end
|
|
|
|
def setup
|
|
@cls = SubHash
|
|
super
|
|
end
|
|
end
|
|
|
|
ruby2_keywords def get_flagged_hash(*args)
|
|
args.last
|
|
end
|
|
|
|
def check_flagged_hash(k: :NG)
|
|
k
|
|
end
|
|
|
|
def test_ruby2_keywords_hash?
|
|
flagged_hash = get_flagged_hash(k: 1)
|
|
assert_equal(true, Hash.ruby2_keywords_hash?(flagged_hash))
|
|
assert_equal(false, Hash.ruby2_keywords_hash?({}))
|
|
assert_raise(TypeError) { Hash.ruby2_keywords_hash?(1) }
|
|
end
|
|
|
|
def test_ruby2_keywords_hash
|
|
hash = {k: 1}
|
|
assert_equal(false, Hash.ruby2_keywords_hash?(hash))
|
|
hash = Hash.ruby2_keywords_hash(hash)
|
|
assert_equal(true, Hash.ruby2_keywords_hash?(hash))
|
|
assert_equal(1, check_flagged_hash(*[hash]))
|
|
assert_raise(TypeError) { Hash.ruby2_keywords_hash(1) }
|
|
end
|
|
|
|
def test_ar2st
|
|
# insert
|
|
obj = Object.new
|
|
obj.instance_variable_set(:@h, h = {})
|
|
def obj.hash
|
|
10.times{|i| @h[i] = i}
|
|
0
|
|
end
|
|
def obj.inspect
|
|
'test'
|
|
end
|
|
h[obj] = true
|
|
assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect
|
|
|
|
# delete
|
|
obj = Object.new
|
|
obj.instance_variable_set(:@h, h = {})
|
|
def obj.hash
|
|
10.times{|i| @h[i] = i}
|
|
0
|
|
end
|
|
def obj.inspect
|
|
'test'
|
|
end
|
|
def obj.eql? other
|
|
other.class == Object
|
|
end
|
|
obj2 = Object.new
|
|
def obj2.hash
|
|
0
|
|
end
|
|
|
|
h[obj2] = true
|
|
h.delete obj
|
|
assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9}', h.inspect
|
|
|
|
# lookup
|
|
obj = Object.new
|
|
obj.instance_variable_set(:@h, h = {})
|
|
def obj.hash
|
|
10.times{|i| @h[i] = i}
|
|
0
|
|
end
|
|
def obj.inspect
|
|
'test'
|
|
end
|
|
def obj.eql? other
|
|
other.class == Object
|
|
end
|
|
obj2 = Object.new
|
|
def obj2.hash
|
|
0
|
|
end
|
|
|
|
h[obj2] = true
|
|
assert_equal true, h[obj]
|
|
end
|
|
|
|
def test_bug_12706
|
|
assert_raise(ArgumentError) do
|
|
{a: 1}.each(&->(k, v) {})
|
|
end
|
|
end
|
|
|
|
# Previously this test would fail because rb_hash inside opt_aref would look
|
|
# at the current method name
|
|
def test_hash_recursion_independent_of_mid
|
|
o = Class.new do
|
|
def hash(h, k)
|
|
h[k]
|
|
end
|
|
|
|
def any_other_name(h, k)
|
|
h[k]
|
|
end
|
|
end.new
|
|
|
|
rec = []; rec << rec
|
|
|
|
h = @cls[]
|
|
h[rec] = 1
|
|
assert o.hash(h, rec)
|
|
assert o.any_other_name(h, rec)
|
|
end
|
|
|
|
def test_any_hash_fixable
|
|
20.times do
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
require "delegate"
|
|
typename = DelegateClass(String)
|
|
|
|
hash = {
|
|
"Int" => true,
|
|
"Float" => true,
|
|
"String" => true,
|
|
"Boolean" => true,
|
|
"WidgetFilter" => true,
|
|
"WidgetAggregation" => true,
|
|
"WidgetEdge" => true,
|
|
"WidgetSortOrder" => true,
|
|
"WidgetGrouping" => true,
|
|
}
|
|
|
|
hash.each_key do |key|
|
|
assert_send([hash, :key?, typename.new(key)])
|
|
end
|
|
end;
|
|
end
|
|
end
|
|
end
|