From: Michal Suchanek Date: 2007-12-22T07:32:26+09:00 Subject: Re: First stab at a weakref test suite On 21/12/2007, Eric Hodel wrote: > On Dec 21, 2007, at 05:50 AM, Charles Oliver Nutter wrote: > > Eric Hodel wrote: > >> On Dec 20, 2007, at 21:45 PM, Daniel Berger wrote: > >>> I follow the same pattern in the tests, but it doesn't raise the > >>> expected error. Any ideas? > >> Since the GC is conservative, references may exist longer than you > >> expect because the item that should be collected is still > >> referenced from the stack. I don't know of an easy way around this. > > > > This is probably the largest reason why a weakref suite will be a > > little tricky. My best recommendation would be to loop calling > > GC.start until the reference gets collected; perhaps with an upper > > count... "if we reach 1000 iterations and it's not collected, it > > isn't working". > > If it isn't collected after 1, I doubt it will be collected after > 1000. You'd have better luck manipulating the stack (calling methods > recursively that eventually allocate things), but you're still not > guaranteed that any object will be collected when you want with ruby > 1.8 or 1.9. > > Yes, the behavior looks very non-deterministic from the outside, it even depends on naming of the tests. I had better luck making it work with 1.8 (works as is) but it won't work with 1.9. The first test that tests the weakref dies does not succeed, and the second does. This one works for me but it likely depends on exact state of the heap or something like that which is system specific: require 'test/unit' require 'weakref' class TC_WeakRef < Test::Unit::TestCase def setup @ref = nil @str = "hello" GC.enable end # Fails in 1.9 because WeakRef does not have inspect, # and it pretends to be String def test_weakref_constructor assert_respond_to(WeakRef, :new) assert_nothing_raised{ @ref = WeakRef.new(@str) } assert_kind_of(WeakRef, @ref) end # TODO: Figure out why last test fails # somehow using the string makes a reference somewhere def test_weakref_2 str = @str.dup assert_nothing_raised{ @ref = WeakRef.new(@str) } assert_equal(true, @ref.weakref_alive?) assert_equal str, @ref assert_nothing_raised{ GC.start } assert_equal(true, @ref.weakref_alive?) assert_equal str, @ref assert_nothing_raised{ @str = nil } assert_equal(true, @ref.weakref_alive?) assert_equal str, @ref assert_nothing_raised{ GC.start } #assert_equal(false, @ref.weakref_alive?) #assert_raise(WeakRef::RefError){ @str = @ref * 3 } end def test_weakref_is_alive_basic assert_nothing_raised{ @ref = WeakRef.new(@str) } assert_respond_to(@ref, :weakref_alive?) end # TODO: Figure out why last test does not fail def test_weakref_is_alive assert_nothing_raised{ @ref = WeakRef.new(@str) } assert_equal(true, @ref.weakref_alive?) assert_nothing_raised{ GC.start } assert_equal(true, @ref.weakref_alive?) assert_nothing_raised{ @str = nil } assert_equal(true, @ref.weakref_alive?) assert_nothing_raised{ GC.start } assert_equal(false, @ref.weakref_alive?) assert_raise(WeakRef::RefError){ @str = @ref * 3 } end def teardown @str = nil @ref = nil end end