stow_contents: fix bugs and corner cases with type mismatch conflicts

If the target directory as a file named X and a package has a
directory named X, or vice-versa, then it is impossible for Stow
to stow that entry X from the package, even if --adopt is supplied.

However we were previously only handling the former case, and not the
latter, and the test for the former was actually broken.  So fix
stow_contents() to handle both cases correctly, fix the broken test,
and add a new test for the latter case.
This commit is contained in:
Adam Spiers 2024-04-02 00:06:38 +01:00
parent 8ed799a3a3
commit 34421ba5cf
2 changed files with 66 additions and 16 deletions

View file

@ -603,23 +603,45 @@ sub stow_node {
elsif ($self->is_a_node($target_subpath)) { elsif ($self->is_a_node($target_subpath)) {
debug(4, 1, "Evaluate existing node: $target_subpath"); debug(4, 1, "Evaluate existing node: $target_subpath");
if ($self->is_a_dir($target_subpath)) { if ($self->is_a_dir($target_subpath)) {
$self->stow_contents( if (! -d $pkg_path_from_cwd) {
$self->{stow_path}, # FIXME: why wasn't this ever needed before?
$package, $self->conflict(
$pkg_subpath, 'stow',
$target_subpath, $package,
); "cannot stow non-directory $pkg_path_from_cwd over existing directory target $target_subpath"
);
}
else {
$self->stow_contents(
$self->{stow_path},
$package,
$pkg_subpath,
$target_subpath,
);
}
} }
else { else {
# If we're here, $target_subpath is not a current or
# planned directory.
if ($self->{adopt}) { if ($self->{adopt}) {
$self->do_mv($target_subpath, $pkg_path_from_cwd); if (-d $pkg_path_from_cwd) {
$self->do_link($link_dest, $target_subpath); $self->conflict(
'stow',
$package,
"cannot stow directory $pkg_path_from_cwd over existing non-directory target $target_subpath"
);
}
else {
$self->do_mv($target_subpath, $pkg_path_from_cwd);
$self->do_link($link_dest, $target_subpath);
}
} }
else { else {
$self->conflict( $self->conflict(
'stow', 'stow',
$package, $package,
"existing target is neither a link nor a directory: $target_subpath" "cannot stow $pkg_path_from_cwd over existing target $target_subpath since neither a link nor a directory and --adopt not specified"
); );
} }
} }

View file

@ -22,7 +22,7 @@
use strict; use strict;
use warnings; use warnings;
use Test::More tests => 21; use Test::More tests => 22;
use Test::Output; use Test::Output;
use English qw(-no_match_vars); use English qw(-no_match_vars);
@ -103,7 +103,7 @@ subtest("Package dir 'bin4' conflicts with existing non-dir so can't unfold", su
is($stow->get_conflict_count, 1); is($stow->get_conflict_count, 1);
like( like(
$conflicts{stow}{pkg4}[0], $conflicts{stow}{pkg4}[0],
qr/existing target is neither a link nor a directory/ qr!cannot stow ../stow/pkg4/bin4 over existing target bin4 since neither a link nor a directory and --adopt not specified!
=> 'link to new dir bin4 conflicts with existing non-directory' => 'link to new dir bin4 conflicts with existing non-directory'
); );
}); });
@ -111,8 +111,7 @@ subtest("Package dir 'bin4' conflicts with existing non-dir so can't unfold", su
subtest("Package dir 'bin4a' conflicts with existing non-dir " . subtest("Package dir 'bin4a' conflicts with existing non-dir " .
"so can't unfold even with --adopt", sub { "so can't unfold even with --adopt", sub {
plan tests => 2; plan tests => 2;
#my $stow = new_Stow(adopt => 1); my $stow = new_Stow(adopt => 1);
my $stow = new_Stow();
make_file('bin4a'); # this is a file but named like a directory make_file('bin4a'); # this is a file but named like a directory
make_path('../stow/pkg4a/bin4a'); make_path('../stow/pkg4a/bin4a');
@ -121,8 +120,9 @@ subtest("Package dir 'bin4a' conflicts with existing non-dir " .
$stow->plan_stow('pkg4a'); $stow->plan_stow('pkg4a');
%conflicts = $stow->get_conflicts(); %conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 1); is($stow->get_conflict_count, 1);
like($conflicts{stow}{pkg4a}[0], like(
qr/existing target is neither a link nor a directory/ $conflicts{stow}{pkg4a}[0],
qr!cannot stow directory ../stow/pkg4a/bin4a over existing non-directory target bin4a!
=> 'link to new dir bin4a conflicts with existing non-directory' => 'link to new dir bin4a conflicts with existing non-directory'
); );
}); });
@ -146,14 +146,42 @@ subtest("Package files 'file4b' and 'bin4b' conflict with existing files", sub {
%conflicts = $stow->get_conflicts(); %conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 2 => 'conflict per file'); is($stow->get_conflict_count, 2 => 'conflict per file');
for my $i (0, 1) { for my $i (0, 1) {
my $target = $i ? 'file4b' : 'bin4b/file4b';
like( like(
$conflicts{stow}{pkg4b}[$i], $conflicts{stow}{pkg4b}[$i],
qr/existing target is neither a link nor a directory/ qr,cannot stow ../stow/pkg4b/$target over existing target $target since neither a link nor a directory and --adopt not specified,
=> 'link to file4b conflicts with existing non-directory' => 'link to file4b conflicts with existing non-directory'
); );
} }
}); });
subtest("Package files 'file4d' conflicts with existing directories", sub {
plan tests => 3;
my $stow = new_Stow();
# Populate target
make_path('file4d'); # this is a directory but named like a file to create the conflict
make_path('bin4d/file4d'); # same here
# Populate stow package
make_path('../stow/pkg4d');
make_file('../stow/pkg4d/file4d', 'file4d - version originally in stow package');
make_path('../stow/pkg4d/bin4d');
make_file('../stow/pkg4d/bin4d/file4d', 'bin4d/file4d - version originally in stow package');
$stow->plan_stow('pkg4d');
%conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 2 => 'conflict per file');
for my $i (0, 1) {
my $target = $i ? 'file4d' : 'bin4d/file4d';
like(
$conflicts{stow}{pkg4d}[$i],
qr!cannot stow non-directory ../stow/pkg4d/$target over existing directory target $target!
=> 'link to file4d conflicts with existing non-directory'
);
}
});
subtest("Package files 'file4c' and 'bin4c' can adopt existing versions", sub { subtest("Package files 'file4c' and 'bin4c' can adopt existing versions", sub {
plan tests => 8; plan tests => 8;
my $stow = new_Stow(adopt => 1); my $stow = new_Stow(adopt => 1);