summaryrefslogtreecommitdiff
path: root/cpan
diff options
context:
space:
mode:
authorTodd Rinaldo <toddr@cpan.org>2022-10-19 17:42:37 +0000
committerTodd Rinaldo <toddr@cpan.org>2022-10-19 14:46:03 -0500
commitcc628f28ad0caa0a2e6c5a5c8df402f100259a6f (patch)
tree3e6632e5285d25f15b26717f9767414eb4ecbc48 /cpan
parent4e5f10080fe14012dab536cbfaf12b9d775a4412 (diff)
downloadperl-cc628f28ad0caa0a2e6c5a5c8df402f100259a6f.tar.gz
Update Memoize to CPAN version 1.14
[DELTA] 1.14 Sun 16 Oct 2022 * No recursion depth warning from the Memoize wrapper function. This was a backcompat breakage in 1.09 1.13 Tue 30 Aug 2022 * No functional changes * Further test fixes 1.12 Mon 29 Aug 2022 * No functional changes * Test fixes 1.11 Sun 28 Aug 2022 * Large test suite refactor * Additional tests * Updated packaging and package metadata
Diffstat (limited to 'cpan')
-rw-r--r--cpan/Memoize/Memoize.pm66
-rw-r--r--cpan/Memoize/Memoize/AnyDBM_File.pm6
-rw-r--r--cpan/Memoize/Memoize/Expire.pm11
-rw-r--r--cpan/Memoize/Memoize/NDBM_File.pm2
-rw-r--r--cpan/Memoize/Memoize/SDBM_File.pm4
-rw-r--r--cpan/Memoize/Memoize/Storable.pm8
-rw-r--r--cpan/Memoize/inc/boilerplate.pl50
-rw-r--r--cpan/Memoize/t/array.t68
-rw-r--r--cpan/Memoize/t/array_confusion.t46
-rw-r--r--cpan/Memoize/t/basic.t90
-rw-r--r--cpan/Memoize/t/cache.t126
-rw-r--r--cpan/Memoize/t/correctness.t205
-rw-r--r--cpan/Memoize/t/errors.t54
-rw-r--r--cpan/Memoize/t/expfile.t73
-rw-r--r--cpan/Memoize/t/expire.t71
-rw-r--r--cpan/Memoize/t/expmod.t34
-rw-r--r--cpan/Memoize/t/expmod_n.t59
-rw-r--r--cpan/Memoize/t/expmod_t.t22
-rw-r--r--cpan/Memoize/t/flush.t30
-rw-r--r--cpan/Memoize/t/lib/DBMTest.pm102
-rw-r--r--cpan/Memoize/t/lib/ExpireFile.pm57
-rw-r--r--cpan/Memoize/t/lib/ExpireTest.pm51
-rw-r--r--cpan/Memoize/t/merge.t37
-rw-r--r--cpan/Memoize/t/normalize.t42
-rw-r--r--cpan/Memoize/t/prototype.t35
-rw-r--r--cpan/Memoize/t/rememoize.t36
-rw-r--r--cpan/Memoize/t/speed.t107
-rw-r--r--cpan/Memoize/t/tie.t80
-rw-r--r--cpan/Memoize/t/tie_db.t8
-rw-r--r--cpan/Memoize/t/tie_gdbm.t65
-rw-r--r--cpan/Memoize/t/tie_ndbm.t65
-rw-r--r--cpan/Memoize/t/tie_odbm.t8
-rw-r--r--cpan/Memoize/t/tie_sdbm.t71
-rw-r--r--cpan/Memoize/t/tie_storable.t81
-rw-r--r--cpan/Memoize/t/tiefeatures.t47
-rw-r--r--cpan/Memoize/t/unmemoize.t58
36 files changed, 664 insertions, 1311 deletions
diff --git a/cpan/Memoize/Memoize.pm b/cpan/Memoize/Memoize.pm
index d2d1096f62..db99fe73eb 100644
--- a/cpan/Memoize/Memoize.pm
+++ b/cpan/Memoize/Memoize.pm
@@ -10,13 +10,12 @@
use strict; use warnings;
package Memoize;
-our $VERSION = '1.10';
+our $VERSION = '1.14';
use Carp;
-use Exporter;
-our $DEBUG;
use Config; # Dammit.
-*import = \&Exporter::import;
+
+BEGIN { require Exporter; *import = \&Exporter::import }
our @EXPORT = qw(memoize);
our @EXPORT_OK = qw(unmemoize flush_cache);
@@ -31,7 +30,7 @@ sub CLONE {
# Raise an error if the user tries to specify one of thesepackage as a
# tie for LIST_CACHE
-my %scalar_only = map {($_ => 1)} qw(DB_File GDBM_File SDBM_File ODBM_File NDBM_File);
+my %scalar_only = map {($_ => 1)} qw(DB_File GDBM_File SDBM_File ODBM_File), map +($_, "Memoize::$_"), qw(AnyDBM_File NDBM_File);
sub memoize {
my $fn = shift;
@@ -56,8 +55,8 @@ sub memoize {
my $info;
my $wrapper =
$Config{usethreads}
- ? eval "sub $proto { &_memoizer(\$info, \@_); }"
- : eval "sub $proto { unshift \@_, \$info; goto &_memoizer; }";
+ ? eval "no warnings 'recursion'; sub $proto { &_memoizer(\$info, \@_); }"
+ : eval "no warnings 'recursion'; sub $proto { unshift \@_, \$info; goto &_memoizer; }";
my $normalizer = $options{NORMALIZER};
if (defined $normalizer && ! ref $normalizer) {
@@ -198,7 +197,7 @@ sub _memoizer {
if (exists $cache->{$argstr}) {
return @{$cache->{$argstr}};
} else {
- my @q = &{$info->{U}};
+ my @q = do { no warnings 'recursion'; &{$info->{U}} };
$cache->{$argstr} = \@q;
@q;
}
@@ -209,7 +208,7 @@ sub _memoizer {
return $info->{MERGED}
? $cache->{$argstr}[0] : $cache->{$argstr};
} else {
- my $val = &{$info->{U}};
+ my $val = do { no warnings 'recursion'; &{$info->{U}} };
# Scalars are considered to be lists; store appropriately
if ($info->{MERGED}) {
$cache->{$argstr} = [$val];
@@ -269,7 +268,7 @@ sub _make_cref {
my $parent = (caller(1))[3]; # Function that called _make_cref
croak "Usage: argument 1 to `$parent' must be a function name or reference.\n";
}
- $DEBUG and warn "${name}($fn) => $cref in _make_cref\n";
+ our $DEBUG and warn "${name}($fn) => $cref in _make_cref\n";
$cref;
}
@@ -320,12 +319,14 @@ Options include:
=head1 DESCRIPTION
-`Memoizing' a function makes it faster by trading space for time. It
+I<Memoizing> a function makes it faster by trading space for time. It
does this by caching the return values of the function in a table.
If you call the function again with the same arguments, C<memoize>
jumps in and gives you the value out of the table, instead of letting
the function compute the value all over again.
+=head1 EXAMPLE
+
Here is an extreme example. Consider the Fibonacci sequence, defined
by the following function:
@@ -348,14 +349,14 @@ run---fib(14) makes 1,200 extra recursive calls to itself, to compute
and recompute things that it already computed.
This function is a good candidate for memoization. If you memoize the
-`fib' function above, it will compute fib(14) exactly once, the first
+C<fib> function above, it will compute fib(14) exactly once, the first
time it needs to, and then save the result in a table. Then if you
ask for fib(14) again, it gives you the result out of the table.
While computing fib(14), instead of computing fib(12) twice, it does
it once; the second time it needs the value it gets it from the table.
It doesn't compute fib(11) four times; it computes it once, getting it
from the table the next three times. Instead of making 1,200
-recursive calls to `fib', it makes 15. This makes the function about
+recursive calls to C<fib>, it makes 15. This makes the function about
150 times faster.
You could do the memoization yourself, by rewriting the function, like
@@ -382,8 +383,8 @@ This makes it easy to turn memoizing on and off.
Here's an even simpler example: I wrote a simple ray tracer; the
program would look in a certain direction, figure out what it was
-looking at, and then convert the `color' value (typically a string
-like `red') of that object to a red, green, and blue pixel value, like
+looking at, and then convert the C<color> value (typically a string
+like C<red>) of that object to a red, green, and blue pixel value, like
this:
for ($direction = 0; $direction < 300; $direction++) {
@@ -921,8 +922,8 @@ function (or when your program exits):
tie my %cache => 'Memoize::Storable', $filename, 'nstore';
memoize 'function', SCALAR_CACHE => [HASH => \%cache];
-Include the `nstore' option to have the C<Storable> database written
-in `network order'. (See L<Storable> for more details about this.)
+Include the C<nstore> option to have the C<Storable> database written
+in I<network order>. (See L<Storable> for more details about this.)
The C<flush_cache()> function will raise a run-time error unless the
tied package provides a C<CLEAR> method.
@@ -953,27 +954,19 @@ C<f()> (C<f> called with no arguments) will not be memoized. If this
is a big problem, you can supply a normalizer function that prepends
C<"x"> to every key.
-=head1 AUTHOR
-
-Mark-Jason Dominus
+=head1 SEE ALSO
At
-http://perl.plover.com/MiniMemoize/ there is an article about
+L<https://perl.plover.com/MiniMemoize/> there is an article about
memoization and about the internals of Memoize that appeared in The
Perl Journal, issue #13. (This article is also included in the
-Memoize distribution as `article.html'.)
+Memoize distribution as F<article.html>.)
-The author's book I<Higher-Order Perl> (2005, ISBN 1558607013, published
+Mark-Jason Dominus's book I<Higher-Order Perl> (2005, ISBN 1558607013,
+published
by Morgan Kaufmann) discusses memoization (and many other
topics) in tremendous detail. It is available on-line for free.
-For more information, visit http://hop.perl.plover.com/ .
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright 1998, 1999, 2000, 2001, 2012 by Mark Jason Dominus
-
-This library is free software; you may redistribute it and/or modify
-it under the same terms as Perl itself.
+For more information, visit L<https://hop.perl.plover.com/>.
=head1 THANK YOU
@@ -1000,4 +993,15 @@ Special thanks to Jarkko Hietaniemi, the 5.8.0 pumpking, for including
this module in the core and for his patient and helpful guidance
during the integration process.
+=head1 AUTHOR
+
+Mark Jason Dominus
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2012 by Mark Jason Dominus.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
=cut
diff --git a/cpan/Memoize/Memoize/AnyDBM_File.pm b/cpan/Memoize/Memoize/AnyDBM_File.pm
index 70b8dfe46b..9f5298a834 100644
--- a/cpan/Memoize/Memoize/AnyDBM_File.pm
+++ b/cpan/Memoize/Memoize/AnyDBM_File.pm
@@ -1,18 +1,16 @@
use strict; use warnings;
package Memoize::AnyDBM_File;
-our $VERSION = '1.10';
+our $VERSION = '1.14';
our @ISA = qw(DB_File GDBM_File Memoize::NDBM_File SDBM_File ODBM_File) unless @ISA;
-our $Verbose;
-
for my $mod (@ISA) {
if (eval "require $mod") {
$mod = 'NDBM_File'
if $mod eq 'Memoize::NDBM_File'
and eval { NDBM_File->VERSION( '1.16' ) };
- print STDERR "AnyDBM_File => Selected $mod.\n" if $Verbose;
+ print STDERR "AnyDBM_File => Selected $mod.\n" if our $Verbose;
@ISA = $mod;
return 1;
}
diff --git a/cpan/Memoize/Memoize/Expire.pm b/cpan/Memoize/Memoize/Expire.pm
index fd19b8f5c1..85e1b781f8 100644
--- a/cpan/Memoize/Memoize/Expire.pm
+++ b/cpan/Memoize/Memoize/Expire.pm
@@ -1,7 +1,7 @@
use strict; use warnings;
package Memoize::Expire;
-our $VERSION = '1.10';
+our $VERSION = '1.14';
use Carp;
our $DEBUG;
@@ -239,7 +239,7 @@ the cache, and it should return the cache object to the caller.
For example, MyExpirePolicy::TIEHASH might create an object that
contains a regular Perl hash (which it will to store the cached
values) and some extra information about the arguments and how old the
-data is and things like that. Let us call this object `C'.
+data is and things like that. Let us call this object I<C<C>>.
When Memoize needs to check to see if an entry is in the cache
already, it will invoke C<< C->EXISTS(key) >>. C<key> is the normalized
@@ -272,7 +272,7 @@ cache item after ten seconds.
$cache->{$key}{EXPIRE_TIME} > time) {
return 1
} else {
- return 0; # Do NOT return `undef' here.
+ return 0; # Do NOT return undef here
}
}
@@ -308,11 +308,10 @@ See the documentation for details.
=head1 ALTERNATIVES
-Brent Powers has a C<Memoize::ExpireLRU> module that was designed to
+Brent Powers has a L<Memoize::ExpireLRU> module that was designed to
work with Memoize and provides expiration of least-recently-used data.
The cache is held at a fixed number of entries, and when new data
-comes in, the least-recently used data is expired. See
-L<http://search.cpan.org/search?mode=module&query=ExpireLRU>.
+comes in, the least-recently used data is expired.
Joshua Chamas's Tie::Cache module may be useful as an expiration
manager. (If you try this, let me know how it works out.)
diff --git a/cpan/Memoize/Memoize/NDBM_File.pm b/cpan/Memoize/Memoize/NDBM_File.pm
index 1c4427e760..80a84dcdc0 100644
--- a/cpan/Memoize/Memoize/NDBM_File.pm
+++ b/cpan/Memoize/Memoize/NDBM_File.pm
@@ -1,7 +1,7 @@
use strict; use warnings;
package Memoize::NDBM_File;
-our $VERSION = '1.10';
+our $VERSION = '1.14';
use NDBM_File;
our @ISA = qw(NDBM_File);
diff --git a/cpan/Memoize/Memoize/SDBM_File.pm b/cpan/Memoize/Memoize/SDBM_File.pm
index ca84ed697f..ec6c110bac 100644
--- a/cpan/Memoize/Memoize/SDBM_File.pm
+++ b/cpan/Memoize/Memoize/SDBM_File.pm
@@ -1,9 +1,9 @@
use strict; use warnings;
package Memoize::SDBM_File;
-our $VERSION = '1.10';
+our $VERSION = '1.14';
-use SDBM_File 1.01;
+use SDBM_File 1.01; # for EXISTS support
our @ISA = qw(SDBM_File);
1;
diff --git a/cpan/Memoize/Memoize/Storable.pm b/cpan/Memoize/Memoize/Storable.pm
index 08ba6109fb..b63a8dade6 100644
--- a/cpan/Memoize/Memoize/Storable.pm
+++ b/cpan/Memoize/Memoize/Storable.pm
@@ -1,9 +1,9 @@
use strict; use warnings;
package Memoize::Storable;
-our $VERSION = '1.10';
+our $VERSION = '1.14';
-use Storable 1.002 ();
+use Storable 1.002 (); # for lock_* function variants
our $Verbose;
@@ -13,7 +13,7 @@ sub TIEHASH {
my $truehash = (-e $filename) ? Storable::lock_retrieve($filename) : {};
my %options;
print STDERR "Memoize::Storable::TIEHASH($filename, @_)\n" if $Verbose;
- @options{@_} = ();
+ @options{@_} = (1) x @_;
my $self =
{FILENAME => $filename,
H => $truehash,
@@ -43,7 +43,7 @@ sub EXISTS {
sub DESTROY {
my $self= shift;
print STDERR "Memoize::Storable::DESTROY(@_)\n" if $Verbose;
- if (exists $self->{OPTIONS}{'nstore'}) {
+ if ($self->{OPTIONS}{'nstore'}) {
Storable::lock_nstore($self->{H}, $self->{FILENAME});
} else {
Storable::lock_store($self->{H}, $self->{FILENAME});
diff --git a/cpan/Memoize/inc/boilerplate.pl b/cpan/Memoize/inc/boilerplate.pl
new file mode 100644
index 0000000000..e214864199
--- /dev/null
+++ b/cpan/Memoize/inc/boilerplate.pl
@@ -0,0 +1,50 @@
+use strict; use warnings;
+
+use CPAN::Meta;
+use Software::LicenseUtils 0.103011;
+use Pod::Readme::Brief 1.001;
+
+sub slurp { open my $fh, '<', $_[0] or die "Couldn't open $_[0] to read: $!\n"; local $/; readline $fh }
+sub trimnl { s/\A\s*\n//, s/\s*\z/\n/ for @_; wantarray ? @_ : $_[-1] }
+sub mkparentdirs {
+ my @dir = do { my %seen; sort grep s!/[^/]+\z!! && !$seen{ $_ }++, my @copy = @_ };
+ if ( @dir ) { mkparentdirs( @dir ); mkdir for @dir }
+}
+
+chdir $ARGV[0] or die "Cannot chdir to $ARGV[0]: $!\n";
+
+my %file;
+
+my $meta = CPAN::Meta->load_file( 'META.json' );
+
+my $license = do {
+ my @key = ( $meta->license, $meta->meta_spec_version );
+ my ( $class, @ambiguous ) = Software::LicenseUtils->guess_license_from_meta_key( @key );
+ die if @ambiguous or not $class;
+ $class->new( $meta->custom( 'x_copyright' ) );
+};
+
+$file{'LICENSE'} = trimnl $license->fulltext;
+
+my ( $main_module ) = map { s!-!/!g; s!^!lib/! if -d 'lib'; -f "$_.pod" ? "$_.pod" : "$_.pm" } $meta->name;
+
+( $file{ $main_module } = slurp $main_module ) =~ s{(^=cut\s*\z)}{ join "\n", (
+ "=head1 AUTHOR\n", trimnl( $meta->authors ),
+ "=head1 COPYRIGHT AND LICENSE\n", trimnl( $license->notice ),
+ "=cut\n",
+) }me;
+
+die unless -e 'Makefile.PL';
+$file{'README'} = Pod::Readme::Brief->new( $file{ $main_module } )->render( installer => 'eumm', width => 72 );
+
+my @manifest = split /\n/, slurp 'MANIFEST';
+my %manifest = map /\A([^\s#]+)()/, @manifest;
+$file{'MANIFEST'} = join "\n", @manifest, ( sort grep !exists $manifest{ $_ }, keys %file ), '';
+
+mkparentdirs sort keys %file;
+for my $fn ( sort keys %file ) {
+ unlink $fn if -e $fn;
+ open my $fh, '>', $fn or die "Couldn't open $fn to write: $!\n";
+ print $fh $file{ $fn };
+ close $fh or die "Couldn't close $fn after writing: $!\n";
+}
diff --git a/cpan/Memoize/t/array.t b/cpan/Memoize/t/array.t
deleted file mode 100644
index 2a7ca00a09..0000000000
--- a/cpan/Memoize/t/array.t
+++ /dev/null
@@ -1,68 +0,0 @@
-use strict; use warnings;
-use Memoize;
-
-print "1..11\n";
-
-my $timestamp;
-sub timelist {
- return (++$timestamp) x $_[0];
-}
-
-memoize('timelist');
-
-my (@t1, @u1);
-@t1 = &timelist(1);
-@u1 = &timelist(1);
-print ((("@t1" eq "@u1") ? '' : 'not '), "ok 1\n");
-
-my (@t7, @u7, $BAD, $i);
-@t7 = &timelist(7);
-print (((@t7 == 7) ? '' : 'not '), "ok 2\n");
-$BAD = 0;
-for ($i = 1; $i < 7; $i++) {
- $BAD++ unless $t7[$i-1] == $t7[$i];
-}
-print (($BAD ? 'not ' : ''), "ok 3\n");
-
-@u7 = &timelist(7);
-print (((@u7 == 7) ? '' : 'not '), "ok 4\n");
-$BAD = 0;
-for ($i = 1; $i < 7; $i++) {
- $BAD++ unless $u7[$i-1] == $u7[$i];
-}
-print (($BAD ? 'not ' : ''), "ok 5\n");
-# Properly memoized?
-print ((("@t7" eq "@u7") ? '' : 'not '), "ok 6\n");
-
-sub con {
- return wantarray()
-}
-
-# Same arguments yield different results in different contexts?
-memoize('con');
-my ($s, @a);
-$s = con(1);
-@a = con(1);
-print ((($s == $a[0]) ? 'not ' : ''), "ok 7\n");
-
-# Context propagated correctly?
-print ((($s eq '') ? '' : 'not '), "ok 8\n"); # Scalar context
-print ((("@a" eq '1' && @a == 1) ? '' : 'not '), "ok 9\n"); # List context
-
-# Context propagated correctly to normalizer?
-sub n {
- my $arg = shift;
- my $test = shift;
- if (wantarray) {
- sub ARRAY () { 'ARRAY' } # FIXME temporary strict-cleanliness shim
- print ((($arg eq ARRAY) ? '' : 'not '), "ok $test\n"); # List context
- } else {
- sub SCALAR () { 'SCALAR' } # FIXME temporary strict-cleanliness shim
- print ((($arg eq SCALAR) ? '' : 'not '), "ok $test\n"); # Scalar context
- }
-}
-
-sub f { 1 }
-memoize('f', NORMALIZER => 'n');
-$s = f('SCALAR', 10); # Test 10
-@a = f('ARRAY' , 11); # Test 11
diff --git a/cpan/Memoize/t/array_confusion.t b/cpan/Memoize/t/array_confusion.t
deleted file mode 100644
index 3ea078022b..0000000000
--- a/cpan/Memoize/t/array_confusion.t
+++ /dev/null
@@ -1,46 +0,0 @@
-use strict; use warnings;
-use Memoize qw(memoize unmemoize);
-use Test::More;
-
-sub reff {
- return [1,2,3];
-
-}
-
-sub listf {
- return (1,2,3);
-}
-
-sub f17 { return 17 }
-
-plan tests => 7;
-
-memoize 'reff', LIST_CACHE => 'MERGE';
-memoize 'listf';
-
-my ($s, @a);
-$s = reff();
-@a = reff();
-is(scalar(@a), 1, "reff list context");
-
-$s = listf();
-@a = listf();
-is(scalar(@a), 3, "listf list context");
-
-unmemoize 'reff';
-memoize 'reff', LIST_CACHE => 'MERGE';
-unmemoize 'listf';
-memoize 'listf';
-
-@a = reff();
-$s = reff();
-is(scalar @a, 1, "reff list context");
-
-@a = listf();
-$s = listf();
-is(scalar @a, 3, "listf list context");
-
-memoize 'f17', SCALAR_CACHE => 'MERGE';
-is(f17(), 17, "f17 first call");
-is(f17(), 17, "f17 second call");
-is(scalar(f17()), 17, "f17 scalar context call");
diff --git a/cpan/Memoize/t/basic.t b/cpan/Memoize/t/basic.t
new file mode 100644
index 0000000000..fd4527f539
--- /dev/null
+++ b/cpan/Memoize/t/basic.t
@@ -0,0 +1,90 @@
+use strict; use warnings;
+use Memoize;
+use Test::More tests => 27;
+
+# here we test memoize() itself i.e. whether it sets everything up as requested
+# (except for the (LIST|SCALAR)_CACHE options which are tested elsewhere)
+
+my ( $sub, $wrapped );
+
+sub dummy {1}
+$sub = \&dummy;
+$wrapped = memoize 'dummy';
+isnt \&dummy, $sub, 'memoizing replaces the sub';
+is ref $wrapped, 'CODE', '... and returns a coderef';
+is \&dummy, $wrapped, '... which is the replacement';
+
+sub dummy_i {1}
+$sub = \&dummy_i;
+$wrapped = memoize 'dummy_i', INSTALL => 'another';
+is \&dummy_i, $sub, 'INSTALL does not replace the sub';
+is \&another, $wrapped, '... but installs the memoized version where requested';
+
+sub dummy_p {1}
+$sub = \&dummy_p;
+$wrapped = memoize 'dummy_p', INSTALL => 'another::package::too';
+is \&another::package::too, $wrapped, '... even if that is a whole other package';
+
+sub find_sub {
+ my ( $needle, $symtbl ) = ( @_, *main::{'HASH'} );
+ while ( my ( $name, $glob ) = each %$symtbl ) {
+ if ( $name =~ /::\z/ ) {
+ find_sub( $needle, *$glob{'HASH'} ) unless *$glob{'HASH'} == $symtbl;
+ } elsif ( defined( my $sub = eval { *$glob{'CODE'} } ) ) {
+ return 1 if $needle == $sub;
+ }
+ }
+ return !1;
+}
+
+sub dummy_u {1}
+$sub = \&dummy_u;
+$wrapped = memoize 'dummy_u', INSTALL => undef;
+is \&dummy_u, $sub, '... unless the passed name is undef';
+ok !find_sub( $wrapped ), '... which does not install the memoized version anywhere';
+
+$sub = sub {1};
+$wrapped = memoize $sub;
+is ref $wrapped, 'CODE', 'memoizing a $coderef wraps it';
+ok !find_sub( $wrapped ), '... without installing the memoized version anywhere';
+
+$sub = sub {1};
+$wrapped = memoize $sub, INSTALL => 'another';
+is \&another, $wrapped, '... unless requested using INSTALL';
+
+my $num_args;
+sub fake_normalize { $num_args = @_ }
+$wrapped = memoize sub {1}, NORMALIZER => 'fake_normalize';
+$wrapped->( ('x') x 7 );
+is $num_args, 7, 'NORMALIZER installs the requested normalizer; both by name';
+$wrapped = memoize sub {1}, NORMALIZER => \&fake_normalize;
+$wrapped->( ('x') x 23 );
+is $num_args, 23, '... as well as by reference';
+
+$wrapped = eval { memoize 'dummy_none' };
+is $wrapped, undef, 'memoizing a non-existent function fails';
+like $@, qr/^Cannot operate on nonexistent function `dummy_none'/, '... with the expected error';
+
+for my $nonsub ({}, [], \my $x) {
+ is eval { memoize $nonsub }, undef, "memoizing ${\ref $nonsub} ref fails";
+ like $@, qr/^Usage: memoize 'functionname'\|coderef \{OPTIONS\}/, '... with the expected error';
+}
+
+sub no_warnings_ok (&$) {
+ my $w;
+ local $SIG{'__WARN__'} = sub { push @$w, @_; &diag };
+ shift->();
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ is( $w, undef, shift ) or diag join '', @$w;
+}
+
+sub q1 ($) { $_[0] + 1 }
+sub q2 () { time }
+sub q3 { join "--", @_ }
+
+no_warnings_ok { memoize 'q1' } 'no warnings with $ protype';
+no_warnings_ok { memoize 'q2' } 'no warnings with empty protype';
+no_warnings_ok { memoize 'q3' } 'no warnings without protype';
+is q1(@{['a'..'z']}), 27, '$ prototype is honored';
+is eval('q2("test")'), undef, 'empty prototype is honored';
+like $@, qr/^Too many arguments for main::q2 /, '... with the expected error';
diff --git a/cpan/Memoize/t/cache.t b/cpan/Memoize/t/cache.t
new file mode 100644
index 0000000000..2d61b8113a
--- /dev/null
+++ b/cpan/Memoize/t/cache.t
@@ -0,0 +1,126 @@
+use strict; use warnings;
+use Memoize 0.45 qw(memoize unmemoize);
+use Fcntl;
+use Test::More tests => 59;
+
+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_HASH fails';
+like $@, qr/^Unrecognized option to `SCALAR_CACHE': `YOB GORGLE'/, '... with the expected error';
diff --git a/cpan/Memoize/t/correctness.t b/cpan/Memoize/t/correctness.t
index a7235771dd..b42cc3ef1d 100644
--- a/cpan/Memoize/t/correctness.t
+++ b/cpan/Memoize/t/correctness.t
@@ -1,130 +1,103 @@
use strict; use warnings;
use Memoize;
+use Test::More tests => 17;
-my ($c1, $c2, $c3, $c4, $FAIL, $COUNT, $f, $fm);
+# here we test whether memoization actually has the desired effect
-print "1..25\n";
-
-print "# Basic\n";
-
-# A function that should only be called once.
-{ my $COUNT = 0;
- sub no_args {
- $FAIL++ if $COUNT++;
- 11;
- }
+my ($fib, $ns1_calls, $ns2_calls, $total_calls) = ([0,1], 1, 1, 1+1);
+while (@$fib < 23) {
+ push @$fib, $$fib[-1] + $$fib[-2];
+ my $n_calls = 1 + $ns1_calls + $ns2_calls;
+ $total_calls += $n_calls;
+ ($ns2_calls, $ns1_calls) = ($ns1_calls, $n_calls);
}
-#
-memoize('no_args');
-
-$c1 = &no_args();
-print (($c1 == 11) ? "ok 1\n" : "not ok 1\n");
-$c2 = &no_args();
-print (($c2 == 11) ? "ok 2\n" : "not ok 2\n");
-print $FAIL ? "not ok 3\n" : "ok 3\n"; # Was it really memoized?
-
-$FAIL = 0;
-$f = do { my $COUNT = 0; sub { $FAIL++ if $COUNT++; 12 } };
-$fm = memoize($f);
-
-$c1 = &$fm();
-print (($c1 == 12) ? "ok 4\n" : "not ok 4\n");
-$c2 = &$fm();
-print (($c2 == 12) ? "ok 5\n" : "not ok 5\n");
-print $FAIL ? "not ok 6\n" : "ok 6\n"; # Was it really memoized?
-
-$f = do { my $COUNT = 0; sub { $FAIL++ if $COUNT++; 13 } };
-$fm = memoize($f, INSTALL => 'another');
-
-$c1 = &another(); # Was it really installed?
-print (($c1 == 13) ? "ok 7\n" : "not ok 7\n");
-$c2 = &another();
-print (($c2 == 13) ? "ok 8\n" : "not ok 8\n");
-print $FAIL ? "not ok 9\n" : "ok 9\n"; # Was it really memoized?
-$c3 = &$fm(); # Call memoized version through returned ref
-print (($c3 == 13) ? "ok 10\n" : "not ok 10\n");
-print $FAIL ? "not ok 11\n" : "ok 11\n"; # Was it really memoized?
-$c4 = &$f(); # Call original version again
-print (($c4 == 13) ? "ok 12\n" : "not ok 12\n");
-print $FAIL ? "ok 13\n" : "not ok 13\n"; # Did we get the original?
-
-print "# Fibonacci\n";
-
-sub mt1 { # Fibonacci
- my $n = shift;
- return $n if $n < 2;
- mt1($n-1) + mt2($n-2);
-}
-sub mt2 {
- my $n = shift;
- return $n if $n < 2;
- mt1($n-1) + mt2($n-2);
+my $num_calls;
+sub fib {
+ ++$num_calls;
+ my $n = shift;
+ return $n if $n < 2;
+ fib($n-1) + fib($n-2);
}
-my (@f1, @f2, @f3, @f4, $n, $i, $j, $k, @arrays);
-@f1 = map { mt1($_) } (0 .. 15);
-@f2 = map { mt2($_) } (0 .. 15);
-memoize('mt1');
-@f3 = map { mt1($_) } (0 .. 15);
-@f4 = map { mt1($_) } (0 .. 15);
-@arrays = (\@f1, \@f2, \@f3, \@f4);
-$n = 13;
-for ($i=0; $i<3; $i++) {
- for ($j=$i+1; $j<3; $j++) {
- $n++;
- print ((@{$arrays[$i]} == @{$arrays[$j]}) ? "ok $n\n" : "not ok $n\n");
- $n++;
- for ($k=0; $k < @{$arrays[$i]}; $k++) {
- (print "not ok $n\n", next) if $arrays[$i][$k] != $arrays[$j][$k];
- }
- print "ok $n\n";
- }
-}
+my @s1 = map 0+fib($_), 0 .. $#$fib;
+is_deeply \@s1, $fib, 'unmemoized Fibonacci works';
+is $num_calls, $total_calls, '... with the expected amount of calls';
+undef $num_calls;
+memoize 'fib';
-print "# Normalizers\n";
+my @f1 = map 0+fib($_), 0 .. $#$fib;
+my @f2 = map 0+fib($_), 0 .. $#$fib;
+is_deeply \@f1, $fib, 'memoized Fibonacci works';
+is $num_calls, @$fib, '... with a minimal amount of calls';
-sub fake_normalize {
- return '';
-}
+########################################################################
-sub f1 {
- return shift;
-}
-sub f2 {
- return shift;
-}
-sub f3 {
- return shift;
+my $timestamp;
+sub timelist { (++$timestamp) x $_[0] }
+
+memoize('timelist');
+
+my $t1 = [timelist(1)];
+is_deeply [timelist(1)], $t1, 'memoizing a volatile function makes it stable';
+my $t7 = [timelist(7)];
+isnt @$t1, @$t7, '... unless the arguments change';
+is_deeply $t7, [($$t7[0]) x 7], '... which leads to the expected new return value';
+is_deeply [timelist(7)], $t7, '... which then also stays stable';
+
+sub con { wantarray ? 'list' : 'scalar' }
+memoize('con');
+is scalar(con(1)), 'scalar', 'scalar context propgates properly';
+is_deeply [con(1)], ['list'], 'list context propgates properly';
+
+########################################################################
+
+my %underlying;
+sub ExpireTest::TIEHASH { bless \%underlying, shift }
+sub ExpireTest::EXISTS { exists $_[0]{$_[1]} }
+sub ExpireTest::FETCH { $_[0]{$_[1]} }
+sub ExpireTest::STORE { $_[0]{$_[1]} = $_[2] }
+
+my %CALLS;
+sub id {
+ my($arg) = @_;
+ ++$CALLS{$arg};
+ $arg;
}
-&memoize('f1');
-&memoize('f2', NORMALIZER => 'fake_normalize');
-&memoize('f3', NORMALIZER => \&fake_normalize);
-my (@f1r, @f2r, @f3r);
-@f1r = map { f1($_) } (1 .. 10);
-@f2r = map { f2($_) } (1 .. 10);
-@f3r = map { f3($_) } (1 .. 10);
-$n++;
-print (("@f1r" eq "1 2 3 4 5 6 7 8 9 10") ? "ok $n\n" : "not ok $n\n");
-$n++;
-print (("@f2r" eq "1 1 1 1 1 1 1 1 1 1") ? "ok $n\n" : "not ok $n\n");
-$n++;
-print (("@f3r" eq "1 1 1 1 1 1 1 1 1 1") ? "ok $n\n" : "not ok $n\n");
-
-print "# INSTALL => undef option.\n";
-{ my $i = 1;
- sub u1 { $i++ }
+
+tie my %cache => 'ExpireTest';
+memoize 'id',
+ SCALAR_CACHE => [HASH => \%cache],
+ LIST_CACHE => 'FAULT';
+
+my $arg = [1..3, 1, 2, 1];
+is_deeply [map scalar(id($_)), @$arg], $arg, 'memoized function sanity check';
+is_deeply \%CALLS, {1=>1,2=>1,3=>1}, 'amount of initial calls per arg as expected';
+
+delete $underlying{1};
+$arg = [1..3];
+is_deeply [map scalar(id($_)), @$arg], $arg, 'memoized function sanity check';
+is_deeply \%CALLS, {1=>2,2=>1,3=>1}, 'amount of calls per arg after expiring 1 as expected';
+
+delete @underlying{1,2};
+is_deeply [map scalar(id($_)), @$arg], $arg, 'memoized function sanity check';
+is_deeply \%CALLS, {1=>3,2=>2,3=>1}, 'amount of calls per arg after expiring 1 & 2 as expected';
+
+########################################################################
+
+my $fail;
+$SIG{__WARN__} = sub { if ( $_[0] =~ /^Deep recursion/ ) { $fail = 1 } else { warn $_[0] } };
+
+my $limit;
+sub deep_probe { deep_probe() if ++$limit < 100_000 and not $fail }
+sub deep_test { no warnings "recursion"; deep_test() if $limit-- > 0 }
+memoize "deep_test";
+
+SKIP: {
+ deep_probe();
+ skip "no warning after $limit recursive calls (maybe PERL_SUB_DEPTH_WARN was raised?)", 1 if not $fail;
+ undef $fail;
+ deep_test();
+ ok !$fail, 'no recursion warning thrown from Memoize';
}
-my $um = memoize('u1', INSTALL => undef);
-my (@umr, @u1r);
-@umr = (&$um, &$um, &$um);
-@u1r = (&u1, &u1, &u1 ); # Did *not* clobber &u1
-$n++;
-print (("@umr" eq "1 1 1") ? "ok $n\n" : "not ok $n\n"); # Increment once
-$n++;
-print (("@u1r" eq "2 3 4") ? "ok $n\n" : "not ok $n\n"); # Increment thrice
-$n++;
-print ((defined &{"undef"}) ? "not ok $n\n" : "ok $n\n"); # Just in case
-
-print "# $n tests in all.\n";
diff --git a/cpan/Memoize/t/errors.t b/cpan/Memoize/t/errors.t
deleted file mode 100644
index c1c0dece22..0000000000
--- a/cpan/Memoize/t/errors.t
+++ /dev/null
@@ -1,54 +0,0 @@
-use strict; use warnings;
-use Memoize;
-use Config;
-
-$|=1;
-print "1..11\n";
-
-eval { memoize({}) };
-print $@ ? "ok 1\n" : "not ok 1 # $@\n";
-
-eval { memoize([]) };
-print $@ ? "ok 2\n" : "not ok 2 # $@\n";
-
-eval { my $x; memoize(\$x) };
-print $@ ? "ok 3\n" : "not ok 3 # $@\n";
-
-# 4--8
-my $n;
-$n = 4;
-my $dummyfile = './dummydb';
-use Fcntl;
-my %args = ( DB_File => [],
- GDBM_File => [$dummyfile, \&GDBM_File::GDBM_NEWDB, 0666],
- ODBM_File => [$dummyfile, O_RDWR|O_CREAT, 0666],
- NDBM_File => [$dummyfile, O_RDWR|O_CREAT, 0666],
- SDBM_File => [$dummyfile, O_RDWR|O_CREAT, 0666],
- );
-my $mod;
-for $mod (qw(DB_File GDBM_File SDBM_File ODBM_File NDBM_File)) {
- eval {
- require "$mod.pm";
- tie my %cache => $mod, map { (ref($_) eq 'CODE') ? &$_ : $_ } @{$args{$mod}};
- memoize(sub {}, LIST_CACHE => [HASH => \%cache ]);
- };
- print $@ =~ /can only store scalars/
- || $@ =~ /Can't locate.*in \@INC/
- || $@ =~ /Can't load '.*?' for module/ ? "ok $n\n" : "not ok $n # $@\n";
- 1 while unlink $dummyfile, "$dummyfile.dir", "$dummyfile.pag", "$dummyfile.db";
- $n++;
-}
-
-# 9
-eval { no warnings;
- memoize(sub {}, LIST_CACHE => ['TIE', 'WuggaWugga'])
- };
-print $@ ? "ok 9\n" : "not ok 9 # $@\n";
-
-# 10
-eval { memoize(sub {}, LIST_CACHE => 'YOB GORGLE') };
-print $@ ? "ok 10\n" : "not ok 10 # $@\n";
-
-# 11
-eval { memoize(sub {}, SCALAR_CACHE => ['YOB GORGLE']) };
-print $@ ? "ok 11\n" : "not ok 11 # $@\n";
diff --git a/cpan/Memoize/t/expfile.t b/cpan/Memoize/t/expfile.t
deleted file mode 100644
index d6dc08f07f..0000000000
--- a/cpan/Memoize/t/expfile.t
+++ /dev/null
@@ -1,73 +0,0 @@
-use strict; use warnings;
-use Memoize;
-use lib 't/lib';
-
-my $n = 0;
-$|=1;
-
-if ($ENV{PERL_MEMOIZE_TESTS_FAST_ONLY}) {
- print "1..0 # Skipped: Slow tests disabled\n";
- exit 0;
-}
-
-print "1..12\n";
-# (1)
-++$n; print "ok $n\n";
-
-my $READFILE_CALLS = 0;
-my $FILE = './TESTFILE';
-
-sub writefile {
- my $FILE = shift;
- open F, "> $FILE" or die "Couldn't write temporary file $FILE: $!";
- print F scalar(localtime), "\n";
- close F;
-}
-
-sub readfile {
- $READFILE_CALLS++;
- my $FILE = shift;
- open F, "< $FILE" or die "Couldn't write temporary file $FILE: $!";
- my $data = <F>;
- close F;
- $data;
-}
-
-require ExpireFile;
-# (2)
-++$n; print "ok $n\n";
-
-tie my %cache => 'ExpireFile';
-memoize 'readfile',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
-# (3)
-++$n; print "ok $n\n";
-
-# (4)
-writefile($FILE);
-++$n; print "ok $n\n";
-sleep 4;
-
-# (5-6)
-my $t1 = readfile($FILE);
-++$n; print "ok $n\n";
-++$n; print ((($READFILE_CALLS == 1) ? '' : 'not '), "ok $n\n");
-
-# (7-9)
-my $t2 = readfile($FILE);
-++$n; print "ok $n\n";
-++$n; print ((($READFILE_CALLS == 1) ? '' : 'not '), "ok $n\n");
-++$n; print ((($t1 eq $t2) ? '' : 'not '), "ok $n\n");
-
-# (10-12)
-sleep 4;
-writefile($FILE);
-my $t3 = readfile($FILE);
-++$n; print "ok $n\n";
-++$n; print ((($READFILE_CALLS == 2) ? '' : 'not '), "ok $n\n");
-++$n; print ((($t1 ne $t3) ? '' : 'not '), "ok $n\n");
-
-END { 1 while unlink $FILE || () }
diff --git a/cpan/Memoize/t/expire.t b/cpan/Memoize/t/expire.t
deleted file mode 100644
index 72fce35fe4..0000000000
--- a/cpan/Memoize/t/expire.t
+++ /dev/null
@@ -1,71 +0,0 @@
-use strict; use warnings;
-use Memoize;
-use lib 't/lib';
-use ExpireTest;
-
-my $n = 0;
-
-print "1..17\n";
-
-$n++; print "ok $n\n";
-
-my %CALLS;
-sub id {
- my($arg) = @_;
- ++$CALLS{$arg};
- $arg;
-}
-
-tie my %cache => 'ExpireTest';
-memoize 'id',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT';
-$n++; print "ok $n\n";
-
-my $i;
-for $i (1, 2, 3, 1, 2, 1) {
- $n++;
- unless ($i == id($i)) {
- print "not ";
- }
- print "ok $n\n";
-}
-
-for $i (1, 2, 3) {
- $n++;
- unless ($CALLS{$i} == 1) {
- print "not ";
- }
- print "ok $n\n";
-}
-
-ExpireTest::expire(1);
-
-for $i (1, 2, 3) {
- my $v = id($i);
-}
-
-for $i (1, 2, 3) {
- $n++;
- unless ($CALLS{$i} == 1 + ($i == 1)) {
- print "not ";
- }
- print "ok $n\n";
-}
-
-ExpireTest::expire(1);
-ExpireTest::expire(2);
-
-for $i (1, 2, 3) {
- my $v = id($i);
-}
-
-for $i (1, 2, 3) {
- $n++;
- unless ($CALLS{$i} == 4 - $i) {
- print "not ";
- }
- print "ok $n\n";
-}
-
-exit 0;
diff --git a/cpan/Memoize/t/expmod.t b/cpan/Memoize/t/expmod.t
index 55fd85ec72..4e82b3904b 100644
--- a/cpan/Memoize/t/expmod.t
+++ b/cpan/Memoize/t/expmod.t
@@ -1,5 +1,7 @@
+use strict; use warnings;
+use Memoize;
use Memoize::Expire;
-use Test::More tests => 8;
+use Test::More tests => 22;
tie my %h => 'Memoize::Expire', HASH => \my %backing;
@@ -23,3 +25,33 @@ my $contents = eval { +{ %h } };
ok defined $contents, 'dumping the tied hash works';
is_deeply $contents, { foo => 1, bar => $bar }, ' ... with the expected contents';
+
+########################################################################
+
+my $RETURN = 1;
+my %CALLS;
+
+tie my %cache => 'Memoize::Expire', NUM_USES => 2;
+memoize sub { ++$CALLS{$_[0]}; $RETURN },
+ SCALAR_CACHE => [ HASH => \%cache ],
+ LIST_CACHE => 'FAULT',
+ INSTALL => 'call';
+
+is call($_), 1, "$_ gets new val" for 0..3;
+
+is_deeply \%CALLS, {0=>1,1=>1,2=>1,3=>1}, 'memoized function called once per argument';
+
+$RETURN = 2;
+is call(1), 1, '1 expires';
+is call(1), 2, '1 gets new val';
+is call(2), 1, '2 expires';
+
+is_deeply \%CALLS, {0=>1,1=>2,2=>1,3=>1}, 'memoized function called for expired argument';
+
+$RETURN = 3;
+is call(0), 1, '0 expires';
+is call(1), 2, '1 expires';
+is call(2), 3, '2 gets new val';
+is call(3), 1, '3 expires';
+
+is_deeply \%CALLS, {0=>1,1=>2,2=>2,3=>1}, 'memoized function called for other expired argument';
diff --git a/cpan/Memoize/t/expmod_n.t b/cpan/Memoize/t/expmod_n.t
deleted file mode 100644
index df5585add9..0000000000
--- a/cpan/Memoize/t/expmod_n.t
+++ /dev/null
@@ -1,59 +0,0 @@
-use strict; use warnings;
-use Memoize;
-
-my $n = 0;
-
-print "1..22\n";
-
-++$n; print "ok $n\n";
-
-my ($RETURN, %CALLS);
-$RETURN = 1;
-%CALLS = ();
-sub call {
-# print "CALL $_[0] => $RETURN\n";
- ++$CALLS{$_[0]};
- $RETURN;
-}
-
-require Memoize::Expire;
-++$n; print "ok $n\n";
-
-tie my %cache => 'Memoize::Expire', NUM_USES => 2;
-memoize 'call',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT';
-
-# $Memoize::Expire::DEBUG = 1;
-++$n; print "ok $n\n";
-
-# 3--6
-for (0,1,2,3) {
- print "not " unless call($_) == 1;
- ++$n; print "ok $n\n";
-}
-
-# 7--10
-for (keys %CALLS) {
- print "not " unless $CALLS{$_} == (1,1,1,1)[$_];
- ++$n; print "ok $n\n";
-}
-
-# 11--13
-$RETURN = 2;
-++$n; print ((call(1) == 1 ? '' : 'not '), "ok $n\n"); # 1 expires
-++$n; print ((call(1) == 2 ? '' : 'not '), "ok $n\n"); # 1 gets new val
-++$n; print ((call(2) == 1 ? '' : 'not '), "ok $n\n"); # 2 expires
-
-# 14--17
-$RETURN = 3;
-for (0,1,2,3) {
- # 0 expires, 1 expires, 2 gets new val, 3 expires
- print "not " unless call($_) == (1,2,3,1)[$_];
- ++$n; print "ok $n\n";
-}
-
-for (0,1,2,3) {
- print "not " unless $CALLS{$_} == (1,2,2,1)[$_];
- ++$n; print "ok $n\n";
-}
diff --git a/cpan/Memoize/t/expmod_t.t b/cpan/Memoize/t/expmod_t.t
index d3c46efe11..1b63b09def 100644
--- a/cpan/Memoize/t/expmod_t.t
+++ b/cpan/Memoize/t/expmod_t.t
@@ -1,5 +1,6 @@
use strict; use warnings;
use Memoize;
+use Memoize::Expire;
my $DEBUG = 0;
my $LIFETIME = 15;
@@ -15,24 +16,13 @@ if ($ENV{PERL_MEMOIZE_TESTS_FAST_ONLY}) {
print "# Testing the timed expiration policy.\n";
print "# This will take about thirty seconds.\n";
-print "1..26\n";
-
-require Memoize::Expire;
-++$test; print "ok $test - Expire loaded\n";
-
-sub now {
-# print "NOW: @_ ", time(), "\n";
- time;
-}
+print "1..24\n";
tie my %cache => 'Memoize::Expire', LIFETIME => $LIFETIME;
-
-memoize 'now',
- SCALAR_CACHE => [HASH => \%cache ],
- LIST_CACHE => 'FAULT'
- ;
-
-++$test; print "ok $test - function memoized\n";
+memoize sub { time },
+ SCALAR_CACHE => [ HASH => \%cache ],
+ LIST_CACHE => 'FAULT',
+ INSTALL => 'now';
my (@before, @after, @now);
diff --git a/cpan/Memoize/t/flush.t b/cpan/Memoize/t/flush.t
index 15df21787e..33eceac887 100644
--- a/cpan/Memoize/t/flush.t
+++ b/cpan/Memoize/t/flush.t
@@ -1,34 +1,24 @@
use strict; use warnings;
use Memoize qw(flush_cache memoize);
-
-print "1..8\n";
-print "ok 1\n";
+use Test::More tests => 9;
my $V = 100;
sub VAL { $V }
-memoize 'VAL';
-print "ok 2\n";
-
-my $c1 = VAL();
-print (($c1 == 100) ? "ok 3\n" : "not ok 3\n");
+ok eval { memoize('VAL'); 1 }, 'memozing the test function';
+is VAL(), 100, '... with the expected return value';
$V = 200;
-$c1 = VAL();
-print (($c1 == 100) ? "ok 4\n" : "not ok 4\n");
+is VAL(), 100, '... which is expectedly sticky';
-flush_cache('VAL');
-$c1 = VAL();
-print (($c1 == 200) ? "ok 5\n" : "not ok 5\n");
+ok eval { flush_cache('VAL'); 1 }, 'flusing the cache by name works';
+is VAL(), 200, '... with the expected new return value';
$V = 300;
-$c1 = VAL();
-print (($c1 == 200) ? "ok 6\n" : "not ok 6\n");
+is VAL(), 200, '... which is expectedly sticky';
-flush_cache(\&VAL);
-$c1 = VAL();
-print (($c1 == 300) ? "ok 7\n" : "not ok 7\n");
+ok eval { flush_cache(\&VAL); 1 }, 'flusing the cache by name works';
+is VAL(), 300, '... with the expected new return value';
$V = 400;
-$c1 = VAL();
-print (($c1 == 300) ? "ok 8\n" : "not ok 8\n");
+is VAL(), 300, '... which is expectedly sticky';
diff --git a/cpan/Memoize/t/lib/DBMTest.pm b/cpan/Memoize/t/lib/DBMTest.pm
new file mode 100644
index 0000000000..59c18d5d75
--- /dev/null
+++ b/cpan/Memoize/t/lib/DBMTest.pm
@@ -0,0 +1,102 @@
+use strict; use warnings;
+
+package DBMTest;
+
+my ($module, $is_scalar_only);
+
+use Memoize qw(memoize unmemoize);
+use Test::More;
+
+sub errlines { split /\n/, $@ }
+
+my $ARG = 'Keith Bostic is a pinhead';
+
+sub c5 { 5 }
+sub c23 { 23 }
+
+sub test_dbm { SKIP: {
+ tie my %cache, $module, @_ or die $!;
+
+ my $sub = eval { unmemoize memoize sub {}, LIST_CACHE => [ HASH => \%cache ] };
+ my $errx = qr/^You can't use \Q$module\E for LIST_CACHE because it can only store scalars/;
+ if ($is_scalar_only) {
+ is $sub, undef, "use as LIST_CACHE fails";
+ like $@, $errx, '... with the expected error';
+ } else {
+ ok $sub, "use as LIST_CACHE succeeds";
+ }
+
+ $sub = eval { no warnings; unmemoize memoize sub {}, LIST_CACHE => [ TIE => $module, @_ ] };
+ if ($is_scalar_only) {
+ is $sub, undef, '... including under the TIE option';
+ like $@, $errx, '... with the expected error';
+ } else {
+ ok $sub, 'use as LIST_CACHE succeeds';
+ }
+
+ eval { exists $cache{'dummy'}; 1 }
+ or skip join("\n", 'exists() unsupported', errlines), 3;
+
+ memoize 'c5',
+ SCALAR_CACHE => [ HASH => \%cache ],
+ LIST_CACHE => 'FAULT';
+
+ is c5($ARG), 5, 'store value during first memoization';
+ unmemoize 'c5';
+
+ untie %cache;
+
+ tie %cache, $module, @_ or die $!;
+
+ # Now something tricky---we'll memoize c23 with the wrong table that
+ # has the 5 already cached.
+ memoize 'c23',
+ SCALAR_CACHE => [ HASH => \%cache ],
+ LIST_CACHE => 'FAULT';
+
+ is c23($ARG), 5, '... and find it still there after second memoization';
+ unmemoize 'c23';
+
+ untie %cache;
+
+ { no warnings; memoize 'c23',
+ SCALAR_CACHE => [ TIE => $module, @_ ],
+ LIST_CACHE => 'FAULT';
+ }
+
+ is c23($ARG), 5, '... as well as a third memoization via TIE';
+ unmemoize 'c23';
+} }
+
+my @file;
+
+sub cleanup { 1 while unlink @file }
+
+sub import {
+ (undef, $module, my %arg) = (shift, @_);
+
+ $is_scalar_only = $arg{'is_scalar_only'} ? 2 : 0;
+ eval "require $module"
+ ? plan tests => 5 + $is_scalar_only + ($arg{extra_tests}||0)
+ : plan skip_all => join "\n# ", "Could not load $module", errlines;
+
+ my ($basename) = map { s/.*:://; s/_file\z//; 'm_'.$_.$$ } lc $module;
+ my $dirfext = $^O eq 'VMS' ? '.sdbm_dir' : '.dir'; # copypaste from DBD::DBM
+ @file = map { $_, "$_.db", "$_.pag", $_.$dirfext } $basename;
+ cleanup;
+
+ my $pkg = caller;
+ no strict 'refs';
+ *{$pkg.'::'.$_} = \&$_ for qw(test_dbm cleanup);
+ *{$pkg.'::file'} = \$basename;
+}
+
+END {
+ cleanup;
+ if (my @failed = grep -e, @file) {
+ @failed = grep !unlink, @failed; # to set $!
+ warn "Can't unlink @failed! ($!)\n" if @failed;
+ }
+}
+
+1;
diff --git a/cpan/Memoize/t/lib/ExpireFile.pm b/cpan/Memoize/t/lib/ExpireFile.pm
deleted file mode 100644
index 8c66d4bdd4..0000000000
--- a/cpan/Memoize/t/lib/ExpireFile.pm
+++ /dev/null
@@ -1,57 +0,0 @@
-use strict; use warnings;
-
-package ExpireFile;
-
-use Carp;
-
-my $Zero = pack("N", 0);
-
-sub TIEHASH {
- my ($package, %args) = @_;
- my $cache = $args{HASH} || {};
- bless {ARGS => \%args, C => $cache} => $package;
-}
-
-
-sub STORE {
-# print "Expiry manager STORE handler\n";
- my ($self, $key, $data) = @_;
- my $cache = $self->{C};
- my $cur_date = pack("N", (stat($key))[9]);
- $cache->{"C$key"} = $data;
- $cache->{"T$key"} = $cur_date;
-}
-
-sub FETCH {
- my ($self, $key) = @_;
- $self->{C}{"C$key"};
-}
-
-sub EXISTS {
-# print "Expiry manager EXISTS handler\n";
- my ($self, $key) = @_;
- my $cache_date = $self->{C}{"T$key"} || $Zero;
- my $file_date = pack("N", (stat($key))[9]);#
-# if ($self->{ARGS}{CHECK_DATE} && $old_date gt $cur_date) {
-# return $self->{ARGS}{CHECK_DATE}->($key, $old_date, $cur_date);
-# }
- my $res = $cache_date ge $file_date;
-# print $res ? "... still good\n" : "... expired\n";
- $res;
-}
-
-1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-ExpireFile - test for Memoize expiration semantics
-
-=head1 DESCRIPTION
-
-See L<Memoize::Expire>.
-
-=cut
diff --git a/cpan/Memoize/t/lib/ExpireTest.pm b/cpan/Memoize/t/lib/ExpireTest.pm
deleted file mode 100644
index 801f2af1b1..0000000000
--- a/cpan/Memoize/t/lib/ExpireTest.pm
+++ /dev/null
@@ -1,51 +0,0 @@
-use strict; use warnings;
-
-package ExpireTest;
-
-my %cache;
-
-sub TIEHASH {
- my ($pack) = @_;
- bless \%cache => $pack;
-}
-
-sub EXISTS {
- my ($cache, $key) = @_;
- exists $cache->{$key} ? 1 : 0;
-}
-
-sub FETCH {
- my ($cache, $key) = @_;
- $cache->{$key};
-}
-
-sub STORE {
- my ($cache, $key, $val) = @_;
- $cache->{$key} = $val;
-}
-
-sub expire {
- my ($key) = @_;
- delete $cache{$key};
-}
-
-1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-ExpireTest - test for Memoize expiration semantics
-
-=head1 DESCRIPTION
-
-This module is just for testing expiration semantics. It's not a very
-good example of how to write an expiration module.
-
-If you are looking for an example, I recommend that you look at the
-simple example in the Memoize::Expire documentation, or at the code
-for Memoize::Expire itself.
-
-=cut
diff --git a/cpan/Memoize/t/merge.t b/cpan/Memoize/t/merge.t
deleted file mode 100644
index d0fc94990c..0000000000
--- a/cpan/Memoize/t/merge.t
+++ /dev/null
@@ -1,37 +0,0 @@
-use strict; use warnings;
-use Memoize qw(memoize unmemoize);
-use Test::More tests => 18;
-
-my (%cache, $num_cache_misses);
-sub cacheit {
- ++$num_cache_misses;
- "cacheit result";
-}
-
-memoize 'cacheit', LIST_CACHE => [HASH => \%cache], SCALAR_CACHE => 'MERGE';
-my $been_here;
-{
- 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';
-
- last if $been_here++;
-
- unmemoize 'cacheit';
- ( $num_cache_misses, %cache ) = ();
-
- memoize 'cacheit', SCALAR_CACHE => [HASH => \%cache], LIST_CACHE => 'MERGE';
- redo;
-}
diff --git a/cpan/Memoize/t/normalize.t b/cpan/Memoize/t/normalize.t
index 9a1b1caff3..7c4caa3122 100644
--- a/cpan/Memoize/t/normalize.t
+++ b/cpan/Memoize/t/normalize.t
@@ -1,7 +1,6 @@
use strict; use warnings;
use Memoize;
-
-print "1..8\n";
+use Test::More tests => 10;
sub n_null { '' }
@@ -24,37 +23,42 @@ my $a_allmemo = memoize('a3', INSTALL => undef, NORMALIZER => 'n_null');
my @ARGS;
@ARGS = (1, 2, 3, 2, 1);
-my @res;
-@res = map { &$a_normal($_) } @ARGS;
-print ((("@res" eq "1-1 2-2 3-3 2-2 1-1") ? '' : 'not '), "ok 1\n");
-
-@res = map { &$a_nomemo($_) } @ARGS;
-print ((("@res" eq "1-1 2-2 3-3 2-4 1-5") ? '' : 'not '), "ok 2\n");
-
-@res = map { &$a_allmemo($_) } @ARGS;
-print ((("@res" eq "1-1 1-1 1-1 1-1 1-1") ? '' : 'not '), "ok 3\n");
+is_deeply [map $a_normal->($_), @ARGS], [qw(1-1 2-2 3-3 2-2 1-1)], 'no normalizer';
+is_deeply [map $a_nomemo->($_), @ARGS], [qw(1-1 2-2 3-3 2-4 1-5)], 'n_diff';
+is_deeply [map $a_allmemo->($_), @ARGS], [qw(1-1 1-1 1-1 1-1 1-1)], 'n_null';
-
# Test fully-qualified name and installation
my $COUNT;
$COUNT = 0;
sub parity { $COUNT++; $_[0] % 2 }
sub parnorm { $_[0] % 2 }
memoize('parity', NORMALIZER => 'main::parnorm');
-@res = map { &parity($_) } @ARGS;
-print ((("@res" eq "1 0 1 0 1") ? '' : 'not '), "ok 4\n");
-print (( ($COUNT == 2) ? '' : 'not '), "ok 5\n");
+is_deeply [map parity($_), @ARGS], [qw(1 0 1 0 1)], 'parity normalizer';
+is $COUNT, 2, '... with the expected number of calls';
# Test normalization with reference to normalizer function
$COUNT = 0;
sub par2 { $COUNT++; $_[0] % 2 }
memoize('par2', NORMALIZER => \&parnorm);
-@res = map { &par2($_) } @ARGS;
-print ((("@res" eq "1 0 1 0 1") ? '' : 'not '), "ok 6\n");
-print (( ($COUNT == 2) ? '' : 'not '), "ok 7\n");
+is_deeply [map par2($_), @ARGS], [qw(1 0 1 0 1)], '... also installable by coderef';
+is $COUNT, 2, '... still with the expected number of calls';
$COUNT = 0;
sub count_uninitialized { $COUNT += join('', @_) =~ /\AUse of uninitialized value / }
my $war1 = memoize(sub {1}, NORMALIZER => sub {undef});
{ local $SIG{__WARN__} = \&count_uninitialized; $war1->() }
-print (( ($COUNT == 0) ? '' : 'not '), "ok 8\n");
+is $COUNT, 0, 'no warning when normalizer returns undef';
+
+# Context propagated correctly to normalizer?
+sub n {
+ my $which = wantarray ? 'list' : 'scalar';
+ local $Test::Builder::Level = $Test::Builder::Level + 2;
+ $Test::Builder::Level += 2 # wrapper currently uses more stack frames on threaded perls
+ # tripwire: this will only compile for as long as Memoize.pm still loads Config.pm
+ if do { package Memoize; $Config{'usethreads'} };
+ is $_[0], $which, "$which context propagates properly";
+}
+sub f { 1 }
+memoize('f', NORMALIZER => 'n');
+my $s = f 'scalar';
+my @a = f 'list';
diff --git a/cpan/Memoize/t/prototype.t b/cpan/Memoize/t/prototype.t
deleted file mode 100644
index d060173784..0000000000
--- a/cpan/Memoize/t/prototype.t
+++ /dev/null
@@ -1,35 +0,0 @@
-use strict; use warnings;
-use Memoize;
-
-my ($EXPECTED_WARNING, $RES, @q, $r);
-$EXPECTED_WARNING = '(no warning expected)';
-
-print "1..4\n";
-
-sub q1 ($) { $_[0] + 1 }
-sub q2 () { time }
-sub q3 { join "--", @_ }
-
-$SIG{__WARN__} = \&handle_warnings;
-
-$RES = 'ok';
-memoize 'q1';
-print "$RES 1\n";
-
-$RES = 'ok';
-memoize 'q2';
-print "$RES 2\n";
-
-$RES = 'ok';
-memoize 'q3';
-print "$RES 3\n";
-
-# Let's see if the prototype is actually honored
-@q = (1..5);
-$r = q1(@q);
-print (($r == 6) ? '' : 'not ', "ok 4\n");
-
-sub handle_warnings {
- print $_[0];
- $RES = 'not ok' unless $_[0] eq $EXPECTED_WARNING;
-}
diff --git a/cpan/Memoize/t/rememoize.t b/cpan/Memoize/t/rememoize.t
deleted file mode 100644
index 033a676930..0000000000
--- a/cpan/Memoize/t/rememoize.t
+++ /dev/null
@@ -1,36 +0,0 @@
-use strict; use warnings;
-
-use Memoize qw(memoize unmemoize);
-use Test::More tests => 19;
-
-# Memoizing a function multiple times separately is not very useful
-# but it should not break unmemoize or make memoization lose its mind
-
-my $ret;
-my $dummy = sub { $ret };
-ok memoize $dummy, INSTALL => 'memo1';
-ok memoize $dummy, INSTALL => 'memo2';
-ok defined &memo1, 'memoized once';
-ok defined &memo2, 'memoized twice';
-$@ = '';
-ok eval { unmemoize 'memo1' }, 'unmemoized once';
-is $@, '', '... and no exception';
-$@ = '';
-ok eval { unmemoize 'memo2' }, 'unmemoized twice';
-is $@, '', '... and no exception';
-is \&memo1, $dummy, 'unmemoized installed once';
-is \&memo2, $dummy, 'unmemoized installed twice';
-
-my @quux = qw(foo bar baz);
-my %memo = map +($_ => memoize $dummy), @quux;
-for (@quux) { $ret = $_; is $memo{$_}->(), $_, "\$memo{$_}->() returns $_" }
-for (@quux) { undef $ret; is $memo{$_}->(), $_, "\$memo{$_}->() returns $_" }
-
-my $destroyed = 0;
-sub Counted::DESTROY { ++$destroyed }
-{
- my $memo = memoize $dummy, map +( "$_\_CACHE" => [ HASH => bless {}, 'Counted' ] ), qw(LIST SCALAR);
- ok $memo, 'memoize anon';
- ok eval { unmemoize $memo }, 'unmemoized anon';
-}
-is $destroyed, 2, 'no cyclic references';
diff --git a/cpan/Memoize/t/speed.t b/cpan/Memoize/t/speed.t
deleted file mode 100644
index 868a7acb0d..0000000000
--- a/cpan/Memoize/t/speed.t
+++ /dev/null
@@ -1,107 +0,0 @@
-use strict; use warnings;
-use Memoize;
-
-if ($ENV{PERL_MEMOIZE_TESTS_FAST_ONLY}) {
- print "1..0 # Skipped: Slow tests disabled\n";
- exit 0;
-}
-$| = 1;
-
-# If we don't say anything, maybe nobody will notice.
-# print STDERR "\nWarning: I'm testing the speedup. This might take up to thirty seconds.\n ";
-
-my $COARSE_TIME = 1;
-
-sub times_to_time { my ($u) = times; $u; }
-if ($^O eq 'riscos') {
- eval {require Time::HiRes; *my_time = \&Time::HiRes::time };
- if ($@) { *my_time = sub { time }; $COARSE_TIME = 1 }
-} else {
- *my_time = \&times_to_time;
-}
-
-
-print "1..6\n";
-
-
-
-# This next test finds an example that takes a long time to run, then
-# checks to make sure that the run is actually speeded up by memoization.
-# In some sense, this is the most essential correctness test in the package.
-#
-# We do this by running the fib() function with successfily larger
-# arguments until we find one that tales at least $LONG_RUN seconds
-# to execute. Then we memoize fib() and run the same call cagain. If
-# it doesn't produce the same test in less than one-tenth the time,
-# something is seriously wrong.
-#
-# $LONG_RUN is the number of seconds that the function call must last
-# in order for the call to be considered sufficiently long.
-
-my ($N, $COUNT, $RESULT, $ELAPSED, $start, $RESULT2, $ELAPSED2);
-
-sub fib {
- my $n = shift;
- $COUNT++;
- return $n if $n < 2;
- fib($n-1) + fib($n-2);
-}
-
-sub max { $_[0] > $_[1] ?
- $_[0] : $_[1]
- }
-
-$N = 1;
-
-$ELAPSED = 0;
-
-my $LONG_RUN = 11;
-
-while (1) {
- my $start = time;
- $COUNT=0;
- $RESULT = fib($N);
- $ELAPSED = time - $start;
- last if $ELAPSED >= $LONG_RUN;
- if ($ELAPSED > 1) {
- print "# fib($N) took $ELAPSED seconds.\n" if $N % 1 == 0;
- # we'd expect that fib(n+1) takes about 1.618 times as long as fib(n)
- # so now that we have a longish run, let's estimate the value of $N
- # that will get us a sufficiently long run.
- $N += 1 + int(log($LONG_RUN/$ELAPSED)/log(1.618));
- print "# OK, N=$N ought to do it.\n";
- # It's important not to overshoot here because the running time
- # is exponential in $N. If we increase $N too aggressively,
- # the user will be forced to wait a very long time.
- } else {
- $N++;
- }
-}
-
-print "# OK, fib($N) was slow enough; it took $ELAPSED seconds.\n";
-print "# Total calls: $COUNT.\n";
-
-&memoize('fib');
-
-$COUNT=0;
-$start = time;
-$RESULT2 = fib($N);
-$ELAPSED2 = time - $start + .001; # prevent division by 0 errors
-print (($RESULT == $RESULT2) ? "ok 1\n" : "not ok 1\n");
-# If it's not ten times as fast, something is seriously wrong.
-print "# ELAPSED2=$ELAPSED2 seconds.\n";
-print (($ELAPSED/$ELAPSED2 > 10) ? "ok 2\n" : "not ok 2\n");
-
-# If it called the function more than $N times, it wasn't memoized properly
-print (($COUNT > $N) ? "ok 3\n" : "not ok 3\n");
-
-# Do it again. Should be even faster this time.
-$COUNT = 0;
-$start = time;
-$RESULT2 = fib($N);
-$ELAPSED2 = time - $start + .001; # prevent division by 0 errors
-print (($RESULT == $RESULT2) ? "ok 4\n" : "not ok 4\n");
-print "# ELAPSED2=$ELAPSED2 seconds.\n";
-print (($ELAPSED/$ELAPSED2 > 10) ? "ok 5\n" : "not ok 5\n");
-# This time it shouldn't have called the function at all.
-print ($COUNT == 0 ? "ok 6\n" : "not ok 6\n");
diff --git a/cpan/Memoize/t/tie.t b/cpan/Memoize/t/tie.t
index 8f507b6ee2..fbae0e9886 100644
--- a/cpan/Memoize/t/tie.t
+++ b/cpan/Memoize/t/tie.t
@@ -1,80 +1,8 @@
use strict; use warnings;
-use Memoize 0.52 qw(memoize unmemoize);
use Fcntl;
-eval {require Memoize::AnyDBM_File};
-if ($@) {
- print "1..0 # Skipped: Could not load Memoize::AnyDBM_File\n";
- exit 0;
-}
+use lib 't/lib';
+use DBMTest 'Memoize::AnyDBM_File', is_scalar_only => 1;
-print "1..4\n";
-
-sub i {
- $_[0];
-}
-
-my $ARG;
-$ARG = 'Keith Bostic is a pinhead';
-
-sub c119 { 119 }
-sub c7 { 7 }
-sub c43 { 43 }
-sub c23 { 23 }
-sub c5 { 5 }
-
-sub n {
- $_[0]+1;
-}
-
-my ($file, @files);
-$file = "md$$";
-@files = ($file, "$file.db", "$file.dir", "$file.pag");
-1 while unlink @files;
-
-
-tryout('Memoize::AnyDBM_File', $file, 1); # Test 1..4
-# tryout('DB_File', $file, 1); # Test 1..4
-1 while unlink $file, "$file.dir", "$file.pag";
-
-sub tryout {
- my ($tiepack, $file, $testno) = @_;
-
- tie my %cache => $tiepack, $file, O_RDWR | O_CREAT, 0666
- or die $!;
-
- memoize 'c5',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t1 = c5($ARG);
- my $t2 = c5($ARG);
- print (($t1 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t2 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c5';
-
- # Now something tricky---we'll memoize c23 with the wrong table that
- # has the 5 already cached.
- memoize 'c23',
- SCALAR_CACHE => ['HASH', \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t3 = c23($ARG);
- my $t4 = c23($ARG);
- $testno++;
- print (($t3 == 5) ? "ok $testno\n" : "not ok $testno # Result $t3\n");
- $testno++;
- print (($t4 == 5) ? "ok $testno\n" : "not ok $testno # Result $t4\n");
- unmemoize 'c23';
-}
-
-{
- my @present = grep -e, @files;
- my @failed;
- if (@present && (@failed = grep { not unlink } @present)) {
- warn "Can't unlink @failed! ($!)";
- }
-}
+test_dbm $file, O_RDWR | O_CREAT, 0666;
+cleanup;
diff --git a/cpan/Memoize/t/tie_db.t b/cpan/Memoize/t/tie_db.t
new file mode 100644
index 0000000000..3c72e7fbd3
--- /dev/null
+++ b/cpan/Memoize/t/tie_db.t
@@ -0,0 +1,8 @@
+use strict; use warnings;
+use Fcntl;
+
+use lib 't/lib';
+use DBMTest 'DB_File', is_scalar_only => 1;
+
+test_dbm $file, O_RDWR | O_CREAT, 0666;
+cleanup;
diff --git a/cpan/Memoize/t/tie_gdbm.t b/cpan/Memoize/t/tie_gdbm.t
index e124b39599..e738cc454d 100644
--- a/cpan/Memoize/t/tie_gdbm.t
+++ b/cpan/Memoize/t/tie_gdbm.t
@@ -1,65 +1,8 @@
use strict; use warnings;
-use Memoize 0.45 qw(memoize unmemoize);
use Fcntl;
-sub i {
- $_[0];
-}
+use lib 't/lib';
+use DBMTest 'GDBM_File', is_scalar_only => 1;
-sub c119 { 119 }
-sub c7 { 7 }
-sub c43 { 43 }
-sub c23 { 23 }
-sub c5 { 5 }
-
-sub n {
- $_[0]+1;
-}
-
-eval {require GDBM_File};
-if ($@) {
- print "1..0 # Skipped: Could not load GDBM_File\n";
- exit 0;
-}
-
-print "1..4\n";
-
-my $file;
-$file = "md$$";
-1 while unlink $file, "$file.dir", "$file.pag";
-tryout('GDBM_File', $file, 1); # Test 1..4
-1 while unlink $file, "$file.dir", "$file.pag";
-
-sub tryout {
- my ($tiepack, $file, $testno) = @_;
-
- tie my %cache => $tiepack, $file, &GDBM_File::GDBM_NEWDB, 0666
- or die $!;
-
- memoize 'c5',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t1 = c5();
- my $t2 = c5();
- print (($t1 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t2 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c5';
-
- # Now something tricky---we'll memoize c23 with the wrong table that
- # has the 5 already cached.
- memoize 'c23',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t3 = c23();
- my $t4 = c23();
- $testno++;
- print (($t3 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t4 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c23';
-}
+test_dbm $file, &GDBM_File::GDBM_WRCREAT, 0666;
+cleanup;
diff --git a/cpan/Memoize/t/tie_ndbm.t b/cpan/Memoize/t/tie_ndbm.t
index 53e13f3c2e..b261c1cc70 100644
--- a/cpan/Memoize/t/tie_ndbm.t
+++ b/cpan/Memoize/t/tie_ndbm.t
@@ -1,65 +1,8 @@
use strict; use warnings;
-use Memoize 0.45 qw(memoize unmemoize);
use Fcntl;
-sub i {
- $_[0];
-}
+use lib 't/lib';
+use DBMTest 'Memoize::NDBM_File', is_scalar_only => 1;
-sub c119 { 119 }
-sub c7 { 7 }
-sub c43 { 43 }
-sub c23 { 23 }
-sub c5 { 5 }
-
-sub n {
- $_[0]+1;
-}
-
-eval {require Memoize::NDBM_File};
-if ($@) {
- print "1..0 # Skipped: Could not load Memoize::NDBM_File\n";
- exit 0;
-}
-
-print "1..4\n";
-
-my $file;
-$file = "md$$";
-1 while unlink $file, "$file.dir", "$file.pag", "$file.db";
-tryout('Memoize::NDBM_File', $file, 1); # Test 1..4
-1 while unlink $file, "$file.dir", "$file.pag", "$file.db";
-
-sub tryout {
- my ($tiepack, $file, $testno) = @_;
-
- tie my %cache => $tiepack, $file, O_RDWR | O_CREAT, 0666
- or die $!;
-
- memoize 'c5',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t1 = c5();
- my $t2 = c5();
- print (($t1 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t2 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c5';
-
- # Now something tricky---we'll memoize c23 with the wrong table that
- # has the 5 already cached.
- memoize 'c23',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t3 = c23();
- my $t4 = c23();
- $testno++;
- print (($t3 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t4 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c23';
-}
+test_dbm $file, O_RDWR | O_CREAT, 0666;
+cleanup;
diff --git a/cpan/Memoize/t/tie_odbm.t b/cpan/Memoize/t/tie_odbm.t
new file mode 100644
index 0000000000..611afc3ef5
--- /dev/null
+++ b/cpan/Memoize/t/tie_odbm.t
@@ -0,0 +1,8 @@
+use strict; use warnings;
+use Fcntl;
+
+use lib 't/lib';
+use DBMTest 'ODBM_File', is_scalar_only => 1;
+
+test_dbm $file, O_RDWR | O_CREAT, 0666;
+cleanup;
diff --git a/cpan/Memoize/t/tie_sdbm.t b/cpan/Memoize/t/tie_sdbm.t
index 9c5fa00214..d0126c2ed7 100644
--- a/cpan/Memoize/t/tie_sdbm.t
+++ b/cpan/Memoize/t/tie_sdbm.t
@@ -1,71 +1,8 @@
use strict; use warnings;
-use Memoize 0.45 qw(memoize unmemoize);
use Fcntl;
-sub i {
- $_[0];
-}
+use lib 't/lib';
+use DBMTest 'SDBM_File', is_scalar_only => 1;
-sub c119 { 119 }
-sub c7 { 7 }
-sub c43 { 43 }
-sub c23 { 23 }
-sub c5 { 5 }
-
-sub n {
- $_[0]+1;
-}
-
-eval {require SDBM_File};
-if ($@) {
- print "1..0 # Skipped: Could not load SDBM_File\n";
- exit 0;
-}
-
-print "1..4\n";
-
-my $file;
-$file = "md$$";
-1 while unlink $file, "$file.dir", "$file.pag";
-if ( $^O eq 'VMS' ) {
- 1 while unlink "$file.sdbm_dir";
-}
-tryout('SDBM_File', $file, 1); # Test 1..4
-1 while unlink $file, "$file.dir", "$file.pag";
-if ( $^O eq 'VMS' ) {
- 1 while unlink "$file.sdbm_dir";
-}
-
-sub tryout {
- my ($tiepack, $file, $testno) = @_;
-
- tie my %cache => $tiepack, $file, O_RDWR | O_CREAT, 0666
- or die $!;
-
- memoize 'c5',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t1 = c5();
- my $t2 = c5();
- print (($t1 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t2 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c5';
-
- # Now something tricky---we'll memoize c23 with the wrong table that
- # has the 5 already cached.
- memoize 'c23',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
-
- my $t3 = c23();
- my $t4 = c23();
- $testno++;
- print (($t3 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t4 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c23';
-}
+test_dbm $file, O_RDWR | O_CREAT, 0666;
+cleanup;
diff --git a/cpan/Memoize/t/tie_storable.t b/cpan/Memoize/t/tie_storable.t
index da43725507..99e0cfdcd2 100644
--- a/cpan/Memoize/t/tie_storable.t
+++ b/cpan/Memoize/t/tie_storable.t
@@ -1,75 +1,16 @@
-# -*- mode: perl; perl-indent-level: 2 -*-
-# vim: ts=8 sw=2 sts=2 noexpandtab
-
use strict; use warnings;
-use Memoize 0.45 qw(memoize unmemoize);
-# $Memoize::Storable::Verbose = 0;
-
-eval {require Memoize::Storable};
-if ($@) {
- print "1..0 # Skipped: Could not load Memoize::Storable\n";
- exit 0;
-}
-
-sub i {
- $_[0];
-}
-
-sub c119 { 119 }
-sub c7 { 7 }
-sub c43 { 43 }
-sub c23 { 23 }
-sub c5 { 5 }
-
-sub n {
- $_[0]+1;
-}
-
-print "1..9\n";
-
-my $file;
-$file = "storable$$";
-1 while unlink $file;
-tryout('Memoize::Storable', $file, 1); # Test 1..4
-1 while unlink $file;
-tryout('Memoize::Storable', $file, 5, 'nstore'); # Test 5..8
-print eval { Storable->VERSION('2.16') }
- ? (Storable::file_magic($file)->{'netorder'} ? "ok 9\n" : "not ok 9\n")
- : "ok 9 # skip Storable $Storable::VERSION too old for file_magic\n";
-1 while unlink $file;
-
-sub tryout {
- my ($tiepack, $file, $testno, $option) = @_;
-
- tie my %cache => $tiepack, $file, $option || ()
- or die $!;
-
- memoize 'c5',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
+use Test::More;
- my $t1 = c5();
- my $t2 = c5();
- print (($t1 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t2 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c5';
- 1;
- 1;
+use lib 't/lib';
+use DBMTest 'Memoize::Storable', extra_tests => 1;
- # Now something tricky---we'll memoize c23 with the wrong table that
- # has the 5 already cached.
- memoize 'c23',
- SCALAR_CACHE => [HASH => \%cache],
- LIST_CACHE => 'FAULT'
- ;
+test_dbm $file;
+cleanup;
- my $t3 = c23();
- my $t4 = c23();
- $testno++;
- print (($t3 == 5) ? "ok $testno\n" : "not ok $testno\n");
- $testno++;
- print (($t4 == 5) ? "ok $testno\n" : "not ok $testno\n");
- unmemoize 'c23';
+SKIP: {
+ skip "skip Storable $Storable::VERSION too old for last_op_in_netorder", 1
+ unless eval { Storable->VERSION('0.609') };
+ { tie my %cache, 'Memoize::Storable', $file, 'nstore' or die $! }
+ ok Storable::last_op_in_netorder(), 'nstore option works';
+ cleanup;
}
diff --git a/cpan/Memoize/t/tiefeatures.t b/cpan/Memoize/t/tiefeatures.t
deleted file mode 100644
index a3c12659ad..0000000000
--- a/cpan/Memoize/t/tiefeatures.t
+++ /dev/null
@@ -1,47 +0,0 @@
-use strict; use warnings;
-use Memoize 0.45 qw(memoize unmemoize);
-use Fcntl;
-
-print "1..10\n";
-
-# Test MERGE
-sub xx {
- wantarray();
-}
-
-my $s = xx();
-print ((!$s) ? "ok 1\n" : "not ok 1\n");
-my ($a) = xx();
-print (($a) ? "ok 2\n" : "not ok 2\n");
-sub MERGE () { 'MERGE' } # FIXME temporary strict-cleanliness shim
-memoize 'xx', LIST_CACHE => MERGE;
-$s = xx();
-print ((!$s) ? "ok 3\n" : "not ok 3\n");
-($a) = xx(); # Should return cached false value from previous invocation
-print ((!$a) ? "ok 4\n" : "not ok 4\n");
-
-
-# Test FAULT
-sub ns {}
-sub na {}
-sub FAULT () { 'FAULT' } # FIXME temporary strict-cleanliness shim
-memoize 'ns', SCALAR_CACHE => FAULT;
-memoize 'na', LIST_CACHE => FAULT;
-eval { my $s = ns() }; # Should fault
-print (($@) ? "ok 5\n" : "not ok 5\n");
-eval { my ($a) = na() }; # Should fault
-print (($@) ? "ok 6\n" : "not ok 6\n");
-
-
-# Test HASH
-my (%s, %l);
-sub nul {}
-memoize 'nul', SCALAR_CACHE => [HASH => \%s], LIST_CACHE => [HASH => \%l];
-nul('x');
-nul('y');
-print ((join '', sort keys %s) eq 'xy' ? "ok 7\n" : "not ok 7\n");
-print ((join '', sort keys %l) eq '' ? "ok 8\n" : "not ok 8\n");
-() = nul('p');
-() = nul('q');
-print ((join '', sort keys %s) eq 'xy' ? "ok 9\n" : "not ok 9\n");
-print ((join '', sort keys %l) eq 'pq' ? "ok 10\n" : "not ok 10\n");
diff --git a/cpan/Memoize/t/unmemoize.t b/cpan/Memoize/t/unmemoize.t
index 21730ecf2a..f4b9e98991 100644
--- a/cpan/Memoize/t/unmemoize.t
+++ b/cpan/Memoize/t/unmemoize.t
@@ -1,23 +1,51 @@
use strict; use warnings;
use Memoize qw(memoize unmemoize);
+use Test::More tests => 26;
-print "1..5\n";
+is eval { unmemoize('u') }, undef, 'trying to unmemoize without memoizing fails';
+my $errx = qr/^Could not unmemoize function `u', because it was not memoized to begin with/;
+like $@, $errx, '... with the expected error';
-eval { unmemoize('f') }; # Should fail
-print (($@ ? '' : 'not '), "ok 1\n");
+sub u {1}
+my $sub = \&u;
+my $wrapped = memoize('u');
+is \&u, $wrapped, 'trying to memoize succeeds';
-{ my $I = 0;
- sub u { $I++ }
-}
-memoize('u');
-my @ur = (&u, &u, &u);
-print (("@ur" eq "0 0 0") ? "ok 2\n" : "not ok 2\n");
+is eval { unmemoize('u') }, $sub, 'trying to unmemoize succeeds' or diag $@;
+
+is \&u, $sub, '... and does in fact unmemoize it';
+
+is eval { unmemoize('u') }, undef, 'trying to unmemoize it again fails';
+like $@, $errx, '... with the expected error';
-eval { unmemoize('u') }; # Should succeed
-print ($@ ? "not ok 3\n" : "ok 3\n");
+# Memoizing a function multiple times separately is not very useful
+# but it should not break unmemoize or make memoization lose its mind
-@ur = (&u, &u, &u);
-print (("@ur" eq "1 2 3") ? "ok 4\n" : "not ok 4\n");
+my $ret;
+my $dummy = sub { $ret };
+ok memoize $dummy, INSTALL => 'memo1';
+ok memoize $dummy, INSTALL => 'memo2';
+ok defined &memo1, 'memoized once';
+ok defined &memo2, 'memoized twice';
+$@ = '';
+ok eval { unmemoize 'memo1' }, 'unmemoized once';
+is $@, '', '... and no exception';
+$@ = '';
+ok eval { unmemoize 'memo2' }, 'unmemoized twice';
+is $@, '', '... and no exception';
+is \&memo1, $dummy, 'unmemoized installed once';
+is \&memo2, $dummy, 'unmemoized installed twice';
-eval { unmemoize('u') }; # Should fail
-print ($@ ? "ok 5\n" : "not ok 5\n");
+my @quux = qw(foo bar baz);
+my %memo = map +($_ => memoize $dummy), @quux;
+for (@quux) { $ret = $_; is $memo{$_}->(), $_, "\$memo{$_}->() returns $_" }
+for (@quux) { undef $ret; is $memo{$_}->(), $_, "\$memo{$_}->() returns $_" }
+
+my $destroyed = 0;
+sub Counted::DESTROY { ++$destroyed }
+{
+ my $memo = memoize $dummy, map +( "$_\_CACHE" => [ HASH => bless {}, 'Counted' ] ), qw(LIST SCALAR);
+ ok $memo, 'memoize anon';
+ ok eval { unmemoize $memo }, 'unmemoized anon';
+}
+is $destroyed, 2, 'no cyclic references';