File Coverage

blib/lib/Test/DistManifest.pm
Criterion Covered Total %
statement 97 107 90.6
branch 22 26 84.6
condition 15 15 100.0
subroutine 14 14 100.0
pod 1 1 100.0
total 149 163 91.4


line stmt bran cond sub pod time code
1             package Test::DistManifest;
2             {
3             $Test::DistManifest::VERSION = '1.012';
4             }
5             # ABSTRACT: Author test that validates a package MANIFEST
6              
7 4     4   89533 use strict;
  4         11  
  4         146  
8 4     4   23 use warnings;
  4         8  
  4         114  
9 4     4   22 use Carp ();
  4         12  
  4         74  
10 4     4   4305 use ExtUtils::Manifest;
  4         56959  
  4         1658  
11              
12              
13             # File management commands
14 4     4   379 use Cwd ();
  4         9  
  4         261  
15 4     4   24 use File::Spec; # Portability
  4         11  
  4         263  
16 4     4   220 use File::Spec::Unix; # To get UNIX-style paths
  4         8  
  4         494  
17 4     4   209 use File::Find (); # Traverse the filesystem tree
  4         11  
  4         272  
18              
19 4     4   4240 use Module::Manifest;
  4         21853  
  4         128  
20 4     4   1342 use Test::Builder;
  4         11566  
  4         483  
