File Coverage

blib/lib/Module/CPANTS/Analyse.pm
Criterion Covered Total %
statement 128 165 77.5
branch 31 58 53.4
condition 9 18 50.0
subroutine 22 24 91.6
pod 9 9 100.0
total 199 274 72.6


line stmt bran cond sub pod time code
1             package Module::CPANTS::Analyse;
2 7     7   358785 use 5.008001;
  7         22  
3 7     7   27 use strict;
  7         12  
  7         141  
4 7     7   23 use warnings;
  7         10  
  7         341  
5 7     7   28 use base qw(Class::Accessor::Fast);
  7         25  
  7         4418  
6 7     7   18154 use File::Temp qw(tempdir);
  7         57645  
  7         392  
7 7     7   1068 use File::Spec::Functions qw(catfile catdir splitpath);
  7         1806  
  7         382  
8 7     7   2771 use File::Copy;
  7         26277  
  7         390  
9 7     7   2878 use File::stat;
  7         36925  
  7         397  
10 7     7   2858 use Archive::Any::Lite;
  7         856782  
  7         286  
11 7     7   54 use Carp;
  7         10  
  7         410  
12 7     7   2720 use Parse::Distname;
  7         12989  
  7         10659  
13              
14             our $VERSION = '1.03';
15             $VERSION =~ s/_//; ## no critic
16              
17             __PACKAGE__->mk_accessors(qw(dist opts tarball distdir d mck));
18             __PACKAGE__->mk_accessors(qw(_testdir _dont_cleanup _tarball _x_opts));
19              
20             sub import {
21 7     7   58 my $class = shift;
22 7         3265 require Module::CPANTS::Kwalitee;
23 7         39 Module::CPANTS::Kwalitee->import(@_);
24             }
25              
26             sub new {
27 15     15 1 1280607 my $class = shift;
28 15   50     99 my $opts = shift || {};
29 15         99 $opts->{d} = {};
30 15   50     212 $opts->{opts} ||= {};
31 15         61 my $me = bless $opts, $class;
32 15 50       181 Carp::croak("need a dist") if not defined $opts->{dist};
33              
34 15         254 $me->mck(Module::CPANTS::Kwalitee->new);
35              
36             # For Test::Kwalitee and friends
37 15 100       757 $me->d->{is_local_distribution} = 1 if -d $opts->{dist};
38              
39 15         177 return $me;
40             }
41              
42             sub run {
43 12     12 1 39882 my $me = shift;
44 12 100       332 $me->unpack unless $me->d->{is_local_distribution};
45 12         201 $me->analyse;
46 12         61 $me->calc_kwalitee;
47 12         184 $me->d;
48             }
49              
50             sub unpack {
51 1     1 1 22 my $me = shift;
52 1 50       21 return 'cant find dist' unless $me->dist;
53              
54 1         22 my $di = Parse::Distname->new($me->dist);
55 1   50     228 my $ext = $di->extension || 'unknown';
56              
57 1         21 $me->d->{package} = $di->filename;
58 1         79 $me->d->{vname} = $di->distvname;
59 1         34 $me->d->{extension} = $ext;
60 1         8 $me->d->{version} = $di->version;
61 1         20 $me->d->{dist} = $di->dist;
62 1         20 $me->d->{author} = $di->cpanid;
63 1         27 $me->d->{released} = stat($me->dist)->mtime;
64 1         196 $me->d->{size_packed} = -s $me->dist;
65              
66 1 50       44 unless($me->d->{package}) {
67 0         0 $me->d->{package} = $me->tarball;
68             }
69              
70 1         19 copy($me->dist, $me->testfile);
71              
72 1         381 my @warnings;
73             my @link_errors;
74 1         0 my @pax_headers;
75 1         2 eval {
76 1     0   20 local $Archive::Zip::ErrorHandler = sub { die @_ };
  0         0  
77             local $SIG{__WARN__} = sub {
78 0 0   0   0 if ($_[0] =~ /^Making (?:hard|symbolic) link from '([^']+)'/) {
79 0         0 push @link_errors, $1;
80 0         0 return;
81             }
82 0 0       0 if ($_[0] =~ /^Invalid header/) {
83 0         0 push @warnings, $_[0];
84 0         0 return;
85             }
86 0         0 die @_;
87 1         17 };
88              
89 1         4 local $Archive::Tar::CHMOD = 1;
90 1         5 my $archive = Archive::Any::Lite->new($me->testfile);
91             $archive->extract($me->testdir, {tar_filter_cb => sub {
92 3     3   3976 my $entry = shift;
93 3 50 33     8 if ($entry->name eq Archive::Tar::Constant::PAX_HEADER() or $entry->type eq 'x' or $entry->type eq 'g') {
      33        
94 0         0 push @pax_headers, $entry->name;
95 0         0 return;
96             }
97 3         68 return 1;
98 1         85 }});
99             };
100 1 50       1141 if (my $error = $@) {
101 0         0 $me->d->{extractable} = 0;
102 0         0 $me->d->{error}{extractable} = $error;
103 0         0 $me->d->{kwalitee}{extractable} = 0;
104 0         0 my ($vol, $dir, $name) = splitpath($me->dist);
105 0         0 $name =~ s/\..*$//;
106 0         0 $name =~ s/\-[\d\.]+$//;
107 0         0 $name =~ s/\-TRIAL[0-9]*//;
108 0         0 $me->d->{dist} = $name;
109 0         0 return $error;
110             }
111              
112 1 50 33     11 if (@link_errors or @warnings) {
113             # broken but some of the files may probably be extracted
114 0         0 $me->d->{extractable} = 0;
115 0         0 my %errors;
116 0 0       0 $errors{link_errors} = \@link_errors if @link_errors;
117 0 0       0 $errors{warnings} = \@warnings if @warnings;
118 0 0       0 $me->d->{error}{extractable} = \%errors if %errors;
119 0         0 $me->d->{kwalitee}{extractable} = 0;
120             } else {
121 1         28 $me->d->{extractable} = 1;
122             }
123              
124 1 50       9 if (@pax_headers) {
125 0         0 $me->d->{no_pax_headers} = 0;
126 0         0 $me->d->{error}{no_pax_headers} = join ',', @pax_headers;
127             } else {
128 1         15 $me->d->{no_pax_headers} = 1;
129             }
130              
131 1         10 unlink($me->testfile);
132              
133 1 50       140 opendir(my $fh_testdir, $me->testdir) or die "Cannot open ".$me->testdir.": $!";
134 1         74 my @stuff = grep {/\w/} readdir($fh_testdir);
  3         13  
