File Coverage

blib/lib/Test/Files.pm
Criterion Covered Total %
statement 196 199 98.4
branch 57 60 95.0
condition 5 6 83.3
subroutine 19 19 100.0
pod 7 8 87.5
total 284 292 97.2


line stmt bran cond sub pod time code
1             package Test::Files;
2 8     8   313036 use Test::Builder;
  8         20  
  8         227  
3 8     8   9304 use Text::Diff;
  8         112754  
  8         693  
4 8     8   108 use File::Find;
  8         27  
  8         561  
5 8     8   50 use File::Spec;
  8         20  
  8         174  
6              
7 8     8   40 use strict;
  8         15  
  8         275  
8 8     8   44 use warnings; # This is off in Test::More, eventually it may have to go.
  8         14  
  8         20520  
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13              
14             our @EXPORT = qw(
15             file_ok
16             file_filter_ok
17             compare_ok
18             compare_filter_ok
19             dir_contains_ok
20             dir_only_contains_ok
21             compare_dirs_ok
22             compare_dirs_filter_ok
23             );
24              
25             our $VERSION = '0.14';
26              
27             my $Test = Test::Builder->new;
28             my $diff_options = {
29             CONTEXT => 3, # change this one later if needed
30             STYLE => "Table",
31             FILENAME_A => "Got",
32             FILENAME_B => "Expected",
33             OFFSET_A => 1,
34             OFFSET_B => 1,
35             INDEX_LABEL => "Ln",
36             MTIME_A => "",
37             MTIME_B => "",
38             };
39              
40             sub file_ok {
41 3     3 1 3130 my $candidate_file = shift;
42 3         7 my $expected = shift;
43 3         5 my $name = shift;
44            
45 3 100 66     121 unless (-f $candidate_file and -r _) {
46 1         7 $Test->ok(0, $name);
47 1         717 $Test->diag("$candidate_file absent");
48 1         197 return;
49             }
50              
51             # chomping and reappending the line ending was done in
52             # Test::Differences::eq_or_diff
53 2         13 my $diff = diff($candidate_file, \$expected, $diff_options);
54 2         35054 chomp $diff;
55 2         7 my $failed = length $diff;
56 2         5 $diff .= "\n";
57              
58 2 100       9 if ($failed) {
59 1         6 $Test->ok(0, $name);
60 1         532 $Test->diag($diff);
61             }
62             else {
63 1         11 $Test->ok(1, $name);
64             }
65             }
66              
67             sub file_filter_ok {
68 2     2 1 2556 my $candidate_file = shift;
69 2         6 my $expected = shift;
70 2         5 my $filter = shift;
71 2         4 my $name = shift;
72              
73 2 100       98 unless (open CANDIDATE, "$candidate_file") {
74 1         5 $Test->ok(0, $name);
75 1         924 $Test->diag( "$candidate_file absent" );
76 1         73 return;
77             }
78              
79 1         5 my $candidate = _read_and_filter_handle( *CANDIDATE, $filter );
80              
81             # chomping and reappending the line ending was done in
82             # Test::Differences::eq_or_diff
83 1         7 my $diff = diff(\$candidate, \$expected, $diff_options);
84 1         254 chomp $diff;
85 1         3 my $failed = length $diff;
86 1         2 $diff .= "\n";
87              
88 1 50       4 if ($failed) {
89 0         0 $Test->ok(0, $name);
90 0         0 $Test->diag($diff);
91             }
92             else {
93 1         6 $Test->ok(1, $name);
94             }
95             }
96              
97             sub _read_two_files {
98 18     18   33 my $first = shift;
99 18         28 my $second = shift;
100 18         22 my $filter = shift;
101 18         29 my $success = 1;
102 18         24 my @errors;
103              
104 18 100       731 unless (open FIRST, "$first") {
105 4         9 $success = 0;
106 4         14 push @errors, "$first absent";
107             }
108 18 100       23111 unless (open SECOND, "$second") {
109 7         13 $success = 0;
110 7         20 push @errors, "$second absent";
111             }
112 18 100       104 return ($success, @errors) unless $success;
113              
114 9         59 my $first_data = _read_and_filter_handle(*FIRST, $filter);
115 9         34 my $second_data = _read_and_filter_handle(*SECOND, $filter);
116 9         116 close FIRST;
117 9         82 close SECOND;
118              
119 9         46 return ($success, $first_data, $second_data);
120             }
121              
122             sub _read_and_filter_handle {
123 19     19   63 my $handle = shift;
124 19         28 my $filter = shift;
125              
126 19 100       73 if ($filter) {
127 9         10 my @retval;
128 9         203 while (<$handle>) {
129 22         59 my $filtered = $filter->($_);
130 22 50       538 push @retval, $filtered if $filtered;
131             }
132 9         71 return join "", @retval;
133             }
134             else {
135 10         350 return join "", <$handle>;
136             }
137             }
138              
139             sub compare_ok {
140 5     5 1 4450 my $got_file = shift;
141 5         10 my $expected_file = shift;
142 5         8 my $name = shift;
143              
144 5         18 @_ = ($got_file, $expected_file, undef, $name);
145 5         25 goto &compare_filter_ok;
146             }
147              
148             sub compare_filter_ok {
149 10     10 1 4876 my $got_file = shift;
150 10         21 my $expected_file = shift;
151 10         14 my $filter = shift;
152 10         18 my $name = shift;
153 10         32 my @read_result = _read_two_files($got_file, $expected_file, $filter);
154 10         21 my $files_exist = shift @read_result;
155              
156 10 100       28 if ($files_exist) {
157 4         9 my ($got, $expected) = @read_result;
158             # chomping and reappending the line ending was done in
159             # Test::Differences::eq_or_diff
160 4         29 my $diff = diff(\$got, \$expected, $diff_options);
161 4         45692 chomp $diff;
162 4         10 my $failed = length $diff;
163 4         35 $diff .= "\n";
164              
165 4 100       39 if ($failed) {
166 2         11 $Test->ok(0, $name);
167 2         1095 $Test->diag($diff);
168             }
169             else {
170 2         22 $Test->ok(1, $name);
171             }
172             }
173             else {
174 6         26 $Test->ok(0, $name);
175 6         3444 $Test->diag(join "\n", @read_result);
176             }
177             }
178              
179             sub _dir_missing_helper {
180 10     10   19 my $base_dir = shift;
181 10         15 my $list = shift;
182 10         16 my $name = shift;
183 10         15 my $function = shift;
184              
185 10 100       521 unless (-d $base_dir) {
186 2         11 return(0, "$base_dir absent");
187             }
188 8 100       58 if (index(ref $list, 'ARRAY') < 0) {
189 2         12 return(0, "$function requires array ref as second arg");
190 0         0 return;
191             }
192              
193 6         9 my @missing;
194 6         16 foreach my $element (@$list) {
195 20         433 my $elem_path = File::Spec->catfile( $base_dir, $element );
196 20 100       549 push @missing, $element unless (-e $elem_path );
197             }
198 6         30 return (\@missing);
199             }
200              
201             sub dir_contains_ok {
202 4     4 1 3402 my $base_dir = shift;
203 4         6 my $list = shift;
204 4         5 my $name = shift;
205 4         13 my @result = _dir_missing_helper(
206             $base_dir, $list, $name, 'dir_contains_ok'
207             );
208 4 100       14 if (@result == 2) {
209 2         8 $Test->ok(0, $name);
210 2         1012 $Test->diag($result[1]);
211 2         128 return;
212             }
213              
214 2         5 my $missing = $result[0];
215              
216 2 100       8 if (@$missing) {
217 1         5 $Test->ok(0, $name);
218 1         500 $Test->diag("failed to see these: @$missing");
219             }
220             else {
221 1         8 $Test->ok(1, $name);
222             }
223             }
224              
225             sub dir_only_contains_ok {
226 6     6 0 8509 my $base_dir = shift;
227 6         13 my $list = shift;
228 6         11 my $name = shift;
229 6         19 my @result = _dir_missing_helper(
230             $base_dir, $list, $name, 'dir_only_contains_ok'
231             );
232 6 100       94 if (@result == 2) {
233 2         12 $Test->ok(0, $name);
234 2         2829 $Test->diag($result[1]);
235 2         142 return;
236             }
237              
238 4         7 my $missing = $result[0];
239              
240 4         8 my $success;
241             my @diags;
242 4 100       15 if (@$missing) {
243 2         4 $success = 0;
244 2         10 push @diags, "failed to see these: @$missing";
245             }
246             else {
247 2         4 $success = 1;
248             }
249              
250             # Then, make sure no other files are present.
251 4         6 my %expected;
252             my @unexpected;
253 4         20 @expected{ @$list } = ();
254             # by defining $contains here, it can use our scope
255             my $contains = sub {
256 16     16   26 my $name = $File::Find::name;
257 16 100       522 return if ($name eq $base_dir);
258 12         2158 $name = File::Spec->abs2rel( $name, $base_dir );
259 12 100       1304 push @unexpected, $name unless (exists $expected{$name});
260 4         29 };
261              
262 4         529 find($contains, ($base_dir));
263              
264 4 100       18 if (@unexpected) {
265 2         4 $success = 0;
266 2         3 my $unexp = @unexpected;
267 2         8 push @diags, "unexpectedly saw: @unexpected";
268             }
269              
270 4         27 $Test->ok($success, $name);
271 4 100       11175 $Test->diag(join "\n", @diags) if @diags;
272             }
273              
274             sub compare_dirs_ok {
275 5     5 1 4837 my $first_dir = shift;
276 5         10 my $second_dir = shift;
277 5         9 my $name = shift;
278              
279 5         14 @_ = ($first_dir, $second_dir, undef, $name);
280 5         20 goto &compare_dirs_filter_ok;
281             }
282              
283             sub compare_dirs_filter_ok {
284 10     10 1 4267 my $first_dir = shift;
285 10         15 my $second_dir = shift;
286 10         17 my $filter = shift;
287 10         15 my $name = shift;
288              
289 10 100       244 unless (-d $first_dir) {
290 2         11 $Test->ok(0, $name);
291 2         1044 $Test->diag("$first_dir is not a valid directory");
292 2         126 return;
293             }
294 8 100       115 unless (-d $second_dir) {
295 2         11 $Test->ok(0, $name);
296 2         909 $Test->diag("$second_dir is not a valid directory");
297 2         125 return;
298             }
299 6 100 100     49 unless (not defined $filter or ref($filter) =~ /CODE/) {
300 1         5 $Test->ok(0, $filter);
301 1         413 $Test->diag(
302             "Third argument to compare_dirs_filter_ok must be "
303             . "a code reference (or undef)"
304             );
305 1         56 return;
306             }
307              
308 5         8 my @diags;
309              
310             my $matches = sub {
311 17     17   25 my $name = $File::Find::name;
312              
313 17 100       1125 return if (-d $name);
314              
315 8         775 $name = File::Spec->abs2rel( $name, $first_dir );
316 8 50       27 return if length($name) < 1; # skip the base directory
317              
318 8         62 my $first_file = File::Spec->catfile( $first_dir, $name );
319 8         56 my $second_file = File::Spec->catfile( $second_dir, $name );
320              
321 8         29 my @result = _read_two_files(
322             $first_file, $second_file, $filter
323             );
324 8         13 my $files_exist = shift @result;
325              
326 8 100       19 if ($files_exist) {
327 5         10 my ($got, $expected) = @result;
328 5         90 my $diff = diff(
329             \$got,
330             \$expected,
331             {
332             %$diff_options,
333             FILENAME_A => $first_file,
334             FILENAME_B => $second_file,
335             }
336             );
337 5         46929 chomp $diff;
338 5         15 my $failed = length $diff;
339 5         14 $diff .= "\n";
340              
341 5 100       123 if ($failed) {
342 2         49 push @diags, $diff;
343             }
344             }
345             else {
346 3         45 push @diags, "$result[0]\n";
347             }
348 5         45 };
349              
350 5         406 find({ wanted => $matches, no_chdir => 1 }, $first_dir);
351              
352 5 100       25 if (@diags) {
353 3         21 $Test->ok(0, $name);
354 3         16746 $Test->diag(sort @diags);
355             }
356             else {
357 2         20 $Test->ok(1, $name);
358             }
359             }
360              
361             1;
362             __END__