Include variable names in "Use of uninitialized value" warnings
[p5sagit/p5-mst-13.2.git] / lib / Tie / RefHash.t
CommitLineData
778e8f97 1#!/usr/bin/perl -w
2#
3# Basic test suite for Tie::RefHash and Tie::RefHash::Nestable.
4#
5# The testing is in two parts: first, run lots of tests on both a tied
6# hash and an ordinary un-tied hash, and check they give the same
7# answer. Then there are tests for those cases where the tied hashes
8# should behave differently to normal hashes, that is, when using
9# references as keys.
10#
11
12BEGIN {
13 chdir 't' if -d 't';
14 @INC = '.';
15 push @INC, '../lib';
16}
17
18use strict;
19use Tie::RefHash;
20use Data::Dumper;
05d3035d 21my $numtests = 37;
778e8f97 22my $currtest = 1;
23print "1..$numtests\n";
24
25my $ref = []; my $ref1 = [];
26
05d3035d 27package Boustrophedon; # A class with overloaded "".
28sub new { my ($c, $s) = @_; bless \$s, $c }
29use overload '""' => sub { ${$_[0]} . reverse ${$_[0]} };
30package main;
31my $ox = Boustrophedon->new("foobar");
32
778e8f97 33# Test standard hash functionality, by performing the same operations
34# on a tied hash and on a normal hash, and checking that the results
35# are the same. This does of course assume that Perl hashes are not
36# buggy :-)
37#
38my @tests = standard_hash_tests();
24026386 39
778e8f97 40my @ordinary_results = runtests(\@tests, undef);
41foreach my $class ('Tie::RefHash', 'Tie::RefHash::Nestable') {
42 my @tied_results = runtests(\@tests, $class);
43 my $all_ok = 1;
44
45 die if @ordinary_results != @tied_results;
46 foreach my $i (0 .. $#ordinary_results) {
47 my ($or, $ow, $oe) = @{$ordinary_results[$i]};
48 my ($tr, $tw, $te) = @{$tied_results[$i]};
49
50 my $ok = 1;
51 local $^W = 0;
52 $ok = 0 if (defined($or) != defined($tr)) or ($or ne $tr);
53 $ok = 0 if (defined($ow) != defined($tw)) or ($ow ne $tw);
54 $ok = 0 if (defined($oe) != defined($te)) or ($oe ne $te);
55
56 if (not $ok) {
57 print STDERR
58 "failed for $class: $tests[$i]\n",
59 "ordinary hash gave:\n",
60 defined $or ? "\tresult: $or\n" : "\tundef result\n",
61 defined $ow ? "\twarning: $ow\n" : "\tno warning\n",
62 defined $oe ? "\texception: $oe\n" : "\tno exception\n",
63 "tied $class hash gave:\n",
64 defined $tr ? "\tresult: $tr\n" : "\tundef result\n",
65 defined $tw ? "\twarning: $tw\n" : "\tno warning\n",
66 defined $te ? "\texception: $te\n" : "\tno exception\n",
67 "\n";
68 $all_ok = 0;
69 }
70 }
71 test($all_ok);
72}
73
74# Now test Tie::RefHash's special powers
75my (%h, $h);
24026386 76$h = eval { tie %h, 'Tie::RefHash' };
778e8f97 77warn $@ if $@;
78test(not $@);
79test(ref($h) eq 'Tie::RefHash');
80test(defined(tied(%h)) and tied(%h) =~ /^Tie::RefHash/);
81$h{$ref} = 'cholet';
82test($h{$ref} eq 'cholet');
83test(exists $h{$ref});
84test((keys %h) == 1);
85test(ref((keys %h)[0]) eq 'ARRAY');
86test((keys %h)[0] eq $ref);
87test((values %h) == 1);
88test((values %h)[0] eq 'cholet');
89my $count = 0;
90while (my ($k, $v) = each %h) {
91 if ($count++ == 0) {
92 test(ref($k) eq 'ARRAY');
93 test($k eq $ref);
94 }
95}
96test($count == 1);
97delete $h{$ref};
98test(not defined $h{$ref});
99test(not exists($h{$ref}));
100test((keys %h) == 0);
101test((values %h) == 0);
05d3035d 102$h{$ox} = "bellow"; # overloaded ""
103test(exists $h{$ox});
104test($h{$ox} eq "bellow");
105test(not exists $h{"foobarraboof"});
778e8f97 106undef $h;
107untie %h;
108
109# And now Tie::RefHash::Nestable's differences from Tie::RefHash.
24026386 110$h = eval { tie %h, 'Tie::RefHash::Nestable' };
778e8f97 111warn $@ if $@;
112test(not $@);
113test(ref($h) eq 'Tie::RefHash::Nestable');
114test(defined(tied(%h)) and tied(%h) =~ /^Tie::RefHash::Nestable/);
115$h{$ref}->{$ref1} = 'bungo';
116test($h{$ref}->{$ref1} eq 'bungo');
117
118# Test that the nested hash is also tied (for current implementation)
119test(defined(tied(%{$h{$ref}}))
120 and tied(%{$h{$ref}}) =~ /^Tie::RefHash::Nestable=/ );
121
122test((keys %h) == 1);
123test((keys %h)[0] eq $ref);
124test((keys %{$h{$ref}}) == 1);
125test((keys %{$h{$ref}})[0] eq $ref1);
126
24026386 127
778e8f97 128die "expected to run $numtests tests, but ran ", $currtest - 1
129 if $currtest - 1 != $numtests;
24026386 130
131@tests = ();
132undef $ref;
133undef $ref1;
134
778e8f97 135exit();
136
137
138# Print 'ok X' if true, 'not ok X' if false
139# Uses global $currtest.
140#
141sub test {
142 my $t = shift;
143 print 'not ' if not $t;
144 print 'ok ', $currtest++, "\n";
145}
146
147
148# Wrapper for Data::Dumper to 'dump' a scalar as an EXPR string.
149sub dumped {
150 my $s = shift;
151 my $d = Dumper($s);
152 $d =~ s/^\$VAR1 =\s*//;
153 $d =~ s/;$//;
154 chomp $d;
155 return $d;
156}
157
158# Crudely dump a hash into a canonical string representation (because
159# hash keys can appear in any order, Data::Dumper may give different
160# strings for the same hash).
161#
162sub dumph {
163 my $h = shift;
164 my $r = '';
165 foreach (sort keys %$h) {
166 $r = dumped($_) . ' => ' . dumped($h->{$_}) . "\n";
167 }
168 return $r;
169}
170
171# Run the tests and give results.
172#
173# Parameters: reference to list of tests to run
174# name of class to use for tied hash, or undef if not tied
175#
176# Returns: list of [R, W, E] tuples, one for each test.
177# R is the return value from running the test, W any warnings it gave,
178# and E any exception raised with 'die'. E and W will be tidied up a
179# little to remove irrelevant details like line numbers :-)
180#
181# Will also run a few of its own 'ok N' tests.
182#
183sub runtests {
184 my ($tests, $class) = @_;
185 my @r;
186
187 my (%h, $h);
188 if (defined $class) {
24026386 189 $h = eval { tie %h, $class };
778e8f97 190 warn $@ if $@;
191 test(not $@);
192 test(ref($h) eq $class);
193 test(defined(tied(%h)) and tied(%h) =~ /^\Q$class\E/);
194 }
195
196 foreach (@$tests) {
197 my ($result, $warning, $exception);
198 local $SIG{__WARN__} = sub { $warning .= $_[0] };
199 $result = scalar(eval $_);
24026386 200 if ($@)
201 {
202 die "$@:$_" unless defined $class;
203 $exception = $@;
204 }
778e8f97 205
206 foreach ($warning, $exception) {
207 next if not defined;
208 s/ at .+ line \d+\.$//mg;
209 s/ at .+ line \d+, at .*//mg;
210 s/ at .+ line \d+, near .*//mg;
29489e7c 211 s/(uninitialized value)( within)? [\$@%].*? in /$1 in /g;
778e8f97 212 }
213
214 my (@warnings, %seen);
215 foreach (split /\n/, $warning) {
216 push @warnings, $_ unless $seen{$_}++;
217 }
218 $warning = join("\n", @warnings);
219
220 push @r, [ $result, $warning, $exception ];
221 }
222
223 return @r;
224}
225
226
227# Things that should work just the same for an ordinary hash and a
228# Tie::RefHash.
229#
230# Each test is a code string to be eval'd, it should do something with
231# %h and give a scalar return value. The global $ref and $ref1 may
232# also be used.
233#
234# One thing we don't test is that the ordering from 'keys', 'values'
235# and 'each' is the same. You can't reasonably expect that.
236#
237sub standard_hash_tests {
238 my @r;
239
240 # Library of standard tests on keys, values and each
241 my $STD_TESTS = <<'END'
242 join $;, sort keys %h;
243 join $;, sort values %h;
24026386 244 { my ($v, %tmp); $tmp{$v}++ while (defined($v = each %h)); dumph(\%tmp) }
245 { my ($k, $v, %tmp); $tmp{"$k$;$v"}++ while (($k, $v) = each %h); dumph(\%tmp) }
778e8f97 246END
247 ;
248
249 # Tests on the existence of the element 'foo'
250 my $FOO_TESTS = <<'END'
251 defined $h{foo};
252 exists $h{foo};
253 $h{foo};
254END
255 ;
256
257 # Test storing and deleting 'foo'
258 push @r, split /\n/, <<"END"
259 $STD_TESTS;
260 $FOO_TESTS;
261 \$h{foo} = undef;
262 $STD_TESTS;
263 $FOO_TESTS;
264 \$h{foo} = 'hello';
265 $STD_TESTS;
266 $FOO_TESTS;
267 delete \$h{foo};
268 $STD_TESTS;
269 $FOO_TESTS;
270END
271 ;
272
273 # Test storing and removing under ordinary keys
274 my @things = ('boink', 0, 1, '', undef);
275 foreach my $key (map { dumped($_) } @things) {
276 foreach my $value ((map { dumped($_) } @things), '$ref') {
277 push @r, split /\n/, <<"END"
278 \$h{$key} = $value;
279 $STD_TESTS;
280 defined \$h{$key};
281 exists \$h{$key};
282 \$h{$key};
283 delete \$h{$key};
284 $STD_TESTS;
285 defined \$h{$key};
286 exists \$h{$key};
287 \$h{$key};
288END
289 ;
290 }
291 }
292
293 # Test hash slices
294 my @slicetests;
295 @slicetests = split /\n/, <<'END'
778e8f97 296 @h{'b'} = ();
297 @h{'c'} = ('d');
298 @h{'e'} = ('f', 'g');
299 @h{'h', 'i'} = ();
300 @h{'j', 'k'} = ('l');
301 @h{'m', 'n'} = ('o', 'p');
302 @h{'q', 'r'} = ('s', 't', 'u');
303END
304 ;
305 my @aaa = @slicetests;
306 foreach (@slicetests) {
307 push @r, $_;
308 push @r, split(/\n/, $STD_TESTS);
309 }
310
311 # Test CLEAR
24026386 312 push @r, '%h = ();', split(/\n/, $STD_TESTS);
778e8f97 313
314 return @r;
315}
316