135              
136 1 50       4 if (@stuff == 1) {
137 1         2 $me->distdir(catdir($me->testdir, $stuff[0]));
138 1 50       50 if (-d $me->distdir) {
139              
140 1         18 my $vname = $di->distvname;
141 1         5 $vname =~ s/\-TRIAL[0-9]*//;
142              
143 1         14 $me->d->{extracts_nicely} = 1;
144 1 50       11 if ($vname ne $stuff[0]) {
145 0         0 $me->d->{error}{extracts_nicely} = "expected $vname but got $stuff[0]";
146             }
147             } else {
148 0         0 $me->distdir($me->testdir);
149 0         0 $me->d->{extracts_nicely} = 0;
150 0         0 $me->d->{error}{extracts_nicely} = join ",", @stuff;
151             }
152             } else {
153 0         0 $me->distdir($me->testdir);
154 0         0 $me->d->{extracts_nicely} = 0;
155 0         0 $me->d->{error}{extracts_nicely} = join ",", @stuff;
156             }
157 1         20 return;
158             }
159              
160             sub analyse {
161 12     12 1 38 my $me = shift;
162              
163 12         20 foreach my $mod (@{$me->mck->generators}) {
  12         180  
164 192         2946 $mod->analyse($me);
165             }
166             }
167              
168             sub calc_kwalitee {
169 12     12 1 17 my $me = shift;
170              
171 12         23 my $kwalitee = 0;
172 12         184 $me->d->{kwalitee} = {};
173 12 100       74 my %x_ignore = %{$me->x_opts->{ignore} || {}};
  12         52  
174 12         4981 foreach my $i ($me->mck->get_indicators) {
175 396 50       629 next if $i->{needs_db};
176 396         4537 my $rv = $i->{code}($me->d, $i);
177 396         4745 $me->d->{kwalitee}{$i->{name}} = $rv;
178 396 100 100     1995 if ($x_ignore{$i->{name}} && $i->{ignorable}) {
179 2         31 $me->d->{kwalitee}{$i->{name}} = 1;
180 2 50       32 if ($me->d->{error}{$i->{name}}) {
181 2         31 $me->d->{error}{$i->{name}} .= ' [ignored]';
182             }
183             }
184 396         483 $kwalitee += $rv;
185             }
186              
187 12         147 $me->d->{'kwalitee'}{'kwalitee'} = $kwalitee;
188             }
189              
190             #----------------------------------------------------------------
191             # helper methods
192             #----------------------------------------------------------------
193              
194             sub testdir {
195 11     11 1 1185 my $me = shift;
196 11 100       228 return $me->_testdir if $me->_testdir;
197 3 50       92 if ($me->_dont_cleanup) {
198 0         0 return $me->_testdir(tempdir());
199             } else {
200 3         38 return $me->_testdir(tempdir(CLEANUP => 1));
201             }
202             }
203              
204             sub testfile {
205 5     5 1 1767 my $me = shift;
206 5         14 return catfile($me->testdir, $me->tarball);
207             }
208              
209             sub tarball {
210 5     5 1 529 my $me = shift;
211 5 100       80 return $me->_tarball if $me->_tarball;
212 2         46 my (undef, undef, $tb) = splitpath($me->dist);
213 2         75 return $me->_tarball($tb);
214             }
215              
216             sub x_opts {
217 12     12 1 20 my $me = shift;
218 12 50       244 return $me->_x_opts if $me->_x_opts;
219 12         74 my %opts;
220 12 100       139 if (my $x_cpants = $me->d->{meta_yml}{x_cpants}) {
221 2 50       29 if (my $ignore = $x_cpants->{ignore}) {
222 2 50       11 if (ref $ignore eq ref {}) {
223 2         8 $opts{ignore} = $ignore;
224             }
225             else {
226 0         0 $me->d->{error}{x_cpants} = "x_cpants ignore should be a hash reference (key: metric, value: reason to ignore)";
227             }
228             }
229             }
230 12         194 $me->_x_opts(\%opts);
231             }
232              
233             q{Favourite record of the moment:
234             Jahcoozi: Pure Breed Mongrel};
235              
236             __END__