1*f2a19305Safresh1use strict; use warnings; 2*f2a19305Safresh1use Memoize 0.45 qw(memoize unmemoize); 3*f2a19305Safresh1use Fcntl; 4*f2a19305Safresh1use Test::More tests => 65; 5*f2a19305Safresh1 6*f2a19305Safresh1sub list { wantarray ? @_ : $_[-1] } 7*f2a19305Safresh1 8*f2a19305Safresh1# Test FAULT 9*f2a19305Safresh1sub ns {} 10*f2a19305Safresh1sub na {} 11*f2a19305Safresh1ok eval { memoize 'ns', SCALAR_CACHE => 'FAULT'; 1 }, 'SCALAR_CACHE => FAULT'; 12*f2a19305Safresh1ok eval { memoize 'na', LIST_CACHE => 'FAULT'; 1 }, 'LIST_CACHE => FAULT'; 13*f2a19305Safresh1is eval { scalar(ns()) }, undef, 'exception in scalar context'; 14*f2a19305Safresh1is eval { list(na()) }, undef, 'exception in list context'; 15*f2a19305Safresh1 16*f2a19305Safresh1# Test FAULT/FAULT 17*f2a19305Safresh1sub dummy {1} 18*f2a19305Safresh1for ([qw(FAULT FAULT)], [qw(FAULT MERGE)], [qw(MERGE FAULT)]) { 19*f2a19305Safresh1 my ($l_opt, $s_opt) = @$_; 20*f2a19305Safresh1 my $memodummy = memoize 'dummy', LIST_CACHE => $l_opt, SCALAR_CACHE => $s_opt, INSTALL => undef; 21*f2a19305Safresh1 my ($ret, $e); 22*f2a19305Safresh1 { local $@; $ret = eval { scalar $memodummy->() }; $e = $@ } 23*f2a19305Safresh1 is $ret, undef, "scalar context fails under $l_opt/$s_opt"; 24*f2a19305Safresh1 like $e, qr/^Anonymous function called in forbidden scalar context/, '... with the right error message'; 25*f2a19305Safresh1 { local $@; $ret = eval { +($memodummy->())[0] }; $e = $@ } 26*f2a19305Safresh1 is $ret, undef, "list context fails under $l_opt/$s_opt"; 27*f2a19305Safresh1 like $e, qr/^Anonymous function called in forbidden list context/, '... with the right error message'; 28*f2a19305Safresh1 unmemoize $memodummy; 29*f2a19305Safresh1} 30*f2a19305Safresh1 31*f2a19305Safresh1# Test HASH 32*f2a19305Safresh1my (%s, %l); 33*f2a19305Safresh1sub nul {} 34*f2a19305Safresh1ok eval { memoize 'nul', SCALAR_CACHE => [HASH => \%s], LIST_CACHE => [HASH => \%l]; 1 }, '*_CACHE => HASH'; 35*f2a19305Safresh1nul('x'); 36*f2a19305Safresh1nul('y'); 37*f2a19305Safresh1is_deeply [sort keys %s], [qw(x y)], 'scalar context calls populate SCALAR_CACHE'; 38*f2a19305Safresh1is_deeply \%l, {}, '... and does not touch the LIST_CACHE'; 39*f2a19305Safresh1%s = (); 40*f2a19305Safresh1() = nul('p'); 41*f2a19305Safresh1() = nul('q'); 42*f2a19305Safresh1is_deeply [sort keys %l], [qw(p q)], 'list context calls populate LIST_CACHE'; 43*f2a19305Safresh1is_deeply \%s, {}, '... and does not touch the SCALAR_CACHE'; 44*f2a19305Safresh1 45*f2a19305Safresh1# Test MERGE 46*f2a19305Safresh1sub xx { wantarray } 47*f2a19305Safresh1ok !scalar(xx()), 'false in scalar context'; 48*f2a19305Safresh1ok list(xx()), 'true in list context'; 49*f2a19305Safresh1ok eval { memoize 'xx', LIST_CACHE => 'MERGE'; 1 }, 'LIST_CACHE => MERGE'; 50*f2a19305Safresh1ok !scalar(xx()), 'false in scalar context again'; 51*f2a19305Safresh1# Should return cached false value from previous invocation 52*f2a19305Safresh1ok !list(xx()), 'still false in list context'; 53*f2a19305Safresh1 54*f2a19305Safresh1sub reff { [1,2,3] } 55*f2a19305Safresh1sub listf { (1,2,3) } 56*f2a19305Safresh1 57*f2a19305Safresh1memoize 'reff', LIST_CACHE => 'MERGE'; 58*f2a19305Safresh1memoize 'listf'; 59*f2a19305Safresh1 60*f2a19305Safresh1scalar reff(); 61*f2a19305Safresh1is_deeply [reff()], [[1,2,3]], 'reff list context after scalar context'; 62*f2a19305Safresh1 63*f2a19305Safresh1scalar listf(); 64*f2a19305Safresh1is_deeply [listf()], [1,2,3], 'listf list context after scalar context'; 65*f2a19305Safresh1 66*f2a19305Safresh1unmemoize 'reff'; 67*f2a19305Safresh1memoize 'reff', LIST_CACHE => 'MERGE'; 68*f2a19305Safresh1unmemoize 'listf'; 69*f2a19305Safresh1memoize 'listf'; 70*f2a19305Safresh1 71*f2a19305Safresh1is_deeply [reff()], [[1,2,3]], 'reff list context'; 72*f2a19305Safresh1 73*f2a19305Safresh1is_deeply [listf()], [1,2,3], 'listf list context'; 74*f2a19305Safresh1 75*f2a19305Safresh1sub f17 { return 17 } 76*f2a19305Safresh1memoize 'f17', SCALAR_CACHE => 'MERGE'; 77*f2a19305Safresh1is_deeply [f17()], [17], 'f17 first call'; 78*f2a19305Safresh1is_deeply [f17()], [17], 'f17 second call'; 79*f2a19305Safresh1is scalar(f17()), 17, 'f17 scalar context call'; 80*f2a19305Safresh1 81*f2a19305Safresh1my (%cache, $num_cache_misses); 82*f2a19305Safresh1sub cacheit { 83*f2a19305Safresh1 ++$num_cache_misses; 84*f2a19305Safresh1 "cacheit result"; 85*f2a19305Safresh1} 86*f2a19305Safresh1sub test_cacheit { 87*f2a19305Safresh1 is scalar(cacheit()), 'cacheit result', 'scalar context'; 88*f2a19305Safresh1 is $num_cache_misses, 1, 'function called once'; 89*f2a19305Safresh1 90*f2a19305Safresh1 is +(cacheit())[0], 'cacheit result', 'list context'; 91*f2a19305Safresh1 is $num_cache_misses, 1, 'function not called again'; 92*f2a19305Safresh1 93*f2a19305Safresh1 is_deeply [values %cache], [['cacheit result']], 'expected cached value'; 94*f2a19305Safresh1 95*f2a19305Safresh1 %cache = (); 96*f2a19305Safresh1 97*f2a19305Safresh1 is +(cacheit())[0], 'cacheit result', 'list context'; 98*f2a19305Safresh1 is $num_cache_misses, 2, 'function again called after clearing the cache'; 99*f2a19305Safresh1 100*f2a19305Safresh1 is scalar(cacheit()), 'cacheit result', 'scalar context'; 101*f2a19305Safresh1 is $num_cache_misses, 2, 'function not called again'; 102*f2a19305Safresh1} 103*f2a19305Safresh1 104*f2a19305Safresh1memoize 'cacheit', LIST_CACHE => [HASH => \%cache], SCALAR_CACHE => 'MERGE'; 105*f2a19305Safresh1test_cacheit; 106*f2a19305Safresh1unmemoize 'cacheit'; 107*f2a19305Safresh1( $num_cache_misses, %cache ) = (); 108*f2a19305Safresh1memoize 'cacheit', SCALAR_CACHE => [HASH => \%cache], LIST_CACHE => 'MERGE'; 109*f2a19305Safresh1test_cacheit; 110*f2a19305Safresh1 111*f2a19305Safresh1# Test errors 112*f2a19305Safresh1my @w; 113*f2a19305Safresh1my $sub = eval { 114*f2a19305Safresh1 local $SIG{'__WARN__'} = sub { push @w, @_ }; 115*f2a19305Safresh1 memoize(sub {}, LIST_CACHE => ['TIE', 'WuggaWugga']); 116*f2a19305Safresh1}; 117*f2a19305Safresh1is $sub, undef, 'bad TIE fails'; 118*f2a19305Safresh1like $@, qr/^Can't locate WuggaWugga.pm in \@INC/, '... with the expected error'; 119*f2a19305Safresh1like $w[0], qr/^TIE option to memoize\(\) is deprecated; use HASH instead/, '... and the expected deprecation warning'; 120*f2a19305Safresh1is @w, 1, '... and no other warnings'; 121*f2a19305Safresh1 122*f2a19305Safresh1is eval { memoize sub {}, LIST_CACHE => 'YOB GORGLE' }, undef, 'bad LIST_CACHE fails'; 123*f2a19305Safresh1like $@, qr/^Unrecognized option to `LIST_CACHE': `YOB GORGLE'/, '... with the expected error'; 124*f2a19305Safresh1 125*f2a19305Safresh1is eval { memoize sub {}, SCALAR_CACHE => ['YOB GORGLE'] }, undef, 'bad SCALAR_CACHE fails'; 126*f2a19305Safresh1like $@, qr/^Unrecognized option to `SCALAR_CACHE': `YOB GORGLE'/, '... with the expected error'; 127*f2a19305Safresh1 128*f2a19305Safresh1for my $option (qw(LIST_CACHE SCALAR_CACHE)) { 129*f2a19305Safresh1 is eval { memoize sub {}, $option => ['MERGE'] }, undef, "$option=>['MERGE'] fails"; 130*f2a19305Safresh1 like $@, qr/^Unrecognized option to `$option': `MERGE'/, '... with the expected error'; 131*f2a19305Safresh1} 132*f2a19305Safresh1 133*f2a19305Safresh1# this test needs a DBM which 134*f2a19305Safresh1# a) Memoize knows is scalar-only 135*f2a19305Safresh1# b) is always available (on all platforms, perl configs etc) 136*f2a19305Safresh1# c) never fails to load 137*f2a19305Safresh1# so we use AnyDBM_File (which fulfills (a) & (b)) 138*f2a19305Safresh1# on top of a fake dummy DBM (ditto (b) & (c)) 139*f2a19305Safresh1sub DummyDBM::TIEHASH { bless {}, shift } 140*f2a19305Safresh1$INC{'DummyDBM.pm'} = 1; 141*f2a19305Safresh1@AnyDBM_File::ISA = 'DummyDBM'; 142*f2a19305Safresh1$sub = eval { 143*f2a19305Safresh1 no warnings; 144*f2a19305Safresh1 memoize sub {}, SCALAR_CACHE => [ TIE => 'AnyDBM_File' ], LIST_CACHE => 'MERGE'; 145*f2a19305Safresh1}; 146*f2a19305Safresh1is $sub, undef, 'smuggling in a scalar-only LIST_CACHE via MERGE fails'; 147*f2a19305Safresh1like $@, qr/^You can't use AnyDBM_File for LIST_CACHE because it can only store scalars/, 148*f2a19305Safresh1 '... with the expected error'; 149