summaryrefslogtreecommitdiff
path: root/lib/File/stat.t
blob: fc9bb12cef2538d7679d5f07363391efe6521a41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!./perl

BEGIN {
    chdir 't' if -d 't';
    @INC = '../lib';
}

use strict;
use warnings;
use Test::More;
use Config qw( %Config );
use File::Temp qw( tempfile tempdir );

use File::stat;

my (undef, $file) = tempfile(UNLINK => 1);

{
    my @stat = CORE::stat $file;
    my $stat = File::stat::stat($file);
    isa_ok($stat, 'File::stat', 'should build a stat object');
    is_deeply($stat, \@stat, '... and matches the builtin');

    my $i = 0;
    foreach ([dev => 'device number'],
             [ino => 'inode number'],
             [mode => 'file mode'],
             [nlink => 'number of links'],
             [uid => 'owner uid'],
             [gid => 'group id'],
             [rdev => 'device identifier'],
             [size => 'file size'],
             [atime => 'last access time'],
             [mtime => 'last modify time'],
             [ctime => 'change time'],
             [blksize => 'IO block size'],
             [blocks => 'number of blocks']) {
        my ($meth, $desc) = @$_;
        # On OS/2 (fake) ino is not constant, it is incremented each time
    SKIP: {
            skip('inode number is not constant on OS/2', 1)
                if $i == 1 && $^O eq 'os2';
            is($stat->$meth, $stat[$i], "$desc in position $i");
        }
        ++$i;
    }

    my $stat2 = stat $file;
    isa_ok($stat2, 'File::stat',
           'File::stat exports stat, overriding the builtin');
    is_deeply($stat2, $stat, '... and matches the direct call');
}

sub test_X_ops {
    my ($file, $desc_tail, $skip) = @_;
    my @stat = CORE::stat $file;
    my $stat = File::stat::stat($file);
    my $lstat = File::stat::lstat($file);
    isa_ok($stat, 'File::stat', 'should build a stat object');

    for my $op (split //, "rwxoRWXOezsfdlpSbcugkMCA") {
        if ($skip && $op =~ $skip) {
            note("Not testing -A $desc_tail");
            next;
        }
        my $stat = $op eq 'l' ? $lstat : $stat;
        for my $access ('', 'use filetest "access";') {
            my ($warnings, $awarn, $vwarn, $rv);
            my $desc = $access
                ? "for -$op under use filetest 'access' $desc_tail"
                    : "for -$op $desc_tail";
            {
                local $SIG{__WARN__} = sub {
                    my $w = shift;
                    if ($w =~ /^File::stat ignores VMS ACLs/) {
                        ++$vwarn;
                    } elsif ($w =~ /^File::stat ignores use filetest 'access'/) {
                        ++$awarn;
                    } else {
                        $warnings .= $w;
                    }
                };
                $rv = eval "$access; -$op \$stat";
            }
            is($@, '', "Overload succeeds $desc");

            if ($^O eq "VMS" && $op =~ /[rwxRWX]/) {
                is($vwarn, 1, "warning about VMS ACLs $desc");
            } else {
                is($rv, eval "-$op \$file", "correct overload $desc")
                    unless $access;
                is($vwarn, undef, "no warnings about VMS ACLs $desc");
            }

            # 111640 - File::stat bogus index check in overload
            if ($access && $op =~ /[rwxRXW]/) {
                # these should all warn with filetest access
                is($awarn, 1,
                   "produced the right warning $desc");
            } else {
                # -d and others shouldn't warn
                is($awarn, undef, "should be no warning $desc")
            }

            is($warnings, undef, "no other warnings seen $desc");
        }
    }
}

foreach ([file => $file],
         [dir => tempdir(CLEANUP => 1)]) {
    my ($what, $pathname) = @$_;
    test_X_ops($pathname, "for $what $pathname");

    my $orig_mode = (CORE::stat $pathname)[2];

    my $mode = 01000;
    while ($mode) {
        $mode >>= 1;
        my $mode_oct = sprintf "0%03o", $mode;
        chmod $mode, $pathname or die "Can't chmod $mode_oct $pathname: $!";
        test_X_ops($pathname, "for $what with mode=$mode_oct");
    }
    chmod $orig_mode, $pathname
        or die "Can't restore permissions on $pathname to ", sprintf("%#o", $orig_mode);
}

SKIP: {
    -e $^X && -x $^X or skip "$^X is not present and executable", 4;
    $^O eq "VMS" and skip "File::stat ignores VMS ACLs", 4;

    # Other tests running in parallel mean that $^X is read, updating its atime
    test_X_ops($^X, "for $^X", qr/A/);
}

# open early so atime is consistent with the name based call
local *STAT;
my $canopen = open(STAT, '<', $file);

my $stat = File::stat::stat($file);
isa_ok($stat, 'File::stat', 'should build a stat object');

for (split //, "tTB") {
    eval "-$_ \$stat";
    like( $@, qr/\Q-$_ is not implemented/, "-$_ overload fails" );
}

SKIP: {
	skip("Could not open file: $!", 2) unless $canopen;
	isa_ok(File::stat::stat('STAT'), 'File::stat',
	       '... should be able to find filehandle');

	package foo;
	local *STAT = *main::STAT;
	my $stat2 = File::stat::stat('STAT');
	main::isa_ok($stat2, 'File::stat',
		     '... and filehandle in another package');
	close STAT;

#	VOS open() updates atime; ignore this error (posix-975).
	my $stat3 = $stat2;
	if ($^O eq 'vos') {
		$$stat3[8] = $$stat[8];
	}

	main::skip("Win32: different stat-info on filehandle", 1) if $^O eq 'MSWin32';
	main::skip("dos: inode number is fake on dos", 1) if $^O eq 'dos';

	main::skip("OS/2: inode number is not constant on os/2", 1) if $^O eq 'os2';

	main::is_deeply($stat, $stat3, '... and must match normal stat');
}

SKIP:
{   # RT #111638
    skip "We can't check for FIFOs", 2 unless defined &Fcntl::S_ISFIFO;
    skip "No pipes", 2 unless defined $Config{d_pipe};
    pipe my ($rh, $wh)
      or skip "Couldn't create a pipe: $!", 2;
    skip "Built-in -p doesn't detect a pipe", 2 unless -p $rh;

    my $pstat = File::stat::stat($rh);
    ok(!-p($stat), "-p should be false on a file");
    ok(-p($pstat), "check -p detects a pipe");
}

# Testing pretty much anything else is unportable.

done_testing;

# ex: set ts=8 sts=4 sw=4 et: