File Coverage

blib/lib/Test/Files.pm
Criterion Covered Total %
statement 196 198 98.9
branch 57 60 95.0
condition 5 6 83.3
subroutine 19 19 100.0
pod 7 8 87.5
total 284 291 97.5


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