I have some code that examines the objects in ObjectSpace and returns a
set with all those that meet some criteria. I found during unit testing
that, although I have just one (global) varible refering to an object of
a particular type, iterating over ObjectSpace.each_object returns
numerous objects of this class after some test methods have been called.
My unit test code has this:
def setup
$foo = Foo.new( “Test!” )
end
def teardown
$foo = nil
end
After four test methods are called, ObjectSpace still holds two objects
of class Foo, although the code only has that one variable for the one
instance of Foo needed.
Is this a quirk of Test::Unit? Or of ObjectSpace?
I’ve tried adding a call to ObjectSpace.garbage_collect
before the iteration, but it doesn’t change anything.
This appears to be a Test::Unit quirk because if I call the code outside
of the Test::Unit::TestCase subclass then I find no leftover objects.
Direct calls to setup and teardown do not leave any objects around, and
the call to each_object finds the one item that should be there.
How can I ensure that once teardown is called that there are no extra
objects lingering in ObjectSpace, short of doing the setup and teardown
by hand inside of each test method?
I’m using ruby 1.8.0 (2003-05-26) [i386-mswin32] and whatever Test::Unit
that comes with that (there’s no version number in unit.rb)
Thanks,
James Britt
james_b wrote:
I have some code that examines the objects in ObjectSpace and returns a
set with all those that meet some criteria. I found during unit testing
that, although I have just one (global) varible refering to an object of
a particular type, iterating over ObjectSpace.each_object returns
numerous objects of this class after some test methods have been called.
[snip]
After four test methods are called, ObjectSpace still holds two objects
of class Foo, although the code only has that one variable for the one
instance of Foo needed.
Is this a quirk of Test::Unit? Or of ObjectSpace?
I’ve tried adding a call to ObjectSpace.garbage_collect
before the iteration, but it doesn’t change anything.
[snip]
I’m using ruby 1.8.0 (2003-05-26) [i386-mswin32] and whatever Test::Unit
that comes with that (there’s no version number in unit.rb)
I cannot reproduce it using ruby 1.8.0 (2003-07-24) [i686-linux], so
perhaps it was a temporary problem that has been fixed. Suggest you try
a newer version to see if it helps.
[kentda@localhost ruby]$ ruby1.8 -v setup.rb
ruby 1.8.0 (2003-07-24) [i686-linux]
Loaded suite ATest
Started
…
Finished in 0.003337 seconds.
4 tests, 0 assertions, 0 failures, 0 errors
Foos:4
Foos:0
[kentda@localhost ruby]$ cat setup.rb
require ‘test/unit’
class Foo
end
class ATest < Test::Unit::TestCase
def setup
$foo = Foo.new
end
def teardown
$foo = nil
end
def test_a; end
def test_b; end
def test_c; end
def test_d; end
end
def num_foos
n = 0
ObjectSpace.each_object(Foo){|i| n+=1 }
print “Foos:”, n, “\n”
end
if FILE == $0
require ‘test/unit/ui/console/testrunner’
Test::Unit::UI::Console::TestRunner.run ATest.suite
num_foos
ObjectSpace.garbage_collect
num_foos
end
···
–
([ Kent Dahl ]/)_ ~ [ http://www.pvv.org/~kentda/ ]/~
))_student_/(( _d L b_/ (pre-) Master of Science in Technology )
( __õ|õ// ) )Industrial economics and technological management(
_/ö____/ (_engineering.discipline=Computer::Technology)
I don’t think Test::Unit is doing anything funky; my guess is that it’s just
a quirk of Ruby’s garbage collector. I don’t know exactly how the GC
interacts with ObjectSpace, but my guess is that one can think of the
ObjectSpace as having weak references to all the objects created, so it
doesn’t prevent GC of those objects. However, GC timing in Ruby is not
guaranteed, and thus it wouldn’t surprise me if there are cases when objects
with no remaining references are still found by an ObjectSpace walk. Why it
only shows up when testing, I have no earthly idea. Have you tried
sacrificing a chicken to the GC before every test run? If that’s a bit too
messy, your solution of calling ObjectSpace#garbage_collect in teardown is
probably the next best thing. Another solution would be to pass the
ObjectSpace in to the code that uses it - that way you can use a mock when
testing that code and not be dependent on the GC at all.
HTH,
Nathaniel
<:((><
···
james_b [mailto:james_b@neurogami.com] wrote:
I have some code that examines the objects in ObjectSpace and returns a
set with all those that meet some criteria. I found during unit testing
that, although I have just one (global) varible refering to an object of
a particular type, iterating over ObjectSpace.each_object returns
numerous objects of this class after some test methods have
been called.
My unit test code has this:
def setup
$foo = Foo.new( “Test!” )
end
def teardown
$foo = nil
end
After four test methods are called, ObjectSpace still holds two objects
of class Foo, although the code only has that one variable for the one
instance of Foo needed.
Is this a quirk of Test::Unit? Or of ObjectSpace?
Kent Dahl wrote:
I cannot reproduce it using ruby 1.8.0 (2003-07-24) [i686-linux], so
perhaps it was a temporary problem that has been fixed. Suggest you try
a newer version to see if it helps.
Your example code didn’t quite mimick my situation (left-over objects
during execution of TestCase methods), but it prompted me to stick
ObjectSpace.garbage_collect inside of teardown, correcting the problem
for the time being.
Thanks,
James
Nathaniel Talbott wrote:
Is this a quirk of Test::Unit? Or of ObjectSpace?
I don’t think Test::Unit is doing anything funky; my guess is that it’s just
a quirk of Ruby’s garbage collector. I don’t know exactly how the GC
interacts with ObjectSpace, but my guess is that one can think of the
ObjectSpace as having weak references to all the objects created, so it
doesn’t prevent GC of those objects. However, GC timing in Ruby is not
guaranteed, and thus it wouldn’t surprise me if there are cases when objects
with no remaining references are still found by an ObjectSpace walk. Why it
only shows up when testing, I have no earthly idea. Have you tried
sacrificing a chicken to the GC before every test run?
Hm. That’a an idea …
If that’s a bit too
messy, your solution of calling ObjectSpace#garbage_collect in teardown is
probably the next best thing. Another solution would be to pass the
ObjectSpace in to the code that uses it - that way you can use a mock when
testing that code and not be dependent on the GC at all.
The code being tested loops over ObjectSapce. Oddly, calling
ObjectSpace#garbage_collect in that code right before iterating didn’t
help.
Is there a way to ask an object if nothing refers to it?
HTH,
Thanks,
James
···
james_b [mailto:james_b@neurogami.com] wrote:
Nathaniel
<:((><
Nathaniel Talbott wrote:
If that’s a bit too
messy, your solution of calling ObjectSpace#garbage_collect in
teardown is probably the next best thing. Another solution would be to
pass the ObjectSpace in to the code that uses it - that way you can
use a mock when testing that code and not be dependent on the GC at
all.
The code being tested loops over ObjectSapce.
Can you do something like this:
def my_object_space_code(object_space=ObjectSpace)
object_space.each_object(SomeClass) do |object|
… do stuff …
end
end
…
def test_my_object_space_code
object_space = Object.new
def object_space.each_object(klass, &block)
[Thing1.new, Thing2.new].each(&block)
end
my_object_space_code(object_space)
... make assertions ...
end
I’ve done exactly that and found it to be much more deterministic than
trying to test ObjectSpace directly. If I assume that ObjectSpace itself
works, which I do, it makes my life much simpler to completely remove it
from the equation when testing.
Oddly, calling
ObjectSpace#garbage_collect in that code right before iterating didn’t
help.
My guess is that doing that didn’t leave enough time between when you asked
for collection and when you wanted that collection to have happened.
TestCase#teardown for the last test happens a lot earlier than a call in the
current test. This is why using ObjectSpace in the test itself would worry
me… what’s the magic timing? Could some seemingly unrelated change (in
hardware, in OS, etc.) cause the test to start failing all of the sudden
when the code still works just fine?
Is there a way to ask an object if nothing refers to it?
My guess is no… usually that would be a contradiction in terms - by the
very fact that you can ask, “Does anything refer to you?”, the answer is
“Yes.” The weird case of ObjectSpace (and I’m guessing WeakRefs, although I
haven’t used them at all) is just, well, weird; perhaps someone more
familiar with Ruby internals can give a more satisfactory answer here.
HTH,
Nathaniel
<:((><
···
james_b [mailto:james_b@neurogami.com] wrote: