X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=t%2Fop%2Flocal.t;h=8bfea00b9a4006b5b7fe19f5d58dd0d63b5fe82b;hb=21fa6956243df9cb622bebfa0934ea7923519b4f;hp=d23b20002212e5aeb621c89546e3eb1f9d73b90e;hpb=c39e6ab0a7545b7ae425e21f045aa689291b268e;p=p5sagit%2Fp5-mst-13.2.git diff --git a/t/op/local.t b/t/op/local.t index d23b200..8bfea00 100755 --- a/t/op/local.t +++ b/t/op/local.t @@ -1,55 +1,84 @@ #!./perl -print "1..71\n"; +BEGIN { + chdir 't' if -d 't'; + @INC = qw(. ../lib); + require './test.pl'; +} +plan tests => 122; + +my $list_assignment_supported = 1; + +#mg.c says list assignment not supported on VMS, EPOC, and SYMBIAN. +$list_assignment_supported = 0 if ($^O eq 'VMS'); + sub foo { local($a, $b) = @_; local($c, $d); - $c = "ok 3\n"; - $d = "ok 4\n"; - { local($a,$c) = ("ok 9\n", "ok 10\n"); ($x, $y) = ($a, $c); } - print $a, $b; - $c . $d; + $c = "c 3"; + $d = "d 4"; + { local($a,$c) = ("a 9", "c 10"); ($x, $y) = ($a, $c); } + is($a, "a 1"); + is($b, "b 2"); + $c, $d; } -$a = "ok 5\n"; -$b = "ok 6\n"; -$c = "ok 7\n"; -$d = "ok 8\n"; +$a = "a 5"; +$b = "b 6"; +$c = "c 7"; +$d = "d 8"; -print &foo("ok 1\n","ok 2\n"); +my @res; +@res = &foo("a 1","b 2"); +is($res[0], "c 3"); +is($res[1], "d 4"); -print $a,$b,$c,$d,$x,$y; +is($a, "a 5"); +is($b, "b 6"); +is($c, "c 7"); +is($d, "d 8"); +is($x, "a 9"); +is($y, "c 10"); # same thing, only with arrays and associative arrays sub foo2 { local($a, @b) = @_; local(@c, %d); - @c = "ok 13\n"; - $d{''} = "ok 14\n"; - { local($a,@c) = ("ok 19\n", "ok 20\n"); ($x, $y) = ($a, @c); } - print $a, @b; - $c[0] . $d{''}; + @c = "c 3"; + $d{''} = "d 4"; + { local($a,@c) = ("a 19", "c 20"); ($x, $y) = ($a, @c); } + is($a, "a 1"); + is("@b", "b 2"); + $c[0], $d{''}; } -$a = "ok 15\n"; -@b = "ok 16\n"; -@c = "ok 17\n"; -$d{''} = "ok 18\n"; +$a = "a 5"; +@b = "b 6"; +@c = "c 7"; +$d{''} = "d 8"; -print &foo2("ok 11\n","ok 12\n"); +@res = &foo2("a 1","b 2"); +is($res[0], "c 3"); +is($res[1], "d 4"); + +is($a, "a 5"); +is("@b", "b 6"); +is($c[0], "c 7"); +is($d{''}, "d 8"); +is($x, "a 19"); +is($y, "c 20"); -print $a,@b,@c,%d,$x,$y; eval 'local($$e)'; -print +($@ =~ /Can't localize through a reference/) ? "" : "not ", "ok 21\n"; +like($@, qr/Can't localize through a reference/); -eval 'local(@$e)'; -print +($@ =~ /Can't localize through a reference/) ? "" : "not ", "ok 22\n"; +eval '$e = []; local(@$e)'; +like($@, qr/Can't localize through a reference/); -eval 'local(%$e)'; -print +($@ =~ /Can't localize through a reference/) ? "" : "not ", "ok 23\n"; +eval '$e = {}; local(%$e)'; +like($@, qr/Can't localize through a reference/); # Array and hash elements @@ -57,38 +86,48 @@ print +($@ =~ /Can't localize through a reference/) ? "" : "not ", "ok 23\n"; { local($a[1]) = 'foo'; local($a[2]) = $a[2]; - print +($a[1] eq 'foo') ? "" : "not ", "ok 24\n"; - print +($a[2] eq 'c') ? "" : "not ", "ok 25\n"; + is($a[1], 'foo'); + is($a[2], 'c'); undef @a; } -print +($a[1] eq 'b') ? "" : "not ", "ok 26\n"; -print +($a[2] eq 'c') ? "" : "not ", "ok 27\n"; -print +(!defined $a[0]) ? "" : "not ", "ok 28\n"; +is($a[1], 'b'); +is($a[2], 'c'); +ok(!defined $a[0]); @a = ('a', 'b', 'c'); { local($a[1]) = "X"; shift @a; } -print +($a[0].$a[1] eq "Xb") ? "" : "not ", "ok 29\n"; +is($a[0].$a[1], "Xb"); +{ + my $d = "@a"; + local @a = @a; + is("@a", $d); +} %h = ('a' => 1, 'b' => 2, 'c' => 3); { local($h{'a'}) = 'foo'; local($h{'b'}) = $h{'b'}; - print +($h{'a'} eq 'foo') ? "" : "not ", "ok 30\n"; - print +($h{'b'} == 2) ? "" : "not ", "ok 31\n"; + is($h{'a'}, 'foo'); + is($h{'b'}, 2); local($h{'c'}); delete $h{'c'}; } -print +($h{'a'} == 1) ? "" : "not ", "ok 32\n"; -print +($h{'b'} == 2) ? "" : "not ", "ok 33\n"; -print +($h{'c'} == 3) ? "" : "not ", "ok 34\n"; +is($h{'a'}, 1); +is($h{'b'}, 2); +{ + my $d = join("\n", map { "$_=>$h{$_}" } sort keys %h); + local %h = %h; + is(join("\n", map { "$_=>$h{$_}" } sort keys %h), $d); +} +is($h{'c'}, 3); # check for scope leakage $a = 'outer'; if (1) { local $a = 'inner' } -print +($a eq 'outer') ? "" : "not ", "ok 35\n"; +is($a, 'outer'); # see if localization works when scope unwinds local $m = 5; @@ -98,7 +137,7 @@ eval { die "bye"; } }; -print $m == 5 ? "" : "not ", "ok 36\n"; +is($m, 5); # see if localization works on tied arrays { @@ -117,13 +156,18 @@ tie @a, 'TA'; { local($a[1]) = 'foo'; local($a[2]) = $a[2]; - print +($a[1] eq 'foo') ? "" : "not ", "ok 37\n"; - print +($a[2] eq 'c') ? "" : "not ", "ok 38\n"; + is($a[1], 'foo'); + is($a[2], 'c'); @a = (); } -print +($a[1] eq 'b') ? "" : "not ", "ok 39\n"; -print +($a[2] eq 'c') ? "" : "not ", "ok 40\n"; -print +(!defined $a[0]) ? "" : "not ", "ok 41\n"; +is($a[1], 'b'); +is($a[2], 'c'); +ok(!defined $a[0]); +{ + my $d = "@a"; + local @a = @a; + is("@a", $d); +} { package TH; @@ -133,6 +177,8 @@ print +(!defined $a[0]) ? "" : "not ", "ok 41\n"; sub EXISTS { print "# EXISTS [@_]\n"; exists $_[0]->{$_[1]}; } sub DELETE { print "# DELETE [@_]\n"; delete $_[0]->{$_[1]}; } sub CLEAR { print "# CLEAR [@_]\n"; %{$_[0]} = (); } + sub FIRSTKEY { print "# FIRSTKEY [@_]\n"; keys %{$_[0]}; each %{$_[0]} } + sub NEXTKEY { print "# NEXTKEY [@_]\n"; each %{$_[0]} } } # see if localization works on tied hashes @@ -142,21 +188,32 @@ tie %h, 'TH'; { local($h{'a'}) = 'foo'; local($h{'b'}) = $h{'b'}; - print +($h{'a'} eq 'foo') ? "" : "not ", "ok 42\n"; - print +($h{'b'} == 2) ? "" : "not ", "ok 43\n"; + local($h{'y'}); + local($h{'z'}) = 33; + is($h{'a'}, 'foo'); + is($h{'b'}, 2); local($h{'c'}); delete $h{'c'}; } -print +($h{'a'} == 1) ? "" : "not ", "ok 44\n"; -print +($h{'b'} == 2) ? "" : "not ", "ok 45\n"; -print +($h{'c'} == 3) ? "" : "not ", "ok 46\n"; +is($h{'a'}, 1); +is($h{'b'}, 2); +is($h{'c'}, 3); +# local() should preserve the existenceness of tied hash elements +ok(! exists $h{'y'}); +ok(! exists $h{'z'}); +TODO: { + todo_skip("Localize entire tied hash"); + my $d = join("\n", map { "$_=>$h{$_}" } sort keys %h); + local %h = %h; + is(join("\n", map { "$_=>$h{$_}" } sort keys %h), $d); +} @a = ('a', 'b', 'c'); { local($a[1]) = "X"; shift @a; } -print +($a[0].$a[1] eq "Xb") ? "" : "not ", "ok 47\n"; +is($a[0].$a[1], "Xb"); # now try the same for %SIG @@ -167,15 +224,20 @@ $SIG{__WARN__} = $SIG{INT}; local($SIG{TERM}) = $SIG{TERM}; local($SIG{INT}) = $SIG{INT}; local($SIG{__WARN__}) = $SIG{__WARN__}; - print +($SIG{TERM} eq 'main::foo') ? "" : "not ", "ok 48\n"; - print +($SIG{INT} eq \&foo) ? "" : "not ", "ok 49\n"; - print +($SIG{__WARN__} eq \&foo) ? "" : "not ", "ok 50\n"; + is($SIG{TERM}, 'main::foo'); + is($SIG{INT}, \&foo); + is($SIG{__WARN__}, \&foo); local($SIG{INT}); delete $SIG{__WARN__}; } -print +($SIG{TERM} eq 'main::foo') ? "" : "not ", "ok 51\n"; -print +($SIG{INT} eq \&foo) ? "" : "not ", "ok 52\n"; -print +($SIG{__WARN__} eq \&foo) ? "" : "not ", "ok 53\n"; +is($SIG{TERM}, 'main::foo'); +is($SIG{INT}, \&foo); +is($SIG{__WARN__}, \&foo); +{ + my $d = join("\n", map { "$_=>$SIG{$_}" } sort keys %SIG); + local %SIG = %SIG; + is(join("\n", map { "$_=>$SIG{$_}" } sort keys %SIG), $d); +} # and for %ENV @@ -183,25 +245,38 @@ $ENV{_X_} = 'a'; $ENV{_Y_} = 'b'; $ENV{_Z_} = 'c'; { + local($ENV{_A_}); + local($ENV{_B_}) = 'foo'; local($ENV{_X_}) = 'foo'; local($ENV{_Y_}) = $ENV{_Y_}; - print +($ENV{_X_} eq 'foo') ? "" : "not ", "ok 54\n"; - print +($ENV{_Y_} eq 'b') ? "" : "not ", "ok 55\n"; + is($ENV{_X_}, 'foo'); + is($ENV{_Y_}, 'b'); local($ENV{_Z_}); delete $ENV{_Z_}; } -print +($ENV{_X_} eq 'a') ? "" : "not ", "ok 56\n"; -print +($ENV{_Y_} eq 'b') ? "" : "not ", "ok 57\n"; -print +($ENV{_Z_} eq 'c') ? "" : "not ", "ok 58\n"; +is($ENV{_X_}, 'a'); +is($ENV{_Y_}, 'b'); +is($ENV{_Z_}, 'c'); +# local() should preserve the existenceness of %ENV elements +ok(! exists $ENV{_A_}); +ok(! exists $ENV{_B_}); + +SKIP: { + skip("Can't make list assignment to \%ENV on this system") + unless $list_assignment_supported; + my $d = join("\n", map { "$_=>$ENV{$_}" } sort keys %ENV); + local %ENV = %ENV; + is(join("\n", map { "$_=>$ENV{$_}" } sort keys %ENV), $d); +} # does implicit localization in foreach skip magic? -$_ = "ok 59,ok 60,"; +$_ = "o 0,o 1,"; my $iter = 0; while (/(o.+?),/gc) { - print "$1\n"; + is($1, "o $iter"); foreach (1..1) { $iter++ } - if ($iter > 2) { print "not ok 60\n"; last; } + if ($iter > 2) { fail("endless loop"); last; } } { @@ -210,7 +285,6 @@ while (/(o.+?),/gc) { sub FETCH { die "read \$_ forbidden" } sub STORE { die "write \$_ forbidden" } tie $_, __PACKAGE__; - my $t = 61; my @tests = ( "Nesting" => sub { print '#'; for (1..3) { print } print "\n" }, 1, @@ -225,10 +299,8 @@ while (/(o.+?),/gc) { "for local" => sub { for("#ok?\n"){ print } }, 1, ); while ( ($name, $code, $ok) = splice(@tests, 0, 3) ) { - print "# Testing $name\n"; eval { &$code }; - print(($ok xor $@) ? "ok $t\n" : "not ok $t\n"); - ++$t; + main::ok(($ok xor $@), "Underscore '$name'"); } untie $_; } @@ -238,9 +310,155 @@ while (/(o.+?),/gc) { my %x; $x{a} = 1; { local $x{b} = 1; } - print "not " if exists $x{b}; - print "ok 70\n"; + ok(! exists $x{b}); { local @x{c,d,e}; } - print "not " if exists $x{c}; - print "ok 71\n"; + ok(! exists $x{c}); +} + +# local() and readonly magic variables + +eval { local $1 = 1 }; +like($@, qr/Modification of a read-only value attempted/); + +eval { for ($1) { local $_ = 1 } }; +like($@, qr/Modification of a read-only value attempted/); + +# make sure $1 is still read-only +eval { for ($1) { local $_ = 1 } }; +like($@, qr/Modification of a read-only value attempted/); + +# The s/// adds 'g' magic to $_, but it should remain non-readonly +eval { for("a") { for $x (1,2) { local $_="b"; s/(.*)/+$1/ } } }; +is($@, ""); + +# Special local() behavior for $[ +# (see RT #38207 - Useless localization of constant ($[) in getopts.pl} +{ + local $[ = 1; + local $TODO = "local() not currently working correctly with \$["; + ok(1 == $[); + undef $TODO; + f(); +} + +sub f { ok(0 == $[); } + +# sub localisation +{ + package Other; + + sub f1 { "f1" } + sub f2 { "f2" } + + no warnings "redefine"; + { + local *f1 = sub { "g1" }; + ::ok(f1() eq "g1", "localised sub via glob"); + } + ::ok(f1() eq "f1", "localised sub restored"); + { + local $Other::{"f1"} = sub { "h1" }; + ::ok(f1() eq "h1", "localised sub via stash"); + } + ::ok(f1() eq "f1", "localised sub restored"); + { + local @Other::{qw/ f1 f2 /} = (sub { "j1" }, sub { "j2" }); + ::ok(f1() eq "j1", "localised sub via stash slice"); + ::ok(f2() eq "j2", "localised sub via stash slice"); + } + ::ok(f1() eq "f1", "localised sub restored"); + ::ok(f2() eq "f2", "localised sub restored"); +} + +# Localising unicode keys (bug #38815) +{ + my %h; + $h{"\243"} = "pound"; + $h{"\302\240"} = "octects"; + is(scalar keys %h, 2); + { + my $unicode = chr 256; + my $ambigous = "\240" . $unicode; + chop $ambigous; + local $h{$unicode} = 256; + local $h{$ambigous} = 160; + + is(scalar keys %h, 4); + is($h{"\243"}, "pound"); + is($h{$unicode}, 256); + is($h{$ambigous}, 160); + is($h{"\302\240"}, "octects"); + } + is(scalar keys %h, 2); + is($h{"\243"}, "pound"); + is($h{"\302\240"}, "octects"); +} + +# And with slices +{ + my %h; + $h{"\243"} = "pound"; + $h{"\302\240"} = "octects"; + is(scalar keys %h, 2); + { + my $unicode = chr 256; + my $ambigous = "\240" . $unicode; + chop $ambigous; + local @h{$unicode, $ambigous} = (256, 160); + + is(scalar keys %h, 4); + is($h{"\243"}, "pound"); + is($h{$unicode}, 256); + is($h{$ambigous}, 160); + is($h{"\302\240"}, "octects"); + } + is(scalar keys %h, 2); + is($h{"\243"}, "pound"); + is($h{"\302\240"}, "octects"); +} + +# [perl #39012] localizing @_ element then shifting frees element too # soon + +{ + my $x; + my $y = bless [], 'X39012'; + sub X39012::DESTROY { $x++ } + sub { local $_[0]; shift }->($y); + ok(!$x, '[perl #39012]'); + } + +# when localising a hash element, the key should be copied, not referenced + +{ + my %h=('k1' => 111); + my $k='k1'; + { + local $h{$k}=222; + + is($h{'k1'},222); + $k='k2'; + } + ok(! exists($h{'k2'})); + is($h{'k1'},111); +} +{ + my %h=('k1' => 111); + our $k = 'k1'; # try dynamic too + { + local $h{$k}=222; + is($h{'k1'},222); + $k='k2'; + } + ok(! exists($h{'k2'})); + is($h{'k1'},111); +} + +# Keep this test last, as it can SEGV +{ + local *@; + pass("Localised *@"); + eval {1}; + pass("Can eval with *@ localised"); +} +