Developer-Dashboard
view release on metacpan or search on metacpan
t/09-runtime-manager.t view on Meta::CPAN
};
is(
$manager->_restart_web_with_retry( host => '127.0.0.1', port => 7924 ),
8816,
'_restart_web_with_retry retries when a replacement web pid fails the startup stability window',
);
is( $attempt, 2, '_restart_web_with_retry retries after a briefly-live replacement web pid fails stability checks' );
is( scalar @cleanups, 1, '_restart_web_with_retry clears persisted web state before retrying a briefly-live replacement web pid' );
}
{
my $polls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::sleep = sub { return 0 };
local *Local::RuntimeRunner::running_loops = sub {
$polls++;
return ( { name => 'alpha.collector', pid => 7002 } );
};
ok(
$manager->_collector_runtime_ready( 'alpha.collector', 7002 ),
'_collector_runtime_ready returns once the managed collector survives the short confirmation window',
);
is( $polls, 3, '_collector_runtime_ready only spends three ready polls proving a healthy collector loop' );
}
{
my $polls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::sleep = sub { return 0 };
local *Local::RuntimeRunner::running_loops = sub {
$polls++;
return $polls <= 2
? ( { name => 'alpha.collector', pid => 7001 } )
: ();
};
ok(
!$manager->_collector_runtime_ready( 'alpha.collector', 7001 ),
'_collector_runtime_ready fails when a managed collector loop disappears during the startup stability window',
);
}
{
my $polls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::sleep = sub { return 0 };
local *Local::RuntimeRunner::running_loops = sub {
$polls++;
return ();
};
local *Local::RuntimeRunner::loop_state = sub {
return {
pid => $$,
name => 'alpha.collector',
status => 'starting',
};
};
ok(
$manager->_collector_runtime_ready( 'alpha.collector', $$ ),
'_collector_runtime_ready falls back to the persisted loop state while the managed process title is not observable yet',
);
is( $polls, 0, '_collector_runtime_ready trusts the persisted loop-state fallback without consulting running_loops when the pid is already proven alive' );
}
{
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::sleep = sub { return 0 };
local *Local::RuntimeRunner::running_loops = sub {
die "running_loops should not be consulted while the persisted loop-state fallback already proves the pid is alive\n";
};
local *Local::RuntimeRunner::loop_state = sub {
return {
pid => $$,
name => 'alpha.collector',
status => 'starting',
};
};
ok(
$manager->_collector_runtime_ready( 'alpha.collector', $$ ),
'_collector_runtime_ready trusts the persisted loop-state fallback before the destructive running_loops cleanup path can run',
);
}
{
my $polls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::sleep = sub { return 0 };
local *Developer::Dashboard::RuntimeManager::_runtime_stability_polls = sub { return 1 };
local *Local::RuntimeRunner::loop_state = sub { return undef };
local *Local::RuntimeRunner::running_loops = sub {
$polls++;
return ();
};
ok(
!$manager->_collector_runtime_ready( 'alpha.collector', 7999 ),
'_collector_runtime_ready returns false after the stability window expires without ever observing a managed collector loop',
);
is( $polls, 1, '_collector_runtime_ready consults running_loops across the full timeout path before giving up' );
}
{
no warnings 'redefine';
$runner->{started} = [];
$runner->{loops} = [];
$collector_store->write_status( 'missing.collector', {} );
local *Local::RuntimeRunner::running_loops = sub { return () };
my $result = $manager->_supervise_collectors_once( names => ['missing.collector'] );
my $status = $collector_store->read_status('missing.collector') || {};
is_deeply( $result->{restarted}, [], '_supervise_collectors_once does not report restarts for unknown collectors' );
is( $result->{attention}[0]{name}, 'missing.collector', '_supervise_collectors_once reports unknown watched collectors in the attention list' );
is( $status->{watchdog_status}, 'attention_required', '_supervise_collectors_once marks unknown watched collectors as attention_required' );
ok( $status->{watchdog_last_error}, '_supervise_collectors_once records an explicit error for unknown watched collectors' );
}
{
no warnings 'redefine';
$runner->{started} = [];
$runner->{loops} = [ { name => 'alpha.collector', pid => 4401 } ];
local *Local::RuntimeRunner::running_loops = sub { return @{ $runner->{loops} } };
my $result = $manager->_supervise_collectors_once( names => ['alpha.collector'] );
is_deeply( $result->{restarted}, [], '_supervise_collectors_once skips watchdog work for collectors that are already running' );
is_deeply( $result->{attention}, [], '_supervise_collectors_once does not raise attention for collectors that are already running' );
}
{
t/09-runtime-manager.t view on Meta::CPAN
my $state = $manager->running_web;
is( $state->{pid}, $$, 'running_web trusts the recorded live listener pid for a running state even after the server process renames itself' );
}
{
my $listener = fork();
die "fork failed: $!" if !defined $listener;
if ( !$listener ) {
sleep 30;
POSIX::_exit(0);
}
no warnings 'redefine';
my $original_read = Developer::Dashboard::FileRegistry->can('read');
local *Developer::Dashboard::FileRegistry::read = sub {
my ( $self, $name ) = @_;
return "$$\n" if $name eq 'web_pid';
return $original_read->( $self, $name );
};
local *Developer::Dashboard::RuntimeManager::web_state = sub {
return { pid => $$, host => '127.0.0.1', port => 7919, status => 'running' };
};
local *Developer::Dashboard::RuntimeManager::_is_managed_web = sub { return 0 };
local *Developer::Dashboard::RuntimeManager::_find_web_processes = sub { return () };
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub { return ($listener) };
my $state = $manager->running_web;
is( $state->{pid}, $$, 'running_web trusts the recorded master pid when the managed port is still held by a separate listener worker pid' );
kill 'KILL', $listener;
waitpid( $listener, 0 );
}
{
no warnings 'redefine';
$files->write( 'web_pid', "$$\n" );
$manager->_write_web_state( { pid => $$, host => '127.0.0.1', port => 7920, status => 'running' } );
local *Developer::Dashboard::RuntimeManager::_is_managed_web = sub { return 0 };
local *Developer::Dashboard::RuntimeManager::_find_web_processes = sub { return () };
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub { return () };
my $state = $manager->running_web;
is( $state->{pid}, $$, 'running_web trusts the recorded running pid even when listener discovery cannot identify the managed process shape' );
$manager->_cleanup_web_files;
}
{
my $listener = fork();
die "fork failed: $!" if !defined $listener;
if ( !$listener ) {
local $SIG{TERM} = 'IGNORE';
sleep 30;
POSIX::_exit(0);
}
my $calls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::running_web = sub {
return $calls++ == 0 ? { pid => $listener, port => 7908 } : undef;
};
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub { return ($listener) };
local *Developer::Dashboard::RuntimeManager::_find_legacy_web_processes = sub { return () };
local *Developer::Dashboard::RuntimeManager::_pkill_perl = sub { return 1 };
is( $manager->stop_web, $listener, 'stop_web returns the recorded pid while it also tracks listener pids on the bound port' );
waitpid( $listener, 0 );
ok( !kill( 0, $listener ), 'stop_web escalates listener-port pids to KILL when they remain alive after TERM' );
}
{
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::web_state = sub {
return {
pid => 111_111,
host => '127.0.0.1',
port => 7917,
status => 'running',
};
};
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub {
my ( undef, $port ) = @_;
return $port == 7917 ? (333_333) : ();
};
local *Developer::Dashboard::RuntimeManager::_read_process_title = sub {
my ( undef, $pid ) = @_;
return $pid == 333_333 ? 'starman master' : undef;
};
local *Developer::Dashboard::RuntimeManager::_cleanup_web_files = sub { die "_cleanup_web_files should not run while a saved listener still exists\n" };
my $running = $manager->running_web;
is( $running->{pid}, 333_333, 'running_web falls back to the saved listener pid when the real listener no longer keeps the dashboard wrapper title' );
is( $running->{port}, 7917, 'running_web keeps the persisted port when it resolves the live listener from saved state' );
is( $running->{process_name}, 'starman master', 'running_web records the actual listener process title when using saved-state listener fallback' );
}
{
my $late_listener = fork();
die "fork failed: $!" if !defined $late_listener;
if ( !$late_listener ) {
local $SIG{TERM} = 'IGNORE';
sleep 30;
POSIX::_exit(0);
}
my $running_calls = 0;
my $listener_calls = 0;
my $wait_calls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::running_web = sub {
return $running_calls++ == 0 ? { pid => $late_listener, port => 7918 } : undef;
};
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub {
return $listener_calls++ == 0 ? () : ($late_listener);
};
local *Developer::Dashboard::RuntimeManager::_find_legacy_web_processes = sub { return () };
local *Developer::Dashboard::RuntimeManager::_pkill_perl = sub { return 1 };
local *Developer::Dashboard::RuntimeManager::_wait_for_port_release = sub {
return $wait_calls++ == 0 ? 0 : 1;
};
is( $manager->stop_web, $late_listener, 'stop_web returns the recorded pid when it has to re-probe the bound port for late listeners' );
waitpid( $late_listener, 0 );
ok( !kill( 0, $late_listener ), 'stop_web kills late-discovered listener pids after an initial port-release timeout' );
}
{
my $listener = fork();
die "fork failed: $!" if !defined $listener;
if ( !$listener ) {
local $SIG{TERM} = sub { exit 0 };
t/09-runtime-manager.t view on Meta::CPAN
ok( !$manager->_web_runtime_ready( 4321, 7890 ), '_web_runtime_ready returns false on Windows when a listener disappears before the confirmation threshold is reached' );
is( $sleep_calls, 1, '_web_runtime_ready performs the Windows polling sleep before a later poll observes the vanished listener' );
}
{
local $Developer::Dashboard::Platform::OS_NAME = 'MSWin32';
my $sleep_calls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub { return () };
local *Developer::Dashboard::RuntimeManager::_port_accepting_connections = sub { return 0 };
local *Developer::Dashboard::RuntimeManager::_runtime_stability_polls = sub { return 2 };
local *Developer::Dashboard::RuntimeManager::_runtime_poll_interval = sub { return 0.01 };
local *Developer::Dashboard::RuntimeManager::sleep = sub { $sleep_calls++; return 0 };
ok( !$manager->_web_runtime_ready( 4321, 7890 ), '_web_runtime_ready returns false on Windows after exhausting the full startup poll budget with no listener' );
is( $sleep_calls, 2, '_web_runtime_ready performs each Windows startup poll sleep before timing out without a listener' );
}
{
local $Developer::Dashboard::Platform::OS_NAME = 'MSWin32';
ok( !$manager->_web_runtime_matches_pid( { pid => 12 }, 99, undef ), '_web_runtime_matches_pid rejects Windows fallback checks when no listener port is available' );
ok( $manager->_web_runtime_matches_pid( { pid => 12, port => 7890 }, 99, undef ), '_web_runtime_matches_pid reuses the runtime-reported listener port on Windows when no explicit listener port was supplied' );
ok( $manager->_web_runtime_matches_pid( { pid => 12, port => 7890 }, 99, 7890 ), '_web_runtime_matches_pid accepts the Windows listener fallback when the running port matches the requested listener port' );
ok( !$manager->_web_runtime_matches_pid( { pid => 12, port => 7890 }, 99, 7891 ), '_web_runtime_matches_pid rejects Windows fallback checks when the running port does not match the listener port' );
}
{
local $Developer::Dashboard::Platform::OS_NAME = 'linux';
ok( !$manager->_web_runtime_matches_pid( { pid => 12, port => 7890 }, 99, 7890 ), '_web_runtime_matches_pid stops before the Windows listener fallback on non-Windows hosts' );
}
{
local $Developer::Dashboard::Platform::OS_NAME = 'linux';
my $sleep_calls = 0;
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::running_web = sub {
return { pid => 77, port => 7892 };
};
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub {
my ( undef, $port ) = @_;
return $port == 7892 ? (77) : ();
};
local *Developer::Dashboard::RuntimeManager::_port_accepting_connections = sub { return 0 };
local *Developer::Dashboard::RuntimeManager::_runtime_stability_polls = sub { return 1 };
local *Developer::Dashboard::RuntimeManager::_runtime_confirmation_polls = sub { return 1 };
local *Developer::Dashboard::RuntimeManager::_runtime_poll_interval = sub { return 0.01 };
local *Developer::Dashboard::RuntimeManager::sleep = sub { $sleep_calls++; return 0 };
ok( $manager->_web_runtime_ready( 77, undef ), '_web_runtime_ready reuses the runtime-reported listener port when no explicit port argument was provided' );
is( $sleep_calls, 0, '_web_runtime_ready does not need an extra poll sleep when the runtime-reported listener port is immediately ready' );
}
{
local $Developer::Dashboard::Platform::OS_NAME = 'MSWin32';
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::_listener_pids_for_port = sub {
my ( undef, $port ) = @_;
return () if $port != 7890;
return (7123);
};
ok(
$manager->_wait_for_windows_web_shutdown( undef, undef, [$$] ),
'_wait_for_windows_web_shutdown reports the web runtime alive when a tracked listener pid is still running',
);
ok(
$manager->_wait_for_windows_web_shutdown( undef, 7890, [] ),
'_wait_for_windows_web_shutdown reports the web runtime alive while the listen port still has an owning pid',
);
ok(
!$manager->_wait_for_windows_web_shutdown( undef, 7891, [] ),
'_wait_for_windows_web_shutdown reports shutdown complete when there is no saved pid, listener pid, or live port owner',
);
}
{
no warnings 'redefine';
local *Developer::Dashboard::RuntimeManager::stop_web = sub {
my ( undef, %args ) = @_;
return 5101;
};
my $stopped_web = $manager->stop_target( scope => 'web' );
is( $stopped_web->{web_pid}, 5101, 'stop_target web scope reports the stopped web pid' );
is( $stopped_web->{web}{status}, 'stopped', 'stop_target web scope reports stopped status' );
local *Developer::Dashboard::RuntimeManager::stop_collectors = sub {
my ( undef, %args ) = @_;
return (
{ name => 'alpha.collector', pid => 6101, status => 'stopped' },
{ name => 'beta.collector', pid => 6102, status => 'stopped' },
);
};
my $stopped_collectors = $manager->stop_target( scope => 'collector' );
is( scalar @{ $stopped_collectors->{collectors} }, 2, 'stop_target collector scope reports all stopped collectors' );
is( $stopped_collectors->{target}, 'all', 'stop_target collector scope defaults its target label to all' );
my $stopped_named = $manager->stop_target( scope => 'collector', name => 'beta.collector' );
is( $stopped_named->{target}, 'beta.collector', 'stop_target collector scope preserves the requested collector name in the target field' );
local *Developer::Dashboard::RuntimeManager::stop_collectors = sub { return (); };
my $stopped_empty_collectors = $manager->stop_target( scope => 'collector' );
is_deeply(
$stopped_empty_collectors->{collectors},
[
{
name => 'all',
status => 'not running',
details => 'no running collectors',
},
],
'stop_target collector scope still reports a summary row when no collectors are running',
);
{
local $Developer::Dashboard::Platform::OS_NAME = 'MSWin32';
local *Developer::Dashboard::RuntimeManager::stop_collectors = sub {
return (
{ name => 'alpha.collector', pid => 6201, status => 'stopped' },
{ name => 'beta.collector', pid => 6202, status => 'stopped' },
);
};
my $windows_stopped_collectors = $manager->stop_target( scope => 'collector' );
is_deeply(
$windows_stopped_collectors->{collectors},
[
{ name => 'alpha.collector', pid => 6201, status => 'stopped' },
{ name => 'beta.collector', pid => 6202, status => 'stopped' },
],
'stop_target collector scope reports normal stopped collector rows on Windows',
( run in 0.933 second using v1.01-cache-2.11-cpan-14f38c9f855 )