NAME
Test::Weaken - Test that freed references are, indeed, freed
SYNOPSIS
use Test::Weaken qw(poof);
my $test = sub {
my $obj1 = new Module::Test_me1;
my $obj2 = new Module::Test_me2;
[ $obj1, $obj2 ];
};
my $unfreed_count = Test::Weaken::poof( $test );
my ($weak_count, $strong_count, $weak_unfreed, $strong_unfreed)
= Test::Weaken::poof( $test );
print scalar @$weak_unfreed,
" of $weak_count weak references freed\n";
print scalar @$strong_unfreed,
" of $strong_count strong references freed\n";
print "Weak unfreed references: ",
join(" ", map { "".$_ } @$weak_unfreed), "\n";
print "Strong unfreed references: ",
join(" ", map { "".$_ } @$strong_unfreed), "\n";
DESCRIPTION
Frees memory, and checks that the memory is deallocated. Also checks deallocation of any memory referred to indirectly through arrays, hashes, and weak and strong references. Arrays, hashes and references are followed recursively and to unlimited depth.
Circular references are handled gracefully. In fact, a major purpose of Test::Weaken
is to test schemes for deallocating circular references.
METHOD
poof
The poof
static method takes a closure as its only argument. This closure, the test object constructor, should build the test object, and create a primary test reference to it. The return value of the test object constructor must be the primary test reference. The test object should be created as much as possible within the test object constructor, because that makes it easier to construct a test object which has no references into it from poof
's calling environment. More on this below.
By recursively following references, arrays and hashes from the primary test reference, poof
finds all of the references in the test object. These are poof
's test references. In recursing through the test object, poof
keeps track of visited references. poof
never visits the same reference twice, and therefore has no problem when it has to deal with a test object which contains circular references.
In scalar context, poof
returns the number of unfreed test references. If all memory was deallocated successfully, this number will be zero.
In array context, poof
returns a list with four elements. First, the starting count of weak references. Second, the starting count of strong references. Third, a reference to an array containing references to the unfreed weak references. Fourth, a reference to an array containing references to the unfreed strong references.
If @result
is the result array from a call to poof
, then the number of strong references that were freed can be calculated as
$result[1] - @{$result[3]}
that is, the starting count of strong references, less the size of the array containing the unfreed strong references. Similarly, the count of freed weak references will be
$result[0] - @{$result[2]}
The unfreed references in the arrays can be dereferenced and the unfreed data examined. This may offer a clue to locate the source of a memory leak.
One technique a programmer can use to find memory leaks, is to add tags to the arrays and hashes inside his data object as it is created. These tags can indicate the point of creation of the objects. Once poof
's test object is tagged in this manner, poof
's list of unfreed references can be dereferenced to find the tags. The result will be a listing of all the sources of memory leaks.
The name poof
is intended to warn the programmer that the test is destructive. I originally called the main subroutine destroy
, but that choice seemed unfortunate because of similarities to DESTROY
, a name reserved for object destructors.
Obtaining the primary test reference from a test object constructor may seem roundabout. In fact, this indirect method is the easiest. The test object must not have any strong references to it from outside. It takes some craft to create and pass an object without holding a reference to it. Mistakes are easy to make. If a mistake were made, and poof
's calling environment held a strong reference into the test object, not all memory would be freed. If this happened by accident, so that the programmer was not aware of what was going on, the effect would be a false negative. Errors like this are tricky to detect and hard to debug.
If the test object is constructed completely within the test object constructor, and the only objects used to construct it are all in the scope of the test object constructor, there will be no strong references held from outside the test object once the test object constructor returns. Following this discipline, it is relatively easy for a careful programmer to avoid false negatives.
EXPORT
By default, Test::Weaken
exports nothing. Optionally, poof
may be exported.
LIMITATIONS
Test::Weaken
does not look inside code references.
IMPLEMENTATION
Test::Weaken
first recurses through the test object. It follows all weak and strong references, arrays and hashes. The test object is explored to unlimited depth. Visited references are tracked, and no reference is visited twice. Two lists of test references into the original data are generated. One list is of strong test references and the other is of weak test references.
As it recurses, Test::Weaken
creates a probe reference for every test reference. The probe references to the strong test references are weakened, so that the probe reference will not interfere with normal deallocation of memory.
When all the probe references have been created, The primary test reference is set to undef
. Normally, this is expected to cause all memory for the test object to be deallocated. To check this, Test::Weaken
dereferences the probe references. If the referent of the probe reference was deallocated, the value of the probe references will be undef
.
AUTHOR
Jeffrey Kegler
BUGS
Please report any bugs or feature requests to bug-test-weaken at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Weaken. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Test::Weaken
You can also look for information at:
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
RT: CPAN's request tracker
Search CPAN
SEE ALSO
Potential users will want to compare Test::Memory::Cycle and Devel::Cycle, which examine existing structures non-destructively. Devel::Leak also covers similar ground, although it requires Perl to be compiled with -DDEBUGGING
in order to work. Devel::Cycle looks inside closures if PadWalker is present, a feature Test::Weaken
does not have at present.
ACKNOWLEDGEMENTS
Thanks to jettero, Juerd and perrin of Perlmonks for their advice. Thanks also to Lincoln Stein (developer of Devel::Cycle) for test cases and other ideas.
COPYRIGHT & LICENSE
Copyright 2007-2008 Jeffrey Kegler, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.