our
@EXPORT
=
qw(
file_exists_ok file_not_exists_ok
file_empty_ok file_not_empty_ok file_size_ok file_max_size_ok
file_min_size_ok file_readable_ok file_not_readable_ok
file_writeable_ok file_writable_ok file_not_writeable_ok file_not_writable_ok
file_executable_ok file_not_executable_ok
file_mode_is file_mode_isnt
file_mode_has file_mode_hasnt
file_is_symlink_ok file_is_not_symlink_ok
symlink_target_exists_ok symlink_target_is
symlink_target_dangles_ok
dir_exists_ok dir_contains_ok
link_count_is_ok link_count_gt_ok link_count_lt_ok
owner_is owner_isnt
group_is group_isnt
file_line_count_is file_line_count_isnt file_line_count_between
file_contains_like file_contains_unlike
file_contains_utf8_like file_contains_utf8_unlike
file_contains_encoded_like file_contains_encoded_unlike
file_mtime_gt_ok file_mtime_lt_ok file_mtime_age_ok
)
;
our
$VERSION
=
'1.994'
;
XSLoader::load(__PACKAGE__,
$VERSION
)
if
$^O eq
'MSWin32'
;
my
$Test
= Test::Builder->new();
sub
_is_plain_file {
my
$filename
= _normalize(
shift
);
my
$message
=
do
{
if
( ! -e
$filename
) {
"does not exist"
}
elsif
( ! -f _ ) {
"is not a plain file"
}
elsif
( -d _ ) {
"is a directory"
}
else
{ () }
};
if
(
$message
) {
$Test
->diag(
"file [$filename] $message"
);
return
0;
}
return
1;
}
sub
_normalize {
my
$file
=
shift
;
return
unless
defined
$file
;
return
$file
=~ m|/|
? File::Spec->catfile(
split
m|/|,
$file
)
:
$file
;
}
sub
_win32 {
return
0
if
$^O eq
'darwin'
;
return
$ENV
{PRETEND_TO_BE_WIN32}
if
defined
$ENV
{PRETEND_TO_BE_WIN32};
return
$^O =~ m/Win/ || $^O eq
'msys'
;
}
BEGIN {
my
$cannot_symlink
;
sub
_no_symlinks_here {
return
$cannot_symlink
if
defined
$cannot_symlink
;
$cannot_symlink
= !
do
{
eval
{
symlink
(
""
,
""
);
_IsSymlinkCreationAllowed()
}
};
}
sub
_IsSymlinkCreationAllowed {
if
($^O eq
'MSWin32'
) {
my
(
undef
,
$major
,
$minor
,
$build
) = Test::File::Win32::GetOSVersion();
return
!!0
if
$major
< 6;
if
(
$major
> 10 || (
$major
== 10 && (
$minor
> 0 ||
$build
> 15063))) {
return
!!1
if
Test::File::Win32::IsDeveloperModeEnabled();
}
my
$privs
= Test::File::Win32::GetProcessPrivileges();
return
!!0
unless
$privs
;
return
exists
$privs
->{SeCreateSymbolicLinkPrivilege};
}
1;
}
sub
has_symlinks { ! _no_symlinks_here() }
}
sub
_obviously_non_multi_user {
foreach
my
$os
(
qw(dos MacOS)
) {
return
1
if
$^O eq
$os
}
return
0
if
$^O eq
'MSWin32'
;
eval
{
my
$holder
=
getpwuid
(0) };
return
1
if
$@;
eval
{
my
$holder
=
getgrgid
(0) };
return
1
if
$@;
return
0;
}
sub
file_exists_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename exists"
;
my
$ok
= -e
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] does not exist"
);
$Test
->ok(0,
$name
);
}
}
sub
file_not_exists_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename does not exist"
;
my
$ok
= not -e
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] exists"
);
$Test
->ok(0,
$name
);
}
}
sub
file_empty_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is empty"
;
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
my
$ok
= -z
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] exists with non-zero size"
);
$Test
->ok(0,
$name
);
}
}
sub
file_not_empty_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is not empty"
;
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
my
$ok
= not -z _;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] exists with zero size"
);
$Test
->ok(0,
$name
);
}
}
sub
file_size_ok {
my
$filename
= _normalize(
shift
);
my
$expected
=
int
shift
;
my
$name
=
shift
||
"$filename has right size"
;
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
my
$ok
= ( -s
$filename
) ==
$expected
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
my
$actual
= -s
$filename
;
$Test
->diag(
"file [$filename] has actual size [$actual] not [$expected]"
);
$Test
->ok(0,
$name
);
}
}
sub
file_max_size_ok {
my
$filename
= _normalize(
shift
);
my
$max
=
int
shift
;
my
$name
=
shift
||
"$filename is under $max bytes"
;
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
my
$ok
= ( -s
$filename
) <=
$max
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
my
$actual
= -s
$filename
;
$Test
->diag(
"file [$filename] has actual size [$actual] "
.
"greater than [$max]"
);
$Test
->ok(0,
$name
);
}
}
sub
file_min_size_ok {
my
$filename
= _normalize(
shift
);
my
$min
=
int
shift
;
my
$name
=
shift
||
"$filename is over $min bytes"
;
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
my
$ok
= ( -s
$filename
) >=
$min
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
my
$actual
= -s
$filename
;
$Test
->diag(
"file [$filename] has actual size "
.
"[$actual] less than [$min]"
);
$Test
->ok(0,
$name
);
}
}
sub
_ENOFILE () { -1 }
sub
_ECANTOPEN () { -2 }
sub
_ENOTPLAIN () { -3 }
sub
_file_line_counter {
my
$filename
=
shift
;
return
_ENOFILE
unless
-e
$filename
;
return
_ENOTPLAIN
unless
-f
$filename
;
return
_ECANTOPEN
unless
open
my
(
$fh
),
"<"
,
$filename
;
my
$count
= 0;
while
( <
$fh
> ) {
$count
++ }
return
$count
;
}
sub
file_line_count_is {
my
$filename
= _normalize(
shift
);
my
$expected
=
shift
;
my
$name
=
do
{
no
warnings
'uninitialized'
;
shift
||
"$filename line count is $expected lines"
;
};
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
unless
(
defined
$expected
&&
int
(
$expected
) ==
$expected
) {
no
warnings
'uninitialized'
;
$Test
->diag(
"file_line_count_is expects a positive whole number for "
.
"the second argument. Got [$expected]"
);
return
$Test
->ok( 0,
$name
);
}
my
$got
= _file_line_counter(
$filename
);
if
(
$got
eq _ENOFILE ) {
$Test
->diag(
"file [$filename] does not exist"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
eq _ENOTPLAIN ) {
$Test
->diag(
"file [$filename] is not a plain file"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
== _ECANTOPEN ) {
$Test
->diag(
"file [$filename] could not be opened: \$! is [$!]"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
==
$expected
) {
$Test
->ok( 1,
$name
);
}
else
{
$Test
->diag(
"expected [$expected] lines in [$filename], "
.
"got [$got] lines"
);
$Test
->ok( 0,
$name
);
}
}
sub
file_line_count_isnt {
my
$filename
= _normalize(
shift
);
my
$expected
=
shift
;
my
$name
=
do
{
no
warnings
'uninitialized'
;
shift
||
"$filename line count is not $expected lines"
;
};
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
unless
(
defined
$expected
&&
int
(
$expected
) ==
$expected
) {
no
warnings
'uninitialized'
;
$Test
->diag(
"file_line_count_is expects a positive whole number for "
.
"the second argument. Got [$expected]"
);
return
$Test
->ok( 0,
$name
);
}
my
$got
= _file_line_counter(
$filename
);
if
(
$got
eq _ENOFILE ) {
$Test
->diag(
"file [$filename] does not exist"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
eq _ENOTPLAIN ) {
$Test
->diag(
"file [$filename] is not a plain file"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
== _ECANTOPEN ) {
$Test
->diag(
"file [$filename] could not be opened: \$! is [$!]"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
!=
$expected
) {
$Test
->ok( 1,
$name
);
}
else
{
$Test
->diag(
"expected something other than [$expected] lines in [$filename], "
.
"but got [$got] lines"
);
$Test
->ok( 0,
$name
);
}
}
sub
file_line_count_between {
my
$filename
= _normalize(
shift
);
my
$min
=
shift
;
my
$max
=
shift
;
my
$name
=
do
{
no
warnings
'uninitialized'
;
shift
||
"$filename line count is between [$min] and [$max] lines"
;
};
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
foreach
my
$ref
( \
$min
, \
$max
) {
unless
(
defined
$$ref
&&
int
(
$$ref
) ==
$$ref
) {
no
warnings
'uninitialized'
;
$Test
->diag(
"file_line_count_between expects positive whole numbers for "
.
"the second and third arguments. Got [$min] and [$max]"
);
return
$Test
->ok( 0,
$name
);
}
}
my
$got
= _file_line_counter(
$filename
);
if
(
$got
eq _ENOFILE ) {
$Test
->diag(
"file [$filename] does not exist"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
eq _ENOTPLAIN ) {
$Test
->diag(
"file [$filename] is not a plain file"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$got
== _ECANTOPEN ) {
$Test
->diag(
"file [$filename] could not be opened: \$! is [$!]"
);
$Test
->ok( 0,
$name
);
}
elsif
(
$min
<=
$got
and
$got
<=
$max
) {
$Test
->ok( 1,
$name
);
}
else
{
$Test
->diag(
"expected a line count between [$min] and [$max] "
.
"in [$filename], but got [$got] lines"
);
$Test
->ok( 0,
$name
);
}
}
sub
file_contains_like {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
_file_contains(
like
=>
"contains"
,
undef
,
@_
);
}
sub
file_contains_unlike {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
_file_contains(
unlike
=>
"doesn't contain"
,
undef
,
@_
);
}
sub
file_contains_utf8_like {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
_file_contains(
like
=>
"contains"
,
'UTF-8'
,
@_
);
}
sub
file_contains_utf8_unlike {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
_file_contains(
unlike
=>
"doesn't contain"
, 'UTF-8',
@_
);
}
sub
file_contains_encoded_like {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$filename
=
shift
;
my
$encoding
=
shift
;
_file_contains(
like
=>
"contains"
,
$encoding
,
$filename
,
@_
);
}
sub
file_contains_encoded_unlike {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$filename
=
shift
;
my
$encoding
=
shift
;
_file_contains(
unlike
=>
"doesn't contain"
,
$encoding
,
$filename
,
@_
);
}
sub
_file_contains {
my
$method
=
shift
;
my
$verb
=
shift
;
my
$encoding
=
shift
;
my
$filename
= _normalize(
shift
);
my
$patterns
=
shift
;
my
$name
=
shift
;
my
(
@patterns
,
%patterns
);
if
(
ref
$patterns
eq
'ARRAY'
) {
@patterns
=
@$patterns
;
%patterns
=
map
{
$_
=>
$name
||
"$filename $verb $_"
}
@patterns
;
}
else
{
@patterns
= (
$patterns
);
%patterns
= (
$patterns
=>
$name
||
"$filename $verb $patterns"
);
}
$name
=
$patterns
{
$patterns
[0]};
return
$Test
->ok( 0,
$name
)
unless
_is_plain_file(
$filename
);
unless
( -r
$filename
) {
$Test
->diag(
"file [$filename] is not readable"
);
return
$Test
->ok(0,
$name
);
}
my
$file_contents
;
{
unless
(
open
(FH,
$filename
)) {
$Test
->diag(
"file [$filename] could not be opened: \$! is [$!]"
);
return
$Test
->ok( 0,
$name
);
}
if
(
defined
$encoding
) {
binmode
FH,
":encoding($encoding)"
;
}
local
$/ =
undef
;
$file_contents
= <FH>;
close
FH;
}
foreach
my
$p
(
@patterns
) {
$Test
->
$method
(
$file_contents
,
$p
,
$patterns
{
$p
});
}
}
sub
file_readable_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is readable"
;
my
$ok
= -r
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] is not readable"
);
$Test
->ok(0,
$name
);
}
}
sub
file_not_readable_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is not readable"
;
my
$ok
= not -r
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] is readable"
);
$Test
->ok(0,
$name
);
}
}
sub
file_writeable_ok {
carp
"file_writeable_ok is now file_writable_ok"
;
&file_writable_ok
;
}
sub
file_writable_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is writable"
;
my
$ok
= -w
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] is not writable"
);
$Test
->ok(0,
$name
);
}
}
sub
file_not_writeable_ok {
carp
"file_not_writeable_ok is now file_not_writable_ok"
;
&file_not_writable_ok
;
}
sub
file_not_writable_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is not writable"
;
my
$ok
= not -w
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] is writable"
);
$Test
->ok(0,
$name
);
}
}
sub
file_executable_ok {
if
( _win32() ) {
$Test
->skip(
"file_executable_ok doesn't work on Windows"
);
return
;
}
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is executable"
;
my
$ok
= -x
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] is not executable"
);
$Test
->ok(0,
$name
);
}
}
sub
file_not_executable_ok {
if
( _win32() ) {
$Test
->skip(
"file_not_executable_ok doesn't work on Windows"
);
return
;
}
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is not executable"
;
my
$ok
= not -x
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] is executable"
);
$Test
->ok(0,
$name
);
}
}
sub
file_mode_is {
if
( _win32() ) {
$Test
->skip(
"file_mode_is doesn't work on Windows"
);
return
;
}
my
$filename
= _normalize(
shift
);
my
$mode
=
shift
;
my
$name
=
shift
||
sprintf
(
"%s mode is %04o"
,
$filename
,
$mode
);
my
$ok
= -e
$filename
&& ((
stat
(
$filename
))[2] & 07777) ==
$mode
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
sprintf
(
"file [%s] mode is not %04o"
,
$filename
,
$mode
) );
$Test
->ok(0,
$name
);
}
}
sub
file_mode_isnt {
if
( _win32() ) {
$Test
->skip(
"file_mode_isnt doesn't work on Windows"
);
return
;
}
my
$filename
= _normalize(
shift
);
my
$mode
=
shift
;
my
$name
=
shift
||
sprintf
(
"%s mode is not %04o"
,
$filename
,
$mode
);
my
$ok
= not (-e
$filename
&& ((
stat
(
$filename
))[2] & 07777) ==
$mode
);
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
sprintf
(
"file [%s] mode is %04o"
,
$filename
,
$mode
));
$Test
->ok(0,
$name
);
}
}
sub
file_mode_has {
if
( _win32() ) {
$Test
->skip(
"file_mode_has doesn't work on Windows"
);
return
;
}
my
$filename
= _normalize(
shift
);
my
$mode
=
shift
;
my
$name
=
shift
||
sprintf
(
"%s mode has all bits of %04o"
,
$filename
,
$mode
);
my
$present
= -e
$filename
;
my
$gotmode
=
$present
? (
stat
(
$filename
))[2] :
undef
;
my
$ok
=
$present
&& (
$gotmode
&
$mode
) ==
$mode
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
my
$missing
= (
$gotmode
^
$mode
) &
$mode
;
$Test
->diag(
sprintf
(
"file [%s] mode is missing component %04o"
,
$filename
,
$missing
) );
$Test
->ok(0,
$name
);
}
}
sub
file_mode_hasnt {
if
( _win32() ) {
$Test
->skip(
"file_mode_hasnt doesn't work on Windows"
);
return
;
}
my
$filename
= _normalize(
shift
);
my
$mode
=
shift
;
my
$name
=
shift
||
sprintf
(
"%s mode has no bits of %04o"
,
$filename
,
$mode
);
my
$present
= -e
$filename
;
my
$gotmode
=
$present
? (
stat
(
$filename
))[2] :
undef
;
my
$ok
=
$present
&& (
$gotmode
&
$mode
) == 0;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
my
$bad
=
$gotmode
&
$mode
;
$Test
->diag(
sprintf
(
"file [%s] mode has forbidden component %04o"
,
$filename
,
$bad
) );
$Test
->ok(0,
$name
);
}
}
sub
file_is_symlink_ok {
if
( _no_symlinks_here() ) {
$Test
->skip(
"file_is_symlink_ok doesn't work on systems without symlinks"
);
return
;
}
my
$file
=
shift
;
my
$name
=
shift
||
"$file is a symlink"
;
if
( -l
$file
) {
$Test
->ok(1,
$name
)
}
else
{
$Test
->diag(
"file [$file] is not a symlink"
);
$Test
->ok(0,
$name
);
}
}
sub
file_is_not_symlink_ok {
if
( _no_symlinks_here() ) {
$Test
->skip(
"file_is_symlink_ok doesn't work on systems without symlinks"
);
return
;
}
my
$file
=
shift
;
my
$name
=
shift
||
"$file is not a symlink"
;
unless
( -e
$file
) {
$Test
->diag(
"file [$file] does not exist"
);
return
$Test
->ok(0,
$name
);
}
if
( ! -l
$file
) {
$Test
->ok(1,
$name
)
}
else
{
$Test
->diag(
"file [$file] is a symlink"
);
$Test
->ok(0,
$name
);
}
}
sub
symlink_target_exists_ok {
if
( _no_symlinks_here() ) {
$Test
->skip(
"symlink_target_exists_ok doesn't work on systems without symlinks"
);
return
;
}
my
$file
=
shift
;
my
$dest
=
shift
||
readlink
(
$file
);
my
$name
=
shift
||
"$file is a symlink"
;
unless
( -l
$file
)
{
$Test
->diag(
"file [$file] is not a symlink"
);
return
$Test
->ok( 0,
$name
);
}
unless
( -e
$dest
) {
$Test
->diag(
"symlink [$file] points to non-existent target [$dest]"
);
return
$Test
->ok( 0,
$name
);
}
my
$actual
=
readlink
(
$file
);
unless
(
$dest
eq
$actual
) {
$Test
->diag(
"symlink [$file] points to\n"
.
" got: $actual\n"
.
" expected: $dest\n"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->ok( 1,
$name
);
}
sub
symlink_target_dangles_ok
{
if
( _no_symlinks_here() ) {
$Test
->skip(
"symlink_target_dangles_ok doesn't work on systems without symlinks"
);
return
;
}
my
$file
=
shift
;
my
$dest
=
readlink
(
$file
);
my
$name
=
shift
||
"$file is a symlink"
;
unless
( -l
$file
) {
$Test
->diag(
"file [$file] is not a symlink"
);
return
$Test
->ok( 0,
$name
);
}
if
( -e
$dest
) {
$Test
->diag(
"symlink [$file] points to existing file [$dest] but shouldn't"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->ok( 1,
$name
);
}
sub
symlink_target_is {
if
( _no_symlinks_here() ) {
$Test
->skip(
"symlink_target_is doesn't work on systems without symlinks"
);
return
;
}
my
$file
=
shift
;
my
$dest
=
shift
;
my
$name
=
shift
||
"symlink $file points to $dest"
;
unless
( -l
$file
) {
$Test
->diag(
"file [$file] is not a symlink"
);
return
$Test
->ok( 0,
$name
);
}
my
$actual_dest
=
readlink
(
$file
);
my
$link_error
= $!;
unless
(
defined
$actual_dest
) {
$Test
->diag(
"symlink [$file] does not have a defined target"
);
$Test
->diag(
"readlink error: $link_error"
)
if
defined
$link_error
;
return
$Test
->ok( 0,
$name
);
}
if
(
$dest
eq
$actual_dest
) {
$Test
->ok( 1,
$name
);
}
else
{
$Test
->ok( 0,
$name
);
$Test
->diag(
" got: $actual_dest"
);
$Test
->diag(
" expected: $dest"
);
}
}
sub
symlink_target_is_absolute_ok {
if
( _no_symlinks_here() ) {
$Test
->skip(
"symlink_target_exists_ok doesn't work on systems without symlinks"
);
return
;
}
my
(
$from
,
$from_base
,
$to
,
$to_base
,
$name
) =
@_
;
my
$link
=
readlink
(
$from
);
my
$link_err
=
defined
(
$link
) ?
''
: $!;
my
$link_abs
= abs_path( rel2abs(
$link
,
$from_base
) );
my
$to_abs
= abs_path( rel2abs(
$to
,
$to_base
) );
if
(
defined
(
$link_abs
) &&
defined
(
$to_abs
) &&
$link_abs
eq
$to_abs
) {
$Test
->ok( 1,
$name
);
}
else
{
$Test
->ok( 0,
$name
);
$link
||=
'undefined'
;
$link_abs
||=
'undefined'
;
$to_abs
||=
'undefined'
;
$Test
->diag(
" link: $from"
);
$Test
->diag(
" got: $link"
);
$Test
->diag(
" (abs): $link_abs"
);
$Test
->diag(
" expected: $to"
);
$Test
->diag(
" (abs): $to_abs"
);
$Test
->diag(
" readlink() error: $link_err"
)
if
(
$link_err
);
}
}
sub
dir_exists_ok {
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"$filename is a directory"
;
unless
( -e
$filename
) {
$Test
->diag(
"directory [$filename] does not exist"
);
return
$Test
->ok(0,
$name
);
}
my
$ok
= -d
$filename
;
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] exists but is not a directory"
);
$Test
->ok(0,
$name
);
}
}
sub
dir_contains_ok {
my
$dirname
= _normalize(
shift
);
my
$filename
= _normalize(
shift
);
my
$name
=
shift
||
"directory $dirname contains file $filename"
;
unless
( -d
$dirname
) {
$Test
->diag(
"directory [$dirname] does not exist"
);
return
$Test
->ok(0,
$name
);
}
my
$ok
= -e File::Spec->catfile(
$dirname
,
$filename
);
if
(
$ok
) {
$Test
->ok(1,
$name
);
}
else
{
$Test
->diag(
"file [$filename] does not exist in directory $dirname"
);
$Test
->ok(0,
$name
);
}
}
sub
link_count_is_ok {
my
$file
=
shift
;
my
$count
=
int
( 0 +
shift
);
my
$name
=
shift
||
"$file has a link count of [$count]"
;
my
$actual
= (
stat
$file
)[3];
unless
(
$actual
==
$count
) {
$Test
->diag(
"file [$file] points has [$actual] links: expected [$count]"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->ok( 1,
$name
);
}
sub
link_count_gt_ok {
my
$file
=
shift
;
my
$count
=
int
( 0 +
shift
);
my
$name
=
shift
||
"$file has a link count of [$count]"
;
my
$actual
= (
stat
$file
)[3];
unless
(
$actual
>
$count
) {
$Test
->diag(
"file [$file] points has [$actual] links: "
.
"expected more than [$count]"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->ok( 1,
$name
);
}
sub
link_count_lt_ok {
my
$file
=
shift
;
my
$count
=
int
( 0 +
shift
);
my
$name
=
shift
||
"$file has a link count of [$count]"
;
my
$actual
= (
stat
$file
)[3];
unless
(
$actual
<
$count
) {
$Test
->diag(
"file [$file] points has [$actual] links: "
.
"expected less than [$count]"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->ok( 1,
$name
);
}
sub
_dm_skeleton {
no
warnings
'uninitialized'
;
if
( _obviously_non_multi_user() ) {
my
$calling_sub
= (
caller
(1))[3];
$Test
->skip(
$calling_sub
.
" only works on a multi-user OS"
);
return
'skip'
;
}
my
$filename
= _normalize(
shift
);
my
$testing_for
=
shift
;
my
$name
=
shift
;
unless
(
defined
$filename
) {
$Test
->diag(
"file name not specified"
);
return
$Test
->ok( 0,
$name
);
}
unless
( -e
$filename
) {
$Test
->diag(
"file [$filename] does not exist"
);
return
$Test
->ok( 0,
$name
);
}
return
;
}
sub
owner_is {
my
$filename
=
shift
;
my
$owner
=
shift
;
my
$name
=
shift
||
"$filename belongs to $owner"
;
my
$err
= _dm_skeleton(
$filename
,
$owner
,
$name
);
return
if
(
defined
(
$err
) &&
$err
eq
'skip'
);
return
$err
if
defined
(
$err
);
my
$owner_uid
= _get_uid(
$owner
);
unless
(
defined
$owner_uid
) {
$Test
->diag(
"user [$owner] does not exist on this system"
);
return
$Test
->ok( 0,
$name
);
}
my
$file_uid
= (
stat
$filename
)[4];
unless
(
defined
$file_uid
) {
$Test
->skip(
"stat failed to return owner uid for $filename"
);
return
;
}
return
$Test
->ok( 1,
$name
)
if
$file_uid
==
$owner_uid
;
my
$real_owner
= (
getpwuid
$file_uid
)[0];
unless
(
defined
$real_owner
) {
$Test
->diag(
"file does not belong to $owner"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->diag(
"file [$filename] belongs to $real_owner ($file_uid), "
.
"not $owner ($owner_uid)"
);
return
$Test
->ok( 0,
$name
);
}
sub
owner_isnt {
my
$filename
=
shift
;
my
$owner
=
shift
;
my
$name
=
shift
||
"$filename doesn't belong to $owner"
;
my
$err
= _dm_skeleton(
$filename
,
$owner
,
$name
);
return
if
(
defined
(
$err
) &&
$err
eq
'skip'
);
return
$err
if
defined
(
$err
);
my
$owner_uid
= _get_uid(
$owner
);
unless
(
defined
$owner_uid
) {
return
$Test
->ok( 1,
$name
);
}
my
$file_uid
= (
stat
$filename
)[4];
return
$Test
->ok( 1,
$name
)
if
$file_uid
!=
$owner_uid
;
$Test
->diag(
"file [$filename] belongs to $owner ($owner_uid)"
);
return
$Test
->ok( 0,
$name
);
}
sub
group_is {
my
$filename
=
shift
;
my
$group
=
shift
;
my
$name
= (
shift
||
"$filename belongs to group $group"
);
my
$err
= _dm_skeleton(
$filename
,
$group
,
$name
);
return
if
(
defined
(
$err
) &&
$err
eq
'skip'
);
return
$err
if
defined
(
$err
);
my
$group_gid
= _get_gid(
$group
);
unless
(
defined
$group_gid
) {
$Test
->diag(
"group [$group] does not exist on this system"
);
return
$Test
->ok( 0,
$name
);
}
my
$file_gid
= (
stat
$filename
)[5];
unless
(
defined
$file_gid
) {
$Test
->skip(
"stat failed to return group gid for $filename"
);
return
;
}
return
$Test
->ok( 1,
$name
)
if
$file_gid
==
$group_gid
;
my
$real_group
= (
getgrgid
$file_gid
)[0];
unless
(
defined
$real_group
) {
$Test
->diag(
"file does not belong to $group"
);
return
$Test
->ok( 0,
$name
);
}
$Test
->diag(
"file [$filename] belongs to $real_group ($file_gid), "
.
"not $group ($group_gid)"
);
return
$Test
->ok( 0,
$name
);
}
sub
group_isnt {
my
$filename
=
shift
;
my
$group
=
shift
;
my
$name
=
shift
||
"$filename does not belong to group $group"
;
my
$err
= _dm_skeleton(
$filename
,
$group
,
$name
);
return
if
(
defined
(
$err
) &&
$err
eq
'skip'
);
return
$err
if
defined
(
$err
);
my
$group_gid
= _get_gid(
$group
);
my
$file_gid
= (
stat
$filename
)[5];
unless
(
defined
$file_gid
) {
$Test
->skip(
"stat failed to return group gid for $filename"
);
return
;
}
return
$Test
->ok( 1,
$name
)
if
$file_gid
!=
$group_gid
;
$Test
->diag(
"file [$filename] belongs to $group ($group_gid)"
);
return
$Test
->ok( 0,
$name
);
}
sub
_get_uid {
my
$arg
=
shift
;
my
$from_uid
= (
getpwuid
(
$arg
))[2]
if
$arg
=~ /\A[0-9]+\z/;
my
$from_nam
= (
getpwnam
(
$arg
))[2];
return
do
{
if
(
defined
$from_uid
and not
defined
$from_nam
) {
$arg
}
elsif
( not
defined
$from_uid
and
defined
$from_nam
) {
$from_nam
}
elsif
( not
defined
$from_uid
and not
defined
$from_nam
) {
undef
}
else
{
carp(
"Found both a UID or name for <$arg>. Guessing the UID is <$arg>."
);
$arg
}
};
}
sub
_get_gid {
my
$arg
=
shift
;
my
$from_gid
= (
getgrgid
(
$arg
))[2]
if
$arg
=~ /\A[0-9]+\z/;
my
$from_nam
= (
getgrnam
(
$arg
))[2];
return
do
{
if
(
defined
$from_gid
and not
defined
$from_nam
) {
$arg
}
elsif
( not
defined
$from_gid
and
defined
$from_nam
) {
$from_nam
}
elsif
( not
defined
$from_gid
and not
defined
$from_nam
) {
undef
}
else
{
carp(
"Found both a GID or name for <$arg>. Guessing the GID is <$arg>."
);
$arg
;
}
};
}
sub
file_mtime_age_ok {
my
$filename
=
shift
;
my
$within_secs
=
shift
|| 0;
my
$name
=
shift
||
"$filename mtime within $within_secs seconds of current time"
;
my
$time
=
time
();
my
$filetime
= _stat_file(
$filename
, 9);
return
if
(
$filetime
== -1 );
return
$Test
->ok(1,
$name
)
if
(
$filetime
+
$within_secs
>
$time
-1 );
$Test
->diag(
"file [$filename] mtime [$filetime] is not $within_secs seconds within current system time [$time]."
);
return
$Test
->ok(0,
$name
);
}
sub
file_mtime_gt_ok {
my
$filename
=
shift
;
my
$time
=
int
shift
;
my
$name
=
shift
||
"$filename mtime is greater than unix timestamp $time"
;
my
$filetime
= _stat_file(
$filename
, 9);
return
if
(
$filetime
== -1 );
return
$Test
->ok(1,
$name
)
if
(
$filetime
>
$time
);
$Test
->diag(
"file [$filename] mtime [$filetime] not greater than $time"
);
$Test
->ok(0,
$name
);
}
sub
file_mtime_lt_ok {
my
$filename
=
shift
;
my
$time
=
int
shift
;
my
$name
=
shift
||
"$filename mtime less than unix timestamp $time"
;
my
$filetime
= _stat_file(
$filename
, 9);
return
if
(
$filetime
== -1 );
return
$Test
->ok(1,
$name
)
if
(
$filetime
<
$time
);
$Test
->diag(
"file [$filename] mtime [$filetime] not less than $time"
);
$Test
->ok(0,
$name
);
}
sub
_stat_file {
my
$filename
= _normalize(
shift
);
my
$attr_pos
=
shift
;
unless
(
defined
$filename
) {
$Test
->diag(
"file name not specified"
);
return
0;
}
unless
( -e
$filename
) {
$Test
->diag(
"file [$filename] does not exist"
);
return
0;
}
my
$filetime
= (
stat
(
$filename
) )[
$attr_pos
];
unless
(
$filetime
) {
$Test
->diag(
"stat of $filename failed"
);
return
-1;
}
return
$filetime
;
}
"The quick brown fox jumped over the lazy dog"
;