Fugu
view release on metacpan or search on metacpan
t/fugu/daemon.t view on Meta::CPAN
# Run $code in a detached daemon. The method returns the exit
# status of the process that called daemonize, which is the
# parent half of the fork. The daemon itself lives on.
sub start_daemon ($code)
{
my $pid = fork;
die "fork: $!" unless defined $pid;
if ( $pid == 0 ) {
exec { $^X } $^X, "-I$lib", '-e', $code;
exit 127;
}
waitpid $pid, 0;
return $? >> 8;
}
# stop($pid):
# Stop a daemon that a test started.
sub stop ($pid)
{
return unless defined $pid && $pid =~ /^\d+$/;
kill 'TERM', $pid;
return;
}
# Test 1: the child detaches, takes the PID file, changes to the root
# directory, and applies the umask
{
my $pidfile = "$dir/detach.pid";
my $report = "$dir/detach.txt";
my $status = start_daemon(<<"CODE");
use v5.36;
use Cwd ();
use Fugu::Daemon;
Fugu::Daemon->daemonize(
logfile => '/dev/null',
pidfile => '$pidfile',
);
open my \$fh, '>', '$report' or exit 1;
printf {\$fh} "cwd=%s\\numask=%04o\\npid=%d\\n",
Cwd::getcwd(), umask, \$\$;
close \$fh;
sleep 30;
CODE
is( $status, 0, 'the process that daemonized exits with status 0' );
ok( wait_for($pidfile), 'the daemon wrote its PID file' );
ok( wait_for($report), 'the daemon reported its state' );
my $lock = Fugu::Pidfile->new( path => $pidfile );
my $pid = $lock->read_pid;
ok( defined $pid, 'the PID file holds a PID' );
my $state = slurp($report);
like( $state, qr{^cwd=/$}m, 'the daemon changed to /' );
like( $state, qr/^umask=0022$/m, 'the daemon applied the umask' );
like( $state, qr/^pid=\Q$pid\E$/m,
'the PID file names the daemon itself' );
is( $lock->is_running, $pid, 'the daemon is alive' );
stop($pid);
$lock->remove;
}
# Test 2: the lock is exclusive. A second daemon on the same PID file
# must not start.
{
my $pidfile = "$dir/exclusive.pid";
my $marker = "$dir/second-started";
start_daemon(<<"CODE");
use Fugu::Daemon;
Fugu::Daemon->daemonize(logfile => '/dev/null', pidfile => '$pidfile');
sleep 30;
CODE
ok( wait_for($pidfile), 'the first daemon holds the PID file' );
my $lock = Fugu::Pidfile->new( path => $pidfile );
my $pid = $lock->read_pid;
start_daemon(<<"CODE");
use Fugu::Daemon;
Fugu::Daemon->daemonize(logfile => '/dev/null', pidfile => '$pidfile');
open my \$fh, '>', '$marker' or exit 1;
print {\$fh} \$\$;
close \$fh;
sleep 30;
CODE
# The second daemon dies inside daemonize. Give it more time
# than it would need to reach the marker if the lock did not
# hold.
ok( !wait_for( $marker, 2 ), 'the second daemon did not start' );
is( $lock->read_pid, $pid, 'the PID file still names the first' );
stop($pid);
$lock->remove;
}
# Test 4: with no pidfile argument, daemonize creates nothing
{
my $report = "$dir/no-pidfile.txt";
start_daemon(<<"CODE");
use Fugu::Daemon;
my \$held = Fugu::Daemon->daemonize(logfile => '/dev/null');
open my \$fh, '>', '$report' or exit 1;
print {\$fh} defined \$held ? 'held' : 'none';
close \$fh;
CODE
ok( wait_for( $report, 5 ), 'the daemon ran' );
is( slurp($report), 'none', 'daemonize returns nothing' );
}
done_testing();
( run in 1.707 second using v1.01-cache-2.11-cpan-14f38c9f855 )