The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

#########################

use Test::More tests => 13;
BEGIN { use_ok('Cache::FastMmap') };
use strict;

#########################

# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.

# Test a backing store just made of a local hash
my %BackingStore = (
  foo => '123abc',
  bar => '456def'
);

my %WrittenItems = %BackingStore;

my $FC = Cache::FastMmap->new(
  raw_values => 1,
  init_file => 1,
  num_pages => 89,
  page_size => 1024,
  context => \%BackingStore,
  read_cb => sub { return $_[0]->{$_[1]}; },
  write_cb => sub { $_[0]->{$_[1]} = $_[2]; },
  delete_cb => sub { delete $_[0]->{$_[1]} },
  write_action => 'write_back',
  empty_on_exit => 1
);

ok( defined $FC );

srand(6543);

# Put 3000 items in the cache
for (1 .. 3000) {
  my ($Key, $Val) = (RandStr(10), RandStr(100));
  $FC->set($Key, $Val);
  $WrittenItems{$Key} = $Val;
}

# Get values in cache
my %CacheItems = map { $_->{key} => $_->{value} } $FC->get_keys(2);

# Reality check approximate number of items in each
ok( scalar(keys %BackingStore) < 2700, "backing store size 1" );
ok( scalar(keys %CacheItems) > 300, "backing store size 2" );

# Merge with backing store items
my %AllItems = (%BackingStore, %CacheItems);

# Should be equal to all items we wrote
ok( eq_hash(\%AllItems, \%WrittenItems), "items match 1");

# Check we can get the items we wrote
is( $FC->get('foo'), '123abc',  "cb get 1");
is( $FC->get('bar'), '456def',  "cb get 2");

# Read them forward and backward, which should force
#  complete flush and read from backing store
my $Failed = 0;
for (keys %WrittenItems, reverse keys %WrittenItems) {
  $Failed++ if $FC->get($_) ne $WrittenItems{$_};
}

ok( $Failed == 0, "got all written items 1" );

# Delete some items (should be random from cache/backing store)
my @DelKeys = (keys %WrittenItems)[0 .. 300];
for (@DelKeys) {
  $FC->remove($_);
  delete $WrittenItems{$_};
}

# Check it all matches again
%CacheItems = map { $_->{key} => $_->{value} } $FC->get_keys(2);
%AllItems = (%BackingStore, %CacheItems);
ok( eq_hash(\%AllItems, \%WrittenItems), "items match 2");

$Failed = 0;
for (keys %WrittenItems) {
  $Failed++ if $FC->get($_) ne $WrittenItems{$_};
}

ok( $Failed == 0, "got all written items 2" );

# Force flushing of cache
$FC->empty();

# So all written items should be in backing store
ok( eq_hash(\%WrittenItems, \%BackingStore), "items match 3");

my @Keys = $FC->get_keys(0);
ok( scalar(@Keys) == 0, "no items left in cache" );

%WrittenItems = %BackingStore = ();

# Put 3000 items in the cache
for (1 .. 3000) {
  my ($Key, $Val) = (RandStr(10), RandStr(100));
  $FC->set($Key, $Val);
  $WrittenItems{$Key} = $Val;
}

# empty_on_exit is set, so this should push to backing store
$FC = undef;

ok( eq_hash(\%WrittenItems, \%BackingStore), "items match 4");

sub RandStr {
  return join '', map { chr(ord('a') + rand(26)) } (1 .. $_[0]);
}