Skip to content

Commit

Permalink
Test::Quattor: mock CAF::Path link-related methods
Browse files Browse the repository at this point in the history
- is_symlink(), has_hardlink(), is_hardlink() and _make_link()
- See comments for restrictions with mocked xxx_harlink() methods

Fixes quattor#141
  • Loading branch information
jouvin committed Mar 12, 2017
1 parent 86ebce6 commit 88ba8ba
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 5 deletions.
143 changes: 139 additions & 4 deletions build-scripts/src/main/perl/Test/Quattor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ use CAF::Process;
use CAF::FileEditor;
use CAF::Application;
use CAF::Path;
use CAF::Object qw(SUCCESS);
use CAF::Object qw(SUCCESS CHANGED);
use IO::String;
use base 'Exporter';
use Cwd;
Expand All @@ -73,10 +73,14 @@ use Test::More;
use CAF::Service qw(@FLAVOURS);
use Test::Quattor::ProfileCache qw(prepare_profile_cache get_config_for_profile);
use Test::Quattor::Object qw(warn_is_ok);
use Cwd;
use Readonly;

# "File" content that will appear as a directory
Readonly our $DIRECTORY => 'MAGIC STRING, THIS IS A MOCKED DIRECTORY';
# "File" content that will appear as a symlink
Readonly our $SYMLINK => 'MAGIC STRING, THIS IS A MOCKED SYMLINK: ';
Readonly our $HARDLINK => 'MAGIC STRING, THIS IS A MOCKED HARDLINK: ';

=over
Expand Down Expand Up @@ -209,8 +213,7 @@ our @EXPORT = qw(get_command set_file_contents get_file set_desired_output
set_desired_err get_config_for_profile set_command_status
command_history_reset command_history_ok set_service_variant
set_caf_file_close_diff
make_directory remove_any reset_caf_path
warn_is_ok);
make_directory remove_any reset_caf_path warn_is_ok);

my @logopts = qw(--verbose);
my $debuglevel = $ENV{QUATTOR_TEST_LOG_DEBUGLEVEL};
Expand Down Expand Up @@ -520,6 +523,139 @@ Return the mocked C<is_any>

$cpath->mock("any_exists", sub {shift; return is_any(shift); });

=item is_symlink
Test if given C<path> is a mocked symlink
=cut

$cpath->mock("is_symlink", sub {
my ($self, $path) = @_;
$path = sane_path($path);

return $files_contents{$path} && "$files_contents{$path}" =~ qr/^$SYMLINK/;
});

=item has_hardlinks
Test if given C<path> is a mocked hardlink
Note that it is not a perfect replacement for the c<CAF::Path> C<has_hardlinks> because
the current implementation of mocked hardlinks does not allow to mimic multiple references
to an inode. The differences are : the link used at creation time must be queried, not the
target (where in a real hardlink target and link are undistinguishable); if the path is
a hardlink the number of references for the inode is always 1.
=cut

$cpath->mock("has_hardlinks", sub {
my ($self, $path) = @_;
$path = sane_path($path);

return $files_contents{$path} && "$files_contents{$path}" =~ qr/^$HARDLINK/;
});

=item is_hardlink
Test if C<path1> and C<path2> are hardlinked
=cut

$cpath->mock("is_hardlink", sub {
my ($self, $path1, $path2) = @_;
$path1 = sane_path($path1);
$path2 = sane_path($path2);

# Order of arguments does not matter with real hardlink() method:
# mimic this behaviour.
# If both paths are hardlinks, they are considered different
my $link_path;
my $target;
if ( $self->has_hardlinks($path1) ) {
$link_path = $path1;
$target = $path2;
} elsif ( $self->has_hardlinks($path2) && ! defined($link_path) ) {
$link_path = $path2;
$target = $path1;
} else {
return;
}

if ( ($files_contents{$link_path} =~ qr/^$HARDLINK(\S+)/) && ($1 eq $target) ) {
return SUCCESS;
} else {
return;
}

});

=item _make_link
Add a mocked C<_make_link>.
This mocked method implements most of the checks done in C<LC::Check::link>, the function
doing the real work in C<_make_link>, and returns the same values as C<CAF::Path> C<_make_link>.
See C<CAF::Path> comments for details.
=cut

