[patch] Hash::Util::FieldHash v1.01
[p5sagit/p5-mst-13.2.git] / ext / Hash / Util / FieldHash / t / 02_function.t
CommitLineData
1e73acc8 1#!perl
2
3BEGIN {
df6ac08f 4 if ($ENV{PERL_CORE}) {
5 chdir 't' if -d 't';
6 @INC = '../lib';
7 }
1e73acc8 8}
9
10use strict; use warnings;
11use Test::More;
12my $n_tests = 0;
13
14use Hash::Util::FieldHash qw( :all);
6ff38c27 15my $ob_reg = Hash::Util::FieldHash::_ob_reg;
1e73acc8 16
17#########################
18
d74d639b 19my $fieldhash_mode = 2;
20
1e73acc8 21# define ref types to use with some tests
22my @test_types;
23BEGIN {
24 # skipping CODE refs, they are differently scoped
25 @test_types = qw( SCALAR ARRAY HASH GLOB);
26}
27
d74d639b 28### The id() function
29{
30 BEGIN { $n_tests += 4 }
31 my $ref = [];
32 is id( $ref), refaddr( $ref), "id is refaddr";
33 my %h;
34 Hash::Util::FieldHash::_fieldhash \ %h, $fieldhash_mode;
35 $h{ $ref} = ();
36 my ( $key) = keys %h;
37 is id( $ref), $key, "id is FieldHash key";
38 my $scalar = 'string';
39 is id( $scalar), $scalar, "string passes unchanged";
40 $scalar = 1234;
41 is id( $scalar), $scalar, "number passes unchanged";
42}
43
44### idhash functionality
45{
46 BEGIN { $n_tests += 3 }
47 Hash::Util::FieldHash::idhash my %h;
48 my $ref = sub {};
49 my $val = 123;
50 $h{ $ref} = $val;
51 my ( $key) = keys %h;
52 is $key, id( $ref), "idhash key correct";
53 is $h{ $ref}, $val, "value retrieved through ref";
54 is scalar keys %$ob_reg, 0, "no auto-registry in idhash";
55}
56
57### the register() and id_2obj functions
58{
59 BEGIN { $n_tests += 9 }
60 my $obj = {};
61 my $id = id( $obj);
62 is id_2obj( $id), undef, "unregistered object not retrieved";
63 is scalar keys %$ob_reg, 0, "object registry empty";
64 is register( $obj), $obj, "object returned by register";
65 is scalar keys %$ob_reg, 1, "object registry nonempty";
66 is id_2obj( $id), $obj, "registered object retrieved";
67 my %hash;
68 register( $obj, \ %hash);
69 $hash{ $id} = 123;
70 is scalar keys %hash, 1, "key present in registered hash";
71 undef $obj;
72 is scalar keys %hash, 0, "key collected from registered hash";
73 is scalar keys %$ob_reg, 0, "object registry empty again";
74 eval { register( 1234) };
75 like $@, qr/^Attempt to register/, "registering non-ref is fatal";
76
77}
78
79### Object auto-registry
1e73acc8 80
81BEGIN { $n_tests += 3 }
82{
1e73acc8 83 {
84 my $obj = {};
85 {
d74d639b 86 my $h = {};
87 Hash::Util::FieldHash::_fieldhash $h, $fieldhash_mode;
1e73acc8 88 $h->{ $obj} = 123;
89 is( keys %$ob_reg, 1, "one object registered");
90 }
91 # field hash stays alive until $obj dies
92 is( keys %$ob_reg, 1, "object still registered");
93 }
94 is( keys %$ob_reg, 0, "object unregistered");
95}
96
97### existence/retrieval/deletion
98BEGIN { $n_tests += 6 }
99{
100 no warnings 'misc';
101 my $val = 123;
d74d639b 102 Hash::Util::FieldHash::_fieldhash \ my( %h), $fieldhash_mode;
1e73acc8 103 for ( [ str => 'abc'], [ ref => {}] ) {
104 my ( $keytype, $key) = @$_;
105 $h{ $key} = $val;
106 ok( exists $h{ $key}, "existence ($keytype)");
107 is( $h{ $key}, $val, "retrieval ($keytype)");
108 delete $h{ $key};
109 is( keys %h, 0, "deletion ($keytype)");
110 }
111}
112
113### id-action (stringification independent of bless)
d74d639b 114BEGIN { $n_tests += 5 }
115# use Scalar::Util qw( refaddr);
1e73acc8 116{
117 my( %f, %g, %h, %i);
d74d639b 118 Hash::Util::FieldHash::_fieldhash \ %f, $fieldhash_mode;
119 Hash::Util::FieldHash::_fieldhash \ %g, $fieldhash_mode;
1e73acc8 120 my $val = 123;
121 my $key = [];
122 $f{ $key} = $val;
123 is( $f{ $key}, $val, "plain key set in field");
d74d639b 124 my ( $id) = keys %f;
125 my $refaddr = hex +($key =~ /\(0x([[:xdigit:]]+)\)$/)[ 0];
126 is $id, $refaddr, "key is refaddr";
1e73acc8 127 bless $key;
128 is( $f{ $key}, $val, "access through blessed");
129 $key = [];
130 $h{ $key} = $val;
131 is( $h{ $key}, $val, "plain key set in hash");
132 bless $key;
133 isnt( $h{ $key}, $val, "no access through blessed");
134}
135
136# Garbage collection
d74d639b 137BEGIN { $n_tests += 1 + 2*( 3*@test_types + 5) + 1 + 2 }
1e73acc8 138
139{
d74d639b 140 my %h;
141 Hash::Util::FieldHash::_fieldhash \ %h, $fieldhash_mode;
1e73acc8 142 $h{ []} = 123;
143 is( keys %h, 0, "blip");
144}
145
146for my $preload ( [], [ map {}, 1 .. 3] ) {
147 my $pre = @$preload ? ' (preloaded)' : '';
d74d639b 148 my %f;
149 Hash::Util::FieldHash::_fieldhash \ %f, $fieldhash_mode;
1e73acc8 150 my @preval = map "$_", @$preload;
151 @f{ @$preload} = @preval;
152 # Garbage collection separately
153 for my $type ( @test_types) {
154 {
155 my $ref = gen_ref( $type);
156 $f{ $ref} = $type;
157 my ( $val) = grep $_ eq $type, values %f;
158 is( $val, $type, "$type visible$pre");
159 is(
6ff38c27 160 keys %$ob_reg,
1e73acc8 161 1 + @$preload,
162 "$type obj registered$pre"
163 );
164 }
165 is( keys %f, @$preload, "$type gone$pre");
166 }
167
168 # Garbage collection collectively
6ff38c27 169 is( keys %$ob_reg, @$preload, "no objs remaining$pre");
1e73acc8 170 {
171 my @refs = map gen_ref( $_), @test_types;
172 @f{ @refs} = @test_types;
173 ok(
174 eq_set( [ values %f], [ @test_types, @preval]),
175 "all types present$pre",
176 );
177 is(
6ff38c27 178 keys %$ob_reg,
1e73acc8 179 @test_types + @$preload,
180 "all types registered$pre",
181 );
182 }
183 die "preload gone" unless defined $preload;
184 ok( eq_set( [ values %f], \ @preval), "all types gone$pre");
6ff38c27 185 is( keys %$ob_reg, @$preload, "all types unregistered$pre");
1e73acc8 186}
6ff38c27 187is( keys %$ob_reg, 0, "preload gone after loop");
1e73acc8 188
d74d639b 189# autovivified key
190{
191 my %h;
192 Hash::Util::FieldHash::_fieldhash \ %h, $fieldhash_mode;
193 my $ref = {};
194 my $x = $h{ $ref}->[ 0];
195 is keys %h, 1, "autovivified key present";
196 undef $ref;
197 is keys %h, 0, "autovivified key collected";
198}
199
1e73acc8 200# big key sets
201BEGIN { $n_tests += 8 }
202{
203 my $size = 10_000;
d74d639b 204 my %f;
205 Hash::Util::FieldHash::_fieldhash \ %f, $fieldhash_mode;
1e73acc8 206 {
207 my @refs = map [], 1 .. $size;
208 $f{ $_} = 1 for @refs;
209 is( keys %f, $size, "many keys singly");
210 is(
6ff38c27 211 keys %$ob_reg,
1e73acc8 212 $size,
213 "many objects singly",
214 );
215 }
216 is( keys %f, 0, "many keys singly gone");
217 is(
6ff38c27 218 keys %$ob_reg,
1e73acc8 219 0,
220 "many objects singly unregistered",
221 );
222
223 {
224 my @refs = map [], 1 .. $size;
ce809d1f 225 @f{ @refs } = ( 1) x @refs;
1e73acc8 226 is( keys %f, $size, "many keys at once");
227 is(
6ff38c27 228 keys %$ob_reg,
1e73acc8 229 $size,
230 "many objects at once",
231 );
232 }
233 is( keys %f, 0, "many keys at once gone");
234 is(
6ff38c27 235 keys %$ob_reg,
1e73acc8 236 0,
237 "many objects at once unregistered",
238 );
239}
240
241# many field hashes
242BEGIN { $n_tests += 6 }
243{
244 my $n_fields = 1000;
d74d639b 245 my @fields = map {}, $n_fields;
246 Hash::Util::FieldHash::_fieldhash( $_, $fieldhash_mode) for @fields;
1e73acc8 247 my @obs = map gen_ref( $_), @test_types;
248 my $n_obs = @obs;
249 for my $field ( @fields ) {
250 @{ $field }{ @obs} = map ref, @obs;
251 }
252 my $err = grep keys %$_ != @obs, @fields;
253 is( $err, 0, "$n_obs entries in $n_fields fields");
6ff38c27 254 is( keys %$ob_reg, @obs, "$n_obs obs registered");
1e73acc8 255 pop @obs;
256 $err = grep keys %$_ != @obs, @fields;
257 is( $err, 0, "one entry gone from $n_fields fields");
6ff38c27 258 is( keys %$ob_reg, @obs, "one ob unregistered");
1e73acc8 259 @obs = ();
260 $err = grep keys %$_ != @obs, @fields;
261 is( $err, 0, "all entries gone from $n_fields fields");
6ff38c27 262 is( keys %$ob_reg, @obs, "all obs unregistered");
1e73acc8 263}
264
d020b00b 265
266# direct hash assignment
267BEGIN { $n_tests += 4 }
268{
d74d639b 269 Hash::Util::FieldHash::_fieldhash( $_, $fieldhash_mode) for \ my( %f, %g, %h);
d020b00b 270 my $size = 6;
271 my @obs = map [], 1 .. $size;
272 @f{ @obs} = ( 1) x $size;
273 $g{ $_} = $f{ $_} for keys %f; # single assignment
274 %h = %f; # wholesale assignment
275 @obs = ();
276 is keys %$ob_reg, 0, "all keys collected";
277 is keys %f, 0, "orig garbage-collected";
e37bf7df 278 is keys %g, 0, "single-copy garbage-collected";
279 is keys %h, 0, "wholesale-copy garbage-collected";
d020b00b 280}
281
1e73acc8 282{
1e73acc8 283 BEGIN { $n_tests += 1 }
d74d639b 284 Hash::Util::FieldHash::_fieldhash \ my( %h), $fieldhash_mode;
1e73acc8 285 bless \ %h, 'abc'; # this bus-errors with a certain bug
286 ok( 1, "no bus error on bless")
287}
288
289BEGIN { plan tests => $n_tests }
290
291#######################################################################
292
d74d639b 293sub refaddr {
294 my $ref = shift;
295 hex +($ref =~ /\(0x([[:xdigit:]]+)\)$/)[ 0];
296}
297
1e73acc8 298use Symbol qw( gensym);
299
300BEGIN {
301 my %gen = (
d74d639b 302 SCALAR => sub { \ my $o },
1e73acc8 303 ARRAY => sub { [] },
304 HASH => sub { {} },
305 GLOB => sub { gensym },
306 CODE => sub { sub {} },
307 );
308
309 sub gen_ref { $gen{ shift()}->() }
310}