diff options
author | Todd Rinaldo <toddr@cpan.org> | 2022-10-19 17:42:37 +0000 |
---|---|---|
committer | Todd Rinaldo <toddr@cpan.org> | 2022-10-19 14:46:03 -0500 |
commit | cc628f28ad0caa0a2e6c5a5c8df402f100259a6f (patch) | |
tree | 3e6632e5285d25f15b26717f9767414eb4ecbc48 /cpan | |
parent | 4e5f10080fe14012dab536cbfaf12b9d775a4412 (diff) | |
download | perl-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')
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 = \×_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'; |