File Coverage

blib/lib/Test/File.pm
Criterion Covered Total %
statement 520 621 83.7
branch 213 278 76.6
condition 101 182 55.4
subroutine 64 70 91.4
pod 46 46 100.0
total 944 1197 78.8


line stmt bran cond sub pod time code
1             package Test::File;
2 17     17   725626 use strict;
  17         98  
  17         527  
3              
4 17     17   77 use vars qw(@EXPORT $VERSION);
  17         28  
  17         858  
5              
6 17     17   79 use Carp qw(carp);
  17         27  
  17         681  
7 17     17   81 use Exporter qw(import);
  17         25  
  17         458  
8 17     17   78 use File::Spec;
  17         44  
  17         417  
9 17     17   74 use Test::Builder;
  17         27  
  17         20953  
10              
11             @EXPORT = qw(
12             file_exists_ok file_not_exists_ok
13             file_empty_ok file_not_empty_ok file_size_ok file_max_size_ok
14             file_min_size_ok file_readable_ok file_not_readable_ok
15             file_writeable_ok file_writable_ok file_not_writeable_ok file_not_writable_ok
16             file_executable_ok file_not_executable_ok
17             file_mode_is file_mode_isnt
18             file_mode_has file_mode_hasnt
19             file_is_symlink_ok file_is_not_symlink_ok
20             symlink_target_exists_ok symlink_target_is
21             symlink_target_dangles_ok
22             dir_exists_ok dir_contains_ok
23             link_count_is_ok link_count_gt_ok link_count_lt_ok
24             owner_is owner_isnt
25             group_is group_isnt
26             file_line_count_is file_line_count_isnt file_line_count_between
27             file_contains_like file_contains_unlike
28             file_contains_utf8_like file_contains_utf8_unlike
29             file_contains_encoded_like file_contains_encoded_unlike
30             file_mtime_gt_ok file_mtime_lt_ok file_mtime_age_ok
31             );
32              
33             $VERSION = '1.992';
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),
62             you can set the C 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             =cut
71              
72             sub _is_plain_file {
73 74     74   133 my $filename = _normalize( shift );
74              
75 74         112 my $message = do {
76 74 100       1657 if( ! -e $filename ) { "does not exist" }
  15 50       51  
    50          
77 0         0 elsif( ! -f _ ) { "is not a plain file" }
78 0         0 elsif( -d _ ) { "is a directory" }
79 59         163 else { () }
80             };
81              
82 74 100       182 if( $message ) {
83 15         83 $Test->diag( "file [$filename] $message");
84 15         4090 return 0;
85             }
86              
87 59         172 return 1;
88             }
89              
90             sub _normalize {
91 241     241   24632 my $file = shift;
92 241 100       595 return unless defined $file;
93              
94 237 100       753 return $file =~ m|/|
95             ? File::Spec->catfile( split m|/|, $file )
96             : $file;
97             }
98              
99             sub _win32 {
100 40 100   40   61258 return 0 if $^O eq 'darwin';
101 39 100       172 return $ENV{PRETEND_TO_BE_WIN32} if defined $ENV{PRETEND_TO_BE_WIN32};
102 38   66     250 return $^O =~ m/Win/ || $^O eq 'msys';
103             }
104              
105             # returns true if symlinks can't exist
106 18     18   23 sub _no_symlinks_here { ! eval { symlink("",""); 1 } }
  18         122  
  18         79  
