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