summaryrefslogtreecommitdiff
path: root/test/ruby/test_weakkeymap.rb
blob: 5df53749ca04aebe8429f2ae6f7232ebc2d42c73 (plain)
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
# frozen_string_literal: false
require 'test/unit'

class TestWeakKeyMap < Test::Unit::TestCase
  def setup
    @wm = ObjectSpace::WeakKeyMap.new
  end

  def test_map
    x = Object.new
    k = "foo"
    @wm[k] = x
    assert_same(x, @wm[k])
    assert_same(x, @wm["FOO".downcase])
  end

  def test_aset_const
    x = Object.new
    assert_raise(ArgumentError) { @wm[true] = x }
    assert_raise(ArgumentError) { @wm[false] = x }
    assert_raise(ArgumentError) { @wm[nil] = x }
    assert_raise(ArgumentError) { @wm[42] = x }
    assert_raise(ArgumentError) { @wm[2**128] = x }
    assert_raise(ArgumentError) { @wm[1.23] = x }
    assert_raise(ArgumentError) { @wm[:foo] = x }
    assert_raise(ArgumentError) { @wm["foo#{rand}".to_sym] = x }
  end

  def test_getkey
    k = "foo"
    @wm[k] = true
    assert_same(k, @wm.getkey("FOO".downcase))
  end

  def test_key?
    assert_weak_include(:key?, "foo")
    assert_not_send([@wm, :key?, "bar"])
  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_clear
    k = "foo"
    @wm[k] = true
    assert @wm[k]
    assert_same @wm, @wm.clear
    refute @wm[k]
  end

  def test_inspect
    x = Object.new
    k = Object.new
    @wm[k] = x
    assert_match(/\A\#<#{@wm.class.name}:[\dxa-f]+ size=\d+>\z/, @wm.inspect)

    1000.times do |i|
      @wm[i.to_s] = Object.new
      @wm.inspect
    end
    assert_match(/\A\#<#{@wm.class.name}:[\dxa-f]+ size=\d+>\z/, @wm.inspect)
  end

  def test_no_hash_method
    k = BasicObject.new
    assert_raise NoMethodError do
      @wm[k] = 42
    end
  end

  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_inconsistent_hash_key
    assert_no_memory_leak [], '', <<~RUBY
      class BadHash
        def initialize
          @hash = 0
        end

        def hash
          @hash += 1
        end
      end

      k = BadHash.new
      wm = ObjectSpace::WeakKeyMap.new

      100_000.times do |i|
        wm[k] = i
      end
    RUBY
  end

  private

  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_send([@wm, m, "FOO".downcase])
      x = Object.new
    end
  end
end