mirror of
https://github.com/webmin/webmin.git
synced 2026-08-25 16:10:54 +01:00
This PR adds DNF 4 and DNF 5 versionlock support to Package Updates, including listing, holding, unholding, and explicitly updating held packages. DNF 4 requires the versionlock plugin to be installed, while DNF 5 supports versionlock natively. Hold controls are shown only when the versionlock command is available. It also previews DNF transactions before confirmation so users can see which packages will be installed or updated in the table, similar to APT.
196 lines
5.9 KiB
Perl
196 lines
5.9 KiB
Perl
#!/usr/local/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Test::More;
|
|
use File::Basename qw(dirname);
|
|
use File::Spec;
|
|
use Cwd qw(abs_path);
|
|
|
|
our %config;
|
|
our $apt_get_command;
|
|
|
|
my $root = abs_path(File::Spec->catdir(dirname(__FILE__), '..'));
|
|
chdir($root) or die "chdir($root): $!";
|
|
|
|
do './software/apt-lib.pl' or die $@ || $!;
|
|
|
|
is(strip_apt_package_arch('libtinfo6:amd64'), 'libtinfo6',
|
|
'strips native Debian architecture suffix');
|
|
is(strip_apt_package_arch('libgomp1:i386'), 'libgomp1',
|
|
'strips foreign Debian architecture suffix');
|
|
is(strip_apt_package_arch('ncurses-base'), 'ncurses-base',
|
|
'leaves unqualified package names unchanged');
|
|
|
|
{
|
|
no warnings qw(once redefine);
|
|
my $command;
|
|
local *backquote_command = sub {
|
|
$command = $_[0];
|
|
return "Inst libtinfo6:amd64 [6.3-2ubuntu0.1] ".
|
|
"(6.3-2ubuntu0.2 Ubuntu:22.04/jammy-updates [amd64])\n";
|
|
};
|
|
local *clean_language = sub { };
|
|
local *reset_environment = sub { };
|
|
|
|
my @ops = update_system_operations('libtinfo6');
|
|
is($ops[0]->{'name'}, 'libtinfo6',
|
|
'normalizes package names from simulated APT operations');
|
|
update_system_operations('libtinfo6', update_system_hold_flags());
|
|
like($command, qr/^apt-get -s --allow-change-held-packages install /,
|
|
'overrides holds while resolving an explicit held-package update');
|
|
}
|
|
|
|
{
|
|
no warnings qw(once redefine);
|
|
my $apt_output = "Setting up libtinfo6:amd64 (6.3-2ubuntu0.2) ...\n";
|
|
my $yes_input = "";
|
|
my $executed_command = "";
|
|
my @reheld;
|
|
local *additional_log = sub { };
|
|
local *backquote_logged = sub { return ""; };
|
|
local *clean_language = sub { };
|
|
local *html_escape = sub { return $_[0]; };
|
|
local *reset_environment = sub { };
|
|
local *list_update_system_holds = sub {
|
|
return ('libtinfo6:amd64', 'held-dependency:i386');
|
|
};
|
|
local *update_system_hold = sub {
|
|
my ($packages, $hold) = @_;
|
|
@reheld = @$packages if ($hold);
|
|
return undef;
|
|
};
|
|
local *text = sub { return $_[0]; };
|
|
local *transname = sub { return "/tmp/software-apt-test-yes"; };
|
|
local *open_tempfile = sub {
|
|
my ($fh) = @_;
|
|
no strict 'refs';
|
|
open(ref($fh) ? $fh : \*{$fh}, ">", \$yes_input)
|
|
or die "open temp input: $!";
|
|
};
|
|
local *print_tempfile = sub {
|
|
my ($fh, @text) = @_;
|
|
no strict 'refs';
|
|
my $handle = ref($fh) ? $fh : \*{$fh};
|
|
print $handle @text;
|
|
};
|
|
local *close_tempfile = sub {
|
|
my ($fh) = @_;
|
|
no strict 'refs';
|
|
close(ref($fh) ? $fh : \*{$fh});
|
|
};
|
|
local *open_execute_command = sub {
|
|
my ($fh, $command) = @_;
|
|
$executed_command = $command;
|
|
no strict 'refs';
|
|
open(ref($fh) ? $fh : \*{$fh}, "<", \$apt_output)
|
|
or die "open simulated apt output: $!";
|
|
};
|
|
local $config{'package_system'} = 'debian';
|
|
local $apt_get_command = 'aptitude';
|
|
|
|
my $printed = "";
|
|
open(my $stdout, ">", \$printed) or die "open captured stdout: $!";
|
|
local *STDOUT = $stdout;
|
|
my @installed = update_system_install(
|
|
'libtinfo6', undef, 1, '--allow-change-held-packages');
|
|
is_deeply(\@installed, [ 'libtinfo6' ],
|
|
'normalizes package names returned by apt install output');
|
|
like($executed_command, qr/apt-get -y --allow-change-held-packages install/,
|
|
'uses apt-get for the held-package override even in aptitude mode');
|
|
is_deeply(\@reheld, [ 'libtinfo6:amd64', 'held-dependency:i386' ],
|
|
'restores all exact holds after explicitly updating a held package');
|
|
|
|
$executed_command = "";
|
|
@reheld = ( );
|
|
update_system_install('libtinfo6', undef, 1);
|
|
like($executed_command, qr/^aptitude -y install/,
|
|
'continues using configured aptitude mode for regular installs');
|
|
is_deeply(\@reheld, [ ], 'does not reapply holds after a regular install');
|
|
}
|
|
|
|
{
|
|
no warnings qw(once redefine);
|
|
local *clean_language = sub { };
|
|
local *reset_environment = sub { };
|
|
local *has_command = sub {
|
|
return $_[0] eq 'aptitude' || $_[0] eq 'apt-mark';
|
|
};
|
|
local *open_execute_command = sub {
|
|
my ($fh, $command) = @_;
|
|
my $output = $command =~ /^dpkg / ?
|
|
"alpha hold\ndelta:amd64 hold\n" :
|
|
$command =~ /^aptitude / ?
|
|
".h beta 1.0 installed\n" :
|
|
$command =~ /^apt-mark / ?
|
|
"gamma\ndelta:amd64\n" : "";
|
|
no strict 'refs';
|
|
open(ref($fh) ? $fh : \*{$fh}, '<', \$output)
|
|
or die "open simulated holds: $!";
|
|
};
|
|
|
|
is_deeply([ list_update_system_holds() ],
|
|
[ qw(alpha beta delta:amd64 gamma) ],
|
|
'combines held packages without discarding architecture qualifiers');
|
|
}
|
|
|
|
{
|
|
no warnings qw(once redefine);
|
|
my $apt_output =
|
|
"Listing...\n".
|
|
"held-pkg/stable 2.0 amd64 [upgradable from: 1.0]\n".
|
|
"regular-pkg/stable 3.0 amd64 [upgradable from: 2.0]\n";
|
|
local *clean_language = sub { };
|
|
local *reset_environment = sub { };
|
|
local *execute_command = sub { return 0; };
|
|
local *list_update_system_holds = sub { return ('held-pkg'); };
|
|
local *has_command = sub { return $_[0] eq 'apt'; };
|
|
local *open_execute_command = sub {
|
|
my ($fh, $command) = @_;
|
|
my $output = $command =~ /^apt list / ? $apt_output : "";
|
|
no strict 'refs';
|
|
open(ref($fh) ? $fh : \*{$fh}, '<', \$output)
|
|
or die "open simulated updates: $!";
|
|
};
|
|
|
|
my @normal = update_system_updates(0);
|
|
is_deeply([ map { $_->{'name'} } @normal ], [ 'regular-pkg' ],
|
|
'default APT updates still exclude held packages');
|
|
my @with_holds = update_system_updates(1);
|
|
is_deeply([ map { $_->{'name'} } @with_holds ],
|
|
[ 'held-pkg', 'regular-pkg' ],
|
|
'held-update query includes regular and held packages');
|
|
ok($with_holds[0]->{'held'}, 'marks the held update');
|
|
ok(!$with_holds[1]->{'held'}, 'does not mark a regular update as held');
|
|
}
|
|
|
|
{
|
|
no warnings qw(once redefine);
|
|
my $command;
|
|
local *has_command = sub { return '/usr/bin/apt-mark'; };
|
|
local *unique = sub {
|
|
my %seen;
|
|
return grep { !$seen{$_}++ } @_;
|
|
};
|
|
local *trim = sub {
|
|
my ($value) = @_;
|
|
$value =~ s/^\s+|\s+$//g;
|
|
return $value;
|
|
};
|
|
local *clean_language = sub { };
|
|
local *reset_environment = sub { };
|
|
local *execute_command_logged = sub {
|
|
my ($cmd, undef, $stdout) = @_;
|
|
$command = $cmd;
|
|
$$stdout = "";
|
|
return 0;
|
|
};
|
|
|
|
is(update_system_hold([ 'webmin-virtual-server', 'webmin-virtual-server' ], 1),
|
|
undef, 'holds packages successfully');
|
|
is($command, 'apt-mark hold webmin\-virtual\-server',
|
|
'builds a quoted apt-mark hold command without duplicates');
|
|
}
|
|
|
|
done_testing();
|