• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

lib/Test/H26-Jun-2019-33256

t/H26-Jun-2019-235155

Build.PLH A D26-Jun-2019634 3228

ChangesH A D26-Jun-20191.7 KiB5139

LICENSEH A D26-Jun-201918 KiB380292

MANIFESTH A D26-Jun-2019170 1514

META.jsonH A D26-Jun-20191.2 KiB5453

META.ymlH A D26-Jun-2019739 3130

READMEH A D26-Jun-20198.1 KiB229164

README

1NAME
2
3    Test::Refcount - assert reference counts on objects
4
5SYNOPSIS
6
7       use Test::More tests => 2;
8       use Test::Refcount;
9
10       use Some::Class;
11
12       my $object = Some::Class->new();
13
14       is_oneref( $object, '$object has a refcount of 1' );
15
16       my $otherref = $object;
17
18       is_refcount( $object, 2, '$object now has 2 references' );
19
20DESCRIPTION
21
22    The Perl garbage collector uses simple reference counting during the
23    normal execution of a program. This means that cycles or unweakened
24    references in other parts of code can keep an object around for longer
25    than intended. To help avoid this problem, the reference count of a new
26    object from its class constructor ought to be 1. This way, the caller
27    can know the object will be properly DESTROYed when it drops all of its
28    references to it.
29
30    This module provides two test functions to help ensure this property
31    holds for an object class, so as to be polite to its callers.
32
33    If the assertion fails; that is, if the actual reference count is
34    different to what was expected, either of the following two modules may
35    be used to assist the developer in finding where the references are.
36
37      * If Devel::MAT is installed, this test module will use it to dump
38      the state of the memory after a failure. It will create a .pmat file
39      named the same as the unit test, but with the trailing .t suffix
40      replaced with -TEST.pmat where TEST is the number of the test that
41      failed (in case there was more than one).
42
43      * If Devel::FindRef module is installed, a reverse-references trace
44      is printed to the test output.
45
46    See the examples below for more information.
47
48FUNCTIONS
49
50 is_refcount
51
52       is_refcount( $object, $count, $name )
53
54    Test that $object has $count references to it.
55
56 is_oneref
57
58       is_oneref( $object, $name )
59
60    Assert that the $object has only 1 reference to it.
61
62 refcount
63
64       $count = refcount( $object )
65
66    Since version 0.09.
67
68    Returns the reference count of the given object as used by the test
69    functions. This is useful for making tests that don't care what the
70    count is before they start, but simply assert that the count hasn't
71    changed by the end.
72
73       use Test::Refcount import => [qw( is_refcount refcount )];
74       {
75          my $count = refcount( $object );
76
77          do_something( $object );
78
79          is_refcount( $object, $count, 'do_something() preserves refcount' );
80       }
81
82EXAMPLE
83
84    Suppose, having written a new class MyBall, you now want to check that
85    its constructor and methods are well-behaved, and don't leak
86    references. Consider the following test script:
87
88       use Test::More tests => 2;
89       use Test::Refcount;
90
91       use MyBall;
92
93       my $ball = MyBall->new();
94       is_oneref( $ball, 'One reference after construct' );
95
96       $ball->bounce;
97
98       # Any other code here that might be part of the test script
99
100       is_oneref( $ball, 'One reference just before EOF' );
101
102    The first assertion is just after the constructor, to check that the
103    reference returned by it is the only reference to that object. This
104    fact is important if we ever want DESTROY to behave properly. The
105    second call is right at the end of the file, just before the main scope
106    closes. At this stage we expect the reference count also to be one, so
107    that the object is properly cleaned up.
108
109    Suppose, when run, this produces the following output (presuming
110    Devel::MAT::Dumper is available):
111
112       1..2
113       ok 1 - One reference after construct
114       not ok 2 - One reference just before EOF
115       #   Failed test 'One reference just before EOF'
116       #   at ex.pl line 26.
117       #   expected 1 references, found 2
118       # SV address is 0x55e14c310278
119       # Writing heap dump to ex-2.pmat
120       # Looks like you failed 1 test of 2.
121
122    This has written a ex-2.pmat file we can load using the pmat shell and
123    use the identify command on the given address to find where it went:
124
125       $ pmat ex-2.pmat
126       Perl memory dumpfile from perl 5.28.1 threaded
127       Heap contains 25233 objects
128       pmat> identify 0x55e14c310278
129       HASH(0)=MyBall at 0x55e14c310278 is:
130       ├─(via RV) the lexical $ball at depth 1 of CODE() at 0x55e14c3104a0=main_cv, which is:
131       │ └─the main code
132       └─(via RV) value {self} of HASH(2) at 0x55e14cacb860, which is (*A):
133         └─(via RV) value {cycle} of HASH(2) at 0x55e14cacb860, which is:
134           itself
135
136    (This document isn't intended to be a full tutorial on Devel::MAT and
137    the pmat shell; for that see Devel::MAT::UserGuide).
138
139    Alternatively, this produces the following output when using
140    Devel::FindRef instead:
141
142       1..2
143       ok 1 - One reference after construct
144       not ok 2 - One reference just before EOF
145       #   Failed test 'One reference just before EOF'
146       #   at demo.pl line 16.
147       #   expected 1 references, found 2
148       # MyBall=ARRAY(0x817f880) is
149       # +- referenced by REF(0x82c1fd8), which is
150       # |     in the member 'self' of HASH(0x82c1f68), which is
151       # |        referenced by REF(0x81989d0), which is
152       # |           in the member 'cycle' of HASH(0x82c1f68), which was seen before.
153       # +- referenced by REF(0x82811d0), which is
154       #       in the lexical '$ball' in CODE(0x817fa00), which is
155       #          the main body of the program.
156       # Looks like you failed 1 test of 2.
157
158    From this output, we can see that the constructor was well-behaved, but
159    that a reference was leaked by the end of the script - the reference
160    count was 2, when we expected just 1. Reading the trace output, we can
161    see that there were 2 references that could be found - one stored in
162    the $ball lexical in the main program, and one stored in a HASH. Since
163    we expected to find the $ball lexical variable, we know we are now
164    looking for a leak in a hash somewhere in the code. From reading the
165    test script, we can guess this leak is likely to be in the bounce()
166    method. Furthermore, we know that the reference to the object will be
167    stored in a HASH in a member called self.
168
169    By reading the code which implements the bounce() method, we can see
170    this is indeed the case:
171
172       sub bounce
173       {
174          my $self = shift;
175          my $cycle = { self => $self };
176          $cycle->{cycle} = $cycle;
177       }
178
179    From reading the tracing output, we find that the HASH this object is
180    referenced in also contains a reference to itself, in a member called
181    cycle. This comes from the last line in this function, a line that
182    purposely created a cycle, to demonstrate the point. While a real
183    program probably wouldn't do anything quite this obvious, the trace
184    would still be useful in finding the likely cause of the leak.
185
186    If neither Devel::MAT::Dumper nor Devel::FindRef are available, then
187    these detailed traces will not be produced. The basic reference count
188    testing will still take place, but a smaller message will be produced:
189
190       1..2
191       ok 1 - One reference after construct
192       not ok 2 - One reference just before EOF
193       #   Failed test 'One reference just before EOF'
194       #   at demo.pl line 16.
195       #   expected 1 references, found 2
196       # Looks like you failed 1 test of 2.
197
198BUGS
199
200      * Temporaries created on the stack
201
202      Code which creates temporaries on the stack, to be released again
203      when the called function returns does not work correctly on perl 5.8
204      (and probably before). Examples such as
205
206         is_oneref( [] );
207
208      may fail and claim a reference count of 2 instead.
209
210      Passing a variable such as
211
212         my $array = [];
213         is_oneref( $array );
214
215      works fine. Because of the intention of this test module; that is, to
216      assert reference counts on some object stored in a variable during
217      the lifetime of the test script, this is unlikely to cause any
218      problems.
219
220ACKNOWLEDGEMENTS
221
222    Peter Rabbitson <ribasushi@cpan.org> - for suggesting using core's B
223    instead of Devel::Refcount to obtain refcounts
224
225AUTHOR
226
227    Paul Evans <leonerd@leonerd.org.uk>
228
229