File Coverage

blib/lib/Test/File.pm
Criterion Covered Total %
statement 526 635 82.8
branch 216 290 74.4
condition 101 191 52.8
subroutine 67 73 91.7
pod 47 47 100.0
total 957 1236 77.4


line stmt bran cond sub pod time code
1             package Test::File;
2 18     18   3892115 use strict;
  18         40  
  18         911  
3              
4 18     18   106 use Carp qw(carp);
  18         40  
  18         1343  
5 18     18   124 use Exporter qw(import);
  18         28  
  18         616  
6 18     18   87 use File::Spec;
  18         37  
  18         656  
7 18     18   94 use Test::Builder;
  18         29  
  18         593  
8 18     18   117 use XSLoader;
  18         54  
  18         14325  
9              
10             our @EXPORT = qw(
11             file_exists_ok file_not_exists_ok
12             file_empty_ok file_not_empty_ok file_size_ok file_max_size_ok
13             file_min_size_ok file_readable_ok file_not_readable_ok
14             file_writeable_ok file_writable_ok file_not_writeable_ok file_not_writable_ok
15             file_executable_ok file_not_executable_ok
16             file_mode_is file_mode_isnt
17             file_mode_has file_mode_hasnt
18             file_is_symlink_ok file_is_not_symlink_ok
19             symlink_target_exists_ok symlink_target_is
20             symlink_target_dangles_ok
21             dir_exists_ok dir_contains_ok
22             link_count_is_ok link_count_gt_ok link_count_lt_ok
23             owner_is owner_isnt
24             group_is group_isnt
25             file_line_count_is file_line_count_isnt file_line_count_between
26             file_contains_like file_contains_unlike
27             file_contains_utf8_like file_contains_utf8_unlike
28             file_contains_encoded_like file_contains_encoded_unlike
29             file_mtime_gt_ok file_mtime_lt_ok file_mtime_age_ok
30             );
31              
32             our $VERSION = '1.995';
33             XSLoader::load(__PACKAGE__, $VERSION) if $^O eq 'MSWin32';
34              
35             my $Test = Test::Builder->new();
36              
37             =encoding utf8
38              
39             =head1 NAME
40              
41             Test::File -- test file attributes
42              
43             =head1 SYNOPSIS
44              
45             use Test::File;
46              
47             =head1 DESCRIPTION
48              
49             This modules provides a collection of test utilities for file
50             attributes.
51              
52             Some file attributes depend on the owner of the process testing the
53             file in the same way the file test operators do. For instance, root
54             (or super-user or Administrator) may always be able to read files no
55             matter the permissions.
56              
57             Some attributes don't make sense outside of Unix, either, so some
58             tests automatically skip if they think they won't work on the
59             platform. If you have a way to make these functions work on Windows,
60             for instance, please send me a patch. :) If you want to pretend to be
61             Windows on a non-Windows machine (for instance, to test C<skip()>),
62             you can set the C<PRETEND_TO_BE_WINDOWS> environment variable.
63              
64             The optional NAME parameter for every function allows you to specify a
65             name for the test. If not supplied, a reasonable default will be
66             generated.
67              
68             =head2 Functions
69              
70             =over 4
71              
72             =cut
73              
74             sub _is_plain_file {
75 74     74   157 my $filename = _normalize( shift );
76              
77 74         154 my $message = do {
78 74 100       1802 if( ! -e $filename ) { "does not exist" }
  15 50       51  
    50          
79 0         0 elsif( ! -f _ ) { "is not a plain file" }
80 0         0 elsif( -d _ ) { "is a directory" }
81 59         201 else { () }
82             };
83              
84 74 100       222 if( $message ) {
85 15         87 $Test->diag( "file [$filename] $message");
86 15         10731 return 0;
87             }
88              
89 59         189 return 1;
90             }
91              
92             sub _normalize {
93 241     241   50242 my $file = shift;
94 241 100       741 return unless defined $file;
95              
96 237 100       898 return $file =~ m|/|
97             ? File::Spec->catfile( split m|/|, $file )
98             : $file;
99             }
100              
101             sub _win32 {
102 40 100   40   94457 return 0 if $^O eq 'darwin';
103 39 100       175 return $ENV{PRETEND_TO_BE_WIN32} if defined $ENV{PRETEND_TO_BE_WIN32};
104 38   66     306 return $^O =~ m/Win/ || $^O eq 'msys';
105             }
106              
107             # returns true if symlinks can't exist
108 0         0 BEGIN {
109 18     18   24339 my $cannot_symlink;
110              
111             sub _no_symlinks_here {
112 19 100   19   178 return $cannot_symlink if defined $cannot_symlink;
113              
114 1         45 $cannot_symlink = ! do {
115 1         6 eval {
116 1         11 symlink("",""); # symlink exist in perl
117 1         4 _IsSymlinkCreationAllowed() # symlink is ok in current session
118             }
119             };
120             }
121              
122             sub _IsSymlinkCreationAllowed {
123 1 50   1   8 if ($^O eq 'MSWin32') {
124             #
125             # Bare copy of Perl's Win32::IsSymlinkCreationAllowed but with Test::File::Win32 namespace instead of Win32
126             #
127 0         0 my(undef, $major, $minor, $build) = Test::File::Win32::GetOSVersion();
128              
129             # Vista was the first Windows version with symlink support
130 0 0       0 return !!0 if $major < 6;
131              
132             # Since Windows 10 1703, enabling the developer mode allows to create
133             # symlinks regardless of process privileges
134 0 0 0     0 if ($major > 10 || ($major == 10 && ($minor > 0 || $build > 15063))) {
      0        
      0        
135 0 0       0 return !!1 if Test::File::Win32::IsDeveloperModeEnabled();
136             }
137              
138 0         0 my $privs = Test::File::Win32::GetProcessPrivileges();
139              
140 0 0       0 return !!0 unless $privs;
141              
142             # It doesn't matter if the permission is enabled or not, it just has to
143             # exist. CreateSymbolicLink() will automatically enable it when needed.
144 0         0 return exists $privs->{SeCreateSymbolicLinkPrivilege};
145             }
146              
147 1         8 1;
148             }
149              
150             =item has_symlinks
151              
152             Returns true is this module thinks that the current system supports
153             symlinks.
154              
155             This is not a test function. It's something that tests can use to
156             determine what it should expect or skip.
157              
158             =cut
159              
160 1     1 1 256714 sub has_symlinks { ! _no_symlinks_here() }
161             }
162              
163             # owner_is and owner_isn't should skip on OS where the question makes no
164             # sense. I really don't know a good way to test for that, so I'm going
165             # to skip on the two OS's that I KNOW aren't multi-user. I'd love to add
166             # more if anyone knows of any
167             # Note: I don't have a dos or mac os < 10 machine to test this on
168             sub _obviously_non_multi_user {
169 31 100   31   307107 foreach my $os ( qw(dos MacOS) ) { return 1 if $^O eq $os }
  57         223  
170              
171 25 100       111 return 0 if $^O eq 'MSWin32';
172              
173 20         95 eval { my $holder = getpwuid(0) };
  20         1311  
174 20 100       115 return 1 if $@;
175              
176 19         36 eval { my $holder = getgrgid(0) };
  19         786  
177 19 100       104 return 1 if $@;
178              
179 18         60 return 0;
180             }
181              
182             =item file_exists_ok( FILENAME [, NAME ] )
183              
184             Ok if the file exists, and not ok otherwise.
185              
186             =cut
187              
188             sub file_exists_ok {
189 11     11 1 522130 my $filename = _normalize( shift );
190 11   66     78 my $name = shift || "$filename exists";
191              
192 11         438 my $ok = -e $filename;
193              
194 11 100       45 if( $ok ) {
195 10         65 $Test->ok(1, $name);
196             }
197             else {
198 1         11 $Test->diag("file [$filename] does not exist");
199 1         749 $Test->ok(0, $name);
200             }
201             }
202              
203             =item file_not_exists_ok( FILENAME [, NAME ] )
204              
205             Ok if the file does not exist, and not okay if it does exist.
206              
207             =cut
208              
209             sub file_not_exists_ok {
210 5     5 1 29887 my $filename = _normalize( shift );
211 5   66     57 my $name = shift || "$filename does not exist";
212              
213 5         211 my $ok = not -e $filename;
214              
215 5 100       25 if( $ok ) {
216 4         22 $Test->ok(1, $name);
217             }
218             else {
219 1         9 $Test->diag("file [$filename] exists");
220 1         727 $Test->ok(0, $name);
221             }
222             }
223              
224             =item file_empty_ok( FILENAME [, NAME ] )
225              
226             Ok if the file exists and has empty size, not ok if the file does not
227             exist or exists with non-zero size.
228              
229             Previously this tried to test any sort of file. Sometime in the future
230             this will fail if the argument is not a plain file or is a directory.
231              
232             =cut
233              
234             sub file_empty_ok {
235 4     4 1 212746 my $filename = _normalize( shift );
236 4   66     21 my $name = shift || "$filename is empty";
237              
238 4 100       13 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
239              
240 3         37 my $ok = -z $filename;
241              
242 3 100       9 if( $ok ) {
243 2         8 $Test->ok(1, $name);
244             }
245             else {
246 1         6 $Test->diag( "file [$filename] exists with non-zero size" );
247 1         535 $Test->ok(0, $name);
248             }
249             }
250              
251             =item file_not_empty_ok( FILENAME [, NAME ] )
252              
253             Ok if the file exists and has non-zero size, not ok if the file does
254             not exist or exists with zero size.
255              
256             Previously this tried to test any sort of file. Sometime in the future
257             this will fail if the argument is not a plain file or is a directory.
258              
259             =cut
260              
261             sub file_not_empty_ok {
262 7     7 1 72062 my $filename = _normalize( shift );
263 7   66     42 my $name = shift || "$filename is not empty";
264              
265 7 100       23 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
266              
267 5         11 my $ok = not -z _;
268              
269 5 100       14 if( $ok ) {
270 3         15 $Test->ok(1, $name);
271             }
272             else {
273 2         14 $Test->diag( "file [$filename] exists with zero size" );
274 2         1329 $Test->ok(0, $name);
275             }
276             }
277              
278             =item file_size_ok( FILENAME, SIZE [, NAME ] )
279              
280             Ok if the file exists and has SIZE size in bytes (exactly), not ok if
281             the file does not exist or exists with size other than SIZE.
282              
283             Previously this tried to test any sort of file. Sometime in the future
284             this will fail if the argument is not a plain file or is a directory.
285              
286             =cut
287              
288             sub file_size_ok {
289 4     4 1 17220 my $filename = _normalize( shift );
290 4         10 my $expected = int shift;
291 4   66     22 my $name = shift || "$filename has right size";
292              
293 4 100       11 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
294              
295 3         22 my $ok = ( -s $filename ) == $expected;
296              
297 3 100       7 if( $ok ) {
298 2         7 $Test->ok(1, $name);
299             }
300             else {
301 1         7 my $actual = -s $filename;
302 1         7 $Test->diag(
303             "file [$filename] has actual size [$actual] not [$expected]" );
304              
305 1         617 $Test->ok(0, $name);
306             }
307             }
308              
309             =item file_max_size_ok( FILENAME, MAX [, NAME ] )
310              
311             Ok if the file exists and has size less than or equal to MAX bytes, not
312             ok if the file does not exist or exists with size greater than MAX
313             bytes.
314              
315             Previously this tried to test any sort of file. Sometime in the future
316             this will fail if the argument is not a plain file or is a directory.
317              
318             =cut
319              
320             sub file_max_size_ok {
321 4     4 1 28416 my $filename = _normalize( shift );
322 4         7 my $max = int shift;
323 4   66     26 my $name = shift || "$filename is under $max bytes";
324              
325 4 100       11 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
326              
327 3         22 my $ok = ( -s $filename ) <= $max;
328              
329 3 100       8 if( $ok ) {
330 2         8 $Test->ok(1, $name);
331             }
332             else {
333 1         6 my $actual = -s $filename;
334 1         7 $Test->diag(
335             "file [$filename] has actual size [$actual] " .
336             "greater than [$max]"
337             );
338              
339 1         885 $Test->ok(0, $name);
340             }
341             }
342              
343             =item file_min_size_ok( FILENAME, MIN [, NAME ] )
344              
345             Ok if the file exists and has size greater than or equal to MIN bytes,
346             not ok if the file does not exist or exists with size less than MIN
347             bytes.
348              
349             Previously this tried to test any sort of file. Sometime in the future
350             this will fail if the argument is not a plain file or is a directory.
351              
352             =cut
353              
354             sub file_min_size_ok {
355 4     4 1 12920 my $filename = _normalize( shift );
356 4         10 my $min = int shift;
357 4   66     21 my $name = shift || "$filename is over $min bytes";
358              
359 4 100       11 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
360              
361 3         22 my $ok = ( -s $filename ) >= $min;
362              
363 3 100       9 if( $ok ) {
364 2         8 $Test->ok(1, $name);
365             }
366             else {
367 1         6 my $actual = -s $filename;
368 1         7 $Test->diag(
369             "file [$filename] has actual size ".
370             "[$actual] less than [$min]"
371             );
372              
373 1         563 $Test->ok(0, $name);
374             }
375             }
376              
377             =item file_line_count_is( FILENAME, COUNT [, NAME ] )
378              
379             Ok if the file exists and has COUNT lines (exactly), not ok if the
380             file does not exist or exists with a line count other than COUNT.
381              
382             This function uses the current value of C<$/> as the line ending and
383             counts the lines by reading them and counting how many it read.
384              
385             Previously this tried to test any sort of file. Sometime in the future
386             this will fail if the argument is not a plain file or is a directory.
387              
388             =cut
389              
390             sub _ENOFILE () { -1 }
391             sub _ECANTOPEN () { -2 }
392             sub _ENOTPLAIN () { -3 }
393              
394             sub _file_line_counter {
395 9     9   19 my $filename = shift;
396              
397 9 50       108 return _ENOFILE unless -e $filename;
398 9 50       79 return _ENOTPLAIN unless -f $filename;
399 9 50       491 return _ECANTOPEN unless open my( $fh ), "<", $filename;
400              
401 9         26 my $count = 0;
402 9         241 while( <$fh> ) { $count++ }
  18         85  
403              
404 9         219 return $count;
405             }
406              
407             # XXX: lots of cut and pasting here, needs refactoring
408             # looks like the refactoring might be worse than this though
409             sub file_line_count_is {
410 4     4 1 33427 my $filename = _normalize( shift );
411 4         10 my $expected = shift;
412 4         9 my $name = do {
413 18     18   158 no warnings 'uninitialized';
  18         35  
  18         2394  
414 4 50       28 shift || "$filename line count is $expected lines";
415             };
416              
417 4 100       15 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
418              
419 3 100 66     18 unless( defined $expected && int( $expected ) == $expected ) {
420 18     18   130 no warnings 'uninitialized';
  18         48  
  18         5860  
421 1         8 $Test->diag( "file_line_count_is expects a positive whole number for " .
422             "the second argument. Got [$expected]" );
423 1         703 return $Test->ok( 0, $name );
424             }
425              
426 2         10 my $got = _file_line_counter( $filename );
427              
428 2 50       23 if( $got eq _ENOFILE ) {
    50          
    50          
    100          
429 0         0 $Test->diag( "file [$filename] does not exist" );
430 0         0 $Test->ok( 0, $name );
431             }
432             elsif( $got eq _ENOTPLAIN ) {
433 0         0 $Test->diag( "file [$filename] is not a plain file" );
434 0         0 $Test->ok( 0, $name );
435             }
436             elsif( $got == _ECANTOPEN ) {
437 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
438 0         0 $Test->ok( 0, $name );
439             }
440             elsif( $got == $expected ) {
441 1         22 $Test->ok( 1, $name );
442             }
443             else {
444 1         12 $Test->diag( "expected [$expected] lines in [$filename], " .
445             "got [$got] lines" );
446 1         872 $Test->ok( 0, $name );
447             }
448              
449             }
450              
451             =item file_line_count_isnt( FILENAME, COUNT [, NAME ] )
452              
453             Ok if the file exists and doesn't have exactly COUNT lines, not ok if
454             the file does not exist or exists with a line count of COUNT. Read
455             that carefully: the file must exist for this test to pass!
456              
457             This function uses the current value of C<$/> as the line ending and
458             counts the lines by reading them and counting how many it read.
459              
460             Previously this tried to test any sort of file. Sometime in the future
461             this will fail if the argument is not a plain file or is a directory.
462              
463             =cut
464              
465             sub file_line_count_isnt {
466 5     5 1 32866 my $filename = _normalize( shift );
467 5         11 my $expected = shift;
468 5         11 my $name = do {
469 18     18   136 no warnings 'uninitialized';
  18         45  
  18         2083  
470 5 50       38 shift || "$filename line count is not $expected lines";
471             };
472              
473 5 100       18 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
474              
475 4 100 66     26 unless( defined $expected && int( $expected ) == $expected ) {
476 18     18   126 no warnings 'uninitialized';
  18         33  
  18         6644  
477 1         8 $Test->diag( "file_line_count_is expects a positive whole number for " .
478             "the second argument. Got [$expected]" );
479 1         817 return $Test->ok( 0, $name );
480             }
481              
482 3         11 my $got = _file_line_counter( $filename );
483              
484 3 50       24 if( $got eq _ENOFILE ) {
    50          
    50          
    100          
485 0         0 $Test->diag( "file [$filename] does not exist" );
486 0         0 $Test->ok( 0, $name );
487             }
488             elsif( $got eq _ENOTPLAIN ) {
489 0         0 $Test->diag( "file [$filename] is not a plain file" );
490 0         0 $Test->ok( 0, $name );
491             }
492             elsif( $got == _ECANTOPEN ) {
493 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
494 0         0 $Test->ok( 0, $name );
495             }
496             elsif( $got != $expected ) {
497 2         29 $Test->ok( 1, $name );
498             }
499             else {
500 1         10 $Test->diag( "expected something other than [$expected] lines in [$filename], " .
501             "but got [$got] lines" );
502 1         883 $Test->ok( 0, $name );
503             }
504              
505             }
506              
507             =item file_line_count_between( FILENAME, MIN, MAX, [, NAME ] )
508              
509             Ok if the file exists and has a line count between MIN and MAX,
510             inclusively.
511              
512             This function uses the current value of C<$/> as the line ending and
513             counts the lines by reading them and counting how many it read.
514              
515             Previously this tried to test any sort of file. Sometime in the future
516             this will fail if the argument is not a plain file or is a directory.
517              
518             =cut
519              
520             sub file_line_count_between {
521 8     8 1 46518 my $filename = _normalize( shift );
522 8         17 my $min = shift;
523 8         22 my $max = shift;
524              
525 8         13 my $name = do {
526 18     18   155 no warnings 'uninitialized';
  18         126  
  18         2638  
527 8 50       57 shift || "$filename line count is between [$min] and [$max] lines";
528             };
529 8 100       26 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
530              
531 5         19 foreach my $ref ( \$min, \$max ) {
532 9 100 66     55 unless( defined $$ref && int( $$ref ) == $$ref ) {
533 18     18   131 no warnings 'uninitialized';
  18         48  
  18         90205  
534 1         10 $Test->diag( "file_line_count_between expects positive whole numbers for " .
535             "the second and third arguments. Got [$min] and [$max]" );
536 1         824 return $Test->ok( 0, $name );
537             }
538             }
539              
540 4         17 my $got = _file_line_counter( $filename );
541              
542 4 50 66     42 if( $got eq _ENOFILE ) {
    50          
    50          
    100          
543 0         0 $Test->diag( "file [$filename] does not exist" );
544 0         0 $Test->ok( 0, $name );
545             }
546             elsif( $got eq _ENOTPLAIN ) {
547 0         0 $Test->diag( "file [$filename] is not a plain file" );
548 0         0 $Test->ok( 0, $name );
549             }
550             elsif( $got == _ECANTOPEN ) {
551 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
552 0         0 $Test->ok( 0, $name );
553             }
554             elsif( $min <= $got and $got <= $max ) {
555 3         17 $Test->ok( 1, $name );
556             }
557             else {
558 1         10 $Test->diag( "expected a line count between [$min] and [$max] " .
559             "in [$filename], but got [$got] lines"
560             );
561 1         858 $Test->ok( 0, $name );
562             }
563             }
564              
565             =item file_contains_like ( FILENAME, PATTERN [, NAME ] )
566              
567             Ok if the file exists and its contents (as one big string) match
568             PATTERN, not ok if the file does not exist, is not readable, or exists
569             but doesn't match PATTERN.
570              
571             Since the file contents are read into memory, you should not use this
572             for large files. Besides memory consumption, test diagnostics for
573             failing tests might be difficult to decipher. However, for short
574             files this works very well.
575              
576             Because the entire contents are treated as one large string, you can
577             make a pattern that tests multiple lines. Don't forget that you may
578             need to use the /s modifier for such patterns:
579              
580             # make sure file has one or more paragraphs with CSS class X
581             file_contains_like($html_file, qr{<p class="X">.*?</p>}s);
582              
583             Contrariwise, if you need to match at the beginning or end of a line
584             inside the file, use the /m modifier:
585              
586             # make sure file has a setting for foo
587             file_contains_like($config_file, qr/^ foo \s* = \s* \w+ $/mx);
588              
589             If you want to test your file contents against multiple patterns, but
590             don't want to have the file read in repeatedly, you can pass an
591             arrayref of patterns instead of a single pattern, like so:
592              
593             # make sure our template has rendered correctly
594             file_contains_like($template_out,
595             [
596             qr/^ $title_line $/mx,
597             map { qr/^ $_ $/mx } @chapter_headings,
598             qr/^ $footer_line $/mx,
599             ]);
600              
601             Please note that if you do this, and your file does not exist or is
602             not readable, you'll only get one test failure instead of a failure
603             for each pattern. This could cause your test plan to be off, although
604             you may not care at that point because your test failed anyway. If
605             you do care, either skip the test plan altogether by employing
606             L<Test::More>'s C<done_testing()> function, or use
607             L</file_readable_ok> in conjunction with a C<SKIP> block.
608              
609             Contributed by Buddy Burden C<< <barefoot@cpan.org> >>.
610              
611             =item file_contains_unlike ( FILENAME, PATTERN [, NAME ] )
612              
613             Ok if the file exists and its contents (as one big string) do B<not>
614             match PATTERN, not ok if the file does not exist, is not readable, or
615             exists but matches PATTERN.
616              
617             All notes and caveats for L</file_contains_like> apply to this
618             function as well.
619              
620             Contributed by Buddy Burden C<< <barefoot@cpan.org> >>.
621              
622             =item file_contains_utf8_like ( FILENAME, PATTERN [, NAME ] )
623              
624             The same as C<file_contains_like>, except the file is opened as UTF-8.
625              
626             =item file_contains_utf8_unlike ( FILENAME, PATTERN [, NAME ] )
627              
628             The same as C<file_contains_unlike>, except the file is opened as UTF-8.
629              
630             =item file_contains_encoded_like ( FILENAME, ENCODING, PATTERN [, NAME ] )
631              
632             The same as C<file_contains_like>, except the file is opened with ENCODING
633              
634             =item file_contains_encoded_unlike ( FILENAME, ENCODING, PATTERN [, NAME ] )
635              
636             The same as C<file_contains_unlike>, except the file is opened with ENCODING.
637              
638             =cut
639              
640             sub file_contains_like {
641 7     7 1 331386 local $Test::Builder::Level = $Test::Builder::Level + 1;
642 7         26 _file_contains(like => "contains", undef, @_);
643             }
644              
645             sub file_contains_unlike {
646 7     7 1 38402 local $Test::Builder::Level = $Test::Builder::Level + 1;
647 7         27 _file_contains(unlike => "doesn't contain", undef, @_);
648             }
649              
650             sub file_contains_utf8_like {
651 5     5 1 297636 local $Test::Builder::Level = $Test::Builder::Level + 1;
652 5         19 _file_contains(like => "contains", 'UTF-8', @_);
653             }
654              
655             sub file_contains_utf8_unlike {
656 5     5 1 26715 local $Test::Builder::Level = $Test::Builder::Level + 1;
657 5         15 _file_contains(unlike => "doesn't contain", 'UTF-8', @_);
658             }
659              
660             sub file_contains_encoded_like {
661 5     5 1 242286 local $Test::Builder::Level = $Test::Builder::Level + 1;
662 5         10 my $filename = shift;
663 5         11 my $encoding = shift;
664 5         20 _file_contains(like => "contains", $encoding, $filename, @_);
665             }
666              
667             sub file_contains_encoded_unlike {
668 5     5 1 18573 local $Test::Builder::Level = $Test::Builder::Level + 1;
669 5         11 my $filename = shift;
670 5         10 my $encoding = shift;
671 5         18 _file_contains(unlike => "doesn't contain", $encoding, $filename, @_);
672             }
673              
674             sub _file_contains {
675 34     34   66 my $method = shift;
676 34         58 my $verb = shift;
677 34         70 my $encoding = shift;
678 34         100 my $filename = _normalize( shift );
679 34         67 my $patterns = shift;
680 34         59 my $name = shift;
681              
682 34         63 my (@patterns, %patterns);
683 34 100       106 if (ref $patterns eq 'ARRAY') {
684 20         62 @patterns = @$patterns;
685 20   66     53 %patterns = map { $_ => $name || "$filename $verb $_" } @patterns;
  40         265  
686             }
687             else {
688 14         29 @patterns = ($patterns);
689 14   33     125 %patterns = ( $patterns => $name || "$filename $verb $patterns" );
690             }
691              
692             # for purpose of checking the file's existence, just use the first
693             # test name as the name
694 34         116 $name = $patterns{$patterns[0]};
695              
696 34 100       77 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
697              
698 30 50       364 unless( -r $filename ) {
699 0         0 $Test->diag( "file [$filename] is not readable" );
700 0         0 return $Test->ok(0, $name);
701             }
702              
703             # do the slurp
704 30         58 my $file_contents;
705             {
706 30 50       43 unless (open(FH, $filename)) {
  30         1038  
707 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
708 0         0 return $Test->ok( 0, $name );
709             }
710              
711 30 100       108 if (defined $encoding) {
712 20         261 binmode FH, ":encoding($encoding)";
713             }
714              
715 30         1067 local $/ = undef;
716 30         1104 $file_contents = <FH>;
717 30         631 close FH;
718             }
719              
720 30         88 foreach my $p (@patterns) {
721 48         12246 $Test->$method($file_contents, $p, $patterns{$p});
722             }
723             }
724              
725             =item file_readable_ok( FILENAME [, NAME ] )
726              
727             Ok if the file exists and is readable, not ok if the file does not
728             exist or is not readable.
729              
730             =cut
731              
732             sub file_readable_ok {
733 2     2 1 10484 my $filename = _normalize( shift );
734 2   66     12 my $name = shift || "$filename is readable";
735              
736 2         52 my $ok = -r $filename;
737              
738 2 50       8 if( $ok ) {
739 2         12 $Test->ok(1, $name);
740             }
741             else {
742 0         0 $Test->diag( "file [$filename] is not readable" );
743 0         0 $Test->ok(0, $name);
744             }
745             }
746              
747             =item file_not_readable_ok( FILENAME [, NAME ] )
748              
749             Ok if the file exists and is not readable, not ok if the file does not
750             exist or is readable.
751              
752             =cut
753              
754             sub file_not_readable_ok {
755 0     0 1 0 my $filename = _normalize( shift );
756 0   0     0 my $name = shift || "$filename is not readable";
757              
758 0         0 my $ok = not -r $filename;
759              
760 0 0       0 if( $ok ) {
761 0         0 $Test->ok(1, $name);
762             }
763             else {
764 0         0 $Test->diag( "file [$filename] is readable" );
765 0         0 $Test->ok(0, $name);
766             }
767             }
768              
769             =item file_writable_ok( FILENAME [, NAME ] )
770              
771             =item file_writeable_ok( FILENAME [, NAME ] )
772              
773             Ok if the file exists and is writable, not ok if the file does not
774             exist or is not writable.
775              
776             The original name is C<file_writeable_ok> with that extra I<e>. That
777             still works but there's a function with the correct spelling too.
778              
779             =cut
780              
781             sub file_writeable_ok {
782 0     0 1 0 carp "file_writeable_ok is now file_writable_ok";
783              
784 0         0 &file_writable_ok;
785             }
786              
787             sub file_writable_ok {
788 3     3 1 21181 my $filename = _normalize( shift );
789 3   66     16 my $name = shift || "$filename is writable";
790              
791 3         105 my $ok = -w $filename;
792              
793 3 50       13 if( $ok ) {
794 3         14 $Test->ok(1, $name);
795             }
796             else {
797 0         0 $Test->diag( "file [$filename] is not writable" );
798 0         0 $Test->ok(0, $name);
799             }
800             }
801              
802             =item file_not_writeable_ok( FILENAME [, NAME ] )
803              
804             =item file_not_writable_ok( FILENAME [, NAME ] )
805              
806             Ok if the file exists and is not writable, not ok if the file does not
807             exist or is writable.
808              
809             The original name is C<file_not_writeable_ok> with that extra I<e>.
810             That still works but there's a function with the correct spelling too.
811              
812             =cut
813              
814             sub file_not_writeable_ok {
815 0     0 1 0 carp "file_not_writeable_ok is now file_not_writable_ok";
816              
817 0         0 &file_not_writable_ok;
818             }
819              
820             sub file_not_writable_ok {
821 0     0 1 0 my $filename = _normalize( shift );
822 0   0     0 my $name = shift || "$filename is not writable";
823              
824 0         0 my $ok = not -w $filename;
825              
826 0 0       0 if( $ok ) {
827 0         0 $Test->ok(1, $name);
828             }
829             else {
830 0         0 $Test->diag("file [$filename] is writable");
831 0         0 $Test->ok(0, $name);
832             }
833             }
834              
835             =item file_executable_ok( FILENAME [, NAME ] )
836              
837             Ok if the file exists and is executable, not ok if the file does not
838             exist or is not executable.
839              
840             This test automatically skips if it thinks it is on a Windows
841             platform.
842              
843             =cut
844              
845             sub file_executable_ok {
846 4 100   4 1 6807 if( _win32() ) {
847 1         5 $Test->skip( "file_executable_ok doesn't work on Windows" );
848 1         659 return;
849             }
850              
851 3         11 my $filename = _normalize( shift );
852 3   66     16 my $name = shift || "$filename is executable";
853              
854 3         78 my $ok = -x $filename;
855              
856 3 100       12 if( $ok ) {
857 2         12 $Test->ok(1, $name);
858             }
859             else {
860 1         8 $Test->diag("file [$filename] is not executable");
861 1         708 $Test->ok(0, $name);
862             }
863             }
864              
865             =item file_not_executable_ok( FILENAME [, NAME ] )
866              
867             Ok if the file exists and is not executable, not ok if the file does
868             not exist or is executable.
869              
870             This test automatically skips if it thinks it is on a Windows
871             platform.
872              
873             =cut
874              
875             sub file_not_executable_ok {
876 4 100   4 1 6987 if( _win32() ) {
877 1         7 $Test->skip( "file_not_executable_ok doesn't work on Windows" );
878 1         680 return;
879             }
880              
881 3         9 my $filename = _normalize( shift );
882 3   66     33 my $name = shift || "$filename is not executable";
883              
884 3         76 my $ok = not -x $filename;
885              
886 3 100       11 if( $ok ) {
887 2         12 $Test->ok(1, $name);
888             }
889             else {
890 1         8 $Test->diag("file [$filename] is executable");
891 1         714 $Test->ok(0, $name);
892             }
893             }
894              
895             =item file_mode_is( FILENAME, MODE [, NAME ] )
896              
897             Ok if the file exists and the mode matches, not ok if the file does
898             not exist or the mode does not match.
899              
900             This test automatically skips if it thinks it is on a Windows
901             platform.
902              
903             Contributed by Shawn Sorichetti C<< <ssoriche@coloredblocks.net> >>
904              
905             =cut
906              
907             sub file_mode_is {
908 6 100   6 1 12027 if( _win32() ) {
909 1         8 $Test->skip( "file_mode_is doesn't work on Windows" );
910 1         672 return;
911             }
912              
913 5         16 my $filename = _normalize( shift );
914 5         9 my $mode = shift;
915              
916 5   66     36 my $name = shift || sprintf("%s mode is %04o", $filename, $mode);
917              
918 5   66     204 my $ok = -e $filename && ((stat($filename))[2] & 07777) == $mode;
919              
920 5 100       20 if( $ok ) {
921 4         20 $Test->ok(1, $name);
922             }
923             else {
924 1         11 $Test->diag(sprintf("file [%s] mode is not %04o", $filename, $mode) );
925 1         684 $Test->ok(0, $name);
926             }
927             }
928              
929             =item file_mode_isnt( FILENAME, MODE [, NAME ] )
930              
931             Ok if the file exists and mode does not match, not ok if the file does
932             not exist or mode does match.
933              
934             This test automatically skips if it thinks it is on a Windows
935             platform.
936              
937             Contributed by Shawn Sorichetti C<< <ssoriche@coloredblocks.net> >>
938              
939             =cut
940              
941             sub file_mode_isnt {
942 6 100   6 1 11112 if( _win32() ) {
943 1         6 $Test->skip( "file_mode_isnt doesn't work on Windows" );
944 1         495 return;
945             }
946              
947 5         14 my $filename = _normalize( shift );
948 5         12 my $mode = shift;
949              
950 5   66     35 my $name = shift || sprintf("%s mode is not %04o",$filename,$mode);
951              
952 5   66     173 my $ok = not (-e $filename && ((stat($filename))[2] & 07777) == $mode);
953              
954 5 100       19 if( $ok ) {
955 4         20 $Test->ok(1, $name);
956             }
957             else {
958 1         10 $Test->diag(sprintf("file [%s] mode is %04o",$filename,$mode));
959 1         682 $Test->ok(0, $name);
960             }
961             }
962              
963             =item file_mode_has( FILENAME, MODE [, NAME ] )
964              
965             Ok if the file exists and has all the bits in mode turned on, not ok
966             if the file does not exist or the mode does not match. That is, C<<
967             FILEMODE & MODE == MODE >> must be true.
968              
969             This test automatically skips if it thinks it is on a Windows
970             platform.
971              
972             Contributed by Ricardo Signes C<< <rjbs@cpan.org> >>
973              
974             =cut
975              
976             sub file_mode_has {
977 4 50   4 1 5194 if( _win32() ) {
978 0         0 $Test->skip( "file_mode_has doesn't work on Windows" );
979 0         0 return;
980             }
981              
982 4         13 my $filename = _normalize( shift );
983 4         8 my $mode = shift;
984              
985 4   66     26 my $name = shift || sprintf("%s mode has all bits of %04o", $filename, $mode);
986              
987 4         94 my $present = -e $filename;
988 4 50       42 my $gotmode = $present ? (stat($filename))[2] : undef;
989 4   66     23 my $ok = $present && ($gotmode & $mode) == $mode;
990              
991 4 100       10 if( $ok ) {
992 2         10 $Test->ok(1, $name);
993             }
994             else {
995 2         7 my $missing = ($gotmode ^ $mode) & $mode;
996 2         43 $Test->diag(sprintf("file [%s] mode is missing component %04o", $filename, $missing) );
997 2         1436 $Test->ok(0, $name);
998             }
999             }
1000              
1001             =item file_mode_hasnt( FILENAME, MODE [, NAME ] )
1002              
1003             Ok if the file exists and has all the bits in mode turned off, not ok
1004             if the file does not exist or the mode does not match. That is,
1005             C<< FILEMODE & MODE == 0 >> must be true.
1006              
1007             This test automatically skips if it thinks it is on a
1008             Windows platform.
1009              
1010             Contributed by Ricardo Signes C<< <rjbs@cpan.org> >>
1011              
1012             =cut
1013              
1014             sub file_mode_hasnt {
1015 3 50   3 1 3492 if( _win32() ) {
1016 0         0 $Test->skip( "file_mode_hasnt doesn't work on Windows" );
1017 0         0 return;
1018             }
1019              
1020 3         11 my $filename = _normalize( shift );
1021 3         6 my $mode = shift;
1022              
1023 3   66     20 my $name = shift || sprintf("%s mode has no bits of %04o", $filename, $mode);
1024              
1025 3         67 my $present = -e $filename;
1026 3 50       82 my $gotmode = $present ? (stat($filename))[2] : undef;
1027 3   66     17 my $ok = $present && ($gotmode & $mode) == 0;
1028              
1029 3 100       8 if( $ok ) {
1030 2         10 $Test->ok(1, $name);
1031             }
1032             else {
1033 1         4 my $bad = $gotmode & $mode;
1034 1         8 $Test->diag(sprintf("file [%s] mode has forbidden component %04o", $filename, $bad) );
1035 1         673 $Test->ok(0, $name);
1036             }
1037             }
1038              
1039             =item file_is_symlink_ok( FILENAME [, NAME ] )
1040              
1041             Ok if FILENAME is a symlink, even if it points to a non-existent
1042             file. This test automatically skips if the operating system does
1043             not support symlinks.
1044              
1045             =cut
1046              
1047             sub file_is_symlink_ok {
1048 7 100   7 1 68246 if( _no_symlinks_here() ) {
1049 1         11 $Test->skip(
1050             "file_is_symlink_ok doesn't work on systems without symlinks" );
1051 1         956 return;
1052             }
1053              
1054 6         18 my $file = shift;
1055 6   66     39 my $name = shift || "$file is a symlink";
1056              
1057 6 100       192 if( -l $file ) {
1058 4         24 $Test->ok(1, $name)
1059             }
1060             else {
1061 2         16 $Test->diag( "file [$file] is not a symlink" );
1062 2         1292 $Test->ok(0, $name);
1063             }
1064             }
1065              
1066             =item file_is_not_symlink_ok( FILENAME [, NAME ] )
1067              
1068             Ok if FILENAME is a not symlink. This test automatically skips if the
1069             operating system does not support symlinks. If the file does not
1070             exist, the test fails.
1071              
1072             =cut
1073              
1074             sub file_is_not_symlink_ok {
1075 0 0   0 1 0 if( _no_symlinks_here() ) {
1076 0         0 $Test->skip(
1077             "file_is_symlink_ok doesn't work on systems without symlinks" );
1078 0         0 return;
1079             }
1080              
1081 0         0 my $file = shift;
1082 0   0     0 my $name = shift || "$file is not a symlink";
1083              
1084 0 0       0 unless( -e $file ) {
1085 0         0 $Test->diag( "file [$file] does not exist" );
1086 0         0 return $Test->ok(0, $name);
1087             }
1088              
1089 0 0       0 if( ! -l $file ) {
1090 0         0 $Test->ok(1, $name)
1091             }
1092             else {
1093 0         0 $Test->diag( "file [$file] is a symlink" );
1094 0         0 $Test->ok(0, $name);
1095             }
1096             }
1097              
1098             =item symlink_target_exists_ok( SYMLINK [, TARGET] [, NAME ] )
1099              
1100             Ok if FILENAME is a symlink and it points to a existing file. With the
1101             optional TARGET argument, the test fails if SYMLINK's target is not
1102             TARGET. This test automatically skips if the operating system does not
1103             support symlinks. If the file does not exist, the test fails.
1104              
1105             =cut
1106              
1107             sub symlink_target_exists_ok {
1108 6 100   6 1 32608 if( _no_symlinks_here() ) {
1109 1         10 $Test->skip(
1110             "symlink_target_exists_ok doesn't work on systems without symlinks"
1111             );
1112 1         892 return;
1113             }
1114              
1115 5         13 my $file = shift;
1116 5   66     43 my $dest = shift || readlink( $file );
1117 5   66     33 my $name = shift || "$file is a symlink";
1118              
1119 5 100       195 unless( -l $file )
1120             {
1121 1         8 $Test->diag( "file [$file] is not a symlink" );
1122 1         516 return $Test->ok( 0, $name );
1123             }
1124              
1125 4 100       58 unless( -e $dest ) {
1126 1         8 $Test->diag( "symlink [$file] points to non-existent target [$dest]" );
1127 1         558 return $Test->ok( 0, $name );
1128             }
1129              
1130 3         36 my $actual = readlink( $file );
1131 3 100       16 unless( $dest eq $actual ) {
1132 1         9 $Test->diag(
1133             "symlink [$file] points to\n" .
1134             " got: $actual\n" .
1135             " expected: $dest\n"
1136             );
1137 1         689 return $Test->ok( 0, $name );
1138             }
1139              
1140 2         13 $Test->ok( 1, $name );
1141             }
1142              
1143             =item symlink_target_dangles_ok( SYMLINK [, NAME ] )
1144              
1145             Ok if FILENAME is a symlink and if it doesn't point to a existing
1146             file. This test automatically skips if the operating system does not
1147             support symlinks. If the file does not exist, the test fails.
1148              
1149             =cut
1150              
1151             sub symlink_target_dangles_ok
1152             {
1153 5 100   5 1 34912 if( _no_symlinks_here() ) {
1154 1         13 $Test->skip(
1155             "symlink_target_dangles_ok doesn't work on systems without symlinks" );
1156 1         1041 return;
1157             }
1158              
1159 4         11 my $file = shift;
1160 4         94 my $dest = readlink( $file );
1161 4   66     26 my $name = shift || "$file is a symlink";
1162              
1163 4 100       118 unless( -l $file ) {
1164 1         7 $Test->diag( "file [$file] is not a symlink" );
1165 1         473 return $Test->ok( 0, $name );
1166             }
1167              
1168 3 100       34 if( -e $dest ) {
1169 2         12 $Test->diag(
1170             "symlink [$file] points to existing file [$dest] but shouldn't" );
1171 2         1016 return $Test->ok( 0, $name );
1172             }
1173              
1174 1         8 $Test->ok( 1, $name );
1175             }
1176              
1177             =item symlink_target_is( SYMLINK, TARGET [, NAME ] )
1178              
1179             Ok if FILENAME is a symlink and if points to TARGET. This test
1180             automatically skips if the operating system does not support symlinks.
1181             If the file does not exist, the test fails.
1182              
1183             =cut
1184              
1185             sub symlink_target_is {
1186 4 100   4 1 13653 if( _no_symlinks_here() ) {
1187 1         15 $Test->skip(
1188             "symlink_target_is doesn't work on systems without symlinks" );
1189 1         944 return;
1190             }
1191              
1192 3         8 my $file = shift;
1193 3         7 my $dest = shift;
1194 3   66     14 my $name = shift || "symlink $file points to $dest";
1195              
1196 3 100       122 unless( -l $file ) {
1197 1         7 $Test->diag( "file [$file] is not a symlink" );
1198 1         633 return $Test->ok( 0, $name );
1199             }
1200              
1201 2         21 my $actual_dest = readlink( $file );
1202 2         12 my $link_error = $!;
1203              
1204 2 50       9 unless( defined $actual_dest ) {
1205 0         0 $Test->diag( "symlink [$file] does not have a defined target" );
1206 0 0       0 $Test->diag( "readlink error: $link_error" ) if defined $link_error;
1207 0         0 return $Test->ok( 0, $name );
1208             }
1209              
1210 2 100       32 if( $dest eq $actual_dest ) {
1211 1         6 $Test->ok( 1, $name );
1212             }
1213             else {
1214 1         11 $Test->ok( 0, $name );
1215 1         1287 $Test->diag(" got: $actual_dest" );
1216 1         397 $Test->diag(" expected: $dest" );
1217             }
1218             }
1219              
1220             =item symlink_target_is_absolute_ok( SYMLINK [, NAME ] )
1221              
1222             Ok if FILENAME is a symlink and if its target is an absolute path.
1223             This test automatically skips if the operating system does not support
1224             symlinks. If the file does not exist, the test fails.
1225              
1226             =cut
1227              
1228             sub symlink_target_is_absolute_ok {
1229 0 0   0 1 0 if( _no_symlinks_here() ) {
1230 0         0 $Test->skip(
1231             "symlink_target_exists_ok doesn't work on systems without symlinks" );
1232 0         0 return;
1233             }
1234              
1235 0         0 my( $from, $from_base, $to, $to_base, $name ) = @_;
1236 0         0 my $link = readlink( $from );
1237 0 0       0 my $link_err = defined( $link ) ? '' : $!; # $! doesn't always get reset
1238 0         0 my $link_abs = abs_path( rel2abs($link, $from_base) );
1239 0         0 my $to_abs = abs_path( rel2abs($to, $to_base) );
1240              
1241 0 0 0     0 if (defined( $link_abs ) && defined( $to_abs ) && $link_abs eq $to_abs) {
      0        
1242 0         0 $Test->ok( 1, $name );
1243             }
1244             else {
1245 0         0 $Test->ok( 0, $name );
1246 0   0     0 $link ||= 'undefined';
1247 0   0     0 $link_abs ||= 'undefined';
1248 0   0     0 $to_abs ||= 'undefined';
1249              
1250 0         0 $Test->diag(" link: $from");
1251 0         0 $Test->diag(" got: $link");
1252 0         0 $Test->diag(" (abs): $link_abs");
1253 0         0 $Test->diag(" expected: $to");
1254 0         0 $Test->diag(" (abs): $to_abs");
1255 0 0       0 $Test->diag(" readlink() error: $link_err") if ($link_err);
1256             }
1257             }
1258              
1259             =item dir_exists_ok( DIRECTORYNAME [, NAME ] )
1260              
1261             Ok if the file exists and is a directory, not ok if the file doesn't exist, or exists but isn't a
1262             directory.
1263              
1264             Contributed by Buddy Burden C<< <barefoot@cpan.org> >>.
1265              
1266             =cut
1267              
1268             sub dir_exists_ok {
1269 4     4 1 226007 my $filename = _normalize( shift );
1270 4   66     24 my $name = shift || "$filename is a directory";
1271              
1272 4 100       115 unless( -e $filename ) {
1273 1         10 $Test->diag( "directory [$filename] does not exist" );
1274 1         750 return $Test->ok(0, $name);
1275             }
1276              
1277 3         27 my $ok = -d $filename;
1278              
1279 3 100       12 if( $ok ) {
1280 2         10 $Test->ok(1, $name);
1281             }
1282             else {
1283 1         7 $Test->diag( "file [$filename] exists but is not a directory" );
1284 1         612 $Test->ok(0, $name);
1285             }
1286             }
1287              
1288             =item dir_contains_ok( DIRECTORYNAME, FILENAME [, NAME ] )
1289              
1290             Ok if the directory exists and contains the file, not ok if the directory doesn't exist, or exists
1291             but doesn't contain the file.
1292              
1293             Contributed by Buddy Burden C<< <barefoot@cpan.org> >>.
1294              
1295             =cut
1296              
1297             sub dir_contains_ok {
1298 4     4 1 28759 my $dirname = _normalize( shift );
1299 4         12 my $filename = _normalize( shift );
1300 4   66     25 my $name = shift || "directory $dirname contains file $filename";
1301              
1302 4 100       120 unless( -d $dirname ) {
1303 1         9 $Test->diag( "directory [$dirname] does not exist" );
1304 1         651 return $Test->ok(0, $name);
1305             }
1306              
1307 3         128 my $ok = -e File::Spec->catfile($dirname, $filename);
1308              
1309 3 100       14 if( $ok ) {
1310 2         11 $Test->ok(1, $name);
1311             }
1312             else {
1313 1         9 $Test->diag( "file [$filename] does not exist in directory $dirname" );
1314 1         651 $Test->ok(0, $name);
1315             }
1316             }
1317              
1318             =item link_count_is_ok( FILE, LINK_COUNT [, NAME ] )
1319              
1320             Ok if the link count to FILE is LINK_COUNT. LINK_COUNT is interpreted
1321             as an integer. A LINK_COUNT that evaluates to 0 returns Ok if the file
1322             does not exist.
1323              
1324              
1325             =cut
1326              
1327             sub link_count_is_ok {
1328 3     3 1 12583 my $file = shift;
1329 3         7 my $count = int( 0 + shift );
1330              
1331 3   66     14 my $name = shift || "$file has a link count of [$count]";
1332              
1333 3         58 my $actual = ( stat $file )[3];
1334              
1335 3 100       15 unless( $actual == $count ) {
1336 1         8 $Test->diag(
1337             "file [$file] points has [$actual] links: expected [$count]" );
1338 1         575 return $Test->ok( 0, $name );
1339             }
1340              
1341 2         6 $Test->ok( 1, $name );
1342             }
1343              
1344             =item link_count_gt_ok( FILE, LINK_COUNT [, NAME ] )
1345              
1346             Ok if the link count to FILE is greater than LINK_COUNT. LINK_COUNT is
1347             interpreted as an integer. A LINK_COUNT that evaluates to 0 returns Ok
1348             if the file has at least one link.
1349              
1350             =cut
1351              
1352             sub link_count_gt_ok {
1353 3     3 1 5092 my $file = shift;
1354 3         8 my $count = int( 0 + shift );
1355              
1356 3   66     15 my $name = shift || "$file has a link count of [$count]";
1357              
1358 3         51 my $actual = (stat $file )[3];
1359              
1360 3 100       13 unless( $actual > $count ) {
1361 1         8 $Test->diag(
1362             "file [$file] points has [$actual] links: ".
1363             "expected more than [$count]" );
1364 1         537 return $Test->ok( 0, $name );
1365             }
1366              
1367 2         7 $Test->ok( 1, $name );
1368             }
1369              
1370             =item link_count_lt_ok( FILE, LINK_COUNT [, NAME ] )
1371              
1372             Ok if the link count to FILE is less than LINK_COUNT. LINK_COUNT is
1373             interpreted as an integer. A LINK_COUNT that evaluates to 0 returns Ok
1374             if the file has at least one link.
1375              
1376             =cut
1377              
1378             sub link_count_lt_ok {
1379 3     3 1 43357 my $file = shift;
1380 3         9 my $count = int( 0 + shift );
1381              
1382 3   66     14 my $name = shift || "$file has a link count of [$count]";
1383              
1384 3         66 my $actual = (stat $file )[3];
1385              
1386 3 100       13 unless( $actual < $count ) {
1387 1         7 $Test->diag(
1388             "file [$file] points has [$actual] links: ".
1389             "expected less than [$count]" );
1390 1         535 return $Test->ok( 0, $name );
1391             }
1392              
1393 2         8 $Test->ok( 1, $name );
1394             }
1395              
1396              
1397             # owner_is, owner_isnt, group_is and group_isnt are almost
1398             # identical in the beginning, so I'm writing a skeleton they can all use.
1399             # I can't think of a better name...
1400             sub _dm_skeleton {
1401 18     18   181 no warnings 'uninitialized';
  18         35  
  18         44834  
1402              
1403 22 100   22   196 if( _obviously_non_multi_user() ) {
1404 3         16 my $calling_sub = (caller(1))[3];
1405 3         14 $Test->skip( $calling_sub . " only works on a multi-user OS" );
1406 3         1760 return 'skip';
1407             }
1408              
1409 19         63 my $filename = _normalize( shift );
1410 19         43 my $testing_for = shift;
1411 19         34 my $name = shift;
1412              
1413 19 100       48 unless( defined $filename ) {
1414 1         7 $Test->diag( "file name not specified" );
1415 1         637 return $Test->ok( 0, $name );
1416             }
1417              
1418 18 100       307 unless( -e $filename ) {
1419 1         9 $Test->diag( "file [$filename] does not exist" );
1420 1         729 return $Test->ok( 0, $name );
1421             }
1422              
1423 17         57 return;
1424             }
1425              
1426             =item owner_is( FILE , OWNER [, NAME ] )
1427              
1428             Ok if FILE's owner is the same as OWNER. OWNER may be a text user name
1429             or a numeric userid. Test skips on Dos, and Mac OS <= 9.
1430             If the file does not exist, the test fails.
1431              
1432             Contributed by Dylan Martin
1433              
1434             =cut
1435              
1436             sub owner_is {
1437 4     4 1 155441 my $filename = shift;
1438 4         9 my $owner = shift;
1439 4   66     25 my $name = shift || "$filename belongs to $owner";
1440              
1441 4         31 my $err = _dm_skeleton( $filename, $owner, $name );
1442 4 50 33     14 return if( defined( $err ) && $err eq 'skip' );
1443 4 50       11 return $err if defined($err);
1444              
1445 4         12 my $owner_uid = _get_uid( $owner );
1446 4 100       12 unless( defined $owner_uid ) {
1447 1         9 $Test->diag("user [$owner] does not exist on this system");
1448 1         615 return $Test->ok( 0, $name );
1449             }
1450              
1451 3         42 my $file_uid = ( stat $filename )[4];
1452              
1453 3 50       13 unless( defined $file_uid ) {
1454 0         0 $Test->skip("stat failed to return owner uid for $filename");
1455 0         0 return;
1456             }
1457              
1458 3 100       20 return $Test->ok( 1, $name ) if $file_uid == $owner_uid;
1459              
1460 1         82 my $real_owner = ( getpwuid $file_uid )[0];
1461 1 50       8 unless( defined $real_owner ) {
1462 0         0 $Test->diag("file does not belong to $owner");
1463 0         0 return $Test->ok( 0, $name );
1464             }
1465              
1466 1         11 $Test->diag( "file [$filename] belongs to $real_owner ($file_uid), ".
1467             "not $owner ($owner_uid)" );
1468 1         785 return $Test->ok( 0, $name );
1469             }
1470              
1471             =item owner_isnt( FILE, OWNER [, NAME ] )
1472              
1473             Ok if FILE's owner is not the same as OWNER. OWNER may be a text user name
1474             or a numeric userid. Test skips on Dos and Mac OS <= 9. If the file
1475             does not exist, the test fails.
1476              
1477             Contributed by Dylan Martin
1478              
1479             =cut
1480              
1481             sub owner_isnt {
1482 4     4 1 10465 my $filename = shift;
1483 4         9 my $owner = shift;
1484 4   66     23 my $name = shift || "$filename doesn't belong to $owner";
1485              
1486 4         14 my $err = _dm_skeleton( $filename, $owner, $name );
1487 4 50 33     16 return if( defined( $err ) && $err eq 'skip' );
1488 4 50       11 return $err if defined($err);
1489              
1490 4         11 my $owner_uid = _get_uid( $owner );
1491 4 100       15 unless( defined $owner_uid ) {
1492 1         8 return $Test->ok( 1, $name );
1493             }
1494              
1495 3         45 my $file_uid = ( stat $filename )[4];
1496              
1497             #$Test->diag( "owner_isnt: $owner_uid $file_uid" );
1498 3 100       40 return $Test->ok( 1, $name ) if $file_uid != $owner_uid;
1499              
1500 1         11 $Test->diag( "file [$filename] belongs to $owner ($owner_uid)" );
1501 1         630 return $Test->ok( 0, $name );
1502             }
1503              
1504             =item group_is( FILE , GROUP [, NAME ] )
1505              
1506             Ok if FILE's group is the same as GROUP. GROUP may be a text group name or
1507             a numeric group id. Test skips on Dos, Mac OS <= 9 and any other operating
1508             systems that do not support getpwuid() and friends. If the file does not
1509             exist, the test fails.
1510              
1511             Contributed by Dylan Martin
1512              
1513             =cut
1514              
1515             sub group_is {
1516 5     5 1 11677 my $filename = shift;
1517 5         12 my $group = shift;
1518 5   66     20 my $name = ( shift || "$filename belongs to group $group" );
1519              
1520 5         16 my $err = _dm_skeleton( $filename, $group, $name );
1521 5 50 33     19 return if( defined( $err ) && $err eq 'skip' );
1522 5 50       13 return $err if defined($err);
1523              
1524 5         14 my $group_gid = _get_gid( $group );
1525 5 100       16 unless( defined $group_gid ) {
1526 1         8 $Test->diag("group [$group] does not exist on this system");
1527 1         608 return $Test->ok( 0, $name );
1528             }
1529              
1530 4         53 my $file_gid = ( stat $filename )[5];
1531              
1532 4 50       14 unless( defined $file_gid ) {
1533 0         0 $Test->skip("stat failed to return group gid for $filename");
1534 0         0 return;
1535             }
1536              
1537 4 100       20 return $Test->ok( 1, $name ) if $file_gid == $group_gid;
1538              
1539 1         43 my $real_group = ( getgrgid $file_gid )[0];
1540 1 50       7 unless( defined $real_group ) {
1541 0         0 $Test->diag("file does not belong to $group");
1542 0         0 return $Test->ok( 0, $name );
1543             }
1544              
1545 1         10 $Test->diag( "file [$filename] belongs to $real_group ($file_gid), ".
1546             "not $group ($group_gid)" );
1547              
1548 1         646 return $Test->ok( 0, $name );
1549             }
1550              
1551             =item group_isnt( FILE , GROUP [, NAME ] )
1552              
1553             Ok if FILE's group is not the same as GROUP. GROUP may be a text group name or
1554             a numeric group id. Test skips on Dos, Mac OS <= 9 and any other operating
1555             systems that do not support getpwuid() and friends. If the file does not
1556             exist, the test fails.
1557              
1558             Contributed by Dylan Martin
1559              
1560             =cut
1561              
1562             sub group_isnt {
1563 4     4 1 6444 my $filename = shift;
1564 4         8 my $group = shift;
1565 4   66     19 my $name = shift || "$filename does not belong to group $group";
1566              
1567 4         14 my $err = _dm_skeleton( $filename, $group, $name );
1568 4 50 33     14 return if( defined( $err ) && $err eq 'skip' );
1569 4 50       11 return $err if defined($err);
1570              
1571 4         25 my $group_gid = _get_gid( $group );
1572 4         55 my $file_gid = ( stat $filename )[5];
1573              
1574 4 50       15 unless( defined $file_gid ) {
1575 0         0 $Test->skip("stat failed to return group gid for $filename");
1576 0         0 return;
1577             }
1578              
1579 4 100       27 return $Test->ok( 1, $name ) if $file_gid != $group_gid;
1580              
1581 1         9 $Test->diag( "file [$filename] belongs to $group ($group_gid)" );
1582 1         605 return $Test->ok( 0, $name );
1583             }
1584              
1585             sub _get_uid {
1586 8     8   18 my $arg = shift;
1587              
1588             # the name might be numeric (why would you do that?), so we need
1589             # to figure out which of several possibilities we have. And, 0 means
1590             # root, so we have to be very careful with the values.
1591              
1592             # maybe the argument is a UID. First, it has to be numeric. If it's
1593             # a UID, we'll get the same UID back. But, if we get back a value
1594             # that doesn't mean that we are done. There might be a name with
1595             # the same value.
1596             #
1597             # Don't use this value in comparisons! An undef could be turned
1598             # into zero!
1599 8 100       207 my $from_uid = (getpwuid($arg))[2] if $arg =~ /\A[0-9]+\z/;
1600              
1601             # Now try the argument as a name. If it's a name, then we'll get
1602             # back a UID. Maybe we get back nothing.
1603 8         538 my $from_nam = (getpwnam($arg))[2];
1604              
1605 8         28 return do {
1606             # first case, we got back nothing from getpwnam but did get
1607             # something from getpwuid. The arg is not a name and is a
1608             # UID.
1609 8 100 66     70 if( defined $from_uid and not defined $from_nam ) { $arg }
  2 100 66     10  
    50 33        
1610             # second case, we got back nothing from getpwuid but did get
1611             # something from getpwnam. The arg is a name and is not a
1612             # UID.
1613 4         13 elsif( not defined $from_uid and defined $from_nam ) { $from_nam }
1614             # Now, what happens if neither are defined? The argument does
1615             # not correspond to a name or GID on the system. Since no such
1616             # user exists, we return undef.
1617 2         26 elsif( not defined $from_uid and not defined $from_nam ) { undef }
1618             # But what if they are both defined? The argument could represent
1619             # a UID and a name, and those could be different users! In this
1620             # case, we'll choose the original argument. That might be wrong,
1621             # so the best we can do is a warning.
1622             else {
1623 0         0 carp( "Found both a UID or name for <$arg>. Guessing the UID is <$arg>." );
1624 0         0 $arg
1625             }
1626             };
1627             }
1628              
1629             sub _get_gid {
1630 9     9   19 my $arg = shift;
1631              
1632             # the name might be numeric (why would you do that?), so we need
1633             # to figure out which of several possibilities we have. And, 0 means
1634             # root, so we have to be very careful with the values.
1635              
1636             # maybe the argument is a GID. First, it has to be numeric. If it's
1637             # a GID, we'll get the same GID back. But, if we get back a value
1638             # that doesn't mean that we are done. There might be a name with
1639             # the same value.
1640             #
1641             # Don't use this value in comparisons! An undef could be turned
1642             # into zero!
1643 9 100       167 my $from_gid = (getgrgid($arg))[2] if $arg =~ /\A[0-9]+\z/;
1644              
1645             # Now try the argument as a name. If it's a name, then we'll get
1646             # back a GID. Maybe we get back nothing.
1647 9         467 my $from_nam = (getgrnam($arg))[2];
1648              
1649 9         25 return do {
1650             # first case, we got back nothing from getgrnam but did get
1651             # something from getpwuid. The arg is not a name and is a
1652             # GID.
1653 9 100 66     68 if( defined $from_gid and not defined $from_nam ) { $arg }
  3 100 66     13  
    50 33        
1654             # second case, we got back nothing from getgrgid but did get
1655             # something from getgrnam. The arg is a name and is not a
1656             # GID.
1657 5         14 elsif( not defined $from_gid and defined $from_nam ) { $from_nam }
1658             # Now, what happens if neither are defined? The argument does
1659             # not correspond to a name or GID on the system. Since no such
1660             # user exists, we return undef.
1661 1         4 elsif( not defined $from_gid and not defined $from_nam ) { undef }
1662             # But what if they are both defined? The argument could represent
1663             # a GID and a name, and those could be different users! In this
1664             # case, we'll choose the original argument. That might be wrong,
1665             # so the best we can do is a warning.
1666             else {
1667 0         0 carp( "Found both a GID or name for <$arg>. Guessing the GID is <$arg>." );
1668 0         0 $arg;
1669             }
1670             };
1671             }
1672              
1673             =item file_mtime_age_ok( FILE [, WITHIN_SECONDS ] [, NAME ] )
1674              
1675             Ok if FILE's modified time is WITHIN_SECONDS inclusive of the system's current time.
1676             This test uses stat() to obtain the mtime. If the file does not exist the test
1677             returns failure. If stat() fails, the test is skipped.
1678              
1679             =cut
1680              
1681             sub file_mtime_age_ok {
1682 4     4 1 279835 my $filename = shift;
1683 4   100     15 my $within_secs = shift || 0;
1684 4   66     16 my $name = shift || "$filename mtime within $within_secs seconds of current time";
1685              
1686 4         5 my $time = time();
1687              
1688 4         9 my $filetime = _stat_file($filename, 9);
1689              
1690 4 50       10 return if ( $filetime == -1 ); #skip
1691              
1692 4 100       17 return $Test->ok(1, $name) if ( $filetime + $within_secs > $time-1 );
1693              
1694 2         15 $Test->diag( "file [$filename] mtime [$filetime] is not $within_secs seconds within current system time [$time].");
1695 2         943 return $Test->ok(0, $name);
1696             }
1697              
1698             =item file_mtime_gt_ok( FILE, UNIXTIME [, NAME ] )
1699              
1700             Ok if FILE's mtime is > UNIXTIME. This test uses stat() to get the mtime. If stat() fails
1701             this test is skipped. If FILE does not exist, this test fails.
1702              
1703             =cut
1704              
1705             sub file_mtime_gt_ok {
1706 3     3 1 8372 my $filename = shift;
1707 3         8 my $time = int shift;
1708 3   66     11 my $name = shift || "$filename mtime is greater than unix timestamp $time";
1709              
1710 3         5 my $filetime = _stat_file($filename, 9);
1711              
1712 3 50       8 return if ( $filetime == -1 ); #skip
1713              
1714 3 100       11 return $Test->ok(1, $name) if ( $filetime > $time );
1715              
1716 1         6 $Test->diag( "file [$filename] mtime [$filetime] not greater than $time" );
1717 1         424 $Test->ok(0, $name);
1718             }
1719              
1720             =item file_mtime_lt_ok( FILE, UNIXTIME, [, NAME ] )
1721              
1722             Ok if FILE's modified time is < UNIXTIME. This test uses stat() to get the mtime. If stat() fails
1723             this test is skipped. If FILE does not exist, this test fails.
1724              
1725             =cut
1726              
1727             sub file_mtime_lt_ok {
1728 3     3 1 12255 my $filename = shift;
1729 3         6 my $time = int shift;
1730 3   66     13 my $name = shift || "$filename mtime less than unix timestamp $time";
1731              
1732             # gets mtime
1733 3         7 my $filetime = _stat_file($filename, 9);
1734              
1735 3 50       7 return if ( $filetime == -1 ); #skip
1736              
1737 3 100       14 return $Test->ok(1, $name) if ( $filetime < $time );
1738              
1739 1         7 $Test->diag( "file [$filename] mtime [$filetime] not less than $time" );
1740 1         439 $Test->ok(0, $name);
1741             }
1742              
1743             # private function to safely stat a file
1744             #
1745             # Arugments:
1746             # filename file to perform on
1747             # attr_pos pos of the array returned from stat we want to compare. perldoc -f stat
1748             #
1749             # Returns:
1750             # -1 - stat failed
1751             # 0 - failure (file doesn't exist etc)
1752             # filetime - on success, time requested provided by stat
1753             #
1754             sub _stat_file {
1755 12     12   9790 my $filename = _normalize( shift );
1756 12         17 my $attr_pos = shift;
1757              
1758 12 100       21 unless( defined $filename ) {
1759 1         5 $Test->diag( "file name not specified" );
1760 1         403 return 0;
1761             }
1762              
1763 11 100       234 unless( -e $filename ) {
1764 1         11 $Test->diag( "file [$filename] does not exist" );
1765 1         720 return 0;
1766             }
1767              
1768 10         93 my $filetime = ( stat($filename) )[$attr_pos];
1769              
1770 10 50       44 unless( $filetime ) {
1771 0         0 $Test->diag( "stat of $filename failed" );
1772 0         0 return -1; #skip on stat failure
1773             }
1774              
1775 10         20 return $filetime;
1776             }
1777              
1778             =back
1779              
1780             =head1 TO DO
1781              
1782             * check properties for other users (readable_by_root, for instance)
1783              
1784             * check times
1785              
1786             * check number of links to file
1787              
1788             * check path parts (directory, filename, extension)
1789              
1790             =head1 SEE ALSO
1791              
1792             L<Test::Builder>,
1793             L<Test::More>
1794              
1795             If you are using the new C<Test2> stuff, see Test2::Tools::File
1796             (https://github.com/torbjorn/Test2-Tools-File).
1797              
1798             =head1 SOURCE AVAILABILITY
1799              
1800             This module is in Github:
1801              
1802             https://github.com/briandfoy/test-file
1803              
1804             =head1 AUTHOR
1805              
1806             brian d foy, C<< <briandfoy@pobox.com> >>
1807              
1808             =head1 CREDITS
1809              
1810             Shawn Sorichetti C<< <ssoriche@coloredblocks.net> >> provided
1811             some functions.
1812              
1813             Tom Metro helped me figure out some Windows capabilities.
1814              
1815             Dylan Martin added C<owner_is> and C<owner_isnt>.
1816              
1817             David Wheeler added C<file_line_count_is>.
1818              
1819             Buddy Burden C<< <barefoot@cpan.org> >> provided C<dir_exists_ok>,
1820             C<dir_contains_ok>, C<file_contains_like>, and
1821             C<file_contains_unlike>.
1822              
1823             xmikew C<< <https://github.com/xmikew> >> provided the C<mtime_age>
1824             stuff.
1825              
1826             Torbjørn Lindahl is working on L<Test2::Tools::File> and we're
1827             working together to align our interfaces.
1828              
1829             Jean-Damien Durand added bits to use Win32::IsSymlinkCreationAllowed,
1830             new since Win32 0.55.
1831              
1832             =head1 COPYRIGHT AND LICENSE
1833              
1834             Copyright © 2002-2025, brian d foy <briandfoy@pobox.com>. All rights reserved.
1835              
1836             This program is free software; you can redistribute it and/or modify
1837             it under the terms of the Artistic License 2.0
1838              
1839             =cut
1840              
1841             "The quick brown fox jumped over the lazy dog";