2 use strict; use warnings;
6 use Hash::Util::FieldHash qw( :all);
7 my $ob_reg = Hash::Util::FieldHash::_ob_reg;
9 #########################
11 my $fieldhash_mode = 2;
13 # define ref types to use with some tests
16 # skipping CODE refs, they are differently scoped
17 @test_types = qw( SCALAR ARRAY HASH GLOB);
22 BEGIN { $n_tests += 4 }
24 is id( $ref), refaddr( $ref), "id is refaddr";
26 Hash::Util::FieldHash::_fieldhash \ %h, $fieldhash_mode;
29 is id( $ref), $key, "id is FieldHash key";
30 my $scalar = 'string';
31 is id( $scalar), $scalar, "string passes unchanged";
33 is id( $scalar), $scalar, "number passes unchanged";
36 ### idhash functionality
38 BEGIN { $n_tests += 3 }
39 Hash::Util::FieldHash::idhash my %h;
44 is $key, id( $ref), "idhash key correct";
45 is $h{ $ref}, $val, "value retrieved through ref";
46 is scalar keys %$ob_reg, 0, "no auto-registry in idhash";
49 ### the register() and id_2obj functions
51 BEGIN { $n_tests += 9 }
54 is id_2obj( $id), undef, "unregistered object not retrieved";
55 is scalar keys %$ob_reg, 0, "object registry empty";
56 is register( $obj), $obj, "object returned by register";
57 is scalar keys %$ob_reg, 1, "object registry nonempty";
58 is id_2obj( $id), $obj, "registered object retrieved";
60 register( $obj, \ %hash);
62 is scalar keys %hash, 1, "key present in registered hash";
64 is scalar keys %hash, 0, "key collected from registered hash";
65 is scalar keys %$ob_reg, 0, "object registry empty again";
66 eval { register( 1234) };
67 like $@, qr/^Attempt to register/, "registering non-ref is fatal";
71 ### Object auto-registry
73 BEGIN { $n_tests += 3 }
79 Hash::Util::FieldHash::_fieldhash $h, $fieldhash_mode;
81 is( keys %$ob_reg, 1, "one object registered");
83 # field hash stays alive until $obj dies
84 is( keys %$ob_reg, 1, "object still registered");
86 is( keys %$ob_reg, 0, "object unregistered");
89 ### existence/retrieval/deletion
90 BEGIN { $n_tests += 6 }
94 Hash::Util::FieldHash::_fieldhash \ my( %h), $fieldhash_mode;
95 for ( [ str => 'abc'], [ ref => {}] ) {
96 my ( $keytype, $key) = @$_;
98 ok( exists $h{ $key}, "existence ($keytype)");
99 is( $h{ $key}, $val, "retrieval ($keytype)");
101 is( keys %h, 0, "deletion ($keytype)");
105 ### id-action (stringification independent of bless)
106 BEGIN { $n_tests += 5 }
107 # use Scalar::Util qw( refaddr);
110 Hash::Util::FieldHash::_fieldhash \ %f, $fieldhash_mode;
111 Hash::Util::FieldHash::_fieldhash \ %g, $fieldhash_mode;
115 is( $f{ $key}, $val, "plain key set in field");
117 my $refaddr = refaddr($key);
118 is $id, $refaddr, "key is refaddr";
120 is( $f{ $key}, $val, "access through blessed");
123 is( $h{ $key}, $val, "plain key set in hash");
125 isnt( $h{ $key}, $val, "no access through blessed");
129 BEGIN { $n_tests += 1 + 2*( 3*@test_types + 5) + 1 + 2 }
133 Hash::Util::FieldHash::_fieldhash \ %h, $fieldhash_mode;
135 is( keys %h, 0, "blip");
138 for my $preload ( [], [ map {}, 1 .. 3] ) {
139 my $pre = @$preload ? ' (preloaded)' : '';
141 Hash::Util::FieldHash::_fieldhash \ %f, $fieldhash_mode;
142 my @preval = map "$_", @$preload;
143 @f{ @$preload} = @preval;
144 # Garbage collection separately
145 for my $type ( @test_types) {
147 my $ref = gen_ref( $type);
149 my ( $val) = grep $_ eq $type, values %f;
150 is( $val, $type, "$type visible$pre");
154 "$type obj registered$pre"
157 is( keys %f, @$preload, "$type gone$pre");
160 # Garbage collection collectively
161 is( keys %$ob_reg, @$preload, "no objs remaining$pre");
163 my @refs = map gen_ref( $_), @test_types;
164 @f{ @refs} = @test_types;
166 eq_set( [ values %f], [ @test_types, @preval]),
167 "all types present$pre",
171 @test_types + @$preload,
172 "all types registered$pre",
175 die "preload gone" unless defined $preload;
176 ok( eq_set( [ values %f], \ @preval), "all types gone$pre");
177 is( keys %$ob_reg, @$preload, "all types unregistered$pre");
179 is( keys %$ob_reg, 0, "preload gone after loop");
184 Hash::Util::FieldHash::_fieldhash \ %h, $fieldhash_mode;
186 my $x = $h{ $ref}->[ 0];
187 is keys %h, 1, "autovivified key present";
189 is keys %h, 0, "autovivified key collected";
193 BEGIN { $n_tests += 8 }
197 Hash::Util::FieldHash::_fieldhash \ %f, $fieldhash_mode;
199 my @refs = map [], 1 .. $size;
200 $f{ $_} = 1 for @refs;
201 is( keys %f, $size, "many keys singly");
205 "many objects singly",
208 is( keys %f, 0, "many keys singly gone");
212 "many objects singly unregistered",
216 my @refs = map [], 1 .. $size;
217 @f{ @refs } = ( 1) x @refs;
218 is( keys %f, $size, "many keys at once");
222 "many objects at once",
225 is( keys %f, 0, "many keys at once gone");
229 "many objects at once unregistered",
234 BEGIN { $n_tests += 6 }
237 my @fields = map {}, $n_fields;
238 Hash::Util::FieldHash::_fieldhash( $_, $fieldhash_mode) for @fields;
239 my @obs = map gen_ref( $_), @test_types;
241 for my $field ( @fields ) {
242 @{ $field }{ @obs} = map ref, @obs;
244 my $err = grep keys %$_ != @obs, @fields;
245 is( $err, 0, "$n_obs entries in $n_fields fields");
246 is( keys %$ob_reg, @obs, "$n_obs obs registered");
248 $err = grep keys %$_ != @obs, @fields;
249 is( $err, 0, "one entry gone from $n_fields fields");
250 is( keys %$ob_reg, @obs, "one ob unregistered");
252 $err = grep keys %$_ != @obs, @fields;
253 is( $err, 0, "all entries gone from $n_fields fields");
254 is( keys %$ob_reg, @obs, "all obs unregistered");
258 # direct hash assignment
259 BEGIN { $n_tests += 4 }
261 Hash::Util::FieldHash::_fieldhash( $_, $fieldhash_mode) for \ my( %f, %g, %h);
263 my @obs = map [], 1 .. $size;
264 @f{ @obs} = ( 1) x $size;
265 $g{ $_} = $f{ $_} for keys %f; # single assignment
266 %h = %f; # wholesale assignment
268 is keys %$ob_reg, 0, "all keys collected";
269 is keys %f, 0, "orig garbage-collected";
270 is keys %g, 0, "single-copy garbage-collected";
271 is keys %h, 0, "wholesale-copy garbage-collected";
275 # prototypes in place?
287 my @notfound = grep !exists $proto_tab{ $_} =>
288 @Hash::Util::FieldHash::EXPORT_OK;
289 ok @notfound == 0, "All exports in table";
290 is prototype( "Hash::Util::FieldHash::$_") || '', $proto_tab{ $_},
291 "$_ has prototype ($proto_tab{ $_})" for
292 @Hash::Util::FieldHash::EXPORT_OK;
294 BEGIN { $n_tests += 1 + @Hash::Util::FieldHash::EXPORT_OK }
298 BEGIN { $n_tests += 1 }
299 Hash::Util::FieldHash::_fieldhash \ my( %h), $fieldhash_mode;
300 bless \ %h, 'abc'; # this bus-errors with a certain bug
301 ok( 1, "no bus error on bless")
304 BEGIN { plan tests => $n_tests }
306 #######################################################################
309 # silence possible warnings from hex() on 64bit systems
310 no warnings 'portable';
313 hex +($ref =~ /\(0x([[:xdigit:]]+)\)$/)[ 0];
316 use Symbol qw( gensym);
320 SCALAR => sub { \ my $o },
323 GLOB => sub { gensym },
324 CODE => sub { sub {} },
327 sub gen_ref { $gen{ shift()}->() }