line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
####################################################################### |
2
|
|
|
|
|
|
|
# $URL: svn+ssh://equilibrious@equilibrious.net/home/equilibrious/svnrepos/chrisdolan/Test-Virtual-Filesystem/lib/Test/Virtual/Filesystem.pm $ |
3
|
|
|
|
|
|
|
# $Date: 2008-07-27 21:28:05 -0500 (Sun, 27 Jul 2008) $ |
4
|
|
|
|
|
|
|
# $Author: equilibrious $ |
5
|
|
|
|
|
|
|
# $Revision: 785 $ |
6
|
|
|
|
|
|
|
######################################################################## |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package Test::Virtual::Filesystem; |
9
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
100052
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
96
|
|
11
|
3
|
|
|
3
|
|
17
|
use strict; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
100
|
|
12
|
3
|
|
|
3
|
|
80
|
use 5.008; |
|
3
|
|
|
|
|
16
|
|
|
3
|
|
|
|
|
128
|
|
13
|
|
|
|
|
|
|
|
14
|
3
|
|
|
3
|
|
2519
|
use English qw(-no_match_vars); |
|
3
|
|
|
|
|
10982
|
|
|
3
|
|
|
|
|
16
|
|
15
|
3
|
|
|
3
|
|
1361
|
use Carp qw(croak); |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
175
|
|
16
|
3
|
|
|
3
|
|
18
|
use File::Spec; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
78
|
|
17
|
3
|
|
|
3
|
|
3519
|
use List::MoreUtils qw(any); |
|
3
|
|
|
|
|
6484
|
|
|
3
|
|
|
|
|
262
|
|
18
|
3
|
|
|
3
|
|
4816
|
use Attribute::Handlers; |
|
3
|
|
|
|
|
16720
|
|
|
3
|
|
|
|
|
20
|
|
19
|
3
|
|
|
3
|
|
101
|
use Config; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
138
|
|
20
|
3
|
|
|
3
|
|
2796
|
use POSIX qw(:errno_h strerror); |
|
3
|
|
|
|
|
25659
|
|
|
3
|
|
|
|
|
42
|
|
21
|
3
|
|
|
3
|
|
15291
|
use Readonly; |
|
3
|
|
|
|
|
15819
|
|
|
3
|
|
|
|
|
248
|
|
22
|
3
|
|
|
3
|
|
27
|
use Test::More; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
39
|
|
23
|
3
|
|
|
3
|
|
1055
|
use base 'Test::Class'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
5252
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $VERSION = '0.13'; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Readonly::Scalar my $TIME_LENIENCE => 2; # seconds of tolerance between CPU clock and disk mtime |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Currently this must not nest more than one level deep! |
30
|
|
|
|
|
|
|
# (due to implementation of deep copy in new() and the static accessor/mutator constructor) |
31
|
|
|
|
|
|
|
Readonly::Hash my %feature_defaults => ( |
32
|
|
|
|
|
|
|
xattr => 0, |
33
|
|
|
|
|
|
|
time => { |
34
|
|
|
|
|
|
|
atime => 0, |
35
|
|
|
|
|
|
|
mtime => 1, |
36
|
|
|
|
|
|
|
ctime => 1, |
37
|
|
|
|
|
|
|
}, |
38
|
|
|
|
|
|
|
permissions => 0, |
39
|
|
|
|
|
|
|
special => { |
40
|
|
|
|
|
|
|
fifo => 0, |
41
|
|
|
|
|
|
|
}, |
42
|
|
|
|
|
|
|
symlink => 1, |
43
|
|
|
|
|
|
|
hardlink => { |
44
|
|
|
|
|
|
|
nlink => 1, |
45
|
|
|
|
|
|
|
}, |
46
|
|
|
|
|
|
|
chown => 0, |
47
|
|
|
|
|
|
|
); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# if true, the feature is disabled no matter what. For example, most versions |
50
|
|
|
|
|
|
|
# of Windows at this writing do not support symlinks at all, regardless of |
51
|
|
|
|
|
|
|
# whether your virtual filesystem supports them |
52
|
|
|
|
|
|
|
Readonly::Hash my %feature_disabled => ( |
53
|
|
|
|
|
|
|
$Config{d_symlink} ? () : (symlink => 1), |
54
|
|
|
|
|
|
|
$Config{d_chown} ? () : (chown => 1), |
55
|
|
|
|
|
|
|
eval {require File::ExtAttr; 1;} ? () : (xattr => 1), |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=pod |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=for stopwords TODO CPAN MSWin32 |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head1 NAME |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Test::Virtual::Filesystem - Validate a filesystem |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 SYNOPSIS |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
use Test::Virtual::Filesystem; |
69
|
|
|
|
|
|
|
Test::Virtual::Filesystem->new({mountdir => '/path/to/test'})->runtests; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
or with more customization: |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
use Test::Virtual::Filesystem; |
74
|
|
|
|
|
|
|
my $test = Test::Virtual::Filesystem->new({mountdir => '/path/to/test', compatible => '0.03'}); |
75
|
|
|
|
|
|
|
$test->enable_test_xattr(1); |
76
|
|
|
|
|
|
|
$test->enable_test_chown(1); |
77
|
|
|
|
|
|
|
$test->enable_test_atime(1); |
78
|
|
|
|
|
|
|
$test->runtests; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
See the file F in this distribution or the file F in |
81
|
|
|
|
|
|
|
the L distribution for thorough examples. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
WARNING: all of the files in the C will be deleted in the C |
84
|
|
|
|
|
|
|
method so BE CAREFUL that you specify the right folder! |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head1 LICENSE |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Copyright 2008 Chris Dolan, I |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify it |
91
|
|
|
|
|
|
|
under the same terms as Perl itself. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head1 DESCRIPTION |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
If you are creating a filesystem, say via L or L, you |
96
|
|
|
|
|
|
|
need a fairly mundane set of tests to try out lots of typical filesystem |
97
|
|
|
|
|
|
|
operations. This package attempts to accumulate a bunch of those tests into a |
98
|
|
|
|
|
|
|
handy suite to make it easier for you to test your filesystem. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
This suite is based on C, a fantastic library for organizing |
101
|
|
|
|
|
|
|
tests into bite-sized bundles. The power of Test::Class lets you select a |
102
|
|
|
|
|
|
|
subset of tests to run at author time. For example, when I was working on the |
103
|
|
|
|
|
|
|
extended attribute (aka C) tests, I found myself typing this: |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
env TEST_METHOD='xattr_.*' perl -Ilib t/filesys.t |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
which runs just the test methods that begin with C. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
There are several methods that let you turn on or off a subset of the tests. |
110
|
|
|
|
|
|
|
For example, if you do not intend that your filesystem will support symbolic |
111
|
|
|
|
|
|
|
links, you can invoke C<$test->enable_test_symlink(0)> in your test program |
112
|
|
|
|
|
|
|
just before you call C<$test->runtests>. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head1 COMPATIBILITY POLICY |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Every time I add a new test to this suite, I annotate it with a |
117
|
|
|
|
|
|
|
version number. If client code specifies an expected version number |
118
|
|
|
|
|
|
|
(say, 1.10) and it's running against a newer version or this module |
119
|
|
|
|
|
|
|
(say, 1.20) then any newer test will be marked as a TODO test. That |
120
|
|
|
|
|
|
|
way if the test fails, it won't regress published code that used to |
121
|
|
|
|
|
|
|
work. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
This policy will allow us to continue adding new filesystem tests |
124
|
|
|
|
|
|
|
without worrying about breaking existing CPAN modules. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 CAVEATS AND LIMITATIONS |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
This module needs a more complete suite of test cases. In particular, tests |
129
|
|
|
|
|
|
|
are needed for the following filesystem features: |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
hardlinks |
132
|
|
|
|
|
|
|
nlink |
133
|
|
|
|
|
|
|
seek/rewinddir, tell/telldir |
134
|
|
|
|
|
|
|
read, sysread, syswrite |
135
|
|
|
|
|
|
|
overwrite (with open '+<') |
136
|
|
|
|
|
|
|
deep directories |
137
|
|
|
|
|
|
|
very full directories |
138
|
|
|
|
|
|
|
large files |
139
|
|
|
|
|
|
|
filenames with spaces |
140
|
|
|
|
|
|
|
non-ASCII filenames (maybe constructor should specify the encoding?) |
141
|
|
|
|
|
|
|
permissions |
142
|
|
|
|
|
|
|
special file types (fifos, sockets, character and block devices, etc) |
143
|
|
|
|
|
|
|
chown |
144
|
|
|
|
|
|
|
binmode, non-binmode |
145
|
|
|
|
|
|
|
eof |
146
|
|
|
|
|
|
|
fileno |
147
|
|
|
|
|
|
|
statfs (AKA `df` or `mount`) |
148
|
|
|
|
|
|
|
rename corner cases: |
149
|
|
|
|
|
|
|
* dest inside src |
150
|
|
|
|
|
|
|
* src or dest leaf is '.' or '..' |
151
|
|
|
|
|
|
|
* src or dest is FS root |
152
|
|
|
|
|
|
|
* dest leaf is symlink |
153
|
|
|
|
|
|
|
threading and re-entrancy |
154
|
|
|
|
|
|
|
file locking? |
155
|
|
|
|
|
|
|
async I/O?? |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Any help writing tests (or adapting tests from existing suites) will |
158
|
|
|
|
|
|
|
be appreciated! |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head1 METHODS |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
This module is a subclass of L. All methods from that class are |
163
|
|
|
|
|
|
|
available, particularly C. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=over |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=item $pkg->new({mountdir =E $mountdir, ...}) |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Create a new test suite which will operate on files contained within the |
170
|
|
|
|
|
|
|
specified mount directory. WARNING: any and all files and folders in that |
171
|
|
|
|
|
|
|
mount directory will be deleted! |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
The supported options are: |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=over |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=item C |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
This required property indicates where tests should run. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=item C |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
Specify a Test::Virtual::Filesystem version number that is known to |
184
|
|
|
|
|
|
|
work. If the actual Test::Virtual::Filesystem version number is |
185
|
|
|
|
|
|
|
greater, then any test cases added after the specified compatible |
186
|
|
|
|
|
|
|
version are considered C tests. See L for details |
187
|
|
|
|
|
|
|
about C tests. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item $self->init() |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Invoked just before then end of C. This exists solely for |
194
|
|
|
|
|
|
|
subclassing convenience. This implementation does nothing. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=back |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head1 PROPERTIES |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
The following accessor/mutator methods exist to turn on/off various |
201
|
|
|
|
|
|
|
features. They all behave in usual Perl fashion: with no argument, |
202
|
|
|
|
|
|
|
they return the current value. With one argument, they set the |
203
|
|
|
|
|
|
|
current value and return the newly set value. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=over |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item $self->enable_test_all() |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
As a getter, checks whether all of the other tests are enabled. |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
As a setter, turns on/off all the tests. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item $self->enable_test_xattr() |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Default false. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=item $self->enable_test_time() |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Default true. If set false, it also sets C, C and C false. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=item $self->enable_test_atime() |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
Default false. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=item $self->enable_test_mtime() |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Default true. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=item $self->enable_test_ctime() |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Default true. |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=item $self->enable_test_permissions() |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Default false. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=item $self->enable_test_special() |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
Default true. If set false, it also sets C false. |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=item $self->enable_test_fifo() |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Default false. AKA named pipes. |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item $self->enable_test_symlink() |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Default true, except for platforms that do not support symlinks (for example |
248
|
|
|
|
|
|
|
MSWin32 and cygwin) as determined by C<$Config::Config{d_symlink}>. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=item $self->enable_test_hardlink() |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
AKA the C function. Default true. If set false, this also sets C false. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item $self->enable_test_nlink() |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Count hard links. Default true. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=item $self->enable_test_chown() |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Default false. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=back |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head1 TEST CASES |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=over |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=cut |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub new { |
271
|
3
|
|
|
3
|
1
|
4375
|
my ($pkg, $opts) = @_; |
272
|
3
|
|
|
|
|
38
|
my $self = $pkg->SUPER::new(); |
273
|
3
|
|
50
|
|
|
1646
|
$opts ||= {}; |
274
|
3
|
|
|
|
|
12
|
for my $key (qw(mountdir compatible)) { |
275
|
6
|
|
|
|
|
23
|
$self->{$key} = $opts->{$key}; |
276
|
|
|
|
|
|
|
} |
277
|
9
|
|
|
|
|
72
|
$self->{fs_opts} = { |
278
|
|
|
|
|
|
|
# one-level deep copy |
279
|
3
|
100
|
|
|
|
30
|
map {$_ => ref $feature_defaults{$_} ? { %{$feature_defaults{$_}} } : $feature_defaults{$_}} |
|
21
|
|
|
|
|
482
|
|
280
|
|
|
|
|
|
|
keys %feature_defaults, |
281
|
|
|
|
|
|
|
}; |
282
|
3
|
|
|
|
|
120
|
$self->init; |
283
|
3
|
|
|
|
|
7
|
$self->{ntestdir} = 0; |
284
|
3
|
|
|
|
|
26
|
return $self; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub init { |
288
|
|
|
|
|
|
|
# no-op, subclasses may override |
289
|
3
|
|
|
3
|
1
|
8
|
return; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
{ |
293
|
|
|
|
|
|
|
# Create a read-write accessor for each enabling feature |
294
|
3
|
|
|
3
|
|
53184
|
no strict 'refs'; ## no critic(NoStrict) |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
1889
|
|
295
|
|
|
|
|
|
|
for my $field (keys %feature_defaults) { |
296
|
|
|
|
|
|
|
*{'enable_test_'.$field} = sub { |
297
|
0
|
0
|
|
0
|
|
0
|
return $_[0]->{fs_opts}->{$field} if @_ == 1; |
298
|
0
|
0
|
|
|
|
0
|
return $_[0]->{fs_opts}->{$field} = $_[1] if @_ == 2; |
299
|
0
|
|
|
|
|
0
|
croak 'wrong number of arguments to ' . $field; |
300
|
|
|
|
|
|
|
}; |
301
|
|
|
|
|
|
|
my $val = $feature_defaults{$field}; |
302
|
|
|
|
|
|
|
if (ref $val) { |
303
|
|
|
|
|
|
|
for my $subfield (keys %{$val}) { |
304
|
|
|
|
|
|
|
*{'enable_test_'.$subfield} = sub { |
305
|
0
|
0
|
0
|
0
|
|
0
|
return $_[0]->{fs_opts}->{$field} && $_[0]->{fs_opts}->{$field}->{$subfield} if @_ == 1; |
306
|
0
|
0
|
0
|
|
|
0
|
return ($_[0]->{fs_opts}->{$field} ||= {})->{$subfield} = $_[1] if @_ == 2; |
307
|
0
|
|
|
|
|
0
|
croak 'wrong number of arguments to ' . $subfield; |
308
|
|
|
|
|
|
|
}; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub enable_test_all { |
315
|
0
|
|
|
0
|
1
|
0
|
my ($self, @arg) = @_; |
316
|
0
|
|
|
|
|
0
|
return $self->_enable_test_all($self->{fs_opts}, @arg); |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
sub _enable_test_all { |
319
|
0
|
|
|
0
|
|
0
|
my ($self, $hash, @arg) = @_; |
320
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
0
|
my $all_set = 1; |
322
|
0
|
|
|
|
|
0
|
for my $key (keys %{$hash}) { |
|
0
|
|
|
|
|
0
|
|
323
|
0
|
0
|
|
|
|
0
|
if (ref $hash->{$key}) { |
324
|
0
|
|
0
|
|
|
0
|
$all_set = $self->_enable_test_all($hash->{$key}, @arg) && $all_set; #recurse |
325
|
|
|
|
|
|
|
} else { |
326
|
0
|
0
|
|
|
|
0
|
if (@arg) { |
327
|
0
|
0
|
|
|
|
0
|
$hash->{$key} = $arg[0] ? 1 : 0; |
328
|
|
|
|
|
|
|
} |
329
|
0
|
|
0
|
|
|
0
|
$all_set &&= $hash->{$key}; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
} |
332
|
0
|
|
|
|
|
0
|
return $all_set; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item setup() |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Runs before every test to prepare a directory for testing. |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=cut |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub setup : Test(setup) { |
342
|
48
|
|
|
48
|
1
|
18688
|
my ($self) = @_; |
343
|
48
|
50
|
|
|
|
223
|
if (!defined $self->{mountdir}) { |
344
|
0
|
|
|
|
|
0
|
croak 'Programmer error: you did not specify a mountdir'; |
345
|
|
|
|
|
|
|
} |
346
|
48
|
50
|
|
|
|
1159
|
if (!-d $self->{mountdir}) { |
347
|
0
|
|
|
|
|
0
|
croak "Your mountdir '$self->{mountdir}' is not a valid directory"; |
348
|
|
|
|
|
|
|
} |
349
|
48
|
50
|
|
|
|
719
|
if (!File::Spec->file_name_is_absolute($self->{mountdir})) { |
350
|
0
|
|
|
|
|
0
|
croak "Your mountdir '$self->{mountdir}' is not an absolute path"; |
351
|
|
|
|
|
|
|
} |
352
|
48
|
50
|
|
|
|
484
|
if (File::Spec->splitdir($self->{mountdir}) <= 2) { |
353
|
0
|
|
|
|
|
0
|
croak "Your mountdir '$self->{mountdir}' is too close to the root of the filesystem." . |
354
|
|
|
|
|
|
|
' I am too scared of deleting important files to use it'; |
355
|
|
|
|
|
|
|
} |
356
|
48
|
|
|
|
|
651
|
$self->{tempdir} = File::Spec->catdir($self->{mountdir}, 'testdir' . ++$self->{ntestdir}); |
357
|
48
|
|
|
|
|
4606
|
mkdir $self->{tempdir}; |
358
|
48
|
50
|
|
|
|
1021
|
if (! -d $self->{tempdir}) { |
359
|
0
|
|
|
|
|
0
|
die 'Failed to create tempdir'; |
360
|
|
|
|
|
|
|
} |
361
|
48
|
|
|
|
|
246
|
return; |
362
|
3
|
|
|
3
|
|
19
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
19
|
|
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item teardown() |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Runs after every test to clean up the test directory so the next test |
367
|
|
|
|
|
|
|
will have a clean workspace. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=cut |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub teardown : Test(teardown) { |
372
|
48
|
|
|
48
|
1
|
17167
|
my ($self) = @_; |
373
|
48
|
|
|
|
|
189
|
my $tmpdir = delete $self->{tempdir}; |
374
|
48
|
50
|
33
|
|
|
1550
|
if (defined $tmpdir && -e $tmpdir) { |
375
|
48
|
|
|
|
|
193
|
$self->_cleandir($tmpdir); |
376
|
48
|
50
|
|
|
|
351
|
if ($tmpdir ne $self->{mountdir}) { |
377
|
48
|
50
|
|
|
|
5368
|
rmdir $tmpdir or die $OS_ERROR; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
} |
380
|
48
|
50
|
33
|
|
|
1467
|
if (defined $tmpdir && -d $tmpdir) { |
381
|
0
|
|
|
|
|
0
|
die 'Failed to remove tempdir'; |
382
|
|
|
|
|
|
|
} |
383
|
48
|
|
|
|
|
279
|
return; |
384
|
3
|
|
|
3
|
|
1117
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
12
|
|
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub _cleandir { |
387
|
66
|
|
|
66
|
|
117
|
my ($self, $dir) = @_; |
388
|
66
|
|
|
|
|
520
|
for my $file ($self->_read_dir($dir)) { |
389
|
182
|
100
|
|
|
|
502
|
next if q{.} eq $file; |
390
|
116
|
100
|
|
|
|
270
|
next if q{..} eq $file; |
391
|
50
|
|
|
|
|
6839
|
my $path = File::Spec->catfile($dir, $file); |
392
|
50
|
50
|
|
|
|
268
|
die 'Internal error: escaped the temp space!' if length $path <= length $self->{mountdir}; |
393
|
50
|
50
|
66
|
|
|
2284
|
die 'nonsense missing file: ' . $path if !-l $path && !-e $path; |
394
|
50
|
100
|
|
|
|
1966
|
if (-l $path) { |
|
|
100
|
|
|
|
|
|
395
|
8
|
50
|
|
|
|
689
|
unlink $path or die $OS_ERROR; |
396
|
|
|
|
|
|
|
} elsif (-d $path) { |
397
|
18
|
|
|
|
|
66
|
$self->_cleandir($path); |
398
|
18
|
50
|
|
|
|
1956
|
rmdir $path or die $OS_ERROR; |
399
|
|
|
|
|
|
|
} else { |
400
|
24
|
50
|
|
|
|
3922
|
unlink $path or die $OS_ERROR; |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
} |
403
|
66
|
|
|
|
|
175
|
return; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=item Introduced($version) |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
A subroutine attribute used to flag the Test::Virtual::Filesystem |
409
|
|
|
|
|
|
|
version number when that test was introduced. It's used like this: |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub open_nonexistent_file : Tests(1) : Introduced('0.02') { |
412
|
|
|
|
|
|
|
ok(!open(my $f, '<', '/tmp/no_such_file')); |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=cut |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# http://use.perl.org/~ChrisDolan/journal/34906 |
418
|
|
|
|
|
|
|
# http://use.perl.org/~ChrisDolan/journal/34920 |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub Introduced : ATTR(CODE) { ## no critic(MixedCase) |
421
|
134
|
|
|
134
|
1
|
227721
|
my ($class, $symbol, $code_ref, $attr, $introduced_version) = @_; |
422
|
134
|
50
|
|
|
|
407
|
if ($symbol eq 'ANON') { |
423
|
0
|
|
|
|
|
0
|
warn 'cannot test anonymous subs - you probably loaded ' . __PACKAGE__ . ' too late.' . |
424
|
|
|
|
|
|
|
' (after the CHECK block was run)'; |
425
|
|
|
|
|
|
|
} else { |
426
|
|
|
|
|
|
|
# Wrap the sub in a version test |
427
|
3
|
|
|
3
|
|
1409
|
no warnings 'redefine'; ## no critic(TestingAndDebugging::ProhibitNoWarnings) |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
153
|
|
428
|
134
|
|
|
|
|
496
|
*{$symbol} = sub { |
429
|
3
|
|
|
3
|
|
15
|
no strict 'refs'; ## no critic(TestingAndDebugging::ProhibitNoStrict) |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
271
|
|
430
|
43
|
|
|
43
|
|
10340
|
local ${$class.'::TODO'} = $_[0]->_compatible($introduced_version); ## no critic(Local) |
|
43
|
|
|
|
|
406
|
|
431
|
43
|
|
|
|
|
166
|
$code_ref->(@_); |
432
|
134
|
|
|
|
|
468
|
}; |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
#my $name = *{$symbol}{NAME}; |
435
|
|
|
|
|
|
|
#print STDERR "record $class\::$name as $introduced_version\n"; |
436
|
|
|
|
|
|
|
} |
437
|
134
|
|
|
|
|
412
|
return; |
438
|
3
|
|
|
3
|
|
15
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
13
|
|
439
|
|
|
|
|
|
|
sub _compatible { |
440
|
43
|
|
|
43
|
|
81
|
my ($self, $introduced_version) = @_; |
441
|
43
|
100
|
|
|
|
186
|
return if !$self->{compatible}; |
442
|
2
|
50
|
|
|
|
9
|
return if $introduced_version le $self->{compatible}; |
443
|
2
|
|
|
|
|
7
|
return 'compatibility mode ' . $self->{compatible}; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item Features($featurelist) |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
This is a subroutine attribute to specify one or more features used in |
449
|
|
|
|
|
|
|
the test. The features should be listed as a comma-separated list: |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub symlink_create : Tests(1) : Features('symlink') { |
452
|
|
|
|
|
|
|
ok(symlink($src, $dest)); |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
sub symlink_permissions : Tests(2) : Features('symlink, permissions') { |
455
|
|
|
|
|
|
|
ok(symlink($src, $dest)); |
456
|
|
|
|
|
|
|
ok(-w $dest); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Subfeatures must be separated from their parent features by a C>. For example: |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub atime_mtime_set : Tests(1) : Features('time/atime, time/mtime') { |
462
|
|
|
|
|
|
|
my $now = time; |
463
|
|
|
|
|
|
|
ok(utime($now, $now, $file)); |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Look at the source code for C<%feature_defaults> to see the supported features and |
467
|
|
|
|
|
|
|
subfeatures. The C methods above describe the all the |
468
|
|
|
|
|
|
|
features, but in those methods the subfeature names are flattened. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=cut |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
sub Features : ATTR(CODE) { ## no critic(MixedCase) |
473
|
35
|
|
|
35
|
1
|
6909
|
my ($class, $symbol, $code_ref, $attr, $features) = @_; |
474
|
35
|
50
|
|
|
|
112
|
if ($symbol eq 'ANON') { |
475
|
0
|
|
|
|
|
0
|
warn 'cannot test anonymous subs - you probably loaded ' . $class . ' too late.' . |
476
|
|
|
|
|
|
|
' (after the CHECK block was run)'; |
477
|
|
|
|
|
|
|
} else { |
478
|
35
|
50
|
|
|
|
80
|
my @features = ref $features ? @{$features} : split m/\s*,\s*/xms, $features; |
|
35
|
|
|
|
|
174
|
|
479
|
|
|
|
|
|
|
# Wrap the sub in a feature test |
480
|
3
|
|
|
3
|
|
1990
|
no warnings 'redefine'; ## no critic(TestingAndDebugging::ProhibitNoWarnings) |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
343
|
|
481
|
35
|
|
|
|
|
133
|
*{$symbol} = sub { |
482
|
13
|
|
|
13
|
|
2203
|
my $blocking_feature = _blocking_feature(__PACKAGE__, $_[0], @features); |
483
|
13
|
100
|
|
|
|
128
|
return $blocking_feature if $blocking_feature; |
484
|
8
|
|
|
|
|
37
|
return $code_ref->(@_); |
485
|
35
|
|
|
|
|
152
|
}; |
486
|
|
|
|
|
|
|
} |
487
|
35
|
|
|
|
|
108
|
return; |
488
|
3
|
|
|
3
|
|
15
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
11
|
|
489
|
|
|
|
|
|
|
sub _blocking_feature { |
490
|
13
|
|
|
13
|
|
44
|
my ($pkg, $self, @features) = @_; |
491
|
|
|
|
|
|
|
|
492
|
13
|
|
|
|
|
31
|
for my $feature (@features) { |
493
|
13
|
100
|
|
|
|
125
|
return $feature . ' (no OS support)' if $feature_disabled{$feature}; |
494
|
11
|
|
|
|
|
108
|
my $opts = $self->{fs_opts}; |
495
|
11
|
|
|
|
|
46
|
for my $part (split m{/}xms, $feature) { |
496
|
15
|
50
|
|
|
|
45
|
return $feature if !ref $opts; |
497
|
15
|
100
|
|
|
|
51
|
return $feature if !$opts->{$part}; |
498
|
12
|
|
|
|
|
42
|
$opts = $opts->{$part}; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
8
|
|
|
|
|
22
|
return; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=item stat_dir(), introduced in v0.01 |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=cut |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub stat_dir : Test(6) : Introduced('0.01') { |
509
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
510
|
1
|
|
|
|
|
6
|
my $f = $self->_file(q{/}); |
511
|
1
|
|
|
|
|
25
|
ok(-e $f, 'mount dir exists'); |
512
|
1
|
|
|
|
|
416
|
ok(-d $f, 'mount dir is a dir'); |
513
|
1
|
|
|
|
|
301
|
ok(!-f $f, 'mount dir is not a file'); |
514
|
1
|
|
|
|
|
303
|
ok(!-l $f, 'mount dir is not a symlink'); |
515
|
1
|
|
|
|
|
423
|
ok(-r $f, 'mount dir is readable'); |
516
|
1
|
|
|
|
|
340
|
ok(-x $f, 'mount dir is searchable'); |
517
|
1
|
|
|
|
|
307
|
return; |
518
|
3
|
|
|
3
|
|
2308
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
15
|
|
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
## This turned out to be very platform-sensitive. |
521
|
|
|
|
|
|
|
# |
522
|
|
|
|
|
|
|
# =item stat_dir_size(), introduced in v0.02 |
523
|
|
|
|
|
|
|
# |
524
|
|
|
|
|
|
|
# =cut |
525
|
|
|
|
|
|
|
# |
526
|
|
|
|
|
|
|
# sub stat_dir_size : Test(1) : Introduced('0.02') { |
527
|
|
|
|
|
|
|
# my ($self) = @_; |
528
|
|
|
|
|
|
|
# my $f = $self->_file(q{/}); |
529
|
|
|
|
|
|
|
# ok(-s $f, 'mount dir has non-zero size'); |
530
|
|
|
|
|
|
|
# return; |
531
|
|
|
|
|
|
|
# } |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=item read_dir(), introduced in v0.01 |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=cut |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
sub read_dir : Test(3) : Introduced('0.01') { |
538
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
539
|
1
|
|
|
|
|
6
|
my $f = $self->_file(q{/}); |
540
|
1
|
|
|
|
|
6
|
my @files = $self->_read_dir($f); |
541
|
1
|
|
|
|
|
9
|
cmp_ok(scalar @files, '>=', 2, 'dir contains at least two entries'); |
542
|
1
|
|
|
|
|
614
|
ok((any { $_ eq q{.} } @files), 'dir contains "."'); |
|
2
|
|
|
|
|
9
|
|
543
|
1
|
|
|
|
|
425
|
ok((any { $_ eq q{..} } @files), 'dir contains ".."'); |
|
1
|
|
|
|
|
5
|
|
544
|
1
|
|
|
|
|
408
|
return; |
545
|
3
|
|
|
3
|
|
1113
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
13
|
|
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=item read_dir_fail(), introduced in v0.01 |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=cut |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub read_dir_fail : Test(2) : Introduced('0.01') { |
552
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
553
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/no_such'); |
554
|
1
|
|
|
|
|
4
|
eval { |
555
|
1
|
|
|
|
|
4
|
$self->_read_dir_die($f); |
556
|
|
|
|
|
|
|
}; |
557
|
1
|
|
|
|
|
8
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'read non-existent dir'); |
558
|
1
|
|
|
|
|
528
|
ok(!-e $f, 'did not make dir'); |
559
|
1
|
|
|
|
|
513
|
return; |
560
|
3
|
|
|
3
|
|
975
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
12
|
|
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=item read_file_fail(), introduced in v0.01 |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=cut |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub read_file_fail : Test(2) : Introduced('0.01') { |
567
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
568
|
1
|
|
|
|
|
16
|
my $f = $self->_file('/read_file_fail'); |
569
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
570
|
1
|
|
|
|
|
3
|
eval { |
571
|
1
|
|
|
|
|
4
|
$self->_read_file_die($f); |
572
|
|
|
|
|
|
|
}; |
573
|
1
|
|
|
|
|
7
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'read non-existent file'); |
574
|
1
|
|
|
|
|
547
|
ok(!-e $f, 'did not make file'); |
575
|
1
|
|
|
|
|
418
|
return; |
576
|
3
|
|
|
3
|
|
1026
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
16
|
|
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=item write_empty_file(), introduced in v0.01 |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=cut |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
sub write_empty_file : Test(2) : Introduced('0.01') { |
583
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
584
|
1
|
|
|
|
|
16
|
my $f = $self->_file('/create_file'); |
585
|
1
|
|
|
|
|
6
|
$self->_write_file($f); |
586
|
1
|
|
|
|
|
25
|
ok(-f $f, 'created empty file'); |
587
|
1
|
|
|
|
|
377
|
is(-s $f, 0, 'file got right size'); |
588
|
1
|
|
|
|
|
298
|
return; |
589
|
3
|
|
|
3
|
|
923
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
13
|
|
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=item write_file(), introduced in v0.01 |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=cut |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
sub write_file : Test(2) : Introduced('0.01') { |
596
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
597
|
1
|
|
|
|
|
6
|
my $f = $self->_file('/write_file'); |
598
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
599
|
1
|
|
|
|
|
4
|
$self->_write_file($f, $content); |
600
|
1
|
|
|
|
|
25
|
ok(-f $f, 'wrote file'); |
601
|
1
|
|
|
|
|
421
|
is(-s $f, length $content, 'file got right size'); |
602
|
1
|
|
|
|
|
337
|
return; |
603
|
3
|
|
|
3
|
|
975
|
} |
|
3
|
|
|
|
|
17
|
|
|
3
|
|
|
|
|
14
|
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=item write_file_subdir_fail(), introduced in v0.01 |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=cut |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
sub write_file_subdir_fail : Test(2) : Introduced('0.01') { |
610
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
611
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/no_such/write_file'); |
612
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
613
|
1
|
|
|
|
|
2
|
eval { |
614
|
1
|
|
|
|
|
4
|
$self->_write_file($f, $content); |
615
|
|
|
|
|
|
|
}; |
616
|
1
|
|
|
|
|
7
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'write to non-existent folder'); |
617
|
1
|
|
|
|
|
357
|
ok(!-f $f, 'did not make file'); |
618
|
1
|
|
|
|
|
337
|
return; |
619
|
3
|
|
|
3
|
|
1061
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
13
|
|
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=item write_append_file(), introduced in v0.01 |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=cut |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
sub write_append_file : Test(2) : Introduced('0.01') { |
626
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
627
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/append_file'); |
628
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
629
|
1
|
|
|
|
|
5
|
$self->_write_file($f, $content); |
630
|
1
|
|
|
|
|
8
|
$self->_append_file($f, $content); |
631
|
1
|
|
|
|
|
28
|
ok(-f $f, 'wrote file'); |
632
|
1
|
|
|
|
|
439
|
ok(-s $f == 2 * length $content, 'file got right size'); |
633
|
1
|
|
|
|
|
325
|
return; |
634
|
3
|
|
|
3
|
|
964
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
18
|
|
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
## Perl's '>>' creates the file if it doesn't exist |
637
|
|
|
|
|
|
|
# |
638
|
|
|
|
|
|
|
#=item write_append_file_fail(), introduced in v0.01 |
639
|
|
|
|
|
|
|
# |
640
|
|
|
|
|
|
|
#=cut |
641
|
|
|
|
|
|
|
# |
642
|
|
|
|
|
|
|
# sub write_append_file_fail : Test(2) : Introduced('0.01') { |
643
|
|
|
|
|
|
|
# my ($self) = @_; |
644
|
|
|
|
|
|
|
# my $f = $self->_file('/append_file_fail'); |
645
|
|
|
|
|
|
|
# my $content = 'content'; |
646
|
|
|
|
|
|
|
# eval { |
647
|
|
|
|
|
|
|
# $self->_append_file($f, $content); |
648
|
|
|
|
|
|
|
# }; |
649
|
|
|
|
|
|
|
# $self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'append to non-existent file'); |
650
|
|
|
|
|
|
|
# ok(!-f $f, 'did not make file'); |
651
|
|
|
|
|
|
|
# return; |
652
|
|
|
|
|
|
|
# } |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item write_read_file(), introduced in v0.01 |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
=cut |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
sub write_read_file : Test(1) : Introduced('0.01') { |
659
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
660
|
1
|
|
|
|
|
6
|
my $f = $self->_file('/read_file'); |
661
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
662
|
1
|
|
|
|
|
8
|
$self->_write_file($f, $content); |
663
|
1
|
|
|
|
|
7
|
is($self->_read_file($f), $content, 'read file'); |
664
|
1
|
|
|
|
|
435
|
return; |
665
|
3
|
|
|
3
|
|
1038
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
13
|
|
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=item write_read_file_binary(), introduced in v0.08 |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=cut |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
sub write_read_file_binary : Test(1) : Introduced('0.08') { |
672
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
673
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/read_file'); |
674
|
1
|
|
|
|
|
5
|
my $content = 'content'; |
675
|
1
|
|
|
|
|
7
|
for my $ord (0 .. 0xff, 0 .. 0xff) { ## no critic(MagicNumber) |
676
|
512
|
|
|
|
|
762
|
$content .= chr $ord; |
677
|
|
|
|
|
|
|
} |
678
|
1
|
|
|
|
|
6
|
$self->_write_file($f, $content); |
679
|
1
|
|
|
|
|
6
|
is($self->_read_file($f), $content, 'read file'); |
680
|
1
|
|
|
|
|
385
|
return; |
681
|
3
|
|
|
3
|
|
1149
|
} |
|
3
|
|
|
|
|
48
|
|
|
3
|
|
|
|
|
14
|
|
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=item write_unlink_file(), introduced in v0.01 |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=cut |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
sub write_unlink_file : Test(3) : Introduced('0.01') { |
688
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
689
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/read_file'); |
690
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
691
|
1
|
|
|
|
|
5
|
$self->_write_file($f, $content); |
692
|
1
|
|
|
|
|
23
|
ok(-e $f, 'file exists'); |
693
|
1
|
|
|
|
|
1077
|
ok(-f $f, 'file is a file'); |
694
|
1
|
50
|
|
|
|
529
|
unlink $f or die $OS_ERROR; |
695
|
1
|
|
|
|
|
25
|
ok(!-f $f, 'file is deleted'); |
696
|
1
|
|
|
|
|
285
|
return; |
697
|
3
|
|
|
3
|
|
905
|
} |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
10
|
|
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=item write_mkdir(), introduced in v0.01 |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=cut |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
sub write_mkdir : Test(1) : Introduced('0.01') { |
704
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
705
|
1
|
|
|
|
|
4
|
my $f = $self->_file('/mk_dir'); |
706
|
1
|
50
|
|
|
|
85
|
mkdir $f or die $OS_ERROR; |
707
|
1
|
|
|
|
|
22
|
ok(-d $f, 'made dir'); |
708
|
1
|
|
|
|
|
326
|
return; |
709
|
3
|
|
|
3
|
|
776
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
11
|
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
=item write_mkdir_fail(), introduced in v0.01 |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
=cut |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
sub write_mkdir_fail : Test(2) : Introduced('0.01') { |
716
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
717
|
1
|
|
|
|
|
4
|
my $f = $self->_file('/no_such/mk_dir'); |
718
|
1
|
|
|
|
|
3
|
eval { |
719
|
1
|
50
|
|
|
|
43
|
mkdir $f or die $OS_ERROR; |
720
|
|
|
|
|
|
|
}; |
721
|
1
|
|
|
|
|
7
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'mkdir at non-existent path'); |
722
|
1
|
|
|
|
|
330
|
ok(!-d $f, 'did not make dir'); |
723
|
1
|
|
|
|
|
393
|
return; |
724
|
3
|
|
|
3
|
|
879
|
} |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
13
|
|
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=item write_rmdir(), introduced in v0.01 |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=cut |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
sub write_rmdir : Test(2) : Introduced('0.01') { |
731
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
732
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/rm_dir'); |
733
|
1
|
50
|
|
|
|
81
|
mkdir $f or die $OS_ERROR; |
734
|
1
|
|
|
|
|
26
|
ok(-d $f, 'made dir'); |
735
|
1
|
50
|
|
|
|
434
|
rmdir $f or die $OS_ERROR; |
736
|
1
|
|
|
|
|
26
|
ok(!-d $f, 'made dir'); |
737
|
1
|
|
|
|
|
374
|
return; |
738
|
3
|
|
|
3
|
|
908
|
} |
|
3
|
|
|
|
|
22
|
|
|
3
|
|
|
|
|
12
|
|
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=item write_subdir(), introduced in v0.01 |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=cut |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
sub write_subdir : Test(3) : Introduced('0.01') { |
745
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
746
|
1
|
|
|
|
|
7
|
my $d = $self->_file('/mk_dir'); |
747
|
1
|
|
|
|
|
5
|
my $f = $self->_file('/mk_dir/file'); |
748
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
749
|
1
|
50
|
|
|
|
87
|
mkdir $d or die $OS_ERROR; |
750
|
1
|
|
|
|
|
69
|
ok(-d $d, 'made dir'); |
751
|
1
|
|
|
|
|
372
|
$self->_write_file($f, $content); |
752
|
1
|
|
|
|
|
35
|
ok(-f $f, 'wrote file in subdir'); |
753
|
1
|
|
|
|
|
412
|
is($self->_read_file($f), $content, 'right content'); |
754
|
1
|
|
|
|
|
428
|
return; |
755
|
3
|
|
|
3
|
|
1010
|
} |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
11
|
|
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=item symlink_create(), introduced in v0.02 |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
=cut |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
sub symlink_create : Test(10) : Introduced('0.02') : Features('symlink') { |
762
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
763
|
1
|
|
|
|
|
4
|
my $target = 'symlink_target'; |
764
|
1
|
|
|
|
|
7
|
my $src = $self->_file("/$target"); |
765
|
1
|
50
|
|
|
|
85
|
mkdir $src or die $OS_ERROR; |
766
|
1
|
|
|
|
|
25
|
ok(-e $src, 'symlink source exists'); |
767
|
1
|
|
|
|
|
318
|
my $s = $self->_file('/symlink_create'); |
768
|
1
|
50
|
|
|
|
56
|
ok((symlink $target, $s), 'created symlink') or die $OS_ERROR; |
769
|
1
|
|
|
|
|
380
|
ok(-e $s, 'symlink exists'); |
770
|
1
|
|
|
|
|
285
|
ok(-l $s, 'symlink is a symlink'); |
771
|
1
|
|
|
|
|
279
|
ok(-d $s, 'symlink src is a dir'); |
772
|
1
|
|
|
|
|
280
|
ok(!-f $s, 'symlink src is not a file'); |
773
|
1
|
|
|
|
|
436
|
is(-s $s, -s $src, 'symlink size is size of source'); |
774
|
1
|
|
|
|
|
290
|
is(readlink $s, $target, 'read newly created symlink'); |
775
|
1
|
50
|
|
|
|
501
|
unlink $s or die $OS_ERROR; |
776
|
1
|
|
|
|
|
28
|
ok(!-e $s, 'symlink deleted'); |
777
|
1
|
|
|
|
|
301
|
ok(-e $src, 'symlink source not deleted'); |
778
|
1
|
|
|
|
|
268
|
return; |
779
|
3
|
|
|
3
|
|
1238
|
} |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
12
|
|
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=item symlink_follow(), introduced in v0.04 |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
=cut |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
sub symlink_follow : Test(11) : Introduced('0.04') : Features('symlink') { |
786
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
787
|
1
|
|
|
|
|
4
|
my $target = 'symlink_target'; |
788
|
1
|
|
|
|
|
9
|
my $srcdir = $self->_file("/$target"); |
789
|
1
|
|
|
|
|
7
|
my $srcfile = $self->_file("/$target/file.txt"); |
790
|
1
|
|
|
|
|
6
|
my $s = $self->_file('/symlink_follow'); |
791
|
1
|
|
|
|
|
5
|
my $symfile = $s . '/file.txt'; |
792
|
1
|
50
|
|
|
|
82
|
mkdir $srcdir or die $OS_ERROR; |
793
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
794
|
1
|
|
|
|
|
6
|
$self->_write_file($srcfile, $content); |
795
|
1
|
|
|
|
|
26
|
ok(-e $srcfile, 'symlink source exists'); |
796
|
1
|
50
|
|
|
|
4163
|
ok((symlink $target, $s), 'created symlink') or die $OS_ERROR; |
797
|
1
|
|
|
|
|
812
|
ok(-e $s, 'symlink exists'); |
798
|
1
|
|
|
|
|
1201
|
ok(-l $s, 'symlink is a symlink'); |
799
|
1
|
|
|
|
|
1231
|
ok(-d $s, 'symlink src is a dir'); |
800
|
1
|
|
|
|
|
675
|
ok(!-f $s, 'symlink src is not a file'); |
801
|
1
|
|
|
|
|
324
|
is(-s $symfile, length $content, 'size of file though symlink size is size of content'); |
802
|
1
|
|
|
|
|
937
|
is($self->_read_file($symfile), $content, 'read file through newly created symlink'); |
803
|
1
|
50
|
|
|
|
2173
|
unlink $symfile or die $OS_ERROR; |
804
|
1
|
|
|
|
|
38
|
ok(-e $s, 'symlink not deleted'); |
805
|
1
|
|
|
|
|
777
|
ok(-e $srcdir, 'symlink target dir is not deleted'); |
806
|
1
|
|
|
|
|
1044
|
ok(!-e $srcfile, 'file through symlink is deleted'); |
807
|
1
|
|
|
|
|
1010
|
return; |
808
|
3
|
|
|
3
|
|
1602
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
12
|
|
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=item symlink_deep(), introduced in v0.06 |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
=cut |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
sub symlink_deep : Test(17) : Introduced('0.06') : Features('symlink') { |
815
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
816
|
|
|
|
|
|
|
# follow through a chain of non-looping symlinks |
817
|
1
|
|
|
|
|
4
|
my $target = 'symlink_target'; |
818
|
1
|
|
|
|
|
7
|
my $srcdir = $self->_file("/$target"); |
819
|
1
|
|
|
|
|
6
|
my $srcfile = $self->_file("/$target/file.txt"); |
820
|
|
|
|
|
|
|
# 5 is the limit for Linux. Beyond that, we get ELOOP: Too many levels of symbolic links |
821
|
|
|
|
|
|
|
# Other platforms seem to tolerate higher numbers... |
822
|
1
|
|
|
|
|
5
|
my @s = map {$self->_file("/symlink_$_")} 1 .. 5; ## no critic(ValuesAndExpressions::ProhibitMagicNumbers) |
|
5
|
|
|
|
|
17
|
|
823
|
|
|
|
|
|
|
|
824
|
1
|
50
|
|
|
|
74
|
mkdir $srcdir or die $OS_ERROR; |
825
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
826
|
1
|
|
|
|
|
7
|
$self->_write_file($srcfile, $content); |
827
|
1
|
|
|
|
|
78
|
ok(-e $srcfile, 'symlink source exists'); |
828
|
1
|
|
|
|
|
483
|
ok(-f $srcfile, 'symlink source is a file'); |
829
|
|
|
|
|
|
|
|
830
|
1
|
50
|
|
|
|
373
|
ok((symlink $target, $s[0]), 'created symlink') or die $OS_ERROR; |
831
|
1
|
|
|
|
|
450
|
for my $i (1..$#s) { |
832
|
4
|
50
|
|
|
|
1295
|
ok((symlink 'symlink_' . $i, $s[$i]), 'created symlink') or die $OS_ERROR; |
833
|
|
|
|
|
|
|
} |
834
|
1
|
|
|
|
|
6361
|
my $symfile = $self->_file('/symlink_'.@s.'/file.txt'); |
835
|
1
|
|
|
|
|
105
|
ok(-e $symfile, 'file exists'); # or die 'no symlinked file, cannot continue'; |
836
|
1
|
|
|
|
|
1626
|
ok(!-l $symfile, 'file is not a symlink'); |
837
|
1
|
|
|
|
|
591
|
ok(-f $symfile, 'file is a file'); |
838
|
1
|
|
|
|
|
723
|
ok(!-d $symfile, 'file is not a dir'); |
839
|
1
|
|
|
|
|
1026
|
is(-s $symfile, length $content, 'size of file though symlink size is size of content'); |
840
|
1
|
|
|
|
|
1071
|
is($self->_read_file($symfile), $content, 'read file through newly created symlink'); |
841
|
1
|
50
|
|
|
|
1163
|
unlink $symfile or die $OS_ERROR; |
842
|
1
|
|
|
|
|
35
|
ok(-e $s[0], 'symlink not deleted'); |
843
|
1
|
|
|
|
|
1405
|
ok(-e $s[-1], 'symlink not deleted'); |
844
|
1
|
|
|
|
|
1286
|
ok(-e $srcdir, 'symlink target dir is not deleted'); |
845
|
1
|
|
|
|
|
641
|
ok(!-e $srcfile, 'file through symlink is deleted'); |
846
|
1
|
|
|
|
|
715
|
return; |
847
|
3
|
|
|
3
|
|
2016
|
} |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
13
|
|
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
=item symlink_loop(), introduced in v0.06 |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=cut |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
sub symlink_loop : Test(2) : Introduced('0.06') : Features('symlink') { |
854
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
855
|
1
|
|
|
|
|
4
|
my $target = 'symlink_target'; |
856
|
1
|
|
|
|
|
6
|
my $s = $self->_file("/$target"); |
857
|
1
|
50
|
|
|
|
59
|
ok((symlink $target, $s), 'created symlink') or die $OS_ERROR; |
858
|
1
|
|
|
|
|
2682
|
eval { |
859
|
1
|
50
|
|
|
|
644
|
open my $fh, '<', $s or die $OS_ERROR; |
860
|
0
|
|
|
|
|
0
|
close $fh; ## no critic(InputOutput::RequireCheckedClose) |
861
|
|
|
|
|
|
|
}; |
862
|
1
|
|
|
|
|
11
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ELOOP()], 'detected symlink loop'); |
863
|
|
|
|
|
|
|
|
864
|
1
|
|
|
|
|
428
|
return; |
865
|
3
|
|
|
3
|
|
1307
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
10
|
|
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
=item truncate_file(), introduced in v0.06 |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=cut |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
sub truncate_file : Test(7) : Introduced('0.06') { |
872
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
873
|
1
|
|
|
|
|
7
|
my $f = $self->_file(q{/truncate.txt}); |
874
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
## no critic(ValuesAndExpressions::ProhibitMagicNumbers) |
877
|
1
|
|
|
|
|
7
|
$self->_write_file($f, $content); |
878
|
1
|
|
|
|
|
57
|
is(-s $f, length $content, 'wrote test file'); |
879
|
1
|
50
|
|
|
|
783
|
ok((truncate $f, 4), 'truncate to 4 bytes') or die $OS_ERROR; |
880
|
1
|
|
|
|
|
556
|
is(-s $f, 4, 'correct size'); |
881
|
1
|
50
|
|
|
|
801
|
ok((truncate $f, 0), 'truncate to 0 bytes') or die $OS_ERROR; |
882
|
1
|
|
|
|
|
570
|
is(-s $f, 0, 'correct size'); |
883
|
1
|
50
|
|
|
|
608
|
ok((truncate $f, 0), 'truncate to 0 bytes') or die $OS_ERROR; |
884
|
1
|
|
|
|
|
584
|
is(-s $f, 0, 'correct size'); |
885
|
1
|
|
|
|
|
623
|
return; |
886
|
3
|
|
|
3
|
|
1315
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
14
|
|
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
=item truncate_no_file(), introduced in v0.06 |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
=cut |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
sub truncate_no_file : Test(1) : Introduced('0.06') { |
893
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
894
|
1
|
|
|
|
|
8
|
my $f = $self->_file(q{/truncate.txt}); |
895
|
1
|
|
|
|
|
4
|
eval { |
896
|
1
|
50
|
|
|
|
44
|
truncate $f, 0 or die $OS_ERROR; |
897
|
|
|
|
|
|
|
}; |
898
|
1
|
|
|
|
|
7
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'truncate non-existent file'); |
899
|
1
|
|
|
|
|
328
|
return; |
900
|
3
|
|
|
3
|
|
912
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
16
|
|
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=item truncate_file_no_dir(), introduced in v0.06 |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=cut |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
sub truncate_file_no_dir : Test(1) : Introduced('0.06') { |
907
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
908
|
1
|
|
|
|
|
5
|
my $pseudo_dir = $self->_file(q{/dir}); |
909
|
1
|
|
|
|
|
5
|
my $f = $self->_file(q{/dir/truncate.txt}); |
910
|
1
|
|
|
|
|
6
|
$self->_write_file($pseudo_dir, 'foo'); |
911
|
1
|
|
|
|
|
2
|
eval { |
912
|
1
|
50
|
|
|
|
40
|
truncate $f, 0 or die $OS_ERROR; |
913
|
|
|
|
|
|
|
}; |
914
|
|
|
|
|
|
|
# man 2 truncate says "[ENOTDIR] A component of the path prefix is not a directory." |
915
|
|
|
|
|
|
|
# MSWin32 says ENOENT |
916
|
1
|
|
|
|
|
9
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOTDIR(), ENOENT()], 'truncate file in non-existent directory'); |
917
|
1
|
|
|
|
|
375
|
return; |
918
|
3
|
|
|
3
|
|
1063
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
17
|
|
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=item truncate_dir(), introduced in v0.06 |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
=cut |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
sub truncate_dir : Test(1) : Introduced('0.06') { |
925
|
1
|
|
|
|
|
5
|
my ($self) = @_; |
926
|
1
|
|
|
|
|
8
|
my $d = $self->_file(q{/truncate_dir}); |
927
|
1
|
50
|
|
|
|
102
|
mkdir $d or die $OS_ERROR; |
928
|
1
|
|
|
|
|
3
|
eval { |
929
|
1
|
50
|
|
|
|
245
|
truncate $d, 0 or die $OS_ERROR; |
930
|
|
|
|
|
|
|
}; |
931
|
|
|
|
|
|
|
# man 2 truncate says "[EISDIR] The named file is a directory." |
932
|
|
|
|
|
|
|
# MSWin32 says EACCES |
933
|
1
|
|
|
|
|
37
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [EISDIR(), EACCES()], 'truncate dir'); |
934
|
1
|
|
|
|
|
549
|
return; |
935
|
3
|
|
|
3
|
|
1014
|
} |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
13
|
|
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
=item time_mtime_create(), introduced in v0.06 |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
=cut |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
sub time_mtime_create : Test(2) : Introduced('0.06') : Features('time/mtime') { |
942
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
943
|
1
|
|
|
|
|
5
|
my $f = $self->_file(q{/file.txt}); |
944
|
|
|
|
|
|
|
|
945
|
1
|
|
|
|
|
4
|
my $before = time; |
946
|
1
|
|
|
|
|
4
|
$self->_write_file($f); |
947
|
1
|
|
|
|
|
2
|
my $after = time; |
948
|
|
|
|
|
|
|
|
949
|
1
|
|
|
|
|
23
|
my ($mtime) = (stat $f)[9]; ## no critic(ValuesAndExpressions::ProhibitMagicNumbers) |
950
|
1
|
50
|
|
|
|
7
|
cmp_ok($mtime, q{>=}, $before - $TIME_LENIENCE, 'mtime vs. before time') |
951
|
|
|
|
|
|
|
or diag 'Is your clock out of synch?'; |
952
|
1
|
|
|
|
|
349
|
cmp_ok($mtime, q{<=}, $after + $TIME_LENIENCE, 'mtime vs. after time'); |
953
|
1
|
|
|
|
|
1558
|
return; |
954
|
3
|
|
|
3
|
|
995
|
} |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
12
|
|
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=item time_ctime_create(), introduced in v0.06 |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=cut |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
sub time_ctime_create : Test(2) : Introduced('0.06') : Features('time/ctime') { |
961
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
962
|
1
|
|
|
|
|
7
|
my $f = $self->_file(q{/file.txt}); |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
# sleep is needed in case of network filesystem time synch errors |
965
|
1
|
|
|
|
|
14
|
my $before = time; |
966
|
1
|
|
|
|
|
7
|
$self->_write_file($f); |
967
|
1
|
|
|
|
|
3
|
my $after = time; |
968
|
|
|
|
|
|
|
|
969
|
1
|
|
|
|
|
28
|
my ($ctime) = (stat $f)[10]; ## no critic(ValuesAndExpressions::ProhibitMagicNumbers) |
970
|
1
|
50
|
|
|
|
8
|
cmp_ok($ctime, q{>=}, $before - $TIME_LENIENCE, 'ctime vs. before time') |
971
|
|
|
|
|
|
|
or diag 'Is your clock out of synch?'; |
972
|
1
|
|
|
|
|
388
|
cmp_ok($ctime, q{<=}, $after + $TIME_LENIENCE, 'ctime vs. after time'); |
973
|
1
|
|
|
|
|
329
|
return; |
974
|
3
|
|
|
3
|
|
1153
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
14
|
|
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
=item time_mtime_set(), introduced in v0.06 |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
=cut |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
sub time_mtime_set : Test(1) : Introduced('0.06') : Features('time/mtime') { |
981
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
982
|
1
|
|
|
|
|
7
|
my $f = $self->_file(q{/file.txt}); |
983
|
|
|
|
|
|
|
|
984
|
1
|
|
|
|
|
5
|
$self->_write_file($f); |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
## no critic(ValuesAndExpressions::ProhibitMagicNumbers) |
987
|
1
|
|
|
|
|
23
|
my ($old_atime, $old_mtime) = (stat $f)[8,9]; |
988
|
1
|
50
|
|
|
|
49
|
utime $old_atime, $old_mtime - 100, $f or die $OS_ERROR; |
989
|
1
|
|
|
|
|
20
|
my ($new_atime, $new_mtime) = (stat $f)[8,9]; |
990
|
1
|
|
|
|
|
7
|
is($new_mtime, $old_mtime - 100, 'changed mtime'); |
991
|
1
|
|
|
|
|
1866
|
return; |
992
|
3
|
|
|
3
|
|
1041
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
11
|
|
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
=item time_atime_set(), introduced in v0.06 |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
=cut |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
sub time_atime_set : Test(1) : Introduced('0.06') : Features('time/atime') { |
999
|
0
|
|
|
|
|
0
|
my ($self) = @_; |
1000
|
0
|
|
|
|
|
0
|
my $f = $self->_file(q{/file.txt}); |
1001
|
|
|
|
|
|
|
|
1002
|
0
|
|
|
|
|
0
|
$self->_write_file($f); |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
## no critic(ValuesAndExpressions::ProhibitMagicNumbers) |
1005
|
0
|
|
|
|
|
0
|
my ($old_atime, $old_mtime) = (stat $f)[8,9]; |
1006
|
0
|
0
|
|
|
|
0
|
utime $old_atime - 100, $old_mtime, $f or die $OS_ERROR; |
1007
|
0
|
|
|
|
|
0
|
my ($new_atime, $new_mtime) = (stat $f)[8,9]; |
1008
|
0
|
|
|
|
|
0
|
is($new_atime, $old_atime - 100, 'changed atime'); |
1009
|
0
|
|
|
|
|
0
|
return; |
1010
|
3
|
|
|
3
|
|
1119
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
12
|
|
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
=item xattr_list(), introduced in v0.02 |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
=cut |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
sub xattr_list : Test(1) : Introduced('0.02') : Features('xattr') { |
1017
|
0
|
|
|
|
|
0
|
my ($self) = @_; |
1018
|
0
|
|
|
|
|
0
|
my $f = $self->_file(q{/}); |
1019
|
0
|
|
|
|
|
0
|
my @attrs = File::ExtAttr::listfattr($f); |
1020
|
0
|
|
0
|
|
|
0
|
ok(@attrs == 0 || defined $attrs[0], 'got xattr list'); |
1021
|
0
|
|
|
|
|
0
|
return; |
1022
|
3
|
|
|
3
|
|
1043
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
52
|
|
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
=item xattr_set(), introduced in v0.02 |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
=cut |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
sub xattr_set : Test(9) : Introduced('0.02') : Features('xattr') { |
1029
|
0
|
|
|
|
|
0
|
my ($self) = @_; |
1030
|
|
|
|
|
|
|
|
1031
|
0
|
|
|
|
|
0
|
my $f = $self->_file('/foo'); |
1032
|
0
|
|
|
|
|
0
|
$self->_write_file($f); |
1033
|
|
|
|
|
|
|
|
1034
|
0
|
|
|
|
|
0
|
my $xattr_key = 'org.cpan.cdolan'; |
1035
|
0
|
|
|
|
|
0
|
my $xattr_value = 'test'; |
1036
|
0
|
|
|
|
|
0
|
my $xattr_replace = 'test2'; |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
# just in case, clean up. This fails if the value is '0' but that should never happen! |
1039
|
0
|
0
|
|
|
|
0
|
if (File::ExtAttr::getfattr($f, $xattr_key)) { |
1040
|
0
|
|
|
|
|
0
|
File::ExtAttr::delfattr($f, $xattr_key); |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
{ |
1043
|
|
|
|
|
|
|
# File::ExtAttr doesn't look at $^W or 'no warnings'. Grr... |
1044
|
0
|
|
|
|
|
0
|
local $SIG{__WARN__} = sub {}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1045
|
0
|
|
|
|
|
0
|
ok(!File::ExtAttr::setfattr($f, $xattr_key, $xattr_value, {replace => 1}), 'cannot replace missing xattr'); |
1046
|
|
|
|
|
|
|
} |
1047
|
0
|
|
|
|
|
0
|
ok(File::ExtAttr::setfattr($f, $xattr_key, $xattr_value, {create => 1}), 'create xattr'); |
1048
|
0
|
|
|
|
|
0
|
is(File::ExtAttr::getfattr($f, $xattr_key), $xattr_value, 'get xattr'); |
1049
|
0
|
|
|
|
|
0
|
ok((any {$xattr_key eq $_} File::ExtAttr::listfattr($f)), 'list xattr'); |
|
0
|
|
|
|
|
0
|
|
1050
|
|
|
|
|
|
|
{ |
1051
|
0
|
|
|
|
|
0
|
local $SIG{__WARN__} = sub {}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1052
|
0
|
|
|
|
|
0
|
ok(!File::ExtAttr::setfattr($f, $xattr_key, $xattr_value, {create => 1}), 'cannot create existing xattr'); |
1053
|
|
|
|
|
|
|
} |
1054
|
0
|
|
|
|
|
0
|
ok(File::ExtAttr::setfattr($f, $xattr_key, $xattr_replace, {replace => 1}), 'replace xattr'); |
1055
|
0
|
|
|
|
|
0
|
is(File::ExtAttr::getfattr($f, $xattr_key), $xattr_replace, 'get xattr'); |
1056
|
0
|
|
|
|
|
0
|
ok(File::ExtAttr::delfattr($f, $xattr_key), 'delete xattr'); |
1057
|
|
|
|
|
|
|
# Some implementations return undef, some return q{} |
1058
|
0
|
|
|
|
|
0
|
my $get = File::ExtAttr::getfattr($f, $xattr_key); |
1059
|
0
|
|
0
|
|
|
0
|
ok(!defined $get || q{} eq $get, 'xattr deleted'); |
1060
|
|
|
|
|
|
|
|
1061
|
0
|
|
|
|
|
0
|
unlink $f; |
1062
|
0
|
|
|
|
|
0
|
return; |
1063
|
3
|
|
|
3
|
|
1930
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
18
|
|
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
=item rename_file(), introduced in v0.08 |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=cut |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
sub rename_file : Test(4) : Introduced('0.08') { |
1070
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1071
|
1
|
|
|
|
|
6
|
my $src = $self->_file('/rename_src'); |
1072
|
1
|
|
|
|
|
5
|
my $dest = $self->_file('/rename_dest'); |
1073
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1074
|
1
|
|
|
|
|
5
|
$self->_write_file($src, $content); |
1075
|
1
|
|
|
|
|
71
|
ok((rename $src, $dest), 'rename'); |
1076
|
1
|
|
|
|
|
2035
|
ok(-e $dest, 'dest exists'); |
1077
|
1
|
|
|
|
|
970
|
ok(!-e $src, 'src no longer exists'); |
1078
|
1
|
|
|
|
|
299
|
is($self->_read_file($dest), $content, 'read dest'); |
1079
|
1
|
|
|
|
|
303
|
return; |
1080
|
3
|
|
|
3
|
|
1293
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
13
|
|
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
=item rename_file_exists(), introduced in v0.08 |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
=cut |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
sub rename_file_exists : Test(4) : Introduced('0.08') { |
1087
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
1088
|
1
|
|
|
|
|
7
|
my $src = $self->_file('/rename_src'); |
1089
|
1
|
|
|
|
|
6
|
my $dest = $self->_file('/rename_dest'); |
1090
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1091
|
1
|
|
|
|
|
7
|
$self->_write_file($src, $content); |
1092
|
1
|
|
|
|
|
3
|
$self->_write_file($dest); |
1093
|
1
|
|
|
|
|
81
|
ok((rename $src, $dest), 'rename'); |
1094
|
1
|
|
|
|
|
593
|
ok(-e $dest, 'dest exists'); |
1095
|
1
|
|
|
|
|
575
|
ok(!-e $src, 'src no longer exists'); |
1096
|
1
|
|
|
|
|
550
|
is($self->_read_file($dest), $content, 'read dest'); |
1097
|
1
|
|
|
|
|
677
|
return; |
1098
|
3
|
|
|
3
|
|
1001
|
} |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
14
|
|
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
=item rename_file_self(), introduced in v0.08 |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
=cut |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
sub rename_file_self : Test(4) : Introduced('0.08') { |
1105
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1106
|
1
|
|
|
|
|
4
|
my $src = $self->_file('/rename_src'); |
1107
|
1
|
|
|
|
|
3
|
my $dest = $src; |
1108
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1109
|
1
|
|
|
|
|
6
|
$self->_write_file($src, $content); |
1110
|
1
|
|
|
|
|
38
|
ok((rename $src, $dest), 'rename'); |
1111
|
1
|
|
|
|
|
645
|
ok(-e $dest, 'dest exists'); |
1112
|
1
|
|
|
|
|
339
|
ok(-e $src, 'src still exists'); |
1113
|
1
|
|
|
|
|
283
|
is($self->_read_file($dest), $content, 'read dest'); |
1114
|
1
|
|
|
|
|
289
|
return; |
1115
|
3
|
|
|
3
|
|
965
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
23
|
|
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
=item rename_file_subdir(), introduced in v0.08 |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
=cut |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
sub rename_file_subdir : Test(4) : Introduced('0.08') { |
1122
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1123
|
1
|
|
|
|
|
5
|
my $srcdir = $self->_file('/rename_srcdir'); |
1124
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_srcdir/rename_src'); |
1125
|
1
|
|
|
|
|
4
|
my $destdir = $self->_file('/rename_destdir'); |
1126
|
1
|
|
|
|
|
3
|
my $dest = $self->_file('/rename_destdir/rename_dest'); |
1127
|
1
|
50
|
|
|
|
75
|
mkdir $srcdir or die $OS_ERROR; |
1128
|
1
|
50
|
|
|
|
59
|
mkdir $destdir or die $OS_ERROR; |
1129
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
1130
|
1
|
|
|
|
|
5
|
$self->_write_file($src, $content); |
1131
|
1
|
|
|
|
|
71
|
ok((rename $src, $dest), 'rename'); |
1132
|
1
|
|
|
|
|
366
|
ok(-e $dest, 'dest exists'); |
1133
|
1
|
|
|
|
|
276
|
ok(!-e $src, 'src no longer exists'); |
1134
|
1
|
|
|
|
|
358
|
is($self->_read_file($dest), $content, 'read dest'); |
1135
|
1
|
|
|
|
|
284
|
return; |
1136
|
3
|
|
|
3
|
|
1262
|
} |
|
3
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
14
|
|
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
=item rename_file_missing_src(), introduced in v0.08 |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
=cut |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
sub rename_file_missing_src : Test(1) : Introduced('0.08') { |
1143
|
1
|
|
|
|
|
160
|
my ($self) = @_; |
1144
|
1
|
|
|
|
|
6
|
my $src = $self->_file('/rename_src'); |
1145
|
1
|
|
|
|
|
5
|
my $dest = $self->_file('/rename_dest'); |
1146
|
1
|
|
|
|
|
3
|
eval { |
1147
|
1
|
50
|
|
|
|
53
|
rename $src, $dest or die $OS_ERROR; |
1148
|
|
|
|
|
|
|
}; |
1149
|
1
|
|
|
|
|
6
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'src file is missing'); |
1150
|
1
|
|
|
|
|
326
|
return; |
1151
|
3
|
|
|
3
|
|
1098
|
} |
|
3
|
|
|
|
|
12
|
|
|
3
|
|
|
|
|
14
|
|
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
=item rename_file_missing_srcdir(), introduced in v0.08 |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=cut |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
sub rename_file_missing_srcdir : Test(1) : Introduced('0.08') { |
1158
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1159
|
1
|
|
|
|
|
4
|
my $srcdir = $self->_file('/rename_srcdir'); |
1160
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_srcdir/rename_src'); |
1161
|
1
|
|
|
|
|
5
|
my $dest = $self->_file('/rename_dest'); |
1162
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1163
|
1
|
|
|
|
|
2
|
eval { |
1164
|
1
|
50
|
|
|
|
39
|
rename $src, $dest or die $OS_ERROR; |
1165
|
|
|
|
|
|
|
}; |
1166
|
1
|
|
|
|
|
6
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'src dir is missing'); |
1167
|
1
|
|
|
|
|
508
|
return; |
1168
|
3
|
|
|
3
|
|
1019
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
20
|
|
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
=item rename_file_missing_destdir(), introduced in v0.08 |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
=cut |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
sub rename_file_missing_destdir : Test(1) : Introduced('0.08') { |
1175
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1176
|
1
|
|
|
|
|
6
|
my $src = $self->_file('/rename_src'); |
1177
|
1
|
|
|
|
|
6
|
my $destdir = $self->_file('/rename_destdir'); |
1178
|
1
|
|
|
|
|
4
|
my $dest = $self->_file('/rename_destdir/rename_dest'); |
1179
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1180
|
1
|
|
|
|
|
7
|
$self->_write_file($src, $content); |
1181
|
1
|
|
|
|
|
2
|
eval { |
1182
|
1
|
50
|
|
|
|
57
|
rename $src, $dest or die $OS_ERROR; |
1183
|
|
|
|
|
|
|
}; |
1184
|
1
|
|
|
|
|
9
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOENT()], 'dest dir is missing'); |
1185
|
1
|
|
|
|
|
371
|
return; |
1186
|
3
|
|
|
3
|
|
1135
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
27
|
|
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
=item rename_dir(), introduced in v0.08 |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
=cut |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
sub rename_dir : Test(6) : Introduced('0.08') { |
1193
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
1194
|
1
|
|
|
|
|
4
|
my $src = $self->_file('/rename_src'); |
1195
|
1
|
|
|
|
|
5
|
my $srcfile = $self->_file('/rename_src/file.txt'); |
1196
|
1
|
|
|
|
|
5
|
my $dest = $self->_file('/rename_dest'); |
1197
|
1
|
|
|
|
|
4
|
my $destfile = $self->_file('/rename_dest/file.txt'); |
1198
|
1
|
50
|
|
|
|
90
|
mkdir $src or die $OS_ERROR; |
1199
|
1
|
|
|
|
|
2
|
my $content = 'content'; |
1200
|
1
|
|
|
|
|
5
|
$self->_write_file($srcfile, $content); |
1201
|
1
|
|
|
|
|
75
|
ok((rename $src, $dest), 'rename'); |
1202
|
1
|
|
|
|
|
541
|
ok(-e $dest, 'dest exists'); |
1203
|
1
|
|
|
|
|
456
|
ok(-e $destfile, 'dest file exists'); |
1204
|
1
|
|
|
|
|
320
|
ok(!-e $src, 'src no longer exists'); |
1205
|
1
|
|
|
|
|
290
|
is_deeply([sort $self->_read_dir($dest)], [qw(. .. file.txt)], 'read dest'); |
1206
|
1
|
|
|
|
|
678
|
is($self->_read_file($destfile), $content, 'read dest'); |
1207
|
1
|
|
|
|
|
335
|
return; |
1208
|
3
|
|
|
3
|
|
1268
|
} |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
14
|
|
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
=item rename_dir_exists(), introduced in v0.08 |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
=cut |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
sub rename_dir_exists : Test(6) : Introduced('0.08') { |
1215
|
1
|
|
|
|
|
7
|
my ($self) = @_; |
1216
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_src'); |
1217
|
1
|
|
|
|
|
4
|
my $srcfile = $self->_file('/rename_src/file.txt'); |
1218
|
1
|
|
|
|
|
5
|
my $dest = $self->_file('/rename_dest'); |
1219
|
1
|
|
|
|
|
4
|
my $destfile = $self->_file('/rename_dest/file.txt'); |
1220
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
1221
|
1
|
50
|
|
|
|
80
|
mkdir $src or die $OS_ERROR; |
1222
|
1
|
50
|
|
|
|
57
|
mkdir $dest or die $OS_ERROR; |
1223
|
1
|
|
|
|
|
6
|
$self->_write_file($srcfile, $content); |
1224
|
1
|
50
|
|
|
|
7
|
if ($OSNAME eq 'MSWin32') { |
1225
|
|
|
|
|
|
|
# return the skip message |
1226
|
0
|
|
|
|
|
0
|
return 'Cannot overwrite directories via rename on Windows'; |
1227
|
|
|
|
|
|
|
} |
1228
|
1
|
|
|
|
|
139
|
ok((rename $src, $dest), 'rename'); |
1229
|
1
|
|
|
|
|
351
|
ok(-e $dest, 'dest exists'); |
1230
|
1
|
|
|
|
|
277
|
ok(-e $destfile, 'dest file exists'); |
1231
|
1
|
|
|
|
|
278
|
ok(!-e $src, 'src no longer exists'); |
1232
|
1
|
|
|
|
|
256
|
is_deeply([sort $self->_read_dir($dest)], [qw(. .. file.txt)], 'read dest'); |
1233
|
1
|
|
|
|
|
556
|
is($self->_read_file($destfile), $content, 'read dest'); |
1234
|
1
|
|
|
|
|
334
|
return; |
1235
|
3
|
|
|
3
|
|
1311
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
20
|
|
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
=item rename_dir_notempty(), introduced in v0.08 |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
=cut |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
sub rename_dir_notempty : Test(1) : Introduced('0.08') { |
1242
|
1
|
|
|
|
|
2
|
my ($self) = @_; |
1243
|
1
|
|
|
|
|
6
|
my $src = $self->_file('/rename_src'); |
1244
|
1
|
|
|
|
|
6
|
my $dest = $self->_file('/rename_dest'); |
1245
|
1
|
|
|
|
|
4
|
my $destfile = $self->_file('/rename_dest/file.txt'); |
1246
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1247
|
1
|
50
|
|
|
|
82
|
mkdir $src or die $OS_ERROR; |
1248
|
1
|
50
|
|
|
|
71
|
mkdir $dest or die $OS_ERROR; |
1249
|
1
|
|
|
|
|
5
|
$self->_write_file($destfile, $content); |
1250
|
1
|
|
|
|
|
3
|
eval { |
1251
|
1
|
50
|
|
|
|
70
|
rename $src, $dest or die $OS_ERROR; |
1252
|
|
|
|
|
|
|
}; |
1253
|
|
|
|
|
|
|
# man 2 rename says "[ENOTEMPTY] To is a directory and is not empty." |
1254
|
|
|
|
|
|
|
# MSWin32 says EACCES |
1255
|
|
|
|
|
|
|
# Solaris says EEXIST |
1256
|
1
|
|
|
|
|
8
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOTEMPTY(), EACCES(), EEXIST()], 'dest dir is not empty'); |
1257
|
1
|
|
|
|
|
362
|
return; |
1258
|
3
|
|
|
3
|
|
1058
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
14
|
|
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
=item rename_dir_self(), introduced in v0.08 |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=cut |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
sub rename_dir_self : Test(5) : Introduced('0.08') { |
1265
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1266
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_src'); |
1267
|
1
|
|
|
|
|
5
|
my $srcfile = $self->_file('/rename_src/file.txt'); |
1268
|
1
|
|
|
|
|
4
|
my $dest = $src; |
1269
|
1
|
|
|
|
|
2
|
my $destfile = $srcfile; |
1270
|
1
|
50
|
|
|
|
71
|
mkdir $src or die $OS_ERROR; |
1271
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
1272
|
1
|
|
|
|
|
5
|
$self->_write_file($srcfile, $content); |
1273
|
1
|
|
|
|
|
33
|
ok((rename $src, $dest), 'rename'); |
1274
|
1
|
|
|
|
|
339
|
ok(-e $dest, 'dest exists'); |
1275
|
1
|
|
|
|
|
304
|
ok(-e $destfile, 'dest file exists'); |
1276
|
1
|
|
|
|
|
257
|
is_deeply([sort $self->_read_dir($dest)], [qw(. .. file.txt)], 'read dest'); |
1277
|
1
|
|
|
|
|
559
|
is($self->_read_file($destfile), $content, 'read dest'); |
1278
|
1
|
|
|
|
|
273
|
return; |
1279
|
3
|
|
|
3
|
|
1089
|
} |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
11
|
|
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
=item rename_dir_subdir(), introduced in v0.08 |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=cut |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
sub rename_dir_subdir : Test(6) : Introduced('0.08') { |
1286
|
1
|
|
|
|
|
3
|
my ($self) = @_; |
1287
|
1
|
|
|
|
|
5
|
my $srcdir = $self->_file('/rename_srcdir'); |
1288
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_srcdir/rename_src'); |
1289
|
1
|
|
|
|
|
7
|
my $srcfile = $self->_file('/rename_srcdir/rename_src/file.txt'); |
1290
|
1
|
|
|
|
|
5
|
my $destdir = $self->_file('/rename_destdir'); |
1291
|
1
|
|
|
|
|
5
|
my $dest = $self->_file('/rename_destdir/rename_dest'); |
1292
|
1
|
|
|
|
|
5
|
my $destfile = $self->_file('/rename_destdir/rename_dest/file.txt'); |
1293
|
1
|
50
|
|
|
|
76
|
mkdir $srcdir or die $OS_ERROR; |
1294
|
1
|
50
|
|
|
|
62
|
mkdir $destdir or die $OS_ERROR; |
1295
|
1
|
50
|
|
|
|
65
|
mkdir $src or die $OS_ERROR; |
1296
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
1297
|
1
|
|
|
|
|
5
|
$self->_write_file($srcfile, $content); |
1298
|
1
|
|
|
|
|
80
|
ok((rename $src, $dest), 'rename'); |
1299
|
1
|
|
|
|
|
390
|
ok(-e $dest, 'dest exists'); |
1300
|
1
|
|
|
|
|
326
|
ok(-e $destfile, 'dest file exists'); |
1301
|
1
|
|
|
|
|
315
|
ok(!-e $src, 'src no longer exists'); |
1302
|
1
|
|
|
|
|
273
|
is_deeply([sort $self->_read_dir($dest)], [qw(. .. file.txt)], 'read dest'); |
1303
|
1
|
|
|
|
|
540
|
is($self->_read_file($destfile), $content, 'read dest'); |
1304
|
1
|
|
|
|
|
263
|
return; |
1305
|
3
|
|
|
3
|
|
1485
|
} |
|
3
|
|
|
|
|
15
|
|
|
3
|
|
|
|
|
13
|
|
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
=item rename_mismatch_dir(), introduced in v0.08 |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
=cut |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
sub rename_mismatch_dir : Test(1) : Introduced('0.08') { |
1312
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
1313
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_src'); |
1314
|
1
|
|
|
|
|
7
|
my $dest = $self->_file('/rename_dest'); |
1315
|
1
|
|
|
|
|
4
|
my $content = 'content'; |
1316
|
1
|
|
|
|
|
5
|
$self->_write_file($src, $content); |
1317
|
1
|
50
|
|
|
|
72
|
mkdir $dest or die $OS_ERROR; |
1318
|
1
|
|
|
|
|
4
|
eval { |
1319
|
1
|
50
|
|
|
|
49
|
rename $src, $dest or die $OS_ERROR; |
1320
|
|
|
|
|
|
|
}; |
1321
|
|
|
|
|
|
|
# man 2 rename says "[EISDIR] 'to' is a directory, but 'from' is not a directory." |
1322
|
|
|
|
|
|
|
# MSWin32 says EACCES |
1323
|
1
|
|
|
|
|
9
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [EISDIR(), EACCES()], 'dest is a directory'); |
1324
|
1
|
|
|
|
|
341
|
return; |
1325
|
3
|
|
|
3
|
|
1096
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
13
|
|
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
=item rename_mismatch_file(), introduced in v0.08 |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
=cut |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
sub rename_mismatch_file : Test(1) : Introduced('0.08') { |
1332
|
1
|
|
|
|
|
4
|
my ($self) = @_; |
1333
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_srcdir'); |
1334
|
1
|
|
|
|
|
6
|
my $dest = $self->_file('/rename_destfile.txt'); |
1335
|
1
|
50
|
|
|
|
185
|
mkdir $src or die $OS_ERROR; |
1336
|
1
|
|
|
|
|
2
|
my $content = 'content'; |
1337
|
1
|
|
|
|
|
7
|
$self->_write_file($dest, $content); |
1338
|
1
|
50
|
33
|
|
|
14
|
if ($OSNAME eq 'MSWin32' || $OSNAME eq 'cygwin') { |
1339
|
|
|
|
|
|
|
# return the skip message |
1340
|
0
|
|
|
|
|
0
|
return 'Windows and Cygwin allow rename(, ) instead of failing with ENOTDIR'; |
1341
|
|
|
|
|
|
|
} |
1342
|
1
|
|
|
|
|
4
|
eval { |
1343
|
1
|
50
|
|
|
|
123
|
rename $src, $dest or die $OS_ERROR; |
1344
|
|
|
|
|
|
|
}; |
1345
|
1
|
|
|
|
|
8
|
$self->_is_errno($EVAL_ERROR, $OS_ERROR, [ENOTDIR()], 'dest is not a directory'); |
1346
|
|
|
|
|
|
|
|
1347
|
1
|
|
|
|
|
388
|
return; |
1348
|
3
|
|
|
3
|
|
1358
|
} |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
13
|
|
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
=item rename_symlink(), introduced in v0.08 |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
=cut |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
sub rename_symlink : Test(6) : Introduced('0.08') : Features('symlink') { |
1355
|
1
|
|
|
|
|
13
|
my ($self) = @_; |
1356
|
1
|
|
|
|
|
5
|
my $srcfile = $self->_file('/rename_srcfile.txt'); |
1357
|
1
|
|
|
|
|
5
|
my $src = $self->_file('/rename_src'); |
1358
|
1
|
|
|
|
|
4
|
my $dest = $self->_file('/rename_dest'); |
1359
|
1
|
|
|
|
|
3
|
my $content = 'content'; |
1360
|
1
|
|
|
|
|
5
|
$self->_write_file($srcfile, $content); |
1361
|
1
|
50
|
|
|
|
62
|
symlink $srcfile, $src or die $OS_ERROR; |
1362
|
1
|
|
|
|
|
68
|
ok((rename $src, $dest), 'rename'); |
1363
|
1
|
|
|
|
|
518
|
ok(-e $dest, 'dest exists'); |
1364
|
1
|
|
|
|
|
315
|
ok(-e $srcfile, 'source target file still exists'); |
1365
|
1
|
|
|
|
|
392
|
ok(!-e $src, 'src no longer exists'); |
1366
|
1
|
|
|
|
|
337
|
ok(-l $dest, 'dest is a symlink'); |
1367
|
1
|
|
|
|
|
257
|
is($self->_read_file($dest), $content, 'read dest'); |
1368
|
1
|
|
|
|
|
311
|
return; |
1369
|
3
|
|
|
3
|
|
1276
|
} |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
14
|
|
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
######### helpers ######## |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
sub _is_errno { |
1375
|
14
|
|
|
14
|
|
68
|
my ($self, $eval_error, $os_errno, $expected_errnos, $msg) = @_; |
1376
|
14
|
|
|
|
|
124
|
my $num_errno = 0 + $os_errno; |
1377
|
14
|
|
|
|
|
37
|
my $str_errno = "$os_errno"; |
1378
|
14
|
50
|
33
|
14
|
|
139
|
return pass($msg) if $eval_error && $num_errno && any {$_ == $num_errno} @{$expected_errnos}; |
|
14
|
|
33
|
|
|
115
|
|
|
14
|
|
|
|
|
152
|
|
1379
|
0
|
|
|
|
|
0
|
my $expected_str = join q{, }, map {strerror($_)} @{$expected_errnos}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1380
|
0
|
0
|
|
|
|
0
|
if (!$eval_error) { |
|
0
|
0
|
|
|
|
0
|
|
1381
|
0
|
|
|
|
|
0
|
return fail("$msg; didn't throw expected exception"); |
1382
|
|
|
|
|
|
|
} elsif (1 == @{$expected_errnos}) { |
1383
|
0
|
|
|
|
|
0
|
return is("$num_errno ($str_errno)", "$expected_errnos->[0] ($expected_str)", $msg); |
1384
|
|
|
|
|
|
|
} else { |
1385
|
0
|
|
|
|
|
0
|
return is("$num_errno ($str_errno)", "[@{$expected_errnos}] ($expected_str)", $msg); |
|
0
|
|
|
|
|
0
|
|
1386
|
|
|
|
|
|
|
} |
1387
|
|
|
|
|
|
|
} |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
sub _file { |
1390
|
81
|
|
|
81
|
|
265
|
my ($self, $path) = @_; |
1391
|
81
|
50
|
|
|
|
480
|
$path =~ s{\A /}{}xms or croak 'test paths must be absolute'; |
1392
|
|
|
|
|
|
|
# Change path to proper OS format |
1393
|
81
|
|
|
|
|
1099
|
return File::Spec->catfile($self->{tempdir}, split m{/}xms, $path); |
1394
|
|
|
|
|
|
|
} |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
sub _write_file { |
1397
|
29
|
|
|
29
|
|
90
|
my ($self, $f, @content) = @_; |
1398
|
29
|
100
|
|
|
|
3535
|
open my $fh, '>', $f or die $OS_ERROR; |
1399
|
28
|
|
|
|
|
86
|
binmode $fh; |
1400
|
28
|
|
|
|
|
65
|
for my $content (@content) { |
1401
|
23
|
50
|
|
|
|
40
|
print {$fh} $content or die $OS_ERROR; |
|
23
|
|
|
|
|
422
|
|
1402
|
|
|
|
|
|
|
} |
1403
|
28
|
50
|
|
|
|
1506
|
close $fh or die $OS_ERROR; |
1404
|
28
|
|
|
|
|
423
|
return; |
1405
|
|
|
|
|
|
|
} |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
sub _append_file { |
1408
|
1
|
|
|
1
|
|
18
|
my ($self, $f, @content) = @_; |
1409
|
1
|
50
|
|
|
|
54
|
open my $fh, '>>', $f or die $OS_ERROR; |
1410
|
1
|
|
|
|
|
3
|
binmode $fh; |
1411
|
1
|
|
|
|
|
4
|
for my $content (@content) { |
1412
|
1
|
50
|
|
|
|
2
|
print {$fh} $content or die $OS_ERROR; |
|
1
|
|
|
|
|
9
|
|
1413
|
|
|
|
|
|
|
} |
1414
|
1
|
50
|
|
|
|
27
|
close $fh or die $OS_ERROR; |
1415
|
1
|
|
|
|
|
6
|
return; |
1416
|
|
|
|
|
|
|
} |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
sub _read_file { |
1419
|
14
|
|
|
14
|
|
39
|
my ($self, $f) = @_; |
1420
|
14
|
50
|
|
|
|
866
|
open my $fh, '<', $f or return; |
1421
|
14
|
|
|
|
|
42
|
binmode $fh; |
1422
|
14
|
|
|
|
|
22
|
my $content = do { local $INPUT_RECORD_SEPARATOR = undef; <$fh> }; |
|
14
|
|
|
|
|
73
|
|
|
14
|
|
|
|
|
392
|
|
1423
|
14
|
50
|
|
|
|
402
|
close $fh or return; |
1424
|
14
|
|
|
|
|
126
|
return $content; |
1425
|
|
|
|
|
|
|
} |
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
sub _read_file_die { |
1428
|
1
|
|
|
1
|
|
5
|
my ($self, $f) = @_; |
1429
|
1
|
50
|
|
|
|
68
|
open my $fh, '<', $f or die $OS_ERROR; |
1430
|
0
|
|
|
|
|
0
|
binmode $fh; |
1431
|
0
|
|
|
|
|
0
|
my $content = do { local $INPUT_RECORD_SEPARATOR = undef; <$fh> }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1432
|
0
|
0
|
|
|
|
0
|
close $fh or die $OS_ERROR; |
1433
|
0
|
|
|
|
|
0
|
return $content; |
1434
|
|
|
|
|
|
|
} |
1435
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
sub _read_dir { |
1437
|
71
|
|
|
71
|
|
132
|
my ($self, $f) = @_; |
1438
|
71
|
50
|
|
|
|
3082
|
opendir my $fh, $f or return; |
1439
|
71
|
|
|
|
|
2314
|
my @content = readdir $fh; |
1440
|
71
|
50
|
|
|
|
976
|
closedir $fh or return; |
1441
|
71
|
|
|
|
|
701
|
return @content; |
1442
|
|
|
|
|
|
|
} |
1443
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
sub _read_dir_die { |
1445
|
1
|
|
|
1
|
|
3
|
my ($self, $f) = @_; |
1446
|
1
|
50
|
|
|
|
57
|
opendir my $fh, $f or die $OS_ERROR; |
1447
|
0
|
|
|
|
|
|
my @content = readdir $fh; |
1448
|
0
|
0
|
|
|
|
|
closedir $fh or die $OS_ERROR; |
1449
|
0
|
|
|
|
|
|
return @content; |
1450
|
|
|
|
|
|
|
} |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
1; |
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
__END__ |