1*256a93a4Safresh1#!./perl 2*256a93a4Safresh1 3*256a93a4Safresh1BEGIN { 4*256a93a4Safresh1 chdir 't' if -d 't'; 5*256a93a4Safresh1 require './test.pl'; 6*256a93a4Safresh1 set_up_inc('../lib'); 7*256a93a4Safresh1} 8*256a93a4Safresh1 9*256a93a4Safresh1# This is purposefully simple - hence the O(n) linear searches. 10*256a93a4Safresh1package TestIterators { 11*256a93a4Safresh1 sub TIEHASH { 12*256a93a4Safresh1 bless [], $_[0]; 13*256a93a4Safresh1 } 14*256a93a4Safresh1 15*256a93a4Safresh1 sub STORE { 16*256a93a4Safresh1 my ($self, $key, $value) = @_; 17*256a93a4Safresh1 push @{$self->[0]}, $key; 18*256a93a4Safresh1 push @{$self->[1]}, $value; 19*256a93a4Safresh1 return $value; 20*256a93a4Safresh1 } 21*256a93a4Safresh1 22*256a93a4Safresh1 sub FETCH { 23*256a93a4Safresh1 my ($self, $key) = @_; 24*256a93a4Safresh1 my $i = 0; 25*256a93a4Safresh1 while ($i < @{$self->[0]}) { 26*256a93a4Safresh1 return $self->[1][$i] 27*256a93a4Safresh1 if $self->[0][$i] eq $key; 28*256a93a4Safresh1 ++$i; 29*256a93a4Safresh1 } 30*256a93a4Safresh1 die "$key not found in FETCH"; 31*256a93a4Safresh1 } 32*256a93a4Safresh1 33*256a93a4Safresh1 sub FIRSTKEY { 34*256a93a4Safresh1 my $self = shift; 35*256a93a4Safresh1 $self->[0][0]; 36*256a93a4Safresh1 } 37*256a93a4Safresh1 38*256a93a4Safresh1 # As best I can tell, none of our other tie tests actually use the first 39*256a93a4Safresh1 # parameter to nextkey. It's actually (a copy of) the previously returned 40*256a93a4Safresh1 # key. We're not *so* thorough here as to actually hide some state and 41*256a93a4Safresh1 # cross-check that, but the longhand tests below should effectively validate 42*256a93a4Safresh1 # it. 43*256a93a4Safresh1 sub NEXTKEY { 44*256a93a4Safresh1 my ($self, $key) = @_; 45*256a93a4Safresh1 my $i = 0; 46*256a93a4Safresh1 while ($i < @{$self->[0]}) { 47*256a93a4Safresh1 return $self->[0][$i + 1] 48*256a93a4Safresh1 if $self->[0][$i] eq $key; 49*256a93a4Safresh1 ++$i; 50*256a93a4Safresh1 } 51*256a93a4Safresh1 die "$key not found in NEXTKEY"; 52*256a93a4Safresh1 } 53*256a93a4Safresh1}; 54*256a93a4Safresh1 55*256a93a4Safresh1{ 56*256a93a4Safresh1 my %h; 57*256a93a4Safresh1 tie %h, 'TestIterators'; 58*256a93a4Safresh1 59*256a93a4Safresh1 $h{beer} = "foamy"; 60*256a93a4Safresh1 $h{perl} = "rules"; 61*256a93a4Safresh1 62*256a93a4Safresh1 is($h{beer}, "foamy", "found first key"); 63*256a93a4Safresh1 is($h{perl}, "rules", "found second key"); 64*256a93a4Safresh1 is(eval { 65*256a93a4Safresh1 my $k = $h{decaf}; 66*256a93a4Safresh1 1; 67*256a93a4Safresh1 }, undef, "missing key was not found"); 68*256a93a4Safresh1 like($@, qr/\Adecaf not found in FETCH/, "with the correct error"); 69*256a93a4Safresh1 70*256a93a4Safresh1 is(each %h, 'beer', "first iterator"); 71*256a93a4Safresh1 is(each %h, 'perl', "second iterator"); 72*256a93a4Safresh1 is(each %h, undef, "third iterator is undef"); 73*256a93a4Safresh1} 74*256a93a4Safresh1 75*256a93a4Safresh1{ 76*256a93a4Safresh1 require Tie::Hash; 77*256a93a4Safresh1 78*256a93a4Safresh1 my %h = ( 79*256a93a4Safresh1 lolcat => "OH HAI!", 80*256a93a4Safresh1 lolrus => "I HAS A BUCKET", 81*256a93a4Safresh1 ); 82*256a93a4Safresh1 83*256a93a4Safresh1 my @want = sort keys %h; 84*256a93a4Safresh1 85*256a93a4Safresh1 my @have; 86*256a93a4Safresh1 while (1) { 87*256a93a4Safresh1 my $k = each %h; 88*256a93a4Safresh1 last 89*256a93a4Safresh1 unless defined $k; 90*256a93a4Safresh1 push @have, $k; 91*256a93a4Safresh1 } 92*256a93a4Safresh1 @have = sort @have; 93*256a93a4Safresh1 94*256a93a4Safresh1 # This is a sanity test: 95*256a93a4Safresh1 is("@have", "@want", "get all keys from a loop"); 96*256a93a4Safresh1 97*256a93a4Safresh1 @have = (); 98*256a93a4Safresh1 keys %h; 99*256a93a4Safresh1 100*256a93a4Safresh1 my $k1 = each %h; 101*256a93a4Safresh1 102*256a93a4Safresh1 ok(defined $k1, "Got a key"); 103*256a93a4Safresh1 104*256a93a4Safresh1 # no tie/untie here 105*256a93a4Safresh1 106*256a93a4Safresh1 while(1) { 107*256a93a4Safresh1 my $k = each %h; 108*256a93a4Safresh1 last 109*256a93a4Safresh1 unless defined $k; 110*256a93a4Safresh1 push @have, $k; 111*256a93a4Safresh1 } 112*256a93a4Safresh1 113*256a93a4Safresh1 # As are these: 114*256a93a4Safresh1 is(scalar @have, 1, "just 1 key from the loop this time"); 115*256a93a4Safresh1 isnt($k1, $have[0], "two different keys"); 116*256a93a4Safresh1 117*256a93a4Safresh1 @have = sort @have, $k1; 118*256a93a4Safresh1 is("@have", "@want", "get all keys just once"); 119*256a93a4Safresh1 120*256a93a4Safresh1 # And this is the real test. 121*256a93a4Safresh1 # 122*256a93a4Safresh1 # Previously pp_tie would mangle the hash iterator state - it would reset 123*256a93a4Safresh1 # EITER but not RITER, meaning that if the iterator happened to be partway 124*256a93a4Safresh1 # down a chain of entries, the rest of that chain would be skipped, but if 125*256a93a4Safresh1 # the iterator's next position was the start of a (new) chain, nothing would 126*256a93a4Safresh1 # be skipped. 127*256a93a4Safresh1 # We don't have space to store the complete older iterator state (and really 128*256a93a4Safresh1 # nothing should be relying on it), so it seems better to correctly reset 129*256a93a4Safresh1 # the iterator (every time), than leave it in a mess just occasionally. 130*256a93a4Safresh1 131*256a93a4Safresh1 @have = (); 132*256a93a4Safresh1 keys %h; 133*256a93a4Safresh1 134*256a93a4Safresh1 my $k1 = each %h; 135*256a93a4Safresh1 136*256a93a4Safresh1 ok(defined $k1, "Got a key"); 137*256a93a4Safresh1 138*256a93a4Safresh1 tie %h, 'Tie::StdHash'; 139*256a93a4Safresh1 untie %h; 140*256a93a4Safresh1 141*256a93a4Safresh1 while(1) { 142*256a93a4Safresh1 my $k = each %h; 143*256a93a4Safresh1 last 144*256a93a4Safresh1 unless defined $k; 145*256a93a4Safresh1 push @have, $k; 146*256a93a4Safresh1 } 147*256a93a4Safresh1 148*256a93a4Safresh1 @have = sort @have; 149*256a93a4Safresh1 is(scalar @have, 2, "2 keys from the loop this time"); 150*256a93a4Safresh1 is("@have", "@want", "tie/untie resets the hash iterator"); 151*256a93a4Safresh1} 152*256a93a4Safresh1 153*256a93a4Safresh1{ 154*256a93a4Safresh1 require Tie::Hash; 155*256a93a4Safresh1 my $count; 156*256a93a4Safresh1 157*256a93a4Safresh1 package Tie::Count { 158*256a93a4Safresh1 use parent -norequire, 'Tie::StdHash'; 159*256a93a4Safresh1 sub FETCH { 160*256a93a4Safresh1 ++$count; 161*256a93a4Safresh1 return $_[0]->SUPER::FETCH($_[1]); 162*256a93a4Safresh1 } 163*256a93a4Safresh1 } 164*256a93a4Safresh1 165*256a93a4Safresh1 $count = 0; 166*256a93a4Safresh1 my %tied; 167*256a93a4Safresh1 tie %tied, "Tie::Count"; 168*256a93a4Safresh1 %tied = qw(perl rules beer foamy); 169*256a93a4Safresh1 my @a = %tied; 170*256a93a4Safresh1 if ($a[0] eq 'beer') { 171*256a93a4Safresh1 is("@a", "beer foamy perl rules", "tied hash in list context"); 172*256a93a4Safresh1 } else { 173*256a93a4Safresh1 is("@a", "perl rules beer foamy", "tied hash in list context"); 174*256a93a4Safresh1 } 175*256a93a4Safresh1 is($count, 2, "two FETCHes for tied hash in list context"); 176*256a93a4Safresh1 177*256a93a4Safresh1 $count = 0; 178*256a93a4Safresh1 179*256a93a4Safresh1 @a = keys %tied; 180*256a93a4Safresh1 @a = sort @a; 181*256a93a4Safresh1 is("@a", "beer perl", "tied hash keys in list context"); 182*256a93a4Safresh1 is($count, 0, "no FETCHes for tied hash keys in list context"); 183*256a93a4Safresh1 184*256a93a4Safresh1 $count = 0; 185*256a93a4Safresh1 @a = values %tied; 186*256a93a4Safresh1 @a = sort @a; 187*256a93a4Safresh1 188*256a93a4Safresh1 is("@a", "foamy rules", "tied hash values in list context"); 189*256a93a4Safresh1 is($count, 2, "two FETCHes for tied hash values in list context"); 190*256a93a4Safresh1} 191*256a93a4Safresh1 192*256a93a4Safresh1{ 193*256a93a4Safresh1 # tie/untie on a hash resets the iterator 194*256a93a4Safresh1 195*256a93a4Safresh1 # This is not intended as a test of *correctness*. This behaviour is 196*256a93a4Safresh1 # observable by code on CPAN, so potentially some of it will inadvertently 197*256a93a4Safresh1 # be relying on it (and likely not in any regression test). Hence this 198*256a93a4Safresh1 # "test" here is intended as a way to alert us if any core code change has 199*256a93a4Safresh1 # the side effect of alerting this observable behaviour. 200*256a93a4Safresh1 201*256a93a4Safresh1 my @keys = qw(bactrianus dromedarius ferus); 202*256a93a4Safresh1 my %Camelus; 203*256a93a4Safresh1 ++$Camelus{$_} 204*256a93a4Safresh1 for @keys; 205*256a93a4Safresh1 206*256a93a4Safresh1 my @got; 207*256a93a4Safresh1 push @got, scalar each %Camelus; 208*256a93a4Safresh1 push @got, scalar each %Camelus; 209*256a93a4Safresh1 push @got, scalar each %Camelus; 210*256a93a4Safresh1 is(scalar each %Camelus, undef, 'Fourth each returned undef'); 211*256a93a4Safresh1 is(join(' ', sort @got), "@keys", 'The correct three keys'); 212*256a93a4Safresh1 213*256a93a4Safresh1 @got = (); 214*256a93a4Safresh1 keys %Camelus; 215*256a93a4Safresh1 216*256a93a4Safresh1 push @got, scalar each %Camelus; 217*256a93a4Safresh1 218*256a93a4Safresh1 # This resets the hash iterator: 219*256a93a4Safresh1 tie %Camelus, 'Tie::StdHash'; 220*256a93a4Safresh1 my @all = keys %Camelus; 221*256a93a4Safresh1 is(scalar @all, 0, 'Zero keys when tied'); 222*256a93a4Safresh1 untie %Camelus; 223*256a93a4Safresh1 224*256a93a4Safresh1 push @got, scalar each %Camelus; 225*256a93a4Safresh1 push @got, scalar each %Camelus; 226*256a93a4Safresh1 my $fourth = scalar each %Camelus; 227*256a93a4Safresh1 isnt($fourth, undef, 'Fourth each did not return undef'); 228*256a93a4Safresh1 push @got, $fourth; 229*256a93a4Safresh1 is(scalar each %Camelus, undef, 'Fifth each returned undef'); 230*256a93a4Safresh1 my %have; 231*256a93a4Safresh1 @have{@got} = (); 232*256a93a4Safresh1 is(join(' ', sort keys %have), "@keys", 'Still the correct three keys'); 233*256a93a4Safresh1} 234*256a93a4Safresh1done_testing(); 235