File Coverage

blib/lib/Test/Directory.pm
Criterion Covered Total %
statement 177 177 100.0
branch 71 72 98.6
condition 15 15 100.0
subroutine 30 30 100.0
pod 19 19 100.0
total 312 313 99.6


line stmt bran cond sub pod time code
1             package Test::Directory;
2              
3 9     9   220229 use strict;
  9         38  
  9         251  
4 9     9   43 use warnings;
  9         18  
  9         205  
5              
6 9     9   43 use Carp;
  9         16  
  9         533  
7 9     9   53 use Fcntl;
  9         44  
  9         2533  
8 9     9   62 use File::Spec;
  9         34  
  9         241  
9 9     9   6443 use File::Temp;
  9         163181  
  9         654  
10 9     9   77 use Test::Builder::Module;
  9         28  
  9         77  
11              
12             our $VERSION = '0.052';
13             our @ISA = 'Test::Builder::Module';
14              
15             ##############################
16             # Constructor / Destructor
17             ##############################
18              
19             my $template = 'test-directory-tmp-XXXXX';
20              
21             sub new {
22 15     15 1 6467 my $class = shift;
23 15         28 my $dir = shift;
24 15         34 my %opts = @_;
25              
26 15 100       50 if (defined $dir) {
27 12         212 $dir = File::Spec->join(split '/', $dir);
28 12 100       1262 mkdir $dir or croak "Failed to create '$dir': $!";
29             } else {
30 3         26 $dir = File::Temp->newdir( $template, CLEANUP=>0, DIR=>'.' )->dirname;
31             };
32              
33 13         1399 my %self = (dir => $dir);
34 13         136 bless \%self, $class;
35             }
36              
37             sub DESTROY {
38 13     13   2392 $_[0]->clean;
39             }
40              
41             ##############################
42             # Utility Functions
43             ##############################
44              
45             sub name {
46 193     193 1 1718 my ($self,$path) = @_;
47 193         498 my @path = split /\//, $path;
48 193         329 my $file = pop @path;
49 193 100       4566 return @path ? File::Spec->catfile(@path,$file) : $file;
50             };
51              
52             sub path {
53 156     156 1 8021 my ($self,$file) = @_;
54             return defined($file)?
55             File::Spec->catfile($self->{dir}, $self->name($file)):
56 156 100       567 $self->{dir};
57             };
58              
59              
60             sub touch {
61 7     7 1 716 my $self = shift;
62 7         20 foreach my $file (@_) {
63 9         31 my $path = $self->path($file);
64 9 100       617 sysopen my($fh), $path, O_WRONLY|O_CREAT|O_EXCL
65             or croak "$path: $!";
66 8         154 $self->{files}{$file} = 1;
67             };
68             };
69              
70             sub create {
71 5     5 1 908 my ($self, $file, %opt) = @_;
72 5         13 my $path = $self->path($file);
73            
74 5 100       315 sysopen my($fh), $path, O_WRONLY|O_CREAT|O_EXCL
75             or croak "$path: $!";
76            
77 3         15 $self->{files}{$file} = 1;
78            
79 3 100       10 if (defined $opt{content}) {
80 1         19 print $fh $opt{content};
81             };
82 3 100       10 if (defined $opt{time}) {
83 1         24 utime $opt{time}, $opt{time}, $path;
84             };
85 3         95 return $path;
86             }
87              
88             sub mkdir {
89 10     10 1 1332 my ($self, $dir) = @_;
90 10         24 my $path = $self->path($dir);
91 10 100       552 mkdir($path) or croak "$path: $!";
92 9         64 $self->{directories}{$dir} = 1;
93             }
94              
95             sub check_file {
96 15     15 1 742 my ($self,$file) = @_;
97 15         23 my $rv;
98 15 100       32 if (-f $self->path($file)) {
99 10         37 $rv = $self->{files}{$file} = 1;
100             } else {
101 5         20 $rv = $self->{files}{$file} = 0;
102             }
103 15         76 return $rv;
104             }
105              
106             sub check_directory {
107 9     9 1 78 my ($self,$dir) = @_;
108 9         13 my $rv;
109 9 100       21 if (-d $self->path($dir)) {
110 5         25 $rv = $self->{directories}{$dir} = 1;
111             } else {
112 4         16 $rv = $self->{directories}{$dir} = 0;
113             }
114 9         53 return $rv;
115             }
116              
117             sub clean {
118 15     15 1 48 my $self = shift;
119 15         27 foreach my $file ( keys %{$self->{files}} ) {
  15         59  
120 19         63 unlink $self->path($file);
121             };
122             #get subdirs before parents
123 15         46 foreach my $dir (sort {length($b) <=> length($a)}
  17         42  
124 15         82 keys %{$self->{directories}} ) {
125 18         60 rmdir $self->path($dir);
126             };
127 15         617 my $rv = rmdir $self->{dir};
128 15 100       460 carp "$self->{dir}: $!" unless $rv;
129 15         1492 return $rv;
130             }
131              
132             sub _path_map {
133 13     13   23 my $self = shift;
134 13         23 my %path;
135 13         20 while (my ($k,$v) = each %{$self->{files}}) {
  36         113  
136 23         49 $path{ $self->name($k) } = $v;
137             };
138 13         23 while (my ($k,$v) = each %{$self->{directories}}) {
  26         84  
139 13         26 $path{ $self->name($k) } = $v;
140             };
141 13         29 return \%path;
142             }
143              
144             sub count_unknown {
145 4     4 1 159 my $self = shift;
146 4         13 my $path = $self->_path_map;
147 4 100       180 opendir my($dh), $self->{dir} or croak "$self->{dir}: $!";
148              
149 3         12 my $count = 0;
150 3         77 while (my $file = readdir($dh)) {
151 17 100       40 next if $file eq '.';
152 14 100       51 next if $file eq '..';
153 11 100       36 next if $path->{$file};
154 4         44 ++ $count;
155             }
156 3         80 return $count;
157             };
158              
159             sub count_missing {
160 2     2 1 13 my $self = shift;
161              
162 2         4 my $count = 0;
163 2         4 while (my($file,$has) = each %{$self->{files}}) {
  10         45  
164 8 100 100     30 ++ $count if ($has and not(-f $self->path($file)));
165             }
166 2         6 while (my($file,$has) = each %{$self->{directories}}) {
  5         22  
167 3 100 100     10 ++ $count if ($has and not(-d $self->path($file)));
168             }
169 2         10 return $count;
170             }
171              
172              
173             sub remove_files {
174 2     2 1 5 my $self = shift;
175 2         4 my $count = 0;
176 2         8 foreach my $file (@_) {
177 2         7 my $path = $self->path($file);
178 2         7 $self->{files}{$file} = 0;
179 2         98 $count += unlink($path);
180             }
181 2         36 return $count;
182             }
183              
184             sub remove_directories {
185 3     3 1 13 my $self = shift;
186 3         5 my $count = 0;
187 3         7 foreach my $file (@_) {
188 3         8 my $path = $self->path($file);
189 3         9 $self->{directories}{$file} = 0;
190 3 100       182 $count ++ if rmdir($path);
191             }
192 3         21 return $count;
193             }
194              
195             ##############################
196             # Test Functions
197             ##############################
198              
199             sub has {
200 6     6 1 8156 my ($self,$file,$text) = @_;
201 6 100       26 $text = "Has file $file." unless defined $text;
202 6         31 $self->builder->ok( $self->check_file($file), $text );
203             }
204              
205             sub hasnt {
206 5     5 1 9208 my ($self,$file,$text) = @_;
207 5 100       18 $text = "Doesn't have file $file." unless defined $text;
208 5         17 $self->builder->ok( not($self->check_file($file)), $text );
209             }
210              
211             sub has_dir {
212 4     4 1 3927 my ($self,$file,$text) = @_;
213 4 100       17 $text = "Has directory $file." unless defined $text;
214 4         16 $self->builder->ok( $self->check_directory($file), $text );
215             }
216              
217             sub hasnt_dir {
218 3     3 1 4186 my ($self,$file,$text) = @_;
219 3 100       14 $text = "Doesn't have directory $file." unless defined $text;
220 3         12 $self->builder->ok( not($self->check_directory($file)), $text );
221             }
222              
223             sub clean_ok {
224 2     2 1 4966 my ($self,$text) = @_;
225 2         7 $self->builder->ok($self->clean, $text);
226             }
227              
228             sub _check_dir {
229 9     9   21 my ($dir, $path, $unknown) = @_;
230 9 100       318 opendir my($dh), $dir or croak "$dir: $!";
231              
232 8         179 while (my $file = readdir($dh)) {
233 30 100       86 next if $file eq '.';
234 22 100       186 next if $file eq '..';
235 14 100       68 next if $path->{$file};
236 3         25 push @$unknown, $file;
237             }
238             };
239              
240             sub _check_subdir {
241 4     4   11 my ($self, $dir, $path, $unknown) = @_;
242 4 50       24 opendir my($dh), $self->path($dir) or croak "$self->path(dir): $!";
243              
244 4         76 while (my $file = readdir($dh)) {
245 12 100       42 next if $file eq '.';
246 8 100       112 next if $file eq '..';
247 4         17 my $name = $self->name("$dir/$file");
248 4 100       28 next if $path->{ $name };
249 1         5 push @$unknown, $name;
250             }
251             };
252              
253             sub is_ok {
254 9     9 1 9917 my $self = shift;
255 9         17 my $name = shift;
256 9         42 my $test = $self->builder;
257 9 100       124 $name = "Directory is consistent" unless defined $name;
258              
259 9         17 my @miss;
260 9         17 while (my($file,$has) = each %{$self->{files}}) {
  23         96  
261 14 100 100     63 if ($has and not(-f $self->path($file))) {
262 1         6 push @miss, $file;
263             }
264             }
265 9         19 my @miss_d;
266 9         18 while (my($file,$has) = each %{$self->{directories}}) {
  17         72  
267 8 100 100     47 if ($has and not(-d $self->path($file))) {
268 2         11 push @miss_d, $file;
269             }
270             }
271              
272              
273 9         28 my $path = $self->_path_map;
274 9         24 my @unknown;
275              
276 9         33 _check_dir($self->{dir}, $path, \@unknown);
277 8         24 while (my($file,$has) = each %{$self->{directories}}) {
  16         71  
278 8         23 my $dir = $self->path($file);
279 8 100 100     116 if ($has and -d $dir) {
280 4         21 $self->_check_subdir($file, $path, \@unknown);
281             }
282             }
283              
284 8         47 my $rv = $test->ok((@miss+@unknown+@miss_d) == 0, $name);
285 8 100       5421 unless ($rv) {
286 4         16 $test->diag("Missing file: $_") foreach @miss;
287 4         245 $test->diag("Missing directory: $_") foreach @miss_d;
288 4         471 $test->diag("Unknown file: $_") foreach @unknown;
289             }
290 8         959 return $rv;
291             }
292              
293              
294              
295             1;
296             __END__