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