#!perl
BEGIN
{
use
vars
qw( $DEBUG $SERIALISER )
;
$DEBUG
=
exists
(
$ENV
{AUTHOR_TESTING} ) ?
$ENV
{AUTHOR_TESTING} : 0;
$SERIALISER
=
exists
(
$ENV
{SERIALISER} ) ?
$ENV
{SERIALISER} :
'storable'
;
};
eval
"use Cache::FastMmap 1.57;"
;
plan(
skip_all
=>
"Cache::FastMmap 1.57 required for testing mmap with Cache::FastMmap"
)
if
( $@ );
SKIP:
{
my
$cache
= Module::Generic::File::Mmap->new(
debug
=>
$DEBUG
,
key
=>
'test_key'
,
serialiser
=>
$SERIALISER
,
size
=> 2048,
destroy
=> 0,
mode
=> 0666,
);
my
$s
=
$cache
->
open
({
mode
=>
'w'
});
defined
(
$s
) ||
do
{
diag(
"Failed to open shared cache: "
,
$cache
->error )
if
(
$DEBUG
);
};
local
$SIG
{__DIE__} =
sub
{
diag(
"Got error: "
,
join
(
''
,
@_
),
". Cleaning up shared cache."
)
if
(
$DEBUG
);
$s
->unlock;
$s
->remove;
};
skip(
"Failed to create shared cache object: "
.
$cache
->error, 21 )
if
( !
defined
(
$s
) );
ok(
defined
(
$s
),
'open return value'
);
skip(
"Failed to create shared cache object: "
. Module::Generic::File::Mmap->error, 21 )
if
( !
defined
(
$s
) );
isa_ok(
$s
=> [
'Module::Generic::File::Mmap'
] );
isa_ok(
$s
, [
'Module::Generic::File::Mmap'
],
'Shared cache object'
);
my
$test_data
= {
name
=>
'John Doe'
,
location
=>
'Tokyo'
};
my
$cache_object
=
$s
->
write
(
$test_data
);
ok(
defined
(
$cache_object
),
'write'
);
ok( overload::StrVal(
$s
) eq overload::StrVal(
$cache_object
),
'write return value'
);
my
$buffer
=
$s
->
read
;
ok(
defined
(
$buffer
),
'read no argument'
);
ok(
ref
(
$buffer
) eq
'HASH'
,
'read buffer data integrity'
);
if
(
ref
(
$buffer
) eq
'HASH'
&&
$buffer
->{name} eq
'John Doe'
&&
$buffer
->{location} eq
'Tokyo'
)
{
pass(
'read data check'
);
}
else
{
fail(
'read data check'
);
}
my
$os
=
lc
( $^O );
if
(
$os
eq
'amigaos'
||
$os
eq
'riscos'
||
$os
eq
'vms'
)
{
skip(
"Your system does not support fork()"
, 1 );
}
my
$sigset
= POSIX::SigSet->new( POSIX::SIGINT );
POSIX::sigprocmask( POSIX::SIG_BLOCK,
$sigset
) ||
bail_out(
"Cannot block SIGINT for fork: $!"
);
select
((
select
(STDOUT), $|=1)[0]);
select
((
select
(STDERR), $|=1)[0]);
my
$pid
=
fork
();
skip(
"Unable to fork: $!"
, 9 )
unless
(
defined
(
$pid
) );
if
(
$pid
)
{
POSIX::sigprocmask( POSIX::SIG_UNBLOCK,
$sigset
) ||
bail_out(
"Cannot unblock SIGINT for fork: $!"
);
if
(
kill
(
0
=>
$pid
) || $!{EPERM} )
{
diag(
"Child process with pid '$pid' is still running, waiting for it to complete."
)
if
(
$DEBUG
);
waitpid
(
$pid
, 0 );
my
$exit_status
= ( $? >> 8 );
my
$exit_signal
= $? & 127;
my
$has_coredump
= ( $? & 128 );
diag(
"Child process exited with value $?"
)
if
(
$DEBUG
);
if
( WIFEXITED($?) )
{
diag(
"Child with pid '$pid' exited with bit value '$?' (exit=${exit_status}, signal=${exit_signal}, coredump=${has_coredump})."
)
if
(
$DEBUG
);
}
else
{
diag(
"Child with pid '$pid' exited with bit value '$?' -> $!"
)
if
(
$DEBUG
);
}
is(
$exit_status
, 0,
'sub process shared cache access'
);
my
$data
=
$s
->
read
;
ok(
ref
(
$data
) eq
'HASH'
,
'shared updated data type'
);
if
(
ref
(
$data
) eq
'HASH'
)
{
ok( (
exists
(
$data
->{year} ) &&
defined
(
$data
->{year} ) &&
int
(
$data
->{year} ) == 2021 ),
'updated data value'
);
}
else
{
fail(
'updated data value'
);
}
my
$data2
;
$s
->
read
(
$data2
);
ok(
ref
(
$data2
) eq
'HASH'
,
'different read usage'
);
if
(
ref
(
$data
) eq
'HASH'
)
{
ok( (
exists
(
$data2
->{year} ) &&
defined
(
$data2
->{year} ) &&
int
(
$data2
->{year} ) == 2021 ),
'different read data check'
);
}
else
{
fail(
'different read data check'
);
}
my
$rv
=
$s
->
lock
|| diag(
"Unable to lock: "
,
$s
->error );
ok(
$rv
,
'lock'
);
ok(
$s
->locked,
'locked'
);
if
(
ref
(
$data
) eq
'HASH'
)
{
$data
->{test} =
'ok'
;
ok(
defined
(
$s
->
write
(
$data
) ),
'updated data with lock'
);
}
else
{
fail(
'updated data with lock'
);
}
ok(
defined
(
$s
->unlock ),
'unlock'
);
ok(
defined
(
$s
->remove ),
'remove'
);
}
else
{
diag(
"Child process with pid '$pid' is already completed."
)
if
(
$DEBUG
);
pass(
"sub process exited rapidly"
);
}
}
elsif
(
$pid
== 0 )
{
my
$cache2
= Module::Generic::File::Mmap->new(
debug
=>
$DEBUG
,
key
=>
'test_key'
,
serialiser
=>
$SERIALISER
,
mode
=> 0666,
);
my
$c
=
$cache2
->
open
||
do
{
diag(
"[CHILD] cannot open: "
,
$cache2
->error )
if
(
$DEBUG
);
exit
(1);
};
my
$ref
=
$c
->
read
;
defined
(
$ref
) ||
do
{
diag(
"[CHILD] read returned undef: "
,
$c
->error )
if
(
$DEBUG
);
exit
(1);
};
ref
(
$ref
) eq
'HASH'
||
do
{
diag(
"[CHILD] Shared memory data ($ref) is not an hash reference."
)
if
(
$DEBUG
);
exit
(1);
};
$ref
->{year} = 2021;
defined
(
$c
->
write
(
$ref
) ) ||
do
{
diag(
"[CHILD] Unable to write to shared memory: "
,
$c
->error )
if
(
$DEBUG
);
exit
(1);
};
exit
(0);
}
};
subtest
'serialisation with cbor'
=>
sub
{
SKIP:
{
eval
"use CBOR::XS 1.86;"
;
skip(
"CBOR::XS 1.86 required for testing serialisation with CBOR"
, 1 )
if
( $@ );
my
$cbor
= CBOR::XS->new->allow_sharing;
my
$cache
= Module::Generic::File::Mmap->new(
debug
=>
$DEBUG
,
key
=>
'test_key'
,
serialiser
=>
$SERIALISER
,
size
=> 2048,
destroy
=> 0,
mode
=> 0666,
) ||
die
( Module::Generic::File::Mmap->error );
my
$s
=
$cache
->
open
({
mode
=>
'w'
});
diag(
"Error opening mmap cache file: "
,
$cache
->error )
if
(
$DEBUG
&& !
defined
(
$s
) );
ok(
$s
,
'mmap cache opened'
);
skip(
"Failed to instantiate a mmap object."
, 6 )
if
( !
defined
(
$s
) );
ok(
$s
->
write
({
name
=>
'John Doe'
,
location
=>
'Tokyo'
}),
'write to cache mmap'
);
eval
{
my
$serial
=
$cbor
->encode(
$s
);
ok( (
defined
(
$serial
) &&
length
(
$serial
) ),
'object serialised'
);
my
$obj
=
$cbor
->decode(
$serial
);
isa_ok(
$obj
=> [
'Module::Generic::File::Mmap'
],
'deserialised object is an Module::Generic::File::Mmap object'
);
ok(
$s
->cache_file eq
$obj
->cache_file,
'cache mmap file is the same'
);
is(
$s
->cache_file->
length
,
$obj
->cache_file->
length
,
'cache mmap file has same size as before'
);
my
$h
=
$obj
->
read
;
is(
$h
->{name},
'John Doe'
,
'stored data matches'
);
};
if
( $@ )
{
fail(
"Error serialising or deserialising: $@"
);
}
};
};
subtest
'serialisation with sereal'
=>
sub
{
SKIP:
{
eval
"use Sereal 4.023;"
;
skip(
"Sereal 4.023 required for testing serialisation with Sereal"
, 1 )
if
( $@ );
my
$enc
= Sereal::Encoder->new({
freeze_callbacks
=> 1 });
my
$dec
= Sereal::Decoder->new;
my
$cache
= Module::Generic::File::Mmap->new(
debug
=>
$DEBUG
,
key
=>
'test_key'
,
serialiser
=>
$SERIALISER
,
size
=> 2048,
destroy
=> 0,
mode
=> 0666,
) ||
die
( Module::Generic::File::Mmap->error );
my
$s
=
$cache
->
open
({
mode
=>
'w'
});
ok(
$s
->
write
({
name
=>
'John Doe'
,
location
=>
'Tokyo'
}),
'write to cache mmap'
);
eval
{
my
$serial
=
$enc
->encode(
$s
);
ok( (
defined
(
$serial
) &&
length
(
$serial
) ),
'object serialised'
);
my
$obj
=
$dec
->decode(
$serial
);
isa_ok(
$obj
=> [
'Module::Generic::File::Mmap'
],
'deserialised object is an Module::Generic::File::Mmap object'
);
ok(
$s
->cache_file eq
$obj
->cache_file,
'cache mmap file is the same'
);
is(
$s
->cache_file->
length
,
$obj
->cache_file->
length
,
'cache mmap file has same size as before'
);
my
$h
=
$obj
->
read
;
is(
$h
->{name},
'John Doe'
,
'stored data matches'
);
};
if
( $@ )
{
fail(
"Error serialising or deserialising: $@"
);
}
};
};
subtest
'serialisation with storable'
=>
sub
{
SKIP:
{
eval
"use Storable::Improved v0.1.3;"
;
skip(
"Storable::Improved v0.1.3 required for testing serialisation with Storable"
, 1 )
if
( $@ );
my
$cache
= Module::Generic::File::Mmap->new(
debug
=>
$DEBUG
,
key
=>
'test_key'
,
serialiser
=>
$SERIALISER
,
size
=> 2048,
destroy
=> 0,
mode
=> 0666,
) ||
die
( Module::Generic::File::Mmap->error );
my
$s
=
$cache
->
open
({
mode
=>
'w'
});
ok(
$s
->
write
({
name
=>
'John Doe'
,
location
=>
'Tokyo'
}),
'write to cache mmap'
);
eval
{
my
$serial
= Storable::Improved::freeze(
$s
);
ok( (
defined
(
$serial
) &&
length
(
$serial
) ),
'object serialised'
);
my
$obj
= Storable::Improved::thaw(
$serial
);
isa_ok(
$obj
=> [
'Module::Generic::File::Mmap'
],
'deserialised object is an Module::Generic::File::Mmap object'
);
ok(
$s
->cache_file eq
$obj
->cache_file,
'cache mmap file is the same'
);
is(
$s
->cache_file->
length
,
$obj
->cache_file->
length
,
'cache mmap file has same size as before'
);
my
$h
=
$obj
->
read
;
is(
$h
->{name},
'John Doe'
,
'stored data matches'
);
};
if
( $@ )
{
fail(
"Error serialising or deserialising: $@"
);
}
};
};
done_testing();