Developer-Dashboard
view release on metacpan or search on metacpan
t/46-runtime-manager-supervisor.t view on Meta::CPAN
ok(
$manager->_looks_like_collector_supervisor_process(
{ pid => $$, args => 'dashboard collector supervisor' }
),
'collector supervisor title records are recognized',
);
ok(
$manager->_looks_like_collector_supervisor_process(
{ pid => $$, args => '_dashboard-core collector-supervisor-foreground' }
),
'collector supervisor helper command lines are recognized',
);
ok(
$manager->_looks_like_collector_supervisor_process(
{ pid => $$, args => 'perl /tmp/_dashboard-core collector-supervisor-foreground --poll 1' }
),
'collector supervisor perl helper command lines are recognized',
);
ok(
!$manager->_looks_like_collector_supervisor_process(
{ pid => $$, args => 'dashboard collector start alpha' }
),
'unrelated collector commands are not mistaken for the supervisor',
);
{
my @signals;
my @sleeps;
my $reaped_pid = 0;
my $cleaned = 0;
my $running_checks = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::sleep = sub { push @sleeps, $_[0]; return 0 };
local *Developer::Dashboard::RuntimeManager::_collector_supervisor_running = sub { return 4242 };
local *Developer::Dashboard::RuntimeManager::_pid_is_running = sub {
$running_checks++;
return $running_checks <= 22 ? 1 : 0;
};
local *Developer::Dashboard::RuntimeManager::_send_signal = sub {
my ( $self, $signal, @pids ) = @_;
push @signals, [ $signal, @pids ];
return scalar @pids;
};
local *Developer::Dashboard::RuntimeManager::_reap_child_process = sub {
my ( $self, $pid ) = @_;
$reaped_pid = $pid;
return 1;
};
local *Developer::Dashboard::RuntimeManager::_cleanup_collector_supervisor_files = sub {
$cleaned = 1;
return 1;
};
$manager->_stop_collector_supervisor;
is_deeply(
\@signals,
[
[ 'TERM', 4242 ],
[ 'KILL', 4242 ],
],
'_stop_collector_supervisor escalates from TERM to KILL when the supervisor stays alive',
);
is( scalar @sleeps, 21, '_stop_collector_supervisor waits through the TERM loop and the post-KILL confirmation loop' );
is( $reaped_pid, 4242, '_stop_collector_supervisor reaps the stopped supervisor pid after escalation' );
ok( $cleaned, '_stop_collector_supervisor still cleans up supervisor state files after escalation' );
}
{
my $state = $manager->_write_collector_supervisor_state(
{
pid => 3210,
process_name => $manager->_collector_supervisor_process_title,
status => 'running',
watched_names => [ 'alpha', 'beta' ],
}
);
is( $state->{pid}, 3210, 'collector supervisor state write returns the persisted payload' );
is_deeply(
$manager->_collector_supervisor_state,
{
pid => 3210,
process_name => $manager->_collector_supervisor_process_title,
status => 'running',
watched_names => [ 'alpha', 'beta' ],
},
'collector supervisor state reads back the persisted payload',
);
ok( $manager->_cleanup_collector_supervisor_files, 'collector supervisor cleanup returns a true value' );
ok( !-f $manager->_collector_supervisor_statefile, 'collector supervisor cleanup removes the state file' );
}
{
my $rename_attempt = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::is_windows = sub { return 1 };
local *Developer::Dashboard::RuntimeManager::_rename_path = sub {
my ( undef, $source, $target ) = @_;
$rename_attempt++;
if ( $rename_attempt == 1 ) {
$! = EACCES;
return 0;
}
return CORE::rename( $source, $target );
};
local *Developer::Dashboard::RuntimeManager::_replace_path_via_powershell = sub {
my ( undef, $source, $target ) = @_;
return ( CORE::rename( $source, $target ) ? 1 : 0, $! );
};
my $state = $manager->_write_collector_supervisor_state(
{
pid => 6543,
process_name => $manager->_collector_supervisor_process_title,
status => 'running',
watched_names => ['gamma'],
}
);
is( $state->{pid}, 6543, '_write_collector_supervisor_state returns the payload after the Windows PowerShell replacement fallback' );
is_deeply(
$manager->_collector_supervisor_state,
{
pid => 6543,
t/46-runtime-manager-supervisor.t view on Meta::CPAN
local *Developer::Dashboard::RuntimeManager::_is_collector_supervisor = sub {
my ( undef, $pid ) = @_;
return $pid == $child ? 1 : 0;
};
is( $manager->_stop_collector_supervisor, $child, 'collector supervisor stop returns the managed child pid' );
is( waitpid( $child, 1 ), -1, 'collector supervisor stop reaps the managed child instead of leaving a zombie behind' );
ok( !-f $manager->_collector_supervisor_pidfile, 'collector supervisor stop removes the pidfile after shutdown' );
}
{
my $child = fork();
die "fork failed: $!" if !defined $child;
if ( !$child ) {
POSIX::_exit(0);
}
my $reaped = 0;
for ( 1 .. 20 ) {
$reaped = $manager->_reap_any_child_processes;
last if $reaped;
select undef, undef, undef, 0.05;
}
is( $reaped, 1, 'collector supervisor reap helper reaps one exited direct child' );
is( waitpid( $child, 1 ), -1, 'collector supervisor reap helper leaves no zombie behind after reaping the child' );
}
END {
chdir $original_cwd if defined $original_cwd && length $original_cwd;
}
done_testing;
__END__
=pod
=head1 NAME
t/46-runtime-manager-supervisor.t - coverage and regression tests for the collector watchdog supervisor
=head1 WHAT IT IS
This test file exercises the collector watchdog supervisor helpers inside
L<Developer::Dashboard::RuntimeManager>.
=head1 WHAT IT IS FOR
It verifies the state-file, pidfile, process-identification, detached-launch,
shutdown, and watchdog-loop behaviour added for collector self-healing.
=head1 PURPOSE
Test file in the Developer Dashboard codebase. This file verifies collector
watchdog supervisor behaviour, detached lifecycle handling, and coverage for
the RuntimeManager resilience path.
Open this file when you need the implementation, regression coverage, or
runtime entrypoint for that responsibility rather than guessing which part of
the tree owns it.
=head1 WHY IT EXISTS
The collector watchdog is meant to keep managed collectors alive, restart them
after unexpected exits, and escalate to human attention when repeated crashes
continue. These tests keep that resilience path covered so the implementation
does not silently regress.
=head1 WHEN TO USE
This test runs as part of the normal repository test suite and should also be
run when changing collector lifecycle code, watchdog state handling, or
detached helper startup paths.
=head1 HOW TO USE
Run it directly during focused debugging:
perl -Ilib t/46-runtime-manager-supervisor.t
Run it through the standard harness:
prove -lv t/46-runtime-manager-supervisor.t
Run it under coverage with the rest of the suite:
HARNESS_PERL_SWITCHES=-MDevel::Cover prove -lr t
=head1 WHAT USES IT
The repository test harness, coverage gates, release metadata checks, and
maintainers changing the runtime manager use this file to prove the watchdog
behaviour still matches the expected contract.
=head1 EXAMPLES
Example: verify only the watchdog supervisor coverage after editing its state
helpers.
prove -lv t/46-runtime-manager-supervisor.t
Example: rerun the runtime-manager focused suite after changing collector
restart policy.
prove -lv t/09-runtime-manager.t t/46-runtime-manager-supervisor.t
Example: confirm the full repository still keeps watchdog code covered.
HARNESS_PERL_SWITCHES=-MDevel::Cover prove -lr t
=cut
( run in 1.346 second using v1.01-cache-2.11-cpan-14f38c9f855 )