#!/usr/bin/perl -w # Copyright 2008, 2009, 2010, 2011 Kevin Ryde # This file is part of constant-defer. # # constant-defer is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; either version 3, or (at your option) any later # version. # # constant-defer is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with constant-defer. If not, see . use strict; use constant::defer; use Test; BEGIN { plan tests => 49; } use lib 't'; use MyTestHelpers; MyTestHelpers::nowarnings(); sub streq_array { my ($a1, $a2) = @_; while (@$a1 && @$a2) { unless ((! defined $a1->[0] && ! defined $a2->[0]) || (defined $a1->[0] && defined $a2->[0] && $a1->[0] eq $a2->[0])) { return 0; } shift @$a1; shift @$a2; } return (@$a1 == @$a2); } #------------------------------------------------------------------------------ # VERSION { my $want_version = 5; ok ($constant::defer::VERSION, $want_version, 'VERSION variable'); ok (constant::defer->VERSION, $want_version, 'VERSION class method'); ok (eval { constant::defer->VERSION($want_version); 1 }, 1, "VERSION class check $want_version"); my $check_version = $want_version + 1000; ok (! eval { constant::defer->VERSION($check_version); 1 }, 1, "VERSION class check $check_version"); } #------------------------------------------------------------------------------ # plain name my $have_weaken; my $skip_weaken; { my $foo_runs = 0; use constant::defer FOO => sub { $foo_runs++; return 123 }; my $orig_code; $orig_code = \&FOO; ok (FOO, 123, 'FOO first run'); ok ($foo_runs, 1, 'FOO first runs code'); ok (&$orig_code(), 123, 'FOO orig second run'); ok ($foo_runs, 1, "FOO orig second doesn't run code"); $have_weaken = eval { require Scalar::Util; my $x = [ 'hello' ]; Scalar::Util::weaken($x); if (defined $x) { die "Oops, weakening didn't garbage collect"; } 1 }; if (! $have_weaken) { MyTestHelpers::diag ("Scalar::Util::weaken() not available -- ",$@); $skip_weaken = 'due to Scalar::Util::weaken() not available'; } { if ($have_weaken) { Scalar::Util::weaken ($orig_code); } skip ($skip_weaken, ! defined $orig_code, 1, 'orig FOO code garbage collected'); $foo_runs = 0; ok (FOO, 123, 'FOO second run'); ok ($foo_runs, 0, "FOO second doesn't run code"); } } #------------------------------------------------------------------------------ # fully qualified name { my $runs = 0; use constant::defer 'Some::Non::Package::Place::func' => sub { $runs = 1; return 'xyz' }; ok (Some::Non::Package::Place::func(), 'xyz', 'explicit package first run'); ok ($runs, 1, 'explicit package first run runs code'); $runs = 0; ok (Some::Non::Package::Place::func(), 'xyz', 'explicit package second run'); ok ($runs, 0, 'explicit package second run doesn\'t run code'); } #------------------------------------------------------------------------------ # array value { my $runs = 0; use constant::defer THREE => sub { $runs = 1; return ('a','b','c') }; ok (streq_array ([ THREE() ], [ 'a', 'b', 'c' ]), 1, 'THREE return values first run'); ok ($runs, 1, 'THREE return values first run runs code'); $runs = 0; ok (streq_array([ THREE() ], [ 'a', 'b', 'c' ]), 1, 'THREE return values second run'); ok ($runs, 0, 'THREE return values second run doesn\'t run code'); } { my $runs = 0; use constant::defer THREE_SCALAR => sub { $runs = 1; return ('a','b','c') }; my $got = THREE_SCALAR(); ok ($got, 3, 'three values in scalar context return values first run'); ok ($runs, 1, 'three values in scalar context return values first run runs code'); $runs = 0; $got = THREE_SCALAR(); ok ($got, 3, 'three values in scalar context return values second run'); ok ($runs, 0, 'three values in scalar context return values second run doesn\'t run code'); } #------------------------------------------------------------------------------ # multiple names { use constant::defer PAIR_ONE => sub { 123 }, PAIR_TWO => sub { 456 }; ok (PAIR_ONE, 123, 'PAIR_ONE'); ok (PAIR_TWO, 456, 'PAIR_TWO'); } { use constant::defer { HASH_ONE => sub { 123 }, HASH_TWO => sub { 456 } }; ok (HASH_ONE, 123, 'HASH_ONE'); ok (HASH_TWO, 456, 'HASH_TWO'); } { use constant::defer SHASH_ONE => sub { 123 }, { SHASH_TWO => sub { 456 }, SHASH_THREE => sub { 789 } }; ok (SHASH_ONE, 123, 'SHASH_ONE'); ok (SHASH_TWO, 456, 'SHASH_TWO'); ok (SHASH_THREE, 789, 'SHASH_THREE'); } #------------------------------------------------------------------------------ # with can() { my $runs = 0; package MyTestCan; use constant::defer CANNED => sub { $runs++; return 'foo' }; package main; my $func = MyTestCan->can('CANNED'); my $got = &$func(); ok ($got, 'foo', 'through can() - result'); ok ($runs, 1, 'through can() - run once'); $got = &$func(); ok ($got, 'foo', 'through can() - 2nd result'); ok ($runs, 1, 'through can() - 2nd still run once'); } #------------------------------------------------------------------------------ # with Exporter import() { my $runs = 0; package MyTestImport; use vars qw(@ISA @EXPORT); use constant::defer TEST_IMPORT_FOO => sub { $runs++; return 'foo' }; require Exporter; @ISA = ('Exporter'); @EXPORT = ('TEST_IMPORT_FOO'); package main; MyTestImport->import; my $got = TEST_IMPORT_FOO(); ok ($got, 'foo', 'through import - result'); ok ($runs, 1, 'through import - run once'); $got = TEST_IMPORT_FOO(); ok ($got, 'foo', 'through import - 2nd result'); ok ($runs, 1, 'through import - 2nd still run once'); } #------------------------------------------------------------------------------ # gc of supplied $subr after it's been called { my $subr; BEGIN { # crib: anon sub must refer to a lexical or isn't gc'ed, at least in 5.6.0 my $lexical_var = 'gc me'; $subr = sub { return $lexical_var }; } use constant::defer WEAKEN_CONST => $subr; # including when the can() first func is retained # my $cancode = main->can('WEAKEN_CONST'); my @got = WEAKEN_CONST(); ok (streq_array (\@got, ['gc me']), 1, 'WEAKEN_CONST - result'); if ($have_weaken) { Scalar::Util::weaken ($subr); } skip ($skip_weaken, ! defined $subr, 1, 'WEAKEN_CONST - subr now undef'); } { my ($objref, $subr); my $runs = 0; BEGIN { my %obj = (foo => 'bar'); $subr = sub { $runs++; return %obj }; $objref = \%obj; } use constant::defer WEAKEN_OBJRET => $subr; # including when the can() first func is retained # my $cancode = main->can('WEAKEN_OBJRET'); my @got = WEAKEN_OBJRET(); ok (streq_array (\@got, ['foo','bar']), 1, 'WEAKEN_OBJRET - result'); ok ($runs, 1, 'WEAKEN_OBJRET - run once'); if ($have_weaken) { Scalar::Util::weaken ($subr); Scalar::Util::weaken ($objref); } skip ($skip_weaken, ! defined $subr, 1, 'WEAKEN_OBJRET - subr now undef'); skip ($skip_weaken, ! defined $objref, 1, 'WEAKEN_OBJRET - objref now undef'); } #------------------------------------------------------------------------------ # gc of can() after called { use constant::defer CAN_GC => sub { return 'gc me' }; my $cancode = main->can('CAN_GC'); my @got = CAN_GC(); ok (streq_array (\@got, ['gc me']), 1, 'CAN_GC - result'); if ($have_weaken) { Scalar::Util::weaken ($cancode); } skip ($skip_weaken, ! defined $cancode, 1, 'CAN_GC - can() coderef now undef'); } #------------------------------------------------------------------------------ # gc of initial definition after called { use constant::defer INITIAL_GC => sub { return 'gc me' }; my $coderef = \&INITIAL_GC; my @got = INITIAL_GC(); ok (streq_array (\@got, ['gc me']), 1, 'INITIAL_GC - result'); if ($have_weaken) { Scalar::Util::weaken ($coderef); } skip ($skip_weaken, ! defined $coderef, 1, 'INITIAL_GC - saved initial now undef'); } #------------------------------------------------------------------------------ # gc of runner helper after redefine { use constant::defer RUNNER_GC => sub { return 'gc me' }; my $runner; BEGIN { $runner = $constant::defer::DEBUG_LAST_RUNNER; undef $constant::defer::DEBUG_LAST_RUNNER; } if (! $runner) { MyTestHelpers::diag ("DEBUG_LAST_RUNNER not enabled in defer.pm"); } do { local $^W = 0; # no warnings 'redefine'; eval '*RUNNER_GC = sub () { "new value" }; 1'; } or die "Oops, eval error: $@"; if ($have_weaken) { Scalar::Util::weaken ($runner); } skip ($skip_weaken, ! defined $runner, 1, 'RUNNER_GC - now undef'); if ($runner) { require Devel::FindRef; MyTestHelpers::diag (Devel::FindRef::track($runner)); } } exit 0;