Test-Virtual-Filesystem
view release on metacpan or search on metacpan
lib/Test/Virtual/Filesystem.pm view on Meta::CPAN
# http://use.perl.org/~ChrisDolan/journal/34920
sub Introduced : ATTR(CODE) { ## no critic(MixedCase)
my ($class, $symbol, $code_ref, $attr, $introduced_version) = @_;
if ($symbol eq 'ANON') {
warn 'cannot test anonymous subs - you probably loaded ' . __PACKAGE__ . ' too late.' .
' (after the CHECK block was run)';
} else {
# Wrap the sub in a version test
no warnings 'redefine'; ## no critic(TestingAndDebugging::ProhibitNoWarnings)
*{$symbol} = sub {
no strict 'refs'; ## no critic(TestingAndDebugging::ProhibitNoStrict)
local ${$class.'::TODO'} = $_[0]->_compatible($introduced_version); ## no critic(Local)
$code_ref->(@_);
};
#my $name = *{$symbol}{NAME};
#print STDERR "record $class\::$name as $introduced_version\n";
}
return;
}
sub _compatible {
my ($self, $introduced_version) = @_;
return if !$self->{compatible};
return if $introduced_version le $self->{compatible};
return 'compatibility mode ' . $self->{compatible};
}
=item Features($featurelist)
This is a subroutine attribute to specify one or more features used in
the test. The features should be listed as a comma-separated list:
sub symlink_create : Tests(1) : Features('symlink') {
ok(symlink($src, $dest));
}
sub symlink_permissions : Tests(2) : Features('symlink, permissions') {
ok(symlink($src, $dest));
ok(-w $dest);
}
Subfeatures must be separated from their parent features by a C</>. For example:
sub atime_mtime_set : Tests(1) : Features('time/atime, time/mtime') {
my $now = time;
ok(utime($now, $now, $file));
}
Look at the source code for C<%feature_defaults> to see the supported features and
subfeatures. The C<enable_test_*> methods above describe the all the
features, but in those methods the subfeature names are flattened.
=cut
sub Features : ATTR(CODE) { ## no critic(MixedCase)
my ($class, $symbol, $code_ref, $attr, $features) = @_;
if ($symbol eq 'ANON') {
warn 'cannot test anonymous subs - you probably loaded ' . $class . ' too late.' .
' (after the CHECK block was run)';
} else {
my @features = ref $features ? @{$features} : split m/\s*,\s*/xms, $features;
# Wrap the sub in a feature test
no warnings 'redefine'; ## no critic(TestingAndDebugging::ProhibitNoWarnings)
*{$symbol} = sub {
my $blocking_feature = _blocking_feature(__PACKAGE__, $_[0], @features);
return $blocking_feature if $blocking_feature;
return $code_ref->(@_);
};
}
return;
}
sub _blocking_feature {
my ($pkg, $self, @features) = @_;
for my $feature (@features) {
return $feature . ' (no OS support)' if $feature_disabled{$feature};
my $opts = $self->{fs_opts};
for my $part (split m{/}xms, $feature) {
return $feature if !ref $opts;
return $feature if !$opts->{$part};
$opts = $opts->{$part};
}
}
return;
}
=item stat_dir(), introduced in v0.01
=cut
sub stat_dir : Test(6) : Introduced('0.01') {
my ($self) = @_;
my $f = $self->_file(q{/});
ok(-e $f, 'mount dir exists');
ok(-d $f, 'mount dir is a dir');
ok(!-f $f, 'mount dir is not a file');
ok(!-l $f, 'mount dir is not a symlink');
ok(-r $f, 'mount dir is readable');
ok(-x $f, 'mount dir is searchable');
return;
}
## This turned out to be very platform-sensitive.
#
# =item stat_dir_size(), introduced in v0.02
#
# =cut
#
# sub stat_dir_size : Test(1) : Introduced('0.02') {
# my ($self) = @_;
# my $f = $self->_file(q{/});
# ok(-s $f, 'mount dir has non-zero size');
# return;
# }
=item read_dir(), introduced in v0.01
=cut
sub read_dir : Test(3) : Introduced('0.01') {
my ($self) = @_;
my $f = $self->_file(q{/});
my @files = $self->_read_dir($f);
cmp_ok(scalar @files, '>=', 2, 'dir contains at least two entries');
ok((any { $_ eq q{.} } @files), 'dir contains "."');
ok((any { $_ eq q{..} } @files), 'dir contains ".."');
return;
}
=item read_dir_fail(), introduced in v0.01
=cut
sub read_dir_fail : Test(2) : Introduced('0.01') {
my ($self) = @_;
my $f = $self->_file('/no_such');
eval {
$self->_read_dir_die($f);
lib/Test/Virtual/Filesystem.pm view on Meta::CPAN
my $src = $self->_file('/rename_srcdir');
my $dest = $self->_file('/rename_destfile.txt');
mkdir $src or die $OS_ERROR;
my $content = 'content';
$self->_write_file($dest, $content);
if ($OSNAME eq 'MSWin32' || $OSNAME eq 'cygwin') {
# return the skip message
return 'Windows and Cygwin allow rename(<dir>, <file>) instead of failing with ENOTDIR';
}
eval {
rename $src, $dest or die $OS_ERROR;
};
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOTDIR()], 'dest is not a directory');
return;
}
=item rename_symlink(), introduced in v0.08
=cut
sub rename_symlink : Test(6) : Introduced('0.08') : Features('symlink') {
my ($self) = @_;
my $srcfile = $self->_file('/rename_srcfile.txt');
my $src = $self->_file('/rename_src');
my $dest = $self->_file('/rename_dest');
my $content = 'content';
$self->_write_file($srcfile, $content);
symlink $srcfile, $src or die $OS_ERROR;
ok((rename $src, $dest), 'rename');
ok(-e $dest, 'dest exists');
ok(-e $srcfile, 'source target file still exists');
ok(!-e $src, 'src no longer exists');
ok(-l $dest, 'dest is a symlink');
is($self->_read_file($dest), $content, 'read dest');
return;
}
######### helpers ########
sub _is_errno {
my ($self, $eval_error, $os_errno, $expected_errnos, $msg) = @_;
my $num_errno = 0 + $os_errno;
my $str_errno = "$os_errno";
return pass($msg) if $eval_error && $num_errno && any {$_ == $num_errno} @{$expected_errnos};
my $expected_str = join q{, }, map {strerror($_)} @{$expected_errnos};
if (!$eval_error) {
return fail("$msg; didn't throw expected exception");
} elsif (1 == @{$expected_errnos}) {
return is("$num_errno ($str_errno)", "$expected_errnos->[0] ($expected_str)", $msg);
} else {
return is("$num_errno ($str_errno)", "[@{$expected_errnos}] ($expected_str)", $msg);
}
}
sub _file {
my ($self, $path) = @_;
$path =~ s{\A /}{}xms or croak 'test paths must be absolute';
# Change path to proper OS format
return File::Spec->catfile($self->{tempdir}, split m{/}xms, $path);
}
sub _write_file {
my ($self, $f, @content) = @_;
open my $fh, '>', $f or die $OS_ERROR;
binmode $fh;
for my $content (@content) {
print {$fh} $content or die $OS_ERROR;
}
close $fh or die $OS_ERROR;
return;
}
sub _append_file {
my ($self, $f, @content) = @_;
open my $fh, '>>', $f or die $OS_ERROR;
binmode $fh;
for my $content (@content) {
print {$fh} $content or die $OS_ERROR;
}
close $fh or die $OS_ERROR;
return;
}
sub _read_file {
my ($self, $f) = @_;
open my $fh, '<', $f or return;
binmode $fh;
my $content = do { local $INPUT_RECORD_SEPARATOR = undef; <$fh> };
close $fh or return;
return $content;
}
sub _read_file_die {
my ($self, $f) = @_;
open my $fh, '<', $f or die $OS_ERROR;
binmode $fh;
my $content = do { local $INPUT_RECORD_SEPARATOR = undef; <$fh> };
close $fh or die $OS_ERROR;
return $content;
}
sub _read_dir {
my ($self, $f) = @_;
opendir my $fh, $f or return;
my @content = readdir $fh;
closedir $fh or return;
return @content;
}
sub _read_dir_die {
my ($self, $f) = @_;
opendir my $fh, $f or die $OS_ERROR;
my @content = readdir $fh;
closedir $fh or die $OS_ERROR;
return @content;
}
1;
( run in 0.523 second using v1.01-cache-2.11-cpan-71847e10f99 )