3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <https://www.gnu.org/licenses/>.
17
use Dpkg::Path qw(find_command);
24
my $srcdir = $ENV{srcdir} || '.';
25
my $tmpdir = 't.tmp/update_alternatives';
26
my $admindir = File::Spec->rel2abs("$tmpdir/admindir"),
27
my $altdir = File::Spec->rel2abs("$tmpdir/alternatives");
28
my $bindir = File::Spec->rel2abs("$tmpdir/bin");
29
my @ua = ("$ENV{builddir}/update-alternatives", '--log', '/dev/null',
30
'--quiet', '--admindir', "$admindir", '--altdir', "$altdir");
33
true => find_command('true'),
34
false => find_command('false'),
35
yes => find_command('yes'),
36
cat => find_command('cat'),
37
date => find_command('date'),
38
sleep => find_command('sleep'),
41
if (! -x "$ENV{builddir}/update-alternatives") {
42
plan skip_all => 'update-alternatives not available';
46
my $main_link = "$bindir/generic-test";
47
my $main_name = 'generic-test';
54
link => "$bindir/slave2",
59
link => "$bindir/slave3",
64
link => "$bindir/slave1",
69
link => "$bindir/slave4",
76
path => $paths{false},
80
link => "$bindir/slave1",
87
path => $paths{sleep},
93
plan tests => (4 * ($nb_slaves + 1) + 2) * 26 # number of check_choices
97
system("rm -rf $tmpdir && mkdir -p $admindir && mkdir -p $altdir");
98
system("mkdir -p $bindir/more");
102
my ($params, %opts) = @_;
103
spawn(exec => [ @ua, @$params ], nocheck => 1,
104
wait_child => 1, env => { LC_ALL => 'C' }, %opts);
106
$test_id = "$opts{test_id}: " if defined $opts{test_id};
107
if ($opts{expect_failure}) {
108
ok($? != 0, "${test_id}update-alternatives @$params should fail.") or
109
diag("Did not fail as expected: @ua @$params");
111
ok($? == 0, "${test_id}update-alternatives @$params should work.") or
112
diag("Did not succeed as expected: @ua @$params");
117
my ($id, %opts) = @_;
118
my $alt = $choices[$id];
120
push @params, @{$opts{params}} if exists $opts{params};
121
push @params, '--install', "$main_link", "$main_name",
122
$alt->{path}, $alt->{priority};
123
foreach my $slave (@{ $alt->{slaves} }) {
124
push @params, '--slave', $slave->{link}, $slave->{name}, $slave->{path};
126
call_ua(\@params, %opts);
130
my ($id, %opts) = @_;
131
my $alt = $choices[$id];
133
push @params, @{$opts{params}} if exists $opts{params};
134
push @params, '--remove', $main_name, $alt->{path};
135
call_ua(\@params, %opts);
138
sub remove_all_choices {
141
push @params, @{$opts{params}} if exists $opts{params};
142
push @params, '--remove-all', $main_name;
143
call_ua(\@params, %opts);
147
my ($id, %opts) = @_;
148
my $alt = $choices[$id];
150
push @params, @{$opts{params}} if exists $opts{params};
151
push @params, '--set', $main_name, $alt->{path};
152
call_ua(\@params, %opts);
156
my ($id, %opts) = @_;
157
my ($input, $output) = ('', '');
159
my $alt = $choices[$id];
160
$input = $alt->{path};
165
$opts{from_string} = \$input;
166
$opts{to_string} = \$output;
168
push @params, @{$opts{params}} if exists $opts{params};
169
push @params, '--config', $main_name;
170
call_ua(\@params, %opts);
173
sub get_slaves_status {
176
# None of the slaves are installed
177
foreach my $alt (@choices) {
178
for my $i (0 .. @{$alt->{slaves}} - 1) {
179
$slaves{$alt->{slaves}[$i]{name}} = $alt->{slaves}[$i];
180
$slaves{$alt->{slaves}[$i]{name}}{installed} = 0;
183
# except those of the current alternative (minus optional slaves)
185
my $alt = $choices[$id];
186
for my $i (0 .. @{$alt->{slaves}} - 1) {
187
$slaves{$alt->{slaves}[$i]{name}} = $alt->{slaves}[$i];
188
if (-e $alt->{slaves}[$i]{path}) {
189
$slaves{$alt->{slaves}[$i]{name}}{installed} = 1;
193
my @slaves = sort { $a->{name} cmp $b->{name} } values %slaves;
199
my ($link, $value, $msg) = @_;
200
ok(-l $link, "$msg: $link disappeared.");
201
is(readlink($link), $value, "$link doesn't point to $value.");
204
my ($link, $msg) = @_;
206
ok(!-e _, "$msg: $link still exists.");
207
ok(1, 'fake test'); # Same number of tests as check_link
212
foreach my $slave (get_slaves_status($id)) {
213
if ($slave->{installed}) {
214
check_link("$altdir/$slave->{name}", $slave->{path}, $msg);
215
check_link($slave->{link}, "$altdir/$slave->{name}", $msg);
217
check_no_link("$altdir/$slave->{name}", $msg);
218
check_no_link($slave->{link}, $msg);
222
# (4 * (nb_slaves+1) + 2) tests in each check_choice() call
224
my ($id, $mode, $msg) = @_;
228
call_ua([ '--query', "$main_name" ], to_string => \$output, test_id => $msg);
229
$output =~ /^Status: (.*)$/im;
230
is($1, $mode, "$msg: status is not $mode.");
232
my $alt = $choices[$id];
233
check_link("$altdir/$main_name", $alt->{path}, $msg);
234
check_link($main_link, "$altdir/$main_name", $msg);
235
check_slaves($id, $msg);
237
call_ua([ '--query', "$main_name" ], error_to_string => \$output,
238
expect_failure => 1, test_id => $msg);
239
ok($output =~ /no alternatives/, "$msg: bad error message for --query.");
240
# Check that all links have disappeared
241
check_no_link("$altdir/$main_name", $msg);
242
check_no_link($main_link, $msg);
243
check_slaves(undef, $msg);
249
# removal when not installed should not fail
251
# successive install in auto mode
253
check_choice(1, 'auto', 'initial install 1');
254
install_choice(2); # 2 is lower prio, stays at 1
255
check_choice(1, 'auto', 'initial install 2');
256
install_choice(0); # 0 is higher priority
257
check_choice(0, 'auto', 'initial install 3');
259
# verify that the administrative file is sorted properly
262
open(my $db_fh, '<', "$admindir/generic-test") or die $!;
263
my $content = <$db_fh>;
282
# Store slaves in a hash to easily retrieve present and missing ones.
283
foreach my $alt (@choices) {
284
foreach my $slave (@{$alt->{slaves}}) {
285
$slaves{$slave->{name}}{$alt->{path}} = $slave;
289
foreach my $alt (sort { $a->{path} cmp $b->{path} } @choices) {
290
$expected .= $alt->{path} . "\n";
291
$expected .= $alt->{priority} . "\n";
292
foreach my $slave_name (sort keys %slaves) {
293
$expected .= $slaves{$slave_name}{$alt->{path}}{path} || '';
299
is($content, $expected, 'administrative file is as expected');
302
# manual change with --set-selections
303
my $input = "doesntexist auto $paths{date}\ngeneric-test manual $paths{false}\n";
305
call_ua(['--set-selections'], from_string => \$input,
306
to_string => \$output, test_id => 'manual update with --set-selections');
307
check_choice(1, 'manual', 'manual update with --set-selections');
308
$input = "generic-test auto $paths{true}\n";
309
call_ua(['--set-selections'], from_string => \$input,
310
to_string => \$output, test_id => 'auto update with --set-selections');
311
check_choice(0, 'auto', 'auto update with --set-selections');
312
# manual change with set
313
set_choice(2, test_id => 'manual update with --set');
314
check_choice(2, 'manual', 'manual update with --set'); # test #388313
315
remove_choice(2, test_id => 'remove manual, back to auto');
316
check_choice(0, 'auto', 'remove manual, back to auto');
317
remove_choice(0, test_id => 'remove best');
318
check_choice(1, 'auto', 'remove best');
319
remove_choice(1, test_id => 'no alternative left');
320
check_choice(undef, '', 'no alternative left');
321
# single choice in manual mode, to be removed
324
check_choice(1, 'manual', 'single manual choice');
326
check_choice(undef, '', 'removal single manual');
331
remove_all_choices(test_id => 'remove all');
332
check_choice(undef, '', 'no alternative left');
333
# check auto-recovery of user mistakes (#100135)
335
ok(unlink("$bindir/generic-test"), 'failed removal');
336
ok(unlink("$bindir/slave1"), 'failed removal');
338
check_choice(1, 'auto', 'recreate links in auto mode');
340
ok(unlink("$bindir/generic-test"), 'failed removal');
341
ok(unlink("$bindir/slave1"), 'failed removal');
343
check_choice(1, 'manual', 'recreate links in manual mode');
344
# check recovery of /etc/alternatives/*
346
ok(unlink("$altdir/generic-test"), 'failed removal');
348
check_choice(0, 'auto', '<altdir>/generic-test lost, back to auto');
351
check_choice(0, 'manual', 'config to best but manual');
353
check_choice(1, 'manual', 'config to manual');
355
check_choice(0, 'auto', 'config auto');
357
# test rename of links
359
my $old_slave = $choices[0]{slaves}[0]{link};
360
my $old_link = $main_link;
361
$choices[0]{slaves}[0]{link} = "$bindir/more/generic-slave";
362
$main_link = "$bindir/more/mytest";
364
check_choice(0, 'auto', 'test rename of links');
365
check_no_link($old_link, 'test rename of links');
366
check_no_link($old_slave, 'test rename of links');
367
# rename with installing other alternatives
368
$old_link = $main_link;
369
$main_link = "$bindir/generic-test";
371
check_choice(0, 'auto', 'rename link');
372
check_no_link($old_link, 'rename link');
373
# rename with lost file
375
$old_slave = $choices[0]{slaves}[0]{link};
376
$choices[0]{slaves}[0]{link} = "$bindir/generic-slave-bis";
378
check_choice(0, 'auto', 'rename lost file');
379
check_no_link($old_slave, 'rename lost file');
380
# update of alternative with many slaves not currently installed
381
# and the link of the renamed slave exists while it should not
383
symlink("$paths{cat}", "$bindir/generic-slave-bis");
384
$choices[0]{slaves}[0]{link} = "$bindir/slave2";
385
install_choice(0, test_id => 'update with non-installed slaves');
386
check_no_link("$bindir/generic-slave-bis",
387
'drop renamed symlink that should not be installed');
389
# test install with empty admin file (#457863)
391
system("touch $admindir/generic-test");
393
# test install with garbage admin file
395
system("echo garbage > $admindir/generic-test");
396
install_choice(0, error_to_file => '/dev/null', expect_failure => 1);
398
# test invalid usages
401
# try to install a slave alternative as new master
402
call_ua(['--install', "$bindir/testmaster", 'slave1', "$paths{date}", '10'],
403
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
404
# try to install a master alternative as slave
405
call_ua(['--install', "$bindir/testmaster", 'testmaster', "$paths{date}", '10',
406
'--slave', "$bindir/testslave", 'generic-test', "$paths{true}" ],
407
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
408
# try to reuse master link in slave
409
call_ua(['--install', "$bindir/testmaster", 'testmaster', "$paths{date}", '10',
410
'--slave', "$bindir/testmaster", 'testslave', "$paths{true}" ],
411
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
412
# try to reuse links in master alternative
413
call_ua(['--install', "$bindir/slave1", 'testmaster', "$paths{date}", '10'],
414
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
415
# try to reuse links in slave alternative
416
call_ua(['--install', "$bindir/testmaster", 'testmaster', "$paths{date}", '10',
417
'--slave', "$bindir/generic-test", 'testslave', "$paths{true}" ],
418
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
419
# try to reuse slave link in another slave alternative of another choice of
420
# the same main alternative
421
call_ua(['--install', $main_link, $main_name, "$paths{date}", '10',
422
'--slave', "$bindir/slave1", 'testslave', "$paths{true}" ],
423
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
424
# lack of absolute filenames in links or file path, non-existing path,
425
call_ua(['--install', '../testmaster', 'testmaster', "$paths{date}", '10'],
426
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
427
call_ua(['--install', "$bindir/testmaster", 'testmaster', './update-alternatives.pl', '10'],
428
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
429
# non-existing alternative path
430
call_ua(['--install', "$bindir/testmaster", 'testmaster', "$bindir/doesntexist", '10'],
431
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
432
# invalid alternative name in master
433
call_ua(['--install', "$bindir/testmaster", 'test/master', "$paths{date}", '10'],
434
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
435
# invalid alternative name in slave
436
call_ua(['--install', "$bindir/testmaster", 'testmaster', "$paths{date}", '10',
437
'--slave', "$bindir/testslave", 'test slave', "$paths{true}" ],
438
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
439
# install in non-existing dir should fail
440
call_ua(['--install', "$bindir/doesntexist/testmaster", 'testmaster', "$paths{date}", '10',
441
'--slave', "$bindir/testslave", 'testslave', "$paths{true}" ],
442
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
443
call_ua(['--install', "$bindir/testmaster", 'testmaster', "$paths{date}", '10',
444
'--slave', "$bindir/doesntexist/testslave", 'testslave', "$paths{true}" ],
445
expect_failure => 1, to_file => '/dev/null', error_to_file => '/dev/null');
447
# non-existing alternative path in slave is not a failure
448
my $old_path = $choices[0]{slaves}[0]{path};
449
$old_slave = $choices[0]{slaves}[0]{link};
450
$choices[0]{slaves}[0]{path} = "$bindir/doesntexist";
451
$choices[0]{slaves}[0]{link} = "$bindir/baddir/slave2";
452
# test rename of slave link that existed but that doesn't anymore
453
# and link is moved into non-existing dir at the same time
455
check_choice(0, 'auto', 'optional renamed slave2 in non-existing dir');
456
# same but on fresh install
459
check_choice(0, 'auto', 'optional slave2 in non-existing dir');
460
$choices[0]{slaves}[0]{link} = $old_slave;
461
# test fresh install with a non-existing slave file
464
check_choice(0, 'auto', 'optional slave2');
465
$choices[0]{slaves}[0]{path} = $old_path;
467
# test management of pre-existing files
469
system("touch $main_link $bindir/slave1");
471
ok(!-l $main_link, 'install preserves files that should be links');
472
ok(!-l "$bindir/slave1", 'install preserves files that should be slave links');
474
ok(-f $main_link, 'removal keeps real file installed as master link');
475
ok(-f "$bindir/slave1", 'removal keeps real files installed as slave links');
476
install_choice(0, params => ['--force']);
477
check_choice(0, 'auto', 'install --force replaces files with links');
479
# test management of pre-existing files #2
481
system("touch $main_link $bindir/slave2");
484
ok(!-l $main_link, 'inactive install preserves files that should be links');
485
ok(!-l "$bindir/slave2", 'inactive install preserves files that should be slave links');
486
ok(-f $main_link, 'inactive install keeps real file installed as master link');
487
ok(-f "$bindir/slave2", 'inactive install keeps real files installed as slave links');
489
ok(!-l $main_link, 'manual switching preserves files that should be links');
490
ok(!-l "$bindir/slave2", 'manual switching preserves files that should be slave links');
491
ok(-f $main_link, 'manual switching keeps real file installed as master link');
492
ok(-f "$bindir/slave2", 'manual switching keeps real files installed as slave links');
494
ok(!-l $main_link, 'auto switching preserves files that should be links');
495
ok(!-l "$bindir/slave2", 'auto switching preserves files that should be slave links');
496
ok(-f $main_link, 'auto switching keeps real file installed as master link');
497
ok(-f "$bindir/slave2", 'auto switching keeps real files installed as slave links');
498
remove_all_choices(params => ['--force']);
499
ok(!-e "$bindir/slave2", 'forced removeall drops real files installed as slave links');
501
# test management of pre-existing files #3
503
system("touch $main_link $bindir/slave2");
507
ok(!-l $main_link, 'removal + switching preserves files that should be links');
508
ok(!-l "$bindir/slave2", 'removal + switching preserves files that should be slave links');
509
ok(-f $main_link, 'removal + switching keeps real file installed as master link');
510
ok(-f "$bindir/slave2", 'removal + switching keeps real files installed as slave links');
512
ok(!-l $main_link, 'install + switching preserves files that should be links');
513
ok(!-l "$bindir/slave2", 'install + switching preserves files that should be slave links');
514
ok(-f $main_link, 'install + switching keeps real file installed as master link');
515
ok(-f "$bindir/slave2", 'install + switching keeps real files installed as slave links');
516
set_choice(1, params => ['--force']);
517
ok(!-e "$bindir/slave2", 'forced switching w/o slave drops real files installed as slave links');
518
check_choice(1, 'manual', 'set --force replaces files with links');