Developer-Dashboard

 view release on metacpan or  search on metacpan

t/14-coverage-closure-extra.t  view on Meta::CPAN

        cwd        => $timeout_dir,
        timeout_ms => 50,
    );
    is( $exit_code, 124, 'collector command timeout returns 124' );
    ok( $timed_out, 'collector command timeout is marked as timed out' );
    is( $stdout, '', 'collector timeout leaves stdout empty' );
    is( $stderr, '', 'collector timeout leaves stderr empty' );
    ok( Developer::Dashboard::CollectorRunner::_cron_match( '*/5', 10 ), 'collector cron step matches divisible values' );
    ok( !Developer::Dashboard::CollectorRunner::_cron_match( '*/5', 3 ), 'collector cron step does not match unrelated values' );
    ok( !Developer::Dashboard::CollectorRunner::_cron_match( '9', 3 ), 'collector cron exact value can fail to match' );

    my ( $code_stdout, $code_stderr, $code_exit, $code_timed_out ) = $runner->_run_code(
        source     => q{sleep 2; return 0;},
        cwd        => $timeout_dir,
        timeout_ms => 50,
    );
    is( $code_exit, 124, 'collector perl-code timeout returns 124' );
    ok( $code_timed_out, 'collector perl-code timeout is marked as timed out' );
    is( $code_stdout, '', 'collector perl-code timeout leaves stdout empty' );
    is( $code_stderr, '', 'collector perl-code timeout leaves stderr empty' );

    my $mode_error = eval { $runner->_run_job( mode => 'bogus', source => '1', cwd => $timeout_dir ); 1 } ? '' : $@;
    like( $mode_error, qr/Unknown collector mode 'bogus'/, 'collector runner rejects unknown execution modes' );

    no warnings 'redefine';
    local *Developer::Dashboard::CollectorRunner::_run_job = sub { die "outer run wrapper failure\n" };
    my $wrapped = $runner->run_once(
        {
            name    => 'wrapped.failure',
            command => q{printf wrapped},
            cwd     => $timeout_dir,
        }
    );
    is( $wrapped->{exit_code}, 255, 'run_once maps outer execution wrapper failures to exit 255' );
    like( $wrapped->{stderr}, qr/outer run wrapper failure/, 'run_once appends outer execution wrapper failures to stderr' );
    is( $collector_store->read_status('wrapped.failure')->{active_runs}, 0, 'run_once clears active_runs after an outer execution wrapper failure' );
    ok( !$collector_store->read_status('wrapped.failure')->{running}, 'run_once clears running after an outer execution wrapper failure' );
}

{
    my $loop_name = 'coverage.loop';
    my $pidfile   = $runner->_pidfile($loop_name);
    my $statefile = $runner->_statefile($loop_name);

    my $managed_child = fork();
    die "fork failed: $!" if !defined $managed_child;
    if ( !$managed_child ) {
        $ENV{DEVELOPER_DASHBOARD_LOOP_NAME} = $loop_name;
        $0 = $runner->_process_title($loop_name);
        $SIG{TERM} = 'IGNORE';
        sleep 30;
        exit 0;
    }
    open my $fh, '>', $pidfile or die $!;
    print {$fh} $managed_child;
    close $fh;
    # Wait BEFORE the first running_loops call, not around it. This pidfile has
    # no loop state, so the child's process title is the only evidence of its
    # identity, and running_loops deletes the pidfile of any same-namespace pid it
    # cannot recognize. A poll loop therefore destroys its own fixture on the
    # first iteration and can never recover, however many iterations it is given.
    ok(
        wait_for_managed_loop( $runner, $managed_child, $loop_name ),
        'managed loop child becomes identifiable before running_loops reads the collectors root',
    );
    my @loops = $runner->running_loops;
    is( scalar @loops, 1, 'running_loops lists active managed loop pids' );
    is( $loops[0]{name}, $loop_name, 'running_loops returns the managed loop name' );

    $collector_store->mark_run_started( $loop_name, {} );
    ok( $collector_store->read_status($loop_name)->{running}, 'collector status reports running before the loop is stopped' );

    my $stopped_pid = $runner->stop_loop($loop_name);
    is( $stopped_pid, $managed_child, 'stop_loop returns managed loop pid' );
    waitpid( $managed_child, 0 );
    ok( !-e $pidfile, 'stop_loop removes loop pid files after forced kill' );
    ok( !-e $statefile, 'stop_loop removes loop state files after forced kill' );

    my $status_after_stop = $collector_store->read_status($loop_name) || {};
    ok( !$status_after_stop->{running}, 'stop_loop resets the consumer-facing running flag so a stopped collector is no longer reported as running' );
    is( $status_after_stop->{active_runs}, 0, 'stop_loop clears active_runs when the loop is stopped so in-flight counters do not linger' );
}

{
    is( $collector_store->mark_stopped('never.seeded.collector'), undef, 'mark_stopped is a no-op when the collector has no status file yet' );

    $collector_store->mark_run_started( 'reset.direct.collector', {} );
    ok( $collector_store->read_status('reset.direct.collector')->{running}, 'seeded collector status reports running' );
    is( $collector_store->read_status('reset.direct.collector')->{active_runs}, 1, 'seeded collector status reports one active run' );

    $collector_store->mark_stopped('reset.direct.collector');
    my $reset = $collector_store->read_status('reset.direct.collector');
    ok( !$reset->{running}, 'mark_stopped clears the running flag directly' );
    is( $reset->{active_runs}, 0, 'mark_stopped clears active_runs directly' );
    ok( defined $reset->{stopped_at} && $reset->{stopped_at} ne '', 'mark_stopped records when the collector was stopped' );
}

{
    # Fix A: a just-spawned worker must be persisted to active_worker_pids
    # immediately, not only at the top of the next iteration, so a crash cannot
    # orphan a worker that is invisible to the persisted state.
    my $persist_name = 'coverage.persist.on.spawn';
    my $child = fork();
    die "fork failed: $!" if !defined $child;
    if ( !$child ) {
        no warnings 'redefine';
        local *Developer::Dashboard::CollectorRunner::_job_is_due = sub { return 1 };
        my $ok = $runner->_run_loop_child(
            daemonize     => 0,
            interval      => 1,
            job           => { command => 'sleep 30', cwd => $home },
            name          => $persist_name,
            schedule_mode => 'interval',
            single_tick   => 1,
            title         => $runner->_process_title($persist_name),
        );
        exit( $ok ? 0 : 1 );
    }
    waitpid( $child, 0 );
    my @persisted = $runner->_state_active_worker_pids($persist_name);
    ok( scalar(@persisted) >= 1, 'loop persists active_worker_pids immediately on spawn so a just-started worker survives a crash-and-stop' );



( run in 0.472 second using v1.01-cache-2.11-cpan-4ab04211f4c )