#!perl -w use strict; use Test::More tests =>32; use Test::Exception; use Data::Util qw(:all); use constant PP_ONLY => $INC{'Data/Util/PurePerl.pm'}; sub get_subname{ return scalar get_code_info(@_); } sub foo{ 42; } sub bar{ 52; } { package Base; sub foo{ 'Base::foo'; } package Foo; our @ISA = qw(Base); use Data::Util qw(install_subroutine); sub baz{} package Callable; use overload '&{}' => 'codify', ; sub new{ my $class = shift; bless {@_} => $class; } sub codify{ my $self = shift; $self->{code}; } } is_deeply get_subname(\&foo), 'main::foo', 'get_code_info()'; is_deeply [get_code_info(\&foo)], [qw(main foo)]; is_deeply get_subname(\&Foo::baz), 'Foo::baz', 'get_code_info()'; is_deeply [get_code_info(\&Foo::baz)], [qw(Foo baz)]; is_deeply get_subname(\&undefined_subr), 'main::undefined_subr'; is_deeply [get_code_info(\&undefined_subr)], [qw(main undefined_subr)]; no warnings 'redefine'; Foo->foo(); # touch the chache Foo->install_subroutine(foo => \&foo); is Foo::foo(), foo(), 'as function'; is(Foo->foo(), foo(), 'as method'); Foo->install_subroutine(foo => \&bar); is Foo::foo(), bar(), 'redefined'; Foo->install_subroutine(foo => sub{ 314 }); is Foo::foo(), 314, 'install anonymous subr'; SKIP:{ skip 'in testing perl only', 1 if PP_ONLY; is get_subname(\&Foo::foo), 'Foo::foo', '...named'; } Foo->install_subroutine(foo => \&foo); is Foo::foo(), foo(); SKIP:{ skip 'in testing perl only', 1 if PP_ONLY; is get_subname(\&Foo::foo), 'main::foo'; } { my $count = 0; Foo->install_subroutine(foo => sub{ ++$count }); } is Foo::foo(), 1, 'install closure'; is Foo::foo(), 2; SKIP:{ skip 'in testing perl only', 2 if PP_ONLY; Foo->install_subroutine(foo => sub{}); is get_subname(\&Foo::foo), 'Foo::foo', 'name an anonymous subr'; Foo->install_subroutine(bar => \&Foo::foo); is get_subname(\&Foo::bar), 'Foo::foo', 'does not name a named subr'; } # exception Foo->install_subroutine(foo => \&undefined_subr); dies_ok{ Foo->foo(); } 'install undefined subroutine'; Foo->install_subroutine(ov1 => Callable->new(code => sub{ 'overloaded' })); is Foo::ov1(), 'overloaded', 'overload'; Foo->install_subroutine(ov2 => Callable->new(code => sub{ die 'dies in codify' })); throws_ok{ Foo::ov2(); } qr/dies in codify/; dies_ok{ Foo->install_subroutine(ov3 => Callable->new(code => [])); }; dies_ok{ Foo->install_subroutine(ov4 => Callable->new(code => undef)); }; use warnings FATAL => 'redefine'; throws_ok{ get_code_info(undef); } qr/CODE reference/; throws_ok{ install_subroutine(); } qr/^Usage: /; dies_ok{ Foo->install_subroutine('foo'); }; throws_ok{ Data::Util::install_subroutine(undef, foo => \&foo); } qr/package name/; throws_ok{ Foo->install_subroutine(PI => 3.14); } qr/CODE reference/; throws_ok{ Foo->install_subroutine(undef, sub{}); } qr/\b name\b /xms; throws_ok{ Foo->install_subroutine([], sub{}); } qr/\b name\b /xms; # multiple installation install_subroutine(__PACKAGE__, f1 => sub{ 1 }, f2 => sub{ 2 }, f3 => sub{ 3 }); is f1(), 1, 'multiple installation(1)'; is f2(), 2, 'multiple installation(2)'; is f3(), 3, 'multiple installation(3)';;