diff options
author | Father Chrysostomos <sprout@cpan.org> | 2012-07-05 23:28:43 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2012-09-15 22:44:59 -0700 |
commit | 214522520758f1a3f2cb4a43383c4d90400c9604 (patch) | |
tree | 9226ef350ca3cf74a61133cf14ac3bf4f8c276b1 /t | |
parent | 73f3e22850d9df38d36bbb15f00ded7dcce63f27 (diff) | |
download | perl-214522520758f1a3f2cb4a43383c4d90400c9604.tar.gz |
Test state subs
Most of these tests are still to-do. The previous commit got every-
thing compiling at least. Then I went through putting eval{} around
all the dying tests and marking the failing tests as to-do.
At least this way I don’t have to do everything at once (even though
that was how I wrote the tests).
About the only thing that works is constant inlining, of all things.
Diffstat (limited to 't')
-rw-r--r-- | t/cmd/lexsub.t | 189 |
1 files changed, 188 insertions, 1 deletions
diff --git a/t/cmd/lexsub.t b/t/cmd/lexsub.t index 328b410464..be9f5637f2 100644 --- a/t/cmd/lexsub.t +++ b/t/cmd/lexsub.t @@ -5,9 +5,12 @@ BEGIN { @INC = '../lib'; require './test.pl'; *bar::is = *is; + *bar::like = *like; } no warnings 'deprecated'; -plan 22; +plan 58; + +# -------------------- our -------------------- # { our sub foo { 42 } @@ -78,3 +81,187 @@ sub bar::c { 43 } my $y = if if if; is $y, 42, 'our subs from other packages override all keywords'; } + +# -------------------- state -------------------- # + +sub on { $::TODO = ' ' } +sub off { $::TODO = undef } + +use 5.01; # state +{ + state sub foo { 44 } + isnt \&::foo, eval {\&foo}, 'state sub is not stored in the package'; +on; + is eval{foo}, 44, 'calling state sub from same package'; + is eval{&foo}, 44, 'calling state sub from same package (amper)'; + is eval{do foo()}, 44, 'calling state sub from same package (do)'; + package bar; + is eval{foo}, 44, 'calling state sub from another package'; + is eval{&foo}, 44, 'calling state sub from another package (amper)'; + is eval{do foo()}, 44, 'calling state sub from another package (do)'; +} +off; +package bar; +is foo, 43, 'state sub falling out of scope'; +is &foo, 43, 'state sub falling out of scope (called via amper)'; +is do foo(), 43, 'state sub falling out of scope (called via amper)'; +{ + sub sa { 43 } + state sub sa { + if (shift) { + is sa, 43, 'state sub invisible inside itself'; + is &sa, 43, 'state sub invisible inside itself (called via amper)'; + is do sa(), 43, 'state sub invisible inside itself (called via do)'; + } + 44 + } +SKIP: { ::skip "Tests are inside a state sub (still uncallable)", 3; + sa(1); +} + sub sb { 43 } + state sub sb; + state sub sb { + if (shift) { + # ‘state sub foo{}’ creates a new pad entry, not reusing the forward + # declaration. Being invisible inside itself, it sees the stub. + eval{sb}; + like $@, qr/^Undefined subroutine &sb called at /, + 'state sub foo {} after forward declaration'; + eval{&sb}; + like $@, qr/^Undefined subroutine &sb called at /, + 'state sub foo {} after forward declaration (amper)'; + eval{do sb()}; + like $@, qr/^Undefined subroutine &sb called at /, + 'state sub foo {} after forward declaration (do)'; + } + 44 + } +SKIP: { ::skip "Tests are inside a state sub (still uncallable)", 3; + sb(1); +} + sub sb2 { 43 } + state sub sb2; + sub sb2 { + if (shift) { + package bar; + is b, 44, 'state sub visible inside itself after decl'; + is &b, 44, 'state sub visible inside itself after decl (amper)'; + is do b(), 44, 'state sub visible inside itself after decl (do)'; + } + 44 + } +SKIP: { ::skip "Tests are inside a state sub (still uncallable)", 3; + sb2(1); +} + state sub sb3; + { + state sub sb3 { # new pad entry + # The sub containing this comment is invisible inside itself. + # So this one here will assign to the outer pad entry: + sub sb3 { 47 } + } + } +::on; + is eval{sb3}, 47, + 'sub foo{} applying to "state sub foo;" even inside state sub foo{}'; +} +sub sc { 43 } +{ + state sub sc; + eval{sc}; + like $@, qr/^Undefined subroutine &sb called at /, + 'state sub foo; makes no lex alias for existing sub'; + eval{&sc}; + like $@, qr/^Undefined subroutine &sb called at /, + 'state sub foo; makes no lex alias for existing sub (amper)'; + eval{do sc()}; + like $@, qr/^Undefined subroutine &sb called at /, + 'state sub foo; makes no lex alias for existing sub (do)'; +} +package main; +{ + state sub se ($); + is prototype eval{\&se}, '$', 'state sub with proto'; +off; + is prototype "se", undef, 'prototype "..." ignores state subs'; +} +{ + state sub if() { 44 } + my $x = if if if; + is $x, 44, 'state subs override all keywords'; + package bar; + my $y = if if if; + is $y, 44, 'state subs from other packages override all keywords'; +} +{ + use warnings; + state $w ; + local $SIG{__WARN__} = sub { $w .= shift }; + eval '#line 87 squidges + state sub foo; + state sub foo {}; + '; +on; + is $w, + '"state" subroutine foo masks earlier declaration in same scope at ' + . "squidges line 88.\n", + 'redefinition warning for state sub'; +} +# Since state vars inside anonymous subs are cloned at the same time as the +# anonymous subs containing them, the same should happen for state subs. +sub make_closure { + state $x = shift; + sub { + state sub foo { $x } + eval {foo} + } +} +$sub1 = make_closure 48; +$sub2 = make_closure 49; +is &$sub1, 48, 'state sub in closure (1)'; +is &$sub1, 49, 'state sub in closure (2)'; +off; +# But we need to test that state subs actually do persist from one invoca- +# tion of a named sub to another (i.e., that they are not my subs). +{ + use warnings; + state $w; + local $SIG{__WARN__} = sub { $w .= shift }; + eval '#line 65 teetet + sub foom { + my $x = shift; + state sub poom { $x } + eval{\&poom} + } + '; + is $w, "Variable \"\$x\" will not stay shared at teetet line 67.\n", + 'state subs get "Variable will not stay shared" messages'; + my $poom = foom(27); + my $poom2 = foom(678); + is eval{$poom->()}, eval {$poom2->()}, + 'state subs close over the first outer my var, like pkg subs'; + my $x = 43; + for $x (765) { + state sub etetetet { $x } +on; + is eval{etetetet}, $x, 'state sub ignores for() localisation'; +off; + } +} +{ + state sub BEGIN { exit }; + pass 'state subs are never special blocks'; + state sub END { shift } +on; + is eval{END('jkqeudth')}, jkqeudth, + 'state sub END {shift} implies @_, not @ARGV'; +} +{ + state sub redef {} + use warnings; + state $w; + local $SIG{__WARN__} = sub { $w .= shift }; + eval "#line 56 pygpyf\nsub redef {}"; + is $w, "Subroutine redef redefined at pygpyf line 56.\n", + "sub redefinition warnings from state subs"; +} |