############################################################################
# Shared code for Cache::AgainstFile tests
############################################################################
# $Id: CacheAgainstFile.lib,v 1.5 2005/07/25 11:56:36 piersk Exp $
#
# To use this you need to:
# - declare $callcount and $callback
# - define the SLEEP_INT constant
############################################################################
############################################################################
# Some exported variables
$loaddelay = 0;
$callcount = 0;
$callback = sub {
my $fn = shift();
TRACE("CALL : was $callcount");
$callcount++;
sleep $loaddelay if $loaddelay;
TRACE("CALL : now $callcount for file $fn");
return "filename:".$fn;
}; #Callback routine which logs when it's been called
############################################################################
############################################################################
# perl replacement for the touch() external command
sub touch {
my $filename = shift;
if (-e $filename) {
utime(time(), time(), $filename);
} else {
local *X;
open(X, ">$filename") or die "Cannot touch $filename: $!";
close(X);
}
}
############################################################################
############################################################################
#Puts the cache through its paces
sub test_basics
{
my ($cache, $filename, $method, $allow_null_size) = @_;
$method ||= $cache->{method};
$callcount=0;
#First time
$cache->clear();
ASSERT($cache->count() == 0, "$method: count on empty cache");
# if $filename is brand new, cachefile will have same mtime
# and we'll get two cache hits in a row, so wait a moment.
sleep( SLEEP_INT) ;
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method: #1 cache miss");
ASSERT($cache->count() == 1, "$method: count after a fetch");
my $size = $cache->size();
if($allow_null_size && not defined $size){
ASSERT(1, "$method: skipped - size not implemented");
} else {
ASSERT($cache->size() > 0, "$method: size is nonzero - ".$cache->size());
}
#Second time
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method: #2 cache hit");
#Touch the file
sleep( SLEEP_INT );
touch( $filename );
$callcount = 0;
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method: touch => stat & cache miss (call count $callcount)");
#Sneak a sourcefile change in through vulnerable window
sleep (SLEEP_INT);
$callcount = 0;
touch ($filename);
$m_before = (stat($filename))[9];
$loaddelay = 5;
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method: touch => stat & cache miss (call count $callcount)");
# "While we were calculating", the sourcefile was modified
$m_before++;
(utime $m_before, $m_before, $filename) or die "couldn't posttouch $filename: $!";
$callcount = 0;
$cache->get($filename);
ASSERT($callcount == 1, "window-of-vulnerability check");
$loaddelay = 0;
#Full purge
$cache->clear();
$callcount = 0;
ASSERT($cache->count() == 0, "$method: count after cache is cleared");
ASSERT(!defined $cache->size() || $cache->size() == 0, "$method: size is zero");
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method: purge => cache miss (call count $callcount)");
$cache->clear();
}
sub test_nostat
{
my ($cache, $filename, $method) = @_;
$method ||= $cache->{method};
$callcount=0;
#First time
$cache->clear();
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method (no stat): #1 cache miss");
#Second time
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method (no stat): #2 cache hit");
#Poison cache
sleep( SLEEP_INT );
touch($filename);
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 1, "$method (no stat): touch => no stat & cache hit (call count $callcount)");
TRACE("Size before is: ".$cache->count());
ASSERT($cache->count() == 1, "$method (no stat): count on stale cache");
#Purge stale file
$cache->purge();
TRACE("Size after purge is: ".$cache->count());
ASSERT($cache->count() == 0, "$method (no stat): count after purge");
ASSERT($cache->get($filename) eq "filename:$filename" && $callcount == 2, "$method (no stat): purge => expel stale (call count $callcount)");
$cache->clear();
}
sub test_max_items
{
my ($cache, $dir, $max, $method) = @_;
$method ||= $cache->{method};
$callcount=0;
#Fill Cache
$cache->clear();
for(1..$max+5)
{
my $filename = $dir."/file.".$_;
touch($filename);
sleep( SLEEP_INT );
$cache->get($filename);
}
ASSERT($callcount == $max+5, "$method: cache filled with more than MaxItems");
#Purge oldest items
$callcount=0;
$cache->purge();
for(1..$max+5)
{
my $filename = $dir."/file.".$_;
$cache->get($filename);
unlink($filename);
}
ASSERT($callcount == 5, "$method: MaxItems retained from purge");
}
sub test_old_items
{
my ($cache,$dir,$n,$th,$method) = @_;
$method ||= $cache->{method};
$callcount=0;
#Fill Cache
$cache->clear();
for(1..$n)
{
my $filename = $dir."/file.".$_;
touch($filename);
}
for(1..$n)
{
my $filename = $dir."/file.".$_;
sleep( SLEEP_INT ); #Stagger atime
$cache->get($filename);
}
ASSERT($callcount == $n, "$method: filled with items of different ages [callcount=$callcount n=$n]");
#Purge oldest items
$callcount=0;
$cache->purge();
for(1..$n)
{
my $filename = $dir."/file.".$_;
$cache->get($filename);
unlink($filename);
}
ASSERT($callcount == ($n-$th), "$method: old items purged [n=$n th=$th callcount=$callcount]");
}
############################################################################
1;