21              
22             my $test = Test::Builder->new;
23              
24             my @EXPORTS = (
25             'manifest_ok',
26             );
27              
28             # These platforms were copied from File::Spec
29             my %platforms = (
30             MacOS => 1,
31             MSWin32 => 1,
32             os2 => 1,
33             VMS => 1,
34             epoc => 1,
35             NetWare => 1,
36             symbian => 1,
37             dos => 1,
38             cygwin => 1,
39             );
40              
41             # Looking at other Test modules this seems to be an ad-hoc standard
42             sub import {
43 4     4   48 my ($self, @plan) = @_;
44 4         14 my $caller = caller;
45              
46             {
47 4     4   27 no strict 'refs';
  4         8  
  4         3357  
  4         4  
48 4         12 for my $func (@EXPORTS) {
49 4         7 *{$caller . '::' . $func} = \&{$func};
  4         36  
  4         13  
50             }
51             }
52              
53 4         27 $test->exported_to($caller);
54 4         49 $test->plan(@plan);
55 4         1526 return;
56             }
57              
58              
59             sub manifest_ok {
60 8   100 8 1 19690 my $warn_only = $ENV{MANIFEST_WARN_ONLY} || 0;
61              
62 8   100     37 my $manifile = shift || 'MANIFEST';
63 8   100     44 my $skipfile = shift || 'MANIFEST.SKIP';
64              
65 8         225 my $root = Cwd::getcwd(); # this is Build.PL's Cwd
66 8         87 my $manifest = Module::Manifest->new;
67              
68 8 100       167 unless ($test->has_plan) {
69 1         24 $test->plan(tests => 4);
70             }
71              
72             # Try to parse the MANIFEST and MANIFEST.SKIP files
73 8         266 eval {
74 8         278 $manifest->open(manifest => $manifile);
75             };
76 8 100       3638 if ($@) {
77 2         35 $test->diag($!);
78             }
79 8         375 $test->ok(!$@, 'Parse MANIFEST or equivalent');
80              
81 8         3883 eval {
82 8         36 $manifest->open(skip => $skipfile);
83             };
84 8 50       4986 if ($@) {
85 0         0 $test->diag('Unable to parse MANIFEST.SKIP file:');
86 0         0 $test->diag($!);
87 0         0 $test->diag('Using default skip data from ExtUtils::Manifest ' . ExtUtils::Manifest->VERSION);
88              
89 0 0       0 open my $fh, '<', $ExtUtils::Manifest::DEFAULT_MSKIP
90             or die "Cannot open $ExtUtils::Manifest::DEFAULT_MSKIP: $!";
91 0         0 chomp(my @manifest_content = <$fh>);
92 0         0 $manifest->parse( skip => \@manifest_content );
93             }
94              
95 8         19 my @files;
96             # Callback function called by File::Find
97             my $closure = sub {
98             # Trim off the package root to determine the relative path.
99 516     516   46033 my $path = File::Spec->abs2rel($File::Find::name, $root);
100              
101             # Portably deal with different OSes
102 516 50       6361 if ($platforms{$^O}) { # Check if we are on a non-Unix platform
103             # Get path info from File::Spec, split apart
104 0         0 my (undef, $dir, $file) = File::Spec->splitpath($path);
105 0         0 my @dir = File::Spec->splitdir($dir);
106              
107             # Reconstruct the path in Unix-style
108 0         0 $dir = File::Spec::Unix->catdir(@dir);
109 0         0 $path = File::Spec::Unix->catpath(undef, $dir, $file);
110             }
111              
112             # Test that the path is a file and then make sure it's not skipped
113 516 100 100     13648 if (-f $path && !$manifest->skipped($path)) {
114 182         80261 push @files, $path;
115             }
116 516         79305 return;
117 8         66 };
118              
119             # Traverse the directory recursively
120 8         903 File::Find::find({
121             wanted => $closure,
122             untaint => 1,
123             no_chdir => 1,
124             }, $root);
125              
126             # The two arrays have no duplicates. Thus we loop through them and
127             # add the result to a hash.
128 8         37 my %seen;
129             # Allocate buckets for the hash
130 8         44 keys(%seen) = 2 * scalar(@files);
131 8         52 foreach my $path (@files, $manifest->files) {
132 304         698 $seen{$path}++;
133             }
134              
135 8         26 my $flag = 1;
136 8         19 foreach my $path (@files) {
137             # Skip the path if it was seen twice (the expected condition)
138 182 100       13790 next if ($seen{$path} == 2);
139              
140             # Oh no, we have files in @files not in $manifest->files
141 72 100       168 if ($flag == 1) {
142 4         32 $test->diag('Distribution files are missing in MANIFEST:');
143 4         623 $flag = 0;
144             }
145 72         210 $test->diag($path);
146             }
147 8   100     261 $test->ok($warn_only || $flag, 'All files are listed in MANIFEST or ' .
148             'skipped');
149              
150             # Reset the flag and test $manifest->files now
151 8         3741 $flag = 1;
152 8         22 my @circular = (); # for detecting circular logic
153 8         38 foreach my $path ($manifest->files) {
154             # Skip the path if it was seen twice (the expected condition)
155 122 100       1619 next if ($seen{$path} == 2);
156              
157             # If the file should exist but is passed by MANIFEST.SKIP, we have
158             # a strange circular logic condition.
159 12 100       39 if ($manifest->skipped($path)) {
160 2         66 push (@circular, $path);
161 2         6 next;
162             }
163              
164             # Oh no, we have files in $manifest->files not in @files
165 10 100       3063 if ($flag == 1) {
166 2         10 $test->diag('MANIFEST lists the following missing files:');
167 2         158 $flag = 0;
168             }
169 10         33 $test->diag($path);
170             }
171 8   100     219 $test->ok($warn_only || $flag, 'All files listed in MANIFEST exist ' .
172             'on disk');
173              
174             # Test for circular dependencies
175 8 100       3017 $flag = (scalar @circular == 0) ? 1 : 0;
176 8 100       33 if (not $flag) {
177 2         11 $test->diag('MANIFEST and MANIFEST.SKIP have circular dependencies:');
178 2         180 foreach my $path (@circular) {
179 2         12 $test->diag($path);
180             }
181             }
182 8         180 $test->ok($flag, 'No files are in both MANIFEST and MANIFEST.SKIP');
183              
184 8         3474 return;
185             }
186              
187              
188             1;
189              
190             __END__