107              
108             # owner_is and owner_isn't should skip on OS where the question makes no
109             # sense. I really don't know a good way to test for that, so I'm going
110             # to skip on the two OS's that I KNOW aren't multi-user. I'd love to add
111             # more if anyone knows of any
112             # Note: I don't have a dos or mac os < 10 machine to test this on
113             sub _obviously_non_multi_user {
114 31 100   31   40458 foreach my $os ( qw(dos MacOS) ) { return 1 if $^O eq $os }
  57         182  
115              
116 25 100       71 return 0 if $^O eq 'MSWin32';
117              
118 20         34 eval { my $holder = getpwuid(0) };
  20         1455  
119 20 100       90 return 1 if $@;
120              
121 19         33 eval { my $holder = getgrgid(0) };
  19         746  
122 19 100       88 return 1 if $@;
123              
124 18         47 return 0;
125             }
126              
127             =over 4
128              
129             =item file_exists_ok( FILENAME [, NAME ] )
130              
131             Ok if the file exists, and not ok otherwise.
132              
133             =cut
134              
135             sub file_exists_ok {
136 11     11 1 105476 my $filename = _normalize( shift );
137 11   66     66 my $name = shift || "$filename exists";
138              
139 11         269 my $ok = -e $filename;
140              
141 11 100       43 if( $ok ) {
142 10         44 $Test->ok(1, $name);
143             }
144             else {
145 1         10 $Test->diag("file [$filename] does not exist");
146 1         417 $Test->ok(0, $name);
147             }
148             }
149              
150             =item file_not_exists_ok( FILENAME [, NAME ] )
151              
152             Ok if the file does not exist, and not okay if it does exist.
153              
154             =cut
155              
156             sub file_not_exists_ok {
157 5     5 1 16647 my $filename = _normalize( shift );
158 5   66     34 my $name = shift || "$filename does not exist";
159              
160 5         181 my $ok = not -e $filename;
161              
162 5 100       22 if( $ok ) {
163 4         23 $Test->ok(1, $name);
164             }
165             else {
166 1         10 $Test->diag("file [$filename] exists");
167 1         450 $Test->ok(0, $name);
168             }
169             }
170              
171             =item file_empty_ok( FILENAME [, NAME ] )
172              
173             Ok if the file exists and has empty size, not ok if the file does not
174             exist or exists with non-zero size.
175              
176             Previously this tried to test any sort of file. Sometime in the future
177             this will fail if the argument is not a plain file or is a directory.
178              
179             =cut
180              
181             sub file_empty_ok {
182 4     4 1 26065 my $filename = _normalize( shift );
183 4   66     16 my $name = shift || "$filename is empty";
184              
185 4 100       8 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
186              
187 3         22 my $ok = -z $filename;
188              
189 3 100       7 if( $ok ) {
190 2         7 $Test->ok(1, $name);
191             }
192             else {
193 1         7 $Test->diag( "file [$filename] exists with non-zero size" );
194 1         272 $Test->ok(0, $name);
195             }
196             }
197              
198             =item file_not_empty_ok( FILENAME [, NAME ] )
199              
200             Ok if the file exists and has non-zero size, not ok if the file does
201             not exist or exists with zero size.
202              
203             Previously this tried to test any sort of file. Sometime in the future
204             this will fail if the argument is not a plain file or is a directory.
205              
206             =cut
207              
208             sub file_not_empty_ok {
209 7     7 1 42386 my $filename = _normalize( shift );
210 7   66     31 my $name = shift || "$filename is not empty";
211              
212 7 100       15 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
213              
214 5         12 my $ok = not -z _;
215              
216 5 100       11 if( $ok ) {
217 3         13 $Test->ok(1, $name);
218             }
219             else {
220 2         15 $Test->diag( "file [$filename] exists with zero size" );
221 2         585 $Test->ok(0, $name);
222             }
223             }
224              
225             =item file_size_ok( FILENAME, SIZE [, NAME ] )
226              
227             Ok if the file exists and has SIZE size in bytes (exactly), not ok if
228             the file does not exist or exists with size other than SIZE.
229              
230             Previously this tried to test any sort of file. Sometime in the future
231             this will fail if the argument is not a plain file or is a directory.
232              
233             =cut
234              
235             sub file_size_ok {
236 4     4 1 9400 my $filename = _normalize( shift );
237 4         7 my $expected = int shift;
238 4   66     55 my $name = shift || "$filename has right size";
239              
240 4 100       12 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
241              
242 3         24 my $ok = ( -s $filename ) == $expected;
243              
244 3 100       11 if( $ok ) {
245 2         7 $Test->ok(1, $name);
246             }
247             else {
248 1         8 my $actual = -s $filename;
249 1         9 $Test->diag(
250             "file [$filename] has actual size [$actual] not [$expected]" );
251              
252 1         276 $Test->ok(0, $name);
253             }
254             }
255              
256             =item file_max_size_ok( FILENAME, MAX [, NAME ] )
257              
258             Ok if the file exists and has size less than or equal to MAX bytes, not
259             ok if the file does not exist or exists with size greater than MAX
260             bytes.
261              
262             Previously this tried to test any sort of file. Sometime in the future
263             this will fail if the argument is not a plain file or is a directory.
264              
265             =cut
266              
267             sub file_max_size_ok {
268 4     4 1 8356 my $filename = _normalize( shift );
269 4         7 my $max = int shift;
270 4   66     15 my $name = shift || "$filename is under $max bytes";
271              
272 4 100       9 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
273              
274 3         24 my $ok = ( -s $filename ) <= $max;
275              
276 3 100       8 if( $ok ) {
277 2         7 $Test->ok(1, $name);
278             }
279             else {
280 1         9 my $actual = -s $filename;
281 1         7 $Test->diag(
282             "file [$filename] has actual size [$actual] " .
283             "greater than [$max]"
284             );
285              
286 1         264 $Test->ok(0, $name);
287             }
288             }
289              
290             =item file_min_size_ok( FILENAME, MIN [, NAME ] )
291              
292             Ok if the file exists and has size greater than or equal to MIN bytes,
293             not ok if the file does not exist or exists with size less than MIN
294             bytes.
295              
296             Previously this tried to test any sort of file. Sometime in the future
297             this will fail if the argument is not a plain file or is a directory.
298              
299             =cut
300              
301             sub file_min_size_ok {
302 4     4 1 8514 my $filename = _normalize( shift );
303 4         7 my $min = int shift;
304 4   66     30 my $name = shift || "$filename is over $min bytes";
305              
306 4 100       10 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
307              
308 3         24 my $ok = ( -s $filename ) >= $min;
309              
310 3 100       8 if( $ok ) {
311 2         9 $Test->ok(1, $name);
312             }
313             else {
314 1         9 my $actual = -s $filename;
315 1         8 $Test->diag(
316             "file [$filename] has actual size ".
317             "[$actual] less than [$min]"
318             );
319              
320 1         313 $Test->ok(0, $name);
321             }
322             }
323              
324             =item file_line_count_is( FILENAME, COUNT [, NAME ] )
325              
326             Ok if the file exists and has COUNT lines (exactly), not ok if the
327             file does not exist or exists with a line count other than COUNT.
328              
329             This function uses the current value of C<$/> as the line ending and
330             counts the lines by reading them and counting how many it read.
331              
332             Previously this tried to test any sort of file. Sometime in the future
333             this will fail if the argument is not a plain file or is a directory.
334              
335             =cut
336              
337             sub _ENOFILE () { -1 }
338             sub _ECANTOPEN () { -2 }
339             sub _ENOTPLAIN () { -3 }
340              
341             sub _file_line_counter {
342 9     9   19 my $filename = shift;
343              
344 9 50       82 return _ENOFILE unless -e $filename;
345 9 50       80 return _ENOTPLAIN unless -f $filename;
346 9 50       584 return _ECANTOPEN unless open my( $fh ), "<", $filename;
347              
348 9         32 my $count = 0;
349 9         313 while( <$fh> ) { $count++ }
  18         93  
350              
351 9         212 return $count;
352             }
353              
354             # XXX: lots of cut and pasting here, needs refactoring
355             # looks like the refactoring might be worse than this though
356             sub file_line_count_is {
357 4     4 1 16526 my $filename = _normalize( shift );
358 4         7 my $expected = shift;
359 4         9 my $name = do {
360 17     17   130 no warnings 'uninitialized';
  17         34  
  17         1524  
361 4 50       26 shift || "$filename line count is $expected lines";
362             };
363              
364 4 100       12 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
365              
366 3 100 66     18 unless( defined $expected && int( $expected ) == $expected ) {
367 17     17   111 no warnings 'uninitialized';
  17         39  
  17         3754  
368 1         10 $Test->diag( "file_line_count_is expects a positive whole number for " .
369             "the second argument. Got [$expected]" );
370 1         342 return $Test->ok( 0, $name );
371             }
372              
373 2         8 my $got = _file_line_counter( $filename );
374              
375 2 50       25 if( $got eq _ENOFILE ) {
    50          
    50          
    100          
376 0         0 $Test->diag( "file [$filename] does not exist" );
377 0         0 $Test->ok( 0, $name );
378             }
379             elsif( $got eq _ENOTPLAIN ) {
380 0         0 $Test->diag( "file [$filename] is not a plain file" );
381 0         0 $Test->ok( 0, $name );
382             }
383             elsif( $got == _ECANTOPEN ) {
384 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
385 0         0 $Test->ok( 0, $name );
386             }
387             elsif( $got == $expected ) {
388 1         5 $Test->ok( 1, $name );
389             }
390             else {
391 1         10 $Test->diag( "expected [$expected] lines in [$filename], " .
392             "got [$got] lines" );
393 1         270 $Test->ok( 0, $name );
394             }
395              
396             }
397              
398             =item file_line_count_isnt( FILENAME, COUNT [, NAME ] )
399              
400             Ok if the file exists and doesn't have exactly COUNT lines, not ok if
401             the file does not exist or exists with a line count of COUNT. Read
402             that carefully: the file must exist for this test to pass!
403              
404             This function uses the current value of C<$/> as the line ending and
405             counts the lines by reading them and counting how many it read.
406              
407             Previously this tried to test any sort of file. Sometime in the future
408             this will fail if the argument is not a plain file or is a directory.
409              
410             =cut
411              
412             sub file_line_count_isnt {
413 5     5 1 14614 my $filename = _normalize( shift );
414 5         12 my $expected = shift;
415 5         8 my $name = do {
416 17     17   111 no warnings 'uninitialized';
  17         40  
  17         1444  
417 5 50       33 shift || "$filename line count is not $expected lines";
418             };
419              
420 5 100       14 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
421              
422 4 100 66     19 unless( defined $expected && int( $expected ) == $expected ) {
423 17     17   103 no warnings 'uninitialized';
  17         34  
  17         3687  
424 1         15 $Test->diag( "file_line_count_is expects a positive whole number for " .
425             "the second argument. Got [$expected]" );
426 1         351 return $Test->ok( 0, $name );
427             }
428              
429 3         47 my $got = _file_line_counter( $filename );
430              
431 3 50       27 if( $got eq _ENOFILE ) {
    50          
    50          
    100          
432 0         0 $Test->diag( "file [$filename] does not exist" );
433 0         0 $Test->ok( 0, $name );
434             }
435             elsif( $got eq _ENOTPLAIN ) {
436 0         0 $Test->diag( "file [$filename] is not a plain file" );
437 0         0 $Test->ok( 0, $name );
438             }
439             elsif( $got == _ECANTOPEN ) {
440 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
441 0         0 $Test->ok( 0, $name );
442             }
443             elsif( $got != $expected ) {
444 2         10 $Test->ok( 1, $name );
445             }
446             else {
447 1         10 $Test->diag( "expected something other than [$expected] lines in [$filename], " .
448             "but got [$got] lines" );
449 1         254 $Test->ok( 0, $name );
450             }
451              
452             }
453              
454             =item file_line_count_between( FILENAME, MIN, MAX, [, NAME ] )
455              
456             Ok if the file exists and has a line count between MIN and MAX,
457             inclusively.
458              
459             This function uses the current value of C<$/> as the line ending and
460             counts the lines by reading them and counting how many it read.
461              
462             Previously this tried to test any sort of file. Sometime in the future
463             this will fail if the argument is not a plain file or is a directory.
464              
465             =cut
466              
467             sub file_line_count_between {
468 8     8 1 21733 my $filename = _normalize( shift );
469 8         16 my $min = shift;
470 8         17 my $max = shift;
471              
472 8         22 my $name = do {
473 17     17   104 no warnings 'uninitialized';
  17         52  
  17         1548  
474 8 50       63 shift || "$filename line count is between [$min] and [$max] lines";
475             };
476 8 100       28 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
477              
478 5         16 foreach my $ref ( \$min, \$max ) {
479 9 100 66     69 unless( defined $$ref && int( $$ref ) == $$ref ) {
480 17     17   95 no warnings 'uninitialized';
  17         33  
  17         50537  
481 1         43 $Test->diag( "file_line_count_between expects positive whole numbers for " .
482             "the second and third arguments. Got [$min] and [$max]" );
483 1         604 return $Test->ok( 0, $name );
484             }
485             }
486              
487 4         12 my $got = _file_line_counter( $filename );
488              
489 4 50 66     46 if( $got eq _ENOFILE ) {
    50          
    50          
    100          
490 0         0 $Test->diag( "file [$filename] does not exist" );
491 0         0 $Test->ok( 0, $name );
492             }
493             elsif( $got eq _ENOTPLAIN ) {
494 0         0 $Test->diag( "file [$filename] is not a plain file" );
495 0         0 $Test->ok( 0, $name );
496             }
497             elsif( $got == _ECANTOPEN ) {
498 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
499 0         0 $Test->ok( 0, $name );
500             }
501             elsif( $min <= $got and $got <= $max ) {
502 3         16 $Test->ok( 1, $name );
503             }
504             else {
505 1         10 $Test->diag( "expected a line count between [$min] and [$max] " .
506             "in [$filename], but got [$got] lines"
507             );
508 1         268 $Test->ok( 0, $name );
509             }
510             }
511              
512             =item file_contains_like ( FILENAME, PATTERN [, NAME ] )
513              
514             Ok if the file exists and its contents (as one big string) match
515             PATTERN, not ok if the file does not exist, is not readable, or exists
516             but doesn't match PATTERN.
517              
518             Since the file contents are read into memory, you should not use this
519             for large files. Besides memory consumption, test diagnostics for
520             failing tests might be difficult to decipher. However, for short
521             files this works very well.
522              
523             Because the entire contents are treated as one large string, you can
524             make a pattern that tests multiple lines. Don't forget that you may
525             need to use the /s modifier for such patterns:
526              
527             # make sure file has one or more paragraphs with CSS class X
528             file_contains_like($html_file, qr{

.*?

}s);
529              
530             Contrariwise, if you need to match at the beginning or end of a line
531             inside the file, use the /m modifier:
532              
533             # make sure file has a setting for foo
534             file_contains_like($config_file, qr/^ foo \s* = \s* \w+ $/mx);
535              
536             If you want to test your file contents against multiple patterns, but
537             don't want to have the file read in repeatedly, you can pass an
538             arrayref of patterns instead of a single pattern, like so:
539              
540             # make sure our template has rendered correctly
541             file_contains_like($template_out,
542             [
543             qr/^ $title_line $/mx,
544             map { qr/^ $_ $/mx } @chapter_headings,
545             qr/^ $footer_line $/mx,
546             ]);
547              
548             Please note that if you do this, and your file does not exist or is
549             not readable, you'll only get one test failure instead of a failure
550             for each pattern. This could cause your test plan to be off, although
551             you may not care at that point because your test failed anyway. If
552             you do care, either skip the test plan altogether by employing
553             L's C function, or use
554             L in conjunction with a C block.
555              
556             Contributed by Buddy Burden C<< >>.
557              
558             =item file_contains_unlike ( FILENAME, PATTERN [, NAME ] )
559              
560             Ok if the file exists and its contents (as one big string) do B
561             match PATTERN, not ok if the file does not exist, is not readable, or
562             exists but matches PATTERN.
563              
564             All notes and caveats for L apply to this
565             function as well.
566              
567             Contributed by Buddy Burden C<< >>.
568              
569             =item file_contains_utf8_like ( FILENAME, PATTERN [, NAME ] )
570              
571             The same as C, except the file is opened as UTF-8.
572              
573             =item file_contains_utf8_unlike ( FILENAME, PATTERN [, NAME ] )
574              
575             The same as C, except the file is opened as UTF-8.
576              
577             =item file_contains_encoded_like ( FILENAME, ENCODING, PATTERN [, NAME ] )
578              
579             The same as C, except the file is opened with ENCODING
580              
581             =item file_contains_encoded_unlike ( FILENAME, ENCODING, PATTERN [, NAME ] )
582              
583             The same as C, except the file is opened with ENCODING.
584              
585             =cut
586              
587             sub file_contains_like {
588 7     7 1 36719 local $Test::Builder::Level = $Test::Builder::Level + 1;
589 7         16 _file_contains(like => "contains", undef, @_);
590             }
591              
592             sub file_contains_unlike {
593 7     7 1 19589 local $Test::Builder::Level = $Test::Builder::Level + 1;
594 7         15 _file_contains(unlike => "doesn't contain", undef, @_);
595             }
596              
597             sub file_contains_utf8_like {
598 5     5 1 40178 local $Test::Builder::Level = $Test::Builder::Level + 1;
599 5         12 _file_contains(like => "contains", 'UTF-8', @_);
600             }
601              
602             sub file_contains_utf8_unlike {
603 5     5 1 13526 local $Test::Builder::Level = $Test::Builder::Level + 1;
604 5         10 _file_contains(unlike => "doesn't contain", 'UTF-8', @_);
605             }
606              
607             sub file_contains_encoded_like {
608 5     5 1 60910 local $Test::Builder::Level = $Test::Builder::Level + 1;
609 5         16 my $filename = shift;
610 5         10 my $encoding = shift;
611 5         25 _file_contains(like => "contains", $encoding, $filename, @_);
612             }
613              
614             sub file_contains_encoded_unlike {
615 5     5 1 15946 local $Test::Builder::Level = $Test::Builder::Level + 1;
616 5         13 my $filename = shift;
617 5         13 my $encoding = shift;
618 5         20 _file_contains(unlike => "doesn't contain", $encoding, $filename, @_);
619             }
620              
621             sub _file_contains {
622 34     34   58 my $method = shift;
623 34         48 my $verb = shift;
624 34         46 my $encoding = shift;
625 34         82 my $filename = _normalize( shift );
626 34         56 my $patterns = shift;
627 34         41 my $name = shift;
628              
629 34         55 my (@patterns, %patterns);
630 34 100       85 if (ref $patterns eq 'ARRAY') {
631 20         42 @patterns = @$patterns;
632 20   66     43 %patterns = map { $_ => $name || "$filename $verb $_" } @patterns;
  40         218  
633             }
634             else {
635 14         24 @patterns = ($patterns);
636 14   33     99 %patterns = ( $patterns => $name || "$filename $verb $patterns" );
637             }
638              
639             # for purpose of checking the file's existence, just use the first
640             # test name as the name
641 34         81 $name = $patterns{$patterns[0]};
642              
643 34 100       78 return $Test->ok( 0, $name ) unless _is_plain_file( $filename );
644              
645 30 50       318 unless( -r $filename ) {
646 0         0 $Test->diag( "file [$filename] is not readable" );
647 0         0 return $Test->ok(0, $name);
648             }
649              
650             # do the slurp
651 30         95 my $file_contents;
652             {
653 30 50       47 unless (open(FH, $filename)) {
  30         1072  
654 0         0 $Test->diag( "file [$filename] could not be opened: \$! is [$!]" );
655 0         0 return $Test->ok( 0, $name );
656             }
657              
658 30 100       125 if (defined $encoding) {
659 20         346 binmode FH, ":encoding($encoding)";
660             }
661              
662 30         995 local $/ = undef;
663 30         1109 $file_contents = ;
664 30         602 close FH;
665             }
666              
667 30         84 foreach my $p (@patterns) {
668 48         8884 $Test->$method($file_contents, $p, $patterns{$p});
669             }
670             }
671              
672             =item file_readable_ok( FILENAME [, NAME ] )
673              
674             Ok if the file exists and is readable, not ok if the file does not
675             exist or is not readable.
676              
677             =cut
678              
679             sub file_readable_ok {
680 2     2 1 7176 my $filename = _normalize( shift );
681 2   66     13 my $name = shift || "$filename is readable";
682              
683 2         64 my $ok = -r $filename;
684              
685 2 50       7 if( $ok ) {
686 2         12 $Test->ok(1, $name);
687             }
688             else {
689 0         0 $Test->diag( "file [$filename] is not readable" );
690 0         0 $Test->ok(0, $name);
691             }
692             }
693              
694             =item file_not_readable_ok( FILENAME [, NAME ] )
695              
696             Ok if the file exists and is not readable, not ok if the file does not
697             exist or is readable.
698              
699             =cut
700              
701             sub file_not_readable_ok {
702 0     0 1 0 my $filename = _normalize( shift );
703 0   0     0 my $name = shift || "$filename is not readable";
704              
705 0         0 my $ok = not -r $filename;
706              
707 0 0       0 if( $ok ) {
708 0         0 $Test->ok(1, $name);
709             }
710             else {
711 0         0 $Test->diag( "file [$filename] is readable" );
712 0         0 $Test->ok(0, $name);
713             }
714             }
715              
716             =item file_writable_ok( FILENAME [, NAME ] )
717              
718             =item file_writeable_ok( FILENAME [, NAME ] )
719              
720             Ok if the file exists and is writable, not ok if the file does not
721             exist or is not writable.
722              
723             The original name is C with that extra I. That
724             still works but there's a function with the correct spelling too.
725              
726             =cut
727              
728             sub file_writeable_ok {
729 0     0 1 0 carp "file_writeable_ok is now file_writable_ok";
730              
731 0         0 &file_writable_ok;
732             }
733              
734             sub file_writable_ok {
735 3     3 1 14238 my $filename = _normalize( shift );
736 3   66     16 my $name = shift || "$filename is writable";
737              
738 3         83 my $ok = -w $filename;
739              
740 3 50       11 if( $ok ) {
741 3         35 $Test->ok(1, $name);
742             }
743             else {
744 0         0 $Test->diag( "file [$filename] is not writable" );
745 0         0 $Test->ok(0, $name);
746             }
747             }
748              
749             =item file_not_writeable_ok( FILENAME [, NAME ] )
750              
751             =item file_not_writable_ok( FILENAME [, NAME ] )
752              
753             Ok if the file exists and is not writable, not ok if the file does not
754             exist or is writable.
755              
756             The original name is C with that extra I.
757             That still works but there's a function with the correct spelling too.
758              
759             =cut
760              
761             sub file_not_writeable_ok {
762 0     0 1 0 carp "file_not_writeable_ok is now file_not_writable_ok";
763              
764 0         0 &file_not_writable_ok;
765             }
766              
767             sub file_not_writable_ok {
768 0     0 1 0 my $filename = _normalize( shift );
769 0   0     0 my $name = shift || "$filename is not writable";
770              
771 0         0 my $ok = not -w $filename;
772              
773 0 0       0 if( $ok ) {
774 0         0 $Test->ok(1, $name);
775             }
776             else {
777 0         0 $Test->diag("file [$filename] is writable");
778 0         0 $Test->ok(0, $name);
779             }
780             }
781              
782             =item file_executable_ok( FILENAME [, NAME ] )
783              
784             Ok if the file exists and is executable, not ok if the file does not
785             exist or is not executable.
786              
787             This test automatically skips if it thinks it is on a Windows
788             platform.
789              
790             =cut
791              
792             sub file_executable_ok {
793 4 100   4 1 4071 if( _win32() ) {
794 1         4 $Test->skip( "file_executable_ok doesn't work on Windows" );
795 1         315 return;
796             }
797              
798 3         9 my $filename = _normalize( shift );
799 3   66     13 my $name = shift || "$filename is executable";
800              
801 3         75 my $ok = -x $filename;
802              
803 3 100       13 if( $ok ) {
804 2         12 $Test->ok(1, $name);
805             }
806             else {
807 1         10 $Test->diag("file [$filename] is not executable");
808 1         308 $Test->ok(0, $name);
809             }
810             }
811              
812             =item file_not_executable_ok( FILENAME [, NAME ] )
813              
814             Ok if the file exists and is not executable, not ok if the file does
815             not exist or is executable.
816              
817             This test automatically skips if it thinks it is on a Windows
818             platform.
819              
820             =cut
821              
822             sub file_not_executable_ok {
823 4 100   4 1 4143 if( _win32() ) {
824 1         4 $Test->skip( "file_not_executable_ok doesn't work on Windows" );
825 1         318 return;
826             }
827              
828 3         10 my $filename = _normalize( shift );
829 3   66     12 my $name = shift || "$filename is not executable";
830              
831 3         92 my $ok = not -x $filename;
832              
833 3 100       11 if( $ok ) {
834 2         13 $Test->ok(1, $name);
835             }
836             else {
837 1         11 $Test->diag("file [$filename] is executable");
838 1         305 $Test->ok(0, $name);
839             }
840             }
841              
842             =item file_mode_is( FILENAME, MODE [, NAME ] )
843              
844             Ok if the file exists and the mode matches, not ok if the file does
845             not exist or the mode does not match.
846              
847             This test automatically skips if it thinks it is on a Windows
848             platform.
849              
850             Contributed by Shawn Sorichetti C<< >>
851              
852             =cut
853              
854             sub file_mode_is {
855 6 100   6 1 8077 if( _win32() ) {
856 1         5 $Test->skip( "file_mode_is doesn't work on Windows" );
857 1         462 return;
858             }
859              
860 5         18 my $filename = _normalize( shift );
861 5         10 my $mode = shift;
862              
863 5   66     124 my $name = shift || sprintf("%s mode is %04o", $filename, $mode);
864              
865 5   66     214 my $ok = -e $filename && ((stat($filename))[2] & 07777) == $mode;
866              
867 5 100       25 if( $ok ) {
868 4         25 $Test->ok(1, $name);
869             }
870             else {
871 1         18 $Test->diag(sprintf("file [%s] mode is not %04o", $filename, $mode) );
872 1         311 $Test->ok(0, $name);
873             }
874             }
875              
876             =item file_mode_isnt( FILENAME, MODE [, NAME ] )
877              
878             Ok if the file exists and mode does not match, not ok if the file does
879             not exist or mode does match.
880              
881             This test automatically skips if it thinks it is on a Windows
882             platform.
883              
884             Contributed by Shawn Sorichetti C<< >>
885              
886             =cut
887              
888             sub file_mode_isnt {
889 6 100   6 1 6752 if( _win32() ) {
890 1         3 $Test->skip( "file_mode_isnt doesn't work on Windows" );
891 1         296 return;
892             }
893              
894 5         18 my $filename = _normalize( shift );
895 5         9 my $mode = shift;
896              
897 5   66     49 my $name = shift || sprintf("%s mode is not %04o",$filename,$mode);
898              
899 5   66     201 my $ok = not (-e $filename && ((stat($filename))[2] & 07777) == $mode);
900              
901 5 100       29 if( $ok ) {
902 4         26 $Test->ok(1, $name);
903             }
904             else {
905 1         11 $Test->diag(sprintf("file [%s] mode is %04o",$filename,$mode));
906 1         308 $Test->ok(0, $name);
907             }
908             }
909              
910             =item file_mode_has( FILENAME, MODE [, NAME ] )
911              
912             Ok if the file exists and has all the bits in mode turned on, not ok
913             if the file does not exist or the mode does not match. That is, C<<
914             FILEMODE & MODE == MODE >> must be true.
915              
916             This test automatically skips if it thinks it is on a Windows
917             platform.
918              
919             Contributed by Ricardo Signes C<< >>
920              
921             =cut
922              
923             sub file_mode_has {
924 4 50   4 1 3102 if( _win32() ) {
925 0         0 $Test->skip( "file_mode_has doesn't work on Windows" );
926 0         0 return;
927             }
928              
929 4         11 my $filename = _normalize( shift );
930 4         9 my $mode = shift;
931              
932 4   66     30 my $name = shift || sprintf("%s mode has all bits of %04o", $filename, $mode);
933              
934 4         106 my $present = -e $filename;
935 4 50       44 my $gotmode = $present ? (stat($filename))[2] : undef;
936 4   66     75 my $ok = $present && ($gotmode & $mode) == $mode;
937              
938 4 100       13 if( $ok ) {
939 2         10 $Test->ok(1, $name);
940             }
941             else {
942 2         6 my $missing = ($gotmode ^ $mode) & $mode;
943 2         59 $Test->diag(sprintf("file [%s] mode is missing component %04o", $filename, $missing) );
944 2         564 $Test->ok(0, $name);
945             }
946             }
947              
948             =item file_mode_hasnt( FILENAME, MODE [, NAME ] )
949              
950             Ok if the file exists and has all the bits in mode turned off, not ok
951             if the file does not exist or the mode does not match. That is,
952             C<< FILEMODE & MODE == 0 >> must be true.
953              
954             This test automatically skips if it thinks it is on a
955             Windows platform.
956              
957             Contributed by Ricardo Signes C<< >>
958              
959             =cut
960              
961             sub file_mode_hasnt {
962 3 50   3 1 2095 if( _win32() ) {
963 0         0 $Test->skip( "file_mode_hasnt doesn't work on Windows" );
964 0         0 return;
965             }
966              
967 3         9 my $filename = _normalize( shift );
968 3         4 my $mode = shift;
969              
970 3   66     26 my $name = shift || sprintf("%s mode has no bits of %04o", $filename, $mode);
971              
972 3         51 my $present = -e $filename;
973 3 50       31 my $gotmode = $present ? (stat($filename))[2] : undef;
974 3   66     19 my $ok = $present && ($gotmode & $mode) == 0;
975              
976 3 100       8 if( $ok ) {
977 2         10 $Test->ok(1, $name);
978             }
979             else {
980 1         3 my $bad = $gotmode & $mode;
981 1         10 $Test->diag(sprintf("file [%s] mode has forbidden component %04o", $filename, $bad) );
982 1         336 $Test->ok(0, $name);
983             }
984             }
985              
986             =item file_is_symlink_ok( FILENAME [, NAME ] )
987              
988             Ok if FILENAME is a symlink, even if it points to a non-existent
989             file. This test automatically skips if the operating system does
990             not support symlinks.
991              
992             =cut
993              
994             sub file_is_symlink_ok {
995 7 100   7 1 34545 if( _no_symlinks_here() ) {
996 1         8 $Test->skip(
997             "file_is_symlink_ok doesn't work on systems without symlinks" );
998 1         378 return;
999             }
1000              
1001 6         13 my $file = shift;
1002 6   66     18 my $name = shift || "$file is a symlink";
1003              
1004 6 100       73 if( -l $file ) {
1005 4         17 $Test->ok(1, $name)
1006             }
1007             else {
1008 2         13 $Test->diag( "file [$file] is not a symlink" );
1009 2         567 $Test->ok(0, $name);
1010             }
1011             }
1012              
1013             =item file_is_not_symlink_ok( FILENAME [, NAME ] )
1014              
1015             Ok if FILENAME is a not symlink. This test automatically skips if the
1016             operating system does not support symlinks. If the file does not
1017             exist, the test fails.
1018              
1019             =cut
1020              
1021             sub file_is_not_symlink_ok {
1022 0 0   0 1 0 if( _no_symlinks_here() ) {
1023 0         0 $Test->skip(
1024             "file_is_symlink_ok doesn't work on systems without symlinks" );
1025 0         0 return;
1026             }
1027              
1028 0         0 my $file = shift;
1029 0   0     0 my $name = shift || "$file is not a symlink";
1030              
1031 0 0       0 unless( -e $file ) {
1032 0         0 $Test->diag( "file [$file] does not exist" );
1033 0         0 return $Test->ok(0, $name);
1034             }
1035              
1036 0 0       0 if( ! -l $file ) {
1037 0         0 $Test->ok(1, $name)
1038             }
1039             else {
1040 0         0 $Test->diag( "file [$file] is a symlink" );
1041 0         0 $Test->ok(0, $name);
1042             }
1043             }
1044              
1045             =item symlink_target_exists_ok( SYMLINK [, TARGET] [, NAME ] )
1046              
1047             Ok if FILENAME is a symlink and it points to a existing file. With the
1048             optional TARGET argument, the test fails if SYMLINK's target is not
1049             TARGET. This test automatically skips if the operating system does not
1050             support symlinks. If the file does not exist, the test fails.
1051              
1052             =cut
1053              
1054             sub symlink_target_exists_ok {
1055 6 100   6 1 18363 if( _no_symlinks_here() ) {
1056 1         6 $Test->skip(
1057             "symlink_target_exists_ok doesn't work on systems without symlinks"
1058             );
1059 1         293 return;
1060             }
1061              
1062 5         11 my $file = shift;
1063 5   66     21 my $dest = shift || readlink( $file );
1064 5   66     20 my $name = shift || "$file is a symlink";
1065              
1066 5 100       58 unless( -l $file )
1067             {
1068 1         7 $Test->diag( "file [$file] is not a symlink" );
1069 1         250 return $Test->ok( 0, $name );
1070             }
1071              
1072 4 100       36 unless( -e $dest ) {
1073 1         8 $Test->diag( "symlink [$file] points to non-existent target [$dest]" );
1074 1         259 return $Test->ok( 0, $name );
1075             }
1076              
1077 3         27 my $actual = readlink( $file );
1078 3 100       10 unless( $dest eq $actual ) {
1079 1         10 $Test->diag(
1080             "symlink [$file] points to\n" .
1081             " got: $actual\n" .
1082             " expected: $dest\n"
1083             );
1084 1         264 return $Test->ok( 0, $name );
1085             }
1086              
1087 2         8 $Test->ok( 1, $name );
1088             }
1089              
1090             =item symlink_target_dangles_ok( SYMLINK [, NAME ] )
1091              
1092             Ok if FILENAME is a symlink and if it doesn't point to a existing
1093             file. This test automatically skips if the operating system does not
1094             support symlinks. If the file does not exist, the test fails.
1095              
1096             =cut
1097              
1098             sub symlink_target_dangles_ok
1099             {
1100 5 100   5 1 12885 if( _no_symlinks_here() ) {
1101 1         7 $Test->skip(
1102             "symlink_target_dangles_ok doesn't work on systems without symlinks" );
1103 1         294 return;
1104             }
1105              
1106 4         10 my $file = shift;
1107 4         38 my $dest = readlink( $file );
1108 4   66     18 my $name = shift || "$file is a symlink";
1109              
1110 4 100       38 unless( -l $file ) {
1111 1         8 $Test->diag( "file [$file] is not a symlink" );
1112 1         247 return $Test->ok( 0, $name );
1113             }
1114              
1115 3 100       26 if( -e $dest ) {
1116 2         14 $Test->diag(
1117             "symlink [$file] points to existing file [$dest] but shouldn't" );
1118 2         496 return $Test->ok( 0, $name );
1119             }
1120              
1121 1         5 $Test->ok( 1, $name );
1122             }
1123              
1124             =item symlink_target_is( SYMLINK, TARGET [, NAME ] )
1125              
1126             Ok if FILENAME is a symlink and if points to TARGET. This test
1127             automatically skips if the operating system does not support symlinks.
1128             If the file does not exist, the test fails.
1129              
1130             =cut
1131              
1132             sub symlink_target_is {
1133 4 100   4 1 8117 if( _no_symlinks_here() ) {
1134 1         6 $Test->skip(
1135             "symlink_target_is doesn't work on systems without symlinks" );
1136 1         295 return;
1137             }
1138              
1139 3         7 my $file = shift;
1140 3         4 my $dest = shift;
1141 3   66     11 my $name = shift || "symlink $file points to $dest";
1142              
1143 3 100       36 unless( -l $file ) {
1144 1         7 $Test->diag( "file [$file] is not a symlink" );
1145 1         246 return $Test->ok( 0, $name );
1146             }
1147              
1148 2         16 my $actual_dest = readlink( $file );
1149 2         10 my $link_error = $!;
1150              
1151 2 50       6 unless( defined $actual_dest ) {
1152 0         0 $Test->diag( "symlink [$file] does not have a defined target" );
1153 0 0       0 $Test->diag( "readlink error: $link_error" ) if defined $link_error;
1154 0         0 return $Test->ok( 0, $name );
1155             }
1156              
1157 2 100       5 if( $dest eq $actual_dest ) {
1158 1         12 $Test->ok( 1, $name );
1159             }
1160             else {
1161 1         5 $Test->ok( 0, $name );
1162 1         909 $Test->diag(" got: $actual_dest" );
1163 1         231 $Test->diag(" expected: $dest" );
1164             }
1165             }
1166              
1167             =item symlink_target_is_absolute_ok( SYMLINK [, NAME ] )
1168              
1169             Ok if FILENAME is a symlink and if its target is an absolute path.
1170             This test automatically skips if the operating system does not support
1171             symlinks. If the file does not exist, the test fails.
1172              
1173             =cut
1174              
1175             sub symlink_target_is_absolute_ok {
1176 0 0   0 1 0 if( _no_symlinks_here() ) {
1177 0         0 $Test->skip(
1178             "symlink_target_exists_ok doesn't work on systems without symlinks" );
1179 0         0 return;
1180             }
1181              
1182 0         0 my( $from, $from_base, $to, $to_base, $name ) = @_;
1183 0         0 my $link = readlink( $from );
1184 0 0       0 my $link_err = defined( $link ) ? '' : $!; # $! doesn't always get reset
1185 0         0 my $link_abs = abs_path( rel2abs($link, $from_base) );
1186 0         0 my $to_abs = abs_path( rel2abs($to, $to_base) );
1187              
1188 0 0 0     0 if (defined( $link_abs ) && defined( $to_abs ) && $link_abs eq $to_abs) {
      0        
1189 0         0 $Test->ok( 1, $name );
1190             }
1191             else {
1192 0         0 $Test->ok( 0, $name );
1193 0   0     0 $link ||= 'undefined';
1194 0   0     0 $link_abs ||= 'undefined';
1195 0   0     0 $to_abs ||= 'undefined';
1196              
1197 0         0 $Test->diag(" link: $from");
1198 0         0 $Test->diag(" got: $link");
1199 0         0 $Test->diag(" (abs): $link_abs");
1200 0         0 $Test->diag(" expected: $to");
1201 0         0 $Test->diag(" (abs): $to_abs");
1202 0 0       0 $Test->diag(" readlink() error: $link_err") if ($link_err);
1203             }
1204             }
1205              
1206             =item dir_exists_ok( DIRECTORYNAME [, NAME ] )
1207              
1208             Ok if the file exists and is a directory, not ok if the file doesn't exist, or exists but isn't a
1209             directory.
1210              
1211             Contributed by Buddy Burden C<< >>.
1212              
1213             =cut
1214              
1215             sub dir_exists_ok {
1216 4     4 1 30002 my $filename = _normalize( shift );
1217 4   66     20 my $name = shift || "$filename is a directory";
1218              
1219 4 100       89 unless( -e $filename ) {
1220 1         9 $Test->diag( "directory [$filename] does not exist" );
1221 1         313 return $Test->ok(0, $name);
1222             }
1223              
1224 3         36 my $ok = -d $filename;
1225              
1226 3 100       9 if( $ok ) {
1227 2         8 $Test->ok(1, $name);
1228             }
1229             else {
1230 1         7 $Test->diag( "file [$filename] exists but is not a directory" );
1231 1         254 $Test->ok(0, $name);
1232             }
1233             }
1234              
1235             =item dir_contains_ok( DIRECTORYNAME, FILENAME [, NAME ] )
1236              
1237             Ok if the directory exists and contains the file, not ok if the directory doesn't exist, or exists
1238             but doesn't contain the file.
1239              
1240             Contributed by Buddy Burden C<< >>.
1241              
1242             =cut
1243              
1244             sub dir_contains_ok {
1245 4     4 1 9693 my $dirname = _normalize( shift );
1246 4         9 my $filename = _normalize( shift );
1247 4   66     22 my $name = shift || "directory $dirname contains file $filename";
1248              
1249 4 100       119 unless( -d $dirname ) {
1250 1         8 $Test->diag( "directory [$dirname] does not exist" );
1251 1         320 return $Test->ok(0, $name);
1252             }
1253              
1254 3         87 my $ok = -e File::Spec->catfile($dirname, $filename);
1255              
1256 3 100       14 if( $ok ) {
1257 2         7 $Test->ok(1, $name);
1258             }
1259             else {
1260 1         17 $Test->diag( "file [$filename] does not exist in directory $dirname" );
1261 1         262 $Test->ok(0, $name);
1262             }
1263             }
1264              
1265             =item link_count_is_ok( FILE, LINK_COUNT [, NAME ] )
1266              
1267             Ok if the link count to FILE is LINK_COUNT. LINK_COUNT is interpreted
1268             as an integer. A LINK_COUNT that evaluates to 0 returns Ok if the file
1269             does not exist.
1270              
1271              
1272             =cut
1273              
1274             sub link_count_is_ok {
1275 3     3 1 6911 my $file = shift;
1276 3         7 my $count = int( 0 + shift );
1277              
1278 3   66     32 my $name = shift || "$file has a link count of [$count]";
1279              
1280 3         48 my $actual = ( stat $file )[3];
1281              
1282 3 100       29 unless( $actual == $count ) {
1283 1         9 $Test->diag(
1284             "file [$file] points has [$actual] links: expected [$count]" );
1285 1         299 return $Test->ok( 0, $name );
1286             }
1287              
1288 2         9 $Test->ok( 1, $name );
1289             }
1290              
1291             =item link_count_gt_ok( FILE, LINK_COUNT [, NAME ] )
1292              
1293             Ok if the link count to FILE is greater than LINK_COUNT. LINK_COUNT is
1294             interpreted as an integer. A LINK_COUNT that evaluates to 0 returns Ok
1295             if the file has at least one link.
1296              
1297             =cut
1298              
1299             sub link_count_gt_ok {
1300 3     3 1 3512 my $file = shift;
1301 3         6 my $count = int( 0 + shift );
1302              
1303 3   66     12 my $name = shift || "$file has a link count of [$count]";
1304              
1305 3         49 my $actual = (stat $file )[3];
1306              
1307 3 100       14 unless( $actual > $count ) {
1308 1         9 $Test->diag(
1309             "file [$file] points has [$actual] links: ".
1310             "expected more than [$count]" );
1311 1         293 return $Test->ok( 0, $name );
1312             }
1313              
1314 2         8 $Test->ok( 1, $name );
1315             }
1316              
1317             =item link_count_lt_ok( FILE, LINK_COUNT [, NAME ] )
1318              
1319             Ok if the link count to FILE is less than LINK_COUNT. LINK_COUNT is
1320             interpreted as an integer. A LINK_COUNT that evaluates to 0 returns Ok
1321             if the file has at least one link.
1322              
1323             =cut
1324              
1325             sub link_count_lt_ok {
1326 3     3 1 28363 my $file = shift;
1327 3         4 my $count = int( 0 + shift );
1328              
1329 3   66     15 my $name = shift || "$file has a link count of [$count]";
1330              
1331 3         42 my $actual = (stat $file )[3];
1332              
1333 3 100       13 unless( $actual < $count ) {
1334 1         8 $Test->diag(
1335             "file [$file] points has [$actual] links: ".
1336             "expected less than [$count]" );
1337 1         253 return $Test->ok( 0, $name );
1338             }
1339              
1340 2         7 $Test->ok( 1, $name );
1341             }
1342              
1343              
1344             # owner_is, owner_isnt, group_is and group_isnt are almost
1345             # identical in the beginning, so I'm writing a skeleton they can all use.
1346             # I can't think of a better name...
1347             sub _dm_skeleton {
1348 17     17   173 no warnings 'uninitialized';
  17         110  
  17         24132  
1349              
1350 22 100   22   163 if( _obviously_non_multi_user() ) {
1351 3         17 my $calling_sub = (caller(1))[3];
1352 3         15 $Test->skip( $calling_sub . " only works on a multi-user OS" );
1353 3         1082 return 'skip';
1354             }
1355              
1356 19         59 my $filename = _normalize( shift );
1357 19         41 my $testing_for = shift;
1358 19         26 my $name = shift;
1359              
1360 19 100       41 unless( defined $filename ) {
1361 1         4 $Test->diag( "file name not specified" );
1362 1         249 return $Test->ok( 0, $name );
1363             }
1364              
1365 18 100       271 unless( -e $filename ) {
1366 1         7 $Test->diag( "file [$filename] does not exist" );
1367 1         255 return $Test->ok( 0, $name );
1368             }
1369              
1370 17         54 return;
1371             }
1372              
1373             =item owner_is( FILE , OWNER [, NAME ] )
1374              
1375             Ok if FILE's owner is the same as OWNER. OWNER may be a text user name
1376             or a numeric userid. Test skips on Dos, and Mac OS <= 9.
1377             If the file does not exist, the test fails.
1378              
1379             Contributed by Dylan Martin
1380              
1381             =cut
1382              
1383             sub owner_is {
1384 4     4 1 7712 my $filename = shift;
1385 4         10 my $owner = shift;
1386 4   66     25 my $name = shift || "$filename belongs to $owner";
1387              
1388 4         14 my $err = _dm_skeleton( $filename, $owner, $name );
1389 4 50 33     12 return if( defined( $err ) && $err eq 'skip' );
1390 4 50       12 return $err if defined($err);
1391              
1392 4         22 my $owner_uid = _get_uid( $owner );
1393 4 100       12 unless( defined $owner_uid ) {
1394 1         8 $Test->diag("user [$owner] does not exist on this system");
1395 1         356 return $Test->ok( 0, $name );
1396             }
1397              
1398 3         31 my $file_uid = ( stat $filename )[4];
1399              
1400 3 50       14 unless( defined $file_uid ) {
1401 0         0 $Test->skip("stat failed to return owner uid for $filename");
1402 0         0 return;
1403             }
1404              
1405 3 100       22 return $Test->ok( 1, $name ) if $file_uid == $owner_uid;
1406              
1407 1         66 my $real_owner = ( getpwuid $file_uid )[0];
1408 1 50       6 unless( defined $real_owner ) {
1409 0         0 $Test->diag("file does not belong to $owner");
1410 0         0 return $Test->ok( 0, $name );
1411             }
1412              
1413 1         11 $Test->diag( "file [$filename] belongs to $real_owner ($file_uid), ".
1414             "not $owner ($owner_uid)" );
1415 1         399 return $Test->ok( 0, $name );
1416             }
1417              
1418             =item owner_isnt( FILE, OWNER [, NAME ] )
1419              
1420             Ok if FILE's owner is not the same as OWNER. OWNER may be a text user name
1421             or a numeric userid. Test skips on Dos and Mac OS <= 9. If the file
1422             does not exist, the test fails.
1423              
1424             Contributed by Dylan Martin
1425              
1426             =cut
1427              
1428             sub owner_isnt {
1429 4     4 1 5643 my $filename = shift;
1430 4         8 my $owner = shift;
1431 4   66     22 my $name = shift || "$filename doesn't belong to $owner";
1432              
1433 4         14 my $err = _dm_skeleton( $filename, $owner, $name );
1434 4 50 33     16 return if( defined( $err ) && $err eq 'skip' );
1435 4 50       10 return $err if defined($err);
1436              
1437 4         12 my $owner_uid = _get_uid( $owner );
1438 4 100       14 unless( defined $owner_uid ) {
1439 1         7 return $Test->ok( 1, $name );
1440             }
1441              
1442 3         34 my $file_uid = ( stat $filename )[4];
1443              
1444             #$Test->diag( "owner_isnt: $owner_uid $file_uid" );
1445 3 100       16 return $Test->ok( 1, $name ) if $file_uid != $owner_uid;
1446              
1447 1         11 $Test->diag( "file [$filename] belongs to $owner ($owner_uid)" );
1448 1         292 return $Test->ok( 0, $name );
1449             }
1450              
1451             =item group_is( FILE , GROUP [, NAME ] )
1452              
1453             Ok if FILE's group is the same as GROUP. GROUP may be a text group name or
1454             a numeric group id. Test skips on Dos, Mac OS <= 9 and any other operating
1455             systems that do not support getpwuid() and friends. If the file does not
1456             exist, the test fails.
1457              
1458             Contributed by Dylan Martin
1459              
1460             =cut
1461              
1462             sub group_is {
1463 5     5 1 6836 my $filename = shift;
1464 5         10 my $group = shift;
1465 5   66     23 my $name = ( shift || "$filename belongs to group $group" );
1466              
1467 5         16 my $err = _dm_skeleton( $filename, $group, $name );
1468 5 50 33     16 return if( defined( $err ) && $err eq 'skip' );
1469 5 50       11 return $err if defined($err);
1470              
1471 5         15 my $group_gid = _get_gid( $group );
1472 5 100       16 unless( defined $group_gid ) {
1473 1         10 $Test->diag("group [$group] does not exist on this system");
1474 1         292 return $Test->ok( 0, $name );
1475             }
1476              
1477 4         44 my $file_gid = ( stat $filename )[5];
1478              
1479 4 50       15 unless( defined $file_gid ) {
1480 0         0 $Test->skip("stat failed to return group gid for $filename");
1481 0         0 return;
1482             }
1483              
1484 4 100       19 return $Test->ok( 1, $name ) if $file_gid == $group_gid;
1485              
1486 1         37 my $real_group = ( getgrgid $file_gid )[0];
1487 1 50       8 unless( defined $real_group ) {
1488 0         0 $Test->diag("file does not belong to $group");
1489 0         0 return $Test->ok( 0, $name );
1490             }
1491              
1492 1         10 $Test->diag( "file [$filename] belongs to $real_group ($file_gid), ".
1493             "not $group ($group_gid)" );
1494              
1495 1         298 return $Test->ok( 0, $name );
1496             }
1497              
1498             =item group_isnt( FILE , GROUP [, NAME ] )
1499              
1500             Ok if FILE's group is not the same as GROUP. GROUP may be a text group name or
1501             a numeric group id. Test skips on Dos, Mac OS <= 9 and any other operating
1502             systems that do not support getpwuid() and friends. If the file does not
1503             exist, the test fails.
1504              
1505             Contributed by Dylan Martin
1506              
1507             =cut
1508              
1509             sub group_isnt {
1510 4     4 1 3637 my $filename = shift;
1511 4         8 my $group = shift;
1512 4   66     24 my $name = shift || "$filename does not belong to group $group";
1513              
1514 4         13 my $err = _dm_skeleton( $filename, $group, $name );
1515 4 50 33     14 return if( defined( $err ) && $err eq 'skip' );
1516 4 50       8 return $err if defined($err);
1517              
1518 4         12 my $group_gid = _get_gid( $group );
1519 4         41 my $file_gid = ( stat $filename )[5];
1520              
1521 4 50       14 unless( defined $file_gid ) {
1522 0         0 $Test->skip("stat failed to return group gid for $filename");
1523 0         0 return;
1524             }
1525              
1526 4 100       20 return $Test->ok( 1, $name ) if $file_gid != $group_gid;
1527              
1528 1         26 $Test->diag( "file [$filename] belongs to $group ($group_gid)" );
1529 1         297 return $Test->ok( 0, $name );
1530             }
1531              
1532             sub _get_uid {
1533 8     8   17 my $arg = shift;
1534              
1535             # the name might be numeric (why would you do that?), so we need
1536             # to figure out which of several possibilities we have. And, 0 means
1537             # root, so we have to be very careful with the values.
1538              
1539             # maybe the argument is a UID. First, it has to be numeric. If it's
1540             # a UID, we'll get the same UID back. But, if we get back a value
1541             # that doesn't mean that we are done. There might be a name with
1542             # the same value.
1543             #
1544             # Don't use this value in comparisons! An undef could be turned
1545             # into zero!
1546 8 100       180 my $from_uid = (getpwuid($arg))[2] if $arg =~ /\A[0-9]+\z/;
1547              
1548             # Now try the argument as a name. If it's a name, then we'll get
1549             # back a UID. Maybe we get back nothing.
1550 8         495 my $from_nam = (getpwnam($arg))[2];
1551              
1552 8         23 return do {
1553             # first case, we got back nothing from getpwnam but did get
1554             # something from getpwuid. The arg is not a name and is a
1555             # UID.
1556 8 100 66     65 if( defined $from_uid and not defined $from_nam ) { $arg }
  2 100 66     5  
    50 33        
1557             # second case, we got back nothing from getpwuid but did get
1558             # something from getpwnam. The arg is a name and is not a
1559             # UID.
1560 4         12 elsif( not defined $from_uid and defined $from_nam ) { $from_nam }
1561             # Now, what happens if neither are defined? The argument does
1562             # not correspond to a name or GID on the system. Since no such
1563             # user exists, we return undef.
1564 2         7 elsif( not defined $from_uid and not defined $from_nam ) { undef }
1565             # But what if they are both defined? The argument could represent
1566             # a UID and a name, and those could be different users! In this
1567             # case, we'll choose the original argument. That might be wrong,
1568             # so the best we can do is a warning.
1569             else {
1570 0         0 carp( "Found both a UID or name for <$arg>. Guessing the UID is <$arg>." );
1571 0         0 $arg
1572             }
1573             };
1574             }
1575              
1576             sub _get_gid {
1577 9     9   15 my $arg = shift;
1578              
1579             # the name might be numeric (why would you do that?), so we need
1580             # to figure out which of several possibilities we have. And, 0 means
1581             # root, so we have to be very careful with the values.
1582              
1583             # maybe the argument is a GID. First, it has to be numeric. If it's
1584             # a GID, we'll get the same GID back. But, if we get back a value
1585             # that doesn't mean that we are done. There might be a name with
1586             # the same value.
1587             #
1588             # Don't use this value in comparisons! An undef could be turned
1589             # into zero!
1590 9 100       154 my $from_gid = (getgrgid($arg))[2] if $arg =~ /\A[0-9]+\z/;
1591              
1592             # Now try the argument as a name. If it's a name, then we'll get
1593             # back a GID. Maybe we get back nothing.
1594 9         413 my $from_nam = (getgrnam($arg))[2];
1595              
1596 9         24 return do {
1597             # first case, we got back nothing from getgrnam but did get
1598             # something from getpwuid. The arg is not a name and is a
1599             # GID.
1600 9 100 66     65 if( defined $from_gid and not defined $from_nam ) { $arg }
  3 100 66     11  
    50 33        
1601             # second case, we got back nothing from getgrgid but did get
1602             # something from getgrnam. The arg is a name and is not a
1603             # GID.
1604 5         14 elsif( not defined $from_gid and defined $from_nam ) { $from_nam }
1605             # Now, what happens if neither are defined? The argument does
1606             # not correspond to a name or GID on the system. Since no such
1607             # user exists, we return undef.
1608 1         4 elsif( not defined $from_gid and not defined $from_nam ) { undef }
1609             # But what if they are both defined? The argument could represent
1610             # a GID and a name, and those could be different users! In this
1611             # case, we'll choose the original argument. That might be wrong,
1612             # so the best we can do is a warning.
1613             else {
1614 0         0 carp( "Found both a GID or name for <$arg>. Guessing the GID is <$arg>." );
1615 0         0 $arg;
1616             }
1617             };
1618             }
1619              
1620             =item file_mtime_age_ok( FILE [, WITHIN_SECONDS ] [, NAME ] )
1621              
1622             Ok if FILE's modified time is WITHIN_SECONDS inclusive of the system's current time.
1623             This test uses stat() to obtain the mtime. If the file does not exist the test
1624             returns failure. If stat() fails, the test is skipped.
1625              
1626             =cut
1627              
1628             sub file_mtime_age_ok {
1629 4     4 1 28754 my $filename = shift;
1630 4   100     13 my $within_secs = shift || 0;
1631 4   66     16 my $name = shift || "$filename mtime within $within_secs seconds of current time";
1632              
1633 4         6 my $time = time();
1634              
1635 4         9 my $filetime = _stat_file($filename, 9);
1636              
1637 4 50       10 return if ( $filetime == -1 ); #skip
1638              
1639 4 100       13 return $Test->ok(1, $name) if ( $filetime + $within_secs > $time-1 );
1640              
1641 2         15 $Test->diag( "file [$filename] mtime [$filetime] is not $within_secs seconds within current system time [$time].");
1642 2         599 return $Test->ok(0, $name);
1643             }
1644              
1645             =item file_mtime_gt_ok( FILE, UNIXTIME [, NAME ] )
1646              
1647             Ok if FILE's mtime is > UNIXTIME. This test uses stat() to get the mtime. If stat() fails
1648             this test is skipped. If FILE does not exist, this test fails.
1649              
1650             =cut
1651              
1652             sub file_mtime_gt_ok {
1653 3     3 1 7446 my $filename = shift;
1654 3         5 my $time = int shift;
1655 3   66     12 my $name = shift || "$filename mtime is greater than unix timestamp $time";
1656              
1657 3         6 my $filetime = _stat_file($filename, 9);
1658              
1659 3 50       7 return if ( $filetime == -1 ); #skip
1660              
1661 3 100       12 return $Test->ok(1, $name) if ( $filetime > $time );
1662              
1663 1         9 $Test->diag( "file [$filename] mtime [$filetime] not greater than $time" );
1664 1         255 $Test->ok(0, $name);
1665             }
1666              
1667             =item file_mtime_lt_ok( FILE, UNIXTIME, [, NAME ] )
1668              
1669             Ok if FILE's modified time is < UNIXTIME. This test uses stat() to get the mtime. If stat() fails
1670             this test is skipped. If FILE does not exist, this test fails.
1671              
1672             =cut
1673              
1674             sub file_mtime_lt_ok {
1675 3     3 1 7882 my $filename = shift;
1676 3         6 my $time = int shift;
1677 3   66     12 my $name = shift || "$filename mtime less than unix timestamp $time";
1678              
1679             # gets mtime
1680 3         6 my $filetime = _stat_file($filename, 9);
1681              
1682 3 50       8 return if ( $filetime == -1 ); #skip
1683              
1684 3 100       11 return $Test->ok(1, $name) if ( $filetime < $time );
1685              
1686 1         9 $Test->diag( "file [$filename] mtime [$filetime] not less than $time" );
1687 1         253 $Test->ok(0, $name);
1688             }
1689              
1690             # private function to safely stat a file
1691             #
1692             # Arugments:
1693             # filename file to perform on
1694             # attr_pos pos of the array returned from stat we want to compare. perldoc -f stat
1695             #
1696             # Returns:
1697             # -1 - stat failed
1698             # 0 - failure (file doesn't exist etc)
1699             # filetime - on success, time requested provided by stat
1700             #
1701             sub _stat_file {
1702 12     12   6808 my $filename = _normalize( shift );
1703 12         18 my $attr_pos = shift;
1704              
1705 12 100       20 unless( defined $filename ) {
1706 1         4 $Test->diag( "file name not specified" );
1707 1         236 return 0;
1708             }
1709              
1710 11 100       152 unless( -e $filename ) {
1711 1         8 $Test->diag( "file [$filename] does not exist" );
1712 1         249 return 0;
1713             }
1714              
1715 10         86 my $filetime = ( stat($filename) )[$attr_pos];
1716              
1717 10 50       27 unless( $filetime ) {
1718 0         0 $Test->diag( "stat of $filename failed" );
1719 0         0 return -1; #skip on stat failure
1720             }
1721              
1722 10         21 return $filetime;
1723             }
1724              
1725             =back
1726              
1727             =head1 TO DO
1728              
1729             * check properties for other users (readable_by_root, for instance)
1730              
1731             * check times
1732              
1733             * check number of links to file
1734              
1735             * check path parts (directory, filename, extension)
1736              
1737             =head1 SEE ALSO
1738              
1739             L,
1740             L
1741              
1742             If you are using the new C stuff, see Test2::Tools::File
1743             (https://github.com/torbjorn/Test2-Tools-File).
1744              
1745             =head1 SOURCE AVAILABILITY
1746              
1747             This module is in Github:
1748              
1749             https://github.com/briandfoy/test-file.git
1750              
1751             =head1 AUTHOR
1752              
1753             brian d foy, C<< >>
1754              
1755             =head1 CREDITS
1756              
1757             Shawn Sorichetti C<< >> provided
1758             some functions.
1759              
1760             Tom Metro helped me figure out some Windows capabilities.
1761              
1762             Dylan Martin added C and C.
1763              
1764             David Wheeler added C.
1765              
1766             Buddy Burden C<< >> provided C,
1767             C, C, and
1768             C.
1769              
1770             xmikew C<< >> provided the C
1771             stuff.
1772              
1773             Torbjørn Lindahl is working on L and we're
1774             working together to align our interfaces.
1775              
1776             =head1 COPYRIGHT AND LICENSE
1777              
1778             Copyright © 2002-2022, brian d foy . All rights reserved.
1779              
1780             This program is free software; you can redistribute it and/or modify
1781             it under the terms of the Artistic License 2.0
1782              
1783             =cut
1784              
1785             "The quick brown fox jumped over the lazy dog";