Alien-Xrepo

 view release on metacpan or  search on metacpan

t/051_engine.t  view on Meta::CPAN

};
subtest 'probe marks satisfied packages and install skips them' => sub {
    my $spy = Alien::Xrepo::Build::TestSpy->new( preinstalled => { zstd => '1.5.6' } );
    my $b   = build( [ { name => 'zstd', version => '1.5.6' }, 'libsdl3' ], repo => $spy, );
    $b->run;
    my %by_action = map { $_->{action} => 1 } @{ $spy->calls };
    ok $by_action{info}, 'probe asked xrepo for each package';
    is $b->install_prop->{probed}{zstd}{satisfied},    1, 'matching version satisfied';
    is $b->install_prop->{probed}{libsdl3}{satisfied}, 0, 'no store hit for libsdl3';
    my (@installs) = grep { $_->{action} eq 'install' } @{ $spy->calls };
    is scalar @installs,   1,         'only the unsatisfied package was installed';
    is $installs[0]{name}, 'libsdl3', 'skip hit the right package';
    is $b->install_type,   'share',   'a fetched package flips install_type';
};
subtest 'probe_policy always reinstalls despite a hit' => sub {
    my $spy = Alien::Xrepo::Build::TestSpy->new( preinstalled => { zstd => '1.5.6' } );
    my $b   = build( ['zstd'], repo => $spy, probe_policy => 'always' );
    $b->run;
    my (@installs) = grep { $_->{action} eq 'install' } @{ $spy->calls };
    is scalar @installs, 1, 'always policy installed anyway';
};
subtest 'probe_policy off never probes' => sub {
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, probe_policy => 'off' );
    $b->run;
    my (@infos) = grep { $_->{action} eq 'info' } @{ $spy->calls };
    is scalar @infos, 0, 'no probe calls with policy off';
    ok !$b->install_prop->{probed}{zstd}, 'no probe data recorded';
};
subtest 'a failed package isolates: siblings install, error recorded' => sub {
    my $spy = Alien::Xrepo::Build::TestSpy->new( fail => { libsdl3 => 1 } );
    my $b   = build( [ 'zstd', { name => 'libsdl3' }, 'ninja' ], repo => $spy, probe_policy => 'off' );
    $b->run;
    my (@installs) = grep { $_->{action} eq 'install' } @{ $spy->calls };
    is scalar @installs, 3, 'all packages attempted';
    like $b->runtime_prop->{errors}{libsdl3}, qr/boom/, 'failure captured in runtime_prop';
    ok exists $b->runtime_prop->{packages}{zstd},  'sibling before the failure installed';
    ok exists $b->runtime_prop->{packages}{ninja}, 'sibling after the failure installed';
    is $b->install_type, 'share', 'a partial success is still a share install';
};
subtest 'gather fetches anything the install did not produce' => sub {
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, probe_policy => 'off' );
    $b->install;
    $b->gather;
    my (@fetches) = grep { $_->{action} eq 'fetch' } @{ $spy->calls };
    is scalar @fetches,                             0,       'gather skips packages already gathered by install';
    is $b->runtime_prop->{packages}{zstd}{version}, '1.2.3', 'runtime data present';
    my $spy2 = Alien::Xrepo::Build::TestSpy->new;
    my $b2   = build( ['zstd'], repo => $spy2, probe_policy => 'off' );
    $b2->gather;
    my (@f2) = grep { $_->{action} eq 'fetch' } @{ $spy2->calls };
    is scalar @f2, 1, 'gather fetches when install never ran';
};
subtest 'export writes the runtime snapshot' => sub {
    my $snap = path($dir)->child('snapshot.json');
    my $spy  = Alien::Xrepo::Build::TestSpy->new;
    my $b    = build( [ { name => 'zstd', version => '1.5.6' } ], repo => $spy, probe_policy => 'off', snapshot => $snap );
    $b->run;
    ok -e $snap, 'snapshot file written';
    my $data = decode_json( $snap->slurp_utf8 );
    is $data->{install_type},               'share',           'snapshot records install_type';
    is $data->{packages}{zstd}{version},    '1.5.6',           'snapshot records runtime data';
    is $data->{packages}{zstd}{installdir}, '/tmp/store/zstd', 'snapshot records installdir';
};
subtest 'checkpoint/resume skips completed work' => sub {
    my $cp  = path($dir)->child('state.json');
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, probe_policy => 'off', checkpoint => $cp );
    $b->run;
    my $calls_after_first = scalar @{ $spy->calls };
    ok $calls_after_first > 0, 'first run made calls';
    ok -e $cp,                 'checkpoint file written';
    my $spy2      = Alien::Xrepo::Build::TestSpy->new;
    my $b2        = build( ['zstd'], repo => $spy2, probe_policy => 'off', checkpoint => $cp, resume => 1 );
    my @installs2 = grep { $_->{action} eq 'install' } @{ $spy2->calls };
    is scalar @installs2,                            0,       'resume did not reinstall';
    is $b2->install_type,                            'share', 'resumed build kept prior install_type';
    is $b2->runtime_prop->{packages}{zstd}{version}, '1.2.3', 'resumed build kept runtime data';
    is $b2->stage_done->{install},                   1,       'resumed build knows install completed';
};
subtest 'hooks run for every registered stage' => sub {
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, probe_policy => 'off' );
    my @ran;
    for my $stage (qw[install gather]) {
        $b->register_hook( $stage => sub { push @ran, $stage } );
    }
    ok $b->has_hook('install'), 'register_hook/has_hook agree';
    ok !$b->has_hook('probe'),  'unregistered stage has no hooks';
    like dies {
        $b->register_hook( bogus => sub { } )
    }, qr/Unknown stage/, 'bad stage dies';
    $b->run;
    is \@ran, [qw[install gather]], 'hooks fired in stage order';
};
our @HOOK_RAN;
subtest 'recipe hooks load once and register stage callbacks' => sub {
    my $hdir = Path::Tiny->tempdir;
    $hdir->child('RecipeHooksDemo.pm')->spew_utf8(
        q{
        package RecipeHooksDemo;
        sub register_hooks {
            my ($build) = @_;
            push @main::HOOK_RAN, 'register';
            $build->register_hook( install => sub { push @main::HOOK_RAN, 'install' } );
        }
        1;
    }
    );
    local @INC = ( @INC, $hdir->stringify );
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, probe_policy => 'off', recipe_extra => { hooks => ['RecipeHooksDemo'] } );
    is \@main::HOOK_RAN, [], 'nothing registered before configure';
    $b->configure;
    is \@main::HOOK_RAN,        ['register'], 'recipe hook module saw register_hooks';
    is $b->has_hook('install'), 1,            'recipe hook attached a stage hook';
    @main::HOOK_RAN = ();
    $b->configure;
    is scalar @main::HOOK_RAN, 0, 'recipe hooks load once even if configure repeats';
    $b->install;
    is \@main::HOOK_RAN, ['install'], 'recipe-registered hook fired at its stage';
};
subtest 'recipe hooks die clearly when the module lacks register_hooks' => sub {
    my $hdir = Path::Tiny->tempdir;
    $hdir->child('RecipeHooksBad.pm')->spew_utf8("package RecipeHooksBad;\n1;\n");
    local @INC = ( @INC, $hdir->stringify );
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, recipe_extra => { hooks => ['RecipeHooksBad'] } );
    like dies { $b->configure }, qr/register_hooks/, 'missing register_hooks dies at configure';
};
subtest 'pkg_roots resolve a package from a system root' => sub {
    my $root = Path::Tiny->tempdir;
    $root->child('include')->mkpath;
    $root->child('lib')->mkpath;
    $root->child( 'include', 'zstd.h' )->spew_utf8('#define ZSTD 1');
    $root->child( 'lib',     'libzstd.a' )->spew_utf8('');
    local $ENV{ZSTD_ROOT} = $root->stringify;
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, recipe_extra => { pkg_roots => { zstd => 'ZSTD_ROOT' } } );
    $b->run;
    my (@infos)    = grep { $_->{action} eq 'info' } @{ $spy->calls };
    my (@installs) = grep { $_->{action} eq 'install' } @{ $spy->calls };
    my (@fetches)  = grep { $_->{action} eq 'fetch' } @{ $spy->calls };
    is scalar @infos,                                      0,                'probe never consults xrepo for a root-resolved package';
    is scalar @installs,                                   0,                'install never consults xrepo for a root-resolved package';
    is scalar @fetches,                                    0,                'gather never consults xrepo for a root-resolved package';
    is $b->install_prop->{probed}{zstd}{satisfied},        1,                'probe reports the root-satisfied package';
    is $b->install_prop->{probed}{zstd}{root},             $root->stringify, 'probe records the root';
    is $b->runtime_prop->{packages}{zstd}{installdir},     $root->stringify, 'package data points at the root';
    is $b->runtime_prop->{packages}{zstd}{includedirs}[0], $root->child('include')->stringify, 'include dir from the root';
    is $b->runtime_prop->{packages}{zstd}{linkdirs}[0],    $root->child('lib')->stringify,     'lib dir from the root';
    is $b->runtime_prop->{packages}{zstd}{kind},           'system',                           'root entry marked as system-provided';
    is $b->install_type,                                   'system', 'a root-resolved package leaves install_type at system';
    ok !exists $b->runtime_prop->{errors}{zstd}, 'no error recorded';
};
subtest 'pkg_roots ignore a variable that does not name a directory' => sub {
    local $ENV{ZSTD_ROOT} = 'C:/definitely/not/a/real/zstd/root';
    my $spy = Alien::Xrepo::Build::TestSpy->new;
    my $b   = build( ['zstd'], repo => $spy, recipe_extra => { pkg_roots => { zstd => 'ZSTD_ROOT' } } );
    $b->run;
    my (@infos) = grep { $_->{action} eq 'info' } @{ $spy->calls };
    is scalar @infos,                               1, 'probe consulted xrepo when the root env var was unusable';
    is $b->install_prop->{probed}{zstd}{satisfied}, 0, 'package not satisfied by a dead root';
    ok exists $b->runtime_prop->{packages}{zstd}, 'package installed via xrepo as normal';
};
done_testing;



( run in 0.623 second using v1.01-cache-2.11-cpan-364913b4093 )