1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
|
# frozen_string_literal: false
require 'test/unit'
class TestWeakMap < Test::Unit::TestCase
def setup
@wm = ObjectSpace::WeakMap.new
end
def test_map
x = Object.new
k = "foo"
@wm[k] = x
assert_same(x, @wm[k])
assert_not_same(x, @wm["FOO".downcase])
end
def test_aset_const
x = Object.new
@wm[true] = x
assert_same(x, @wm[true])
@wm[false] = x
assert_same(x, @wm[false])
@wm[nil] = x
assert_same(x, @wm[nil])
@wm[42] = x
assert_same(x, @wm[42])
@wm[:foo] = x
assert_same(x, @wm[:foo])
@wm[x] = true
assert_same(true, @wm[x])
@wm[x] = false
assert_same(false, @wm[x])
@wm[x] = nil
assert_same(nil, @wm[x])
@wm[x] = 42
assert_same(42, @wm[x])
@wm[x] = :foo
assert_same(:foo, @wm[x])
end
def assert_weak_include(m, k, n = 100)
if n > 0
return assert_weak_include(m, k, n-1)
end
1.times do
x = Object.new
@wm[k] = x
assert_send([@wm, m, k])
assert_not_send([@wm, m, "FOO".downcase])
x = Object.new
end
end
def test_include?
m = __callee__[/test_(.*)/, 1]
k = "foo"
1.times do
assert_weak_include(m, k)
end
GC.start
pend('TODO: failure introduced from 837fd5e494731d7d44786f29e7d6e8c27029806f')
assert_not_send([@wm, m, k])
end
alias test_member? test_include?
alias test_key? test_include?
def test_inspect
x = Object.new
k = BasicObject.new
@wm[k] = x
assert_match(/\A\#<#{@wm.class.name}:[^:]+:\s\#<BasicObject:[^:]*>\s=>\s\#<Object:[^:]*>>\z/,
@wm.inspect)
end
def test_inspect_garbage
1000.times do |i|
@wm[i] = Object.new
@wm.inspect
end
assert_match(/\A\#<#{@wm.class.name}:0x[\da-f]+(?::(?: \d+ => \#<(?:Object|collected):0x[\da-f]+>,?)+)?>\z/,
@wm.inspect)
end
def test_delete
k1 = "foo"
x1 = Object.new
@wm[k1] = x1
assert_equal x1, @wm[k1]
assert_equal x1, @wm.delete(k1)
assert_nil @wm[k1]
assert_nil @wm.delete(k1)
fallback = @wm.delete(k1) do |key|
assert_equal k1, key
42
end
assert_equal 42, fallback
end
def test_each
m = __callee__[/test_(.*)/, 1]
x1 = Object.new
k1 = "foo"
@wm[k1] = x1
x2 = Object.new
k2 = "bar"
@wm[k2] = x2
n = 0
@wm.__send__(m) do |k, v|
assert_match(/\A(?:foo|bar)\z/, k)
case k
when /foo/
assert_same(k1, k)
assert_same(x1, v)
when /bar/
assert_same(k2, k)
assert_same(x2, v)
end
n += 1
end
assert_equal(2, n)
end
def test_each_key
x1 = Object.new
k1 = "foo"
@wm[k1] = x1
x2 = Object.new
k2 = "bar"
@wm[k2] = x2
n = 0
@wm.each_key do |k|
assert_match(/\A(?:foo|bar)\z/, k)
case k
when /foo/
assert_same(k1, k)
when /bar/
assert_same(k2, k)
end
n += 1
end
assert_equal(2, n)
end
def test_each_value
x1 = "foo"
k1 = Object.new
@wm[k1] = x1
x2 = "bar"
k2 = Object.new
@wm[k2] = x2
n = 0
@wm.each_value do |v|
assert_match(/\A(?:foo|bar)\z/, v)
case v
when /foo/
assert_same(x1, v)
when /bar/
assert_same(x2, v)
end
n += 1
end
assert_equal(2, n)
end
def test_size
m = __callee__[/test_(.*)/, 1]
assert_equal(0, @wm.__send__(m))
x1 = "foo"
k1 = Object.new
@wm[k1] = x1
assert_equal(1, @wm.__send__(m))
x2 = "bar"
k2 = Object.new
@wm[k2] = x2
assert_equal(2, @wm.__send__(m))
end
alias test_length test_size
def test_frozen_object
o = Object.new.freeze
assert_nothing_raised(FrozenError) {@wm[o] = 'foo'}
assert_nothing_raised(FrozenError) {@wm['foo'] = o}
end
def test_no_memory_leak
assert_no_memory_leak([], '', "#{<<~"begin;"}\n#{<<~'end;'}", "[Bug #19398]", rss: true, limit: 1.5, timeout: 60)
begin;
1_000_000.times do
ObjectSpace::WeakMap.new
end
end;
end
def test_compaction
omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
# [Bug #19529]
obj = Object.new
100.times do |i|
GC.compact
@wm[i] = obj
end
assert_separately([], <<-'end;')
wm = ObjectSpace::WeakMap.new
obj = Object.new
100.times do
wm[Object.new] = obj
GC.start
end
GC.compact
end;
assert_separately(%w(-robjspace), <<-'end;')
wm = ObjectSpace::WeakMap.new
key = Object.new
val = Object.new
wm[key] = val
GC.verify_compaction_references(expand_heap: true, toward: :empty)
assert_equal(val, wm[key])
end;
assert_separately(["-W0"], <<-'end;')
wm = ObjectSpace::WeakMap.new
ary = 10_000.times.map do
o = Object.new
wm[o] = 1
o
end
GC.verify_compaction_references(expand_heap: true, toward: :empty)
end;
end
def test_gc_compact_stress
omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
EnvUtil.under_gc_compact_stress { ObjectSpace::WeakMap.new }
end
def test_replaced_values_bug_19531
a = "A".dup
b = "B".dup
@wm[1] = a
@wm[1] = a
@wm[1] = a
@wm[1] = b
assert_equal b, @wm[1]
a = nil
GC.start
assert_equal b, @wm[1]
end
def test_use_after_free_bug_20688
assert_normal_exit(<<~RUBY)
weakmap = ObjectSpace::WeakMap.new
10_000.times { weakmap[Object.new] = Object.new }
RUBY
end
end
|