$cpath->mock('_make_link', sub {
my ($self, $target, $link_path, %opts) = @_;
$link_path = sane_path($link_path);
$target = sane_path($target);

# Check options passed
Readonly my @MAKE_LINK_vALID_OPTS => qw(hard backup nocheck force);
for my $opt (sort keys %opts) {
unless ( grep (/^$opt$/, @MAKE_LINK_vALID_OPTS) ) {
$self->error("Invalid option ($opt) passed to _make_link()");
}
}

my $target_full_path = $target;
unless ( $target_full_path =~ qr{^/} ) {
$target_full_path = cwd() . "/$target_full_path";
}

# Check that target exists if it is a hardlink or if option 'nocheck' is false
if ( $opts{hard} or ! $opts{nocheck} ) {
unless ( $files_contents{$target_full_path} ) {
$self->error("Symlink target ($target_full_path) doesn't exist");
}
}

my $is_link_pattern = 0;
my $is_link_method;
if ( $opts{hard} ) {
$is_link_pattern = $HARDLINK;
$is_link_method = "has_hardlinks";
} else {
$is_link_pattern = $SYMLINK;
$is_link_method = "is_symlink";
}
if ( $self->$is_link_method($link_path) ) {
if ( ($files_contents{$link_path} =~ qr/^$is_link_pattern(\S+)/) && ($1 eq $target) ) {
# Link already properly defined
return SUCCESS;
};
}
if ( ! $self->is_symlink($link_path) ) {
if ( $self->file_exists($link_path) ) {
unless ( $opts{force} ) {
$self->error("File $link_path already exists and option 'force' not specified");
return
}
} elsif ( $self->any_exists($link_path) ) {
$self->error("$link_path already exists and is not a symlink");
return
}
}

$files_contents{$link_path} = "$SYMLINK$target";

return CHANGED;
});

=item C<CAF::Path::directory>
Return directory name unless mocked C<make_directory> or mocked C<LC_Check> fail.
Expand Down Expand Up @@ -943,7 +1079,6 @@ sub is_directory
}

=item is_any
Test if given C<path> is known (as file or directory or anything else)
=cut
Expand Down
25 changes: 24 additions & 1 deletion build-scripts/src/test/perl/quattor_path.t
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use warnings;

use Test::More;

use CAF::Object qw(SUCCESS);
use CAF::Object qw(SUCCESS CHANGED);
use Test::Quattor;
use Test::Quattor::Object;

Expand Down Expand Up @@ -35,6 +35,7 @@ my $filename = "$filebase/file1";

# Test file does not exist (mocked)
ok(! $s->file_exists($filename), "file $filename does not exist");
ok(! $s->is_symlink($filename), "is_symlink returns false (file $filename does not exist)");
# Create file with simple_caf
is($s->make_file($filename, $TEXT), SUCCESS, "make_file returns success");

Expand All @@ -46,6 +47,7 @@ is("$fh", $TEXT, "make_file made correct file");
ok($s->file_exists($filename), "mocked file $filename returns true file_exists");
ok(!$s->directory_exists($filename), "mocked file $filename returns false directory_exists");
ok($s->any_exists($filename), "mocked file $filename returns true any_exists");
ok(! $s->is_symlink($filename), "mocked file $filename returns false is_symlink");

# Test creation of basedir of filename
ok(!$s->file_exists($filebase), "mocked dir $filebase returns false file_exists");
Expand Down Expand Up @@ -106,6 +108,26 @@ is_deeply([sort keys %Test::Quattor::files_contents ], [qw(/ /some)],

is_deeply({ %Test::Quattor::desired_file_contents }, {}, "entries in desired_file_contents are also deleted");


=head2 Test mocked symlink creation
=cut

my $targetbase = "$BASEPATH/files";
my $linkbase = "$BASEPATH/files/subdir";
my $symlink1 = "$linkbase/symlink1";
my $symlink2 = "$linkbase/symlink2";
my $target1 = "$targetbase/target1";
my $target2 = "$targetbase/target2";

is($s->symlink($target1, $symlink1), CHANGED, "Symlink $symlink1 successfully created");
ok($s->is_symlink($symlink1), "$symlink1 is a symlink");
is($s->symlink($target1, $symlink1), SUCCESS, "Symlink $symlink1 already exists with the right target");
ok($s->is_symlink($symlink1), "$symlink1 is still a symlink");
is($s->symlink($target2, $symlink1), CHANGED, "Symlink $symlink1 has been successfully updated");
ok($s->is_symlink($symlink1), "$symlink1 is still a symlink after being updated");


=head2 mock LC and cleanup
=cut
Expand All @@ -119,6 +141,7 @@ ok(! $s->directory_exists($dirbase), "directory $dirbase cleanedup (via recursiv
is_deeply($Test::Quattor::caf_path->{cleanup}, [[[$BASEPATH, 1], {option => 2}]],
"caf_path hash updated after CAF::Path::cleanup");


=head2 mock move
=cut
Expand Down

0 comments on commit 88ba8ba

Please sign in to comment.