File Coverage

blib/lib/Test/Pod/Coverage/Permissive.pm
Criterion Covered Total %
statement 94 97 96.9
branch 31 42 73.8
condition 18 29 62.0
subroutine 13 13 100.0
pod 3 3 100.0
total 159 184 86.4


line stmt bran cond sub pod time code
1             package Test::Pod::Coverage::Permissive;
2              
3 8     8   248810 use warnings;
  8         22  
  8         321  
4 8     8   51 use strict;
  8         15  
  8         276  
5 8     8   242 use 5.008009;
  8         45  
  8         390  
6 8     8   48 use Test::More 0.88;
  8         250  
  8         60  
7 8     8   2534 use File::Spec;
  8         27  
  8         238  
8 8     8   55 use Pod::Coverage;
  8         22  
  8         301  
9 8     8   11295 use YAML::Syck qw(LoadFile DumpFile);
  8         27631  
  8         957  
10              
11             my $Test = Test::Builder->new;
12              
13             sub import {
14 8     8   174 my $self = shift;
15 8         22 my $caller = caller;
16 8     8   71 no strict 'refs';
  8         20  
  8         9240  
17 8         20 *{$caller.'::pod_coverage_ok'} = \&pod_coverage_ok;
  8         51  
18 8         16 *{$caller.'::all_pod_coverage_ok'} = \&all_pod_coverage_ok;
  8         43  
19 8         19 *{$caller.'::all_modules'} = \&all_modules;
  8         38  
20              
21 8         50 $Test->exported_to($caller);
22 8         108 $Test->plan(@_);
23             }
24              
25             =head1 NAME
26              
27             Test::Pod::Coverage::Permissive - Checks for pod coverage regression.
28              
29             =head1 VERSION
30              
31             Version 0.05
32              
33             =cut
34              
35             our $VERSION = '0.05';
36              
37             =head1 SYNOPSIS
38              
39             Checks for POD coverage regressions in your code. This module is for large projects, which can't be covered by POD for a
40             5 minutes. If you have small module or your project is fully covered - use L instead.
41              
42             After first run, this module creates data file, where saves all uncovered subroutines. If you create new uncovered
43             subroutine, it will fail. If you create new package with uncovered subroutines, it will fail. Otherwise it will show
44             diagnostic messages like these:
45              
46             t/03podcoverage.t .. 2/? # YourProject::Controller::Root: naked 4 subroutine(s)
47             # YourProject::Controller::NotRoot: naked 8 subroutine(s)
48             # YorProject::Controller::AlsoNotRoot: naked 3 subroutine(s)
49             ...
50              
51             This module will help you to cover your project step-by-step. And your new code will be covered by POD.
52              
53             Interface is like L:
54              
55             use Test::Pod::Coverage::Permissive;
56              
57             use Test::More;
58             eval "use Test::Pod::Coverage::Permissive";
59             plan skip_all => "Test::Pod::Coverage::Permissive required for testing POD coverage" if $@;
60             all_pod_coverage_ok();
61              
62             =head1 FUNCTIONS
63              
64             =head2 all_pod_coverage_ok( [$parms] )
65              
66             Checks that the POD code in all modules in the distro have proper POD
67             coverage.
68              
69             If the I<$parms> hashref if passed in, they're passed into the
70             C object that the function uses. Check the
71             L manual for what those can be.
72              
73             The exception is the C parameter, which specifies a class to
74             use for coverage testing. It defaults to C.
75              
76             =cut
77              
78             sub all_pod_coverage_ok {
79 1 50 33 1 1 14 my $parms = ( @_ && ( ref $_[0] eq "HASH" ) ) ? shift : {};
80 1         2 my $msg = shift;
81              
82 1         2 my $ok = 1;
83 1         5 my @modules = all_modules();
84 1 50       4 if (@modules) {
85 1         3 for my $module (@modules) {
86 1         4 pod_coverage_ok($module, $parms, $msg);
87             }
88             }
89             else {
90 0         0 ok( 1, "No modules found." );
91             }
92              
93 1         352 return $ok;
94             }
95              
96             =head2 pod_coverage_ok( $module, [$parms,] $msg )
97              
98             Checks that the POD code in I<$module> has proper POD coverage.
99              
100             If the I<$parms> hashref if passed in, they're passed into the
101             C object that the function uses. Check the
102             L manual for what those can be.
103              
104             The exception is the C parameter, which specifies a class to
105             use for coverage testing. It defaults to C.
106              
107             =cut
108              
109             sub pod_coverage_ok {
110 11     11 1 7176 my $module = shift;
111 11 100 100     113 my %parms = (@_ && (ref $_[0] eq "HASH")) ? %{(shift)} : ();
  3         15  
112 11 100       64 my $msg = @_ ? shift : "Pod coverage on $module";
113 11         284 my $first_time = !-e 't/pod_correct.yaml';
114 11   50     25 my $correct = eval { LoadFile('t/pod_correct.yaml') } || {};
115 11         2996 my $coverage = Pod::Coverage->new( package => $module, %parms );
116 11   100     19175 my $v = $coverage->naked || 0;
117 11         4632 my $ok = 1;
118 11 100       14628 if ( defined $coverage->coverage ) {
119 8 50       2605 $correct->{$module} = $v if $first_time;
120 8 100 50     115 if ( $ok = $Test->ok($v <= ($correct->{$module}||0), $msg) ) {
121 7         2932 $correct->{$module} = $v;
122             }
123 8 100       863 if ( my $count = $coverage->naked ) {
124 1         7 $Test->diag("${module}: naked $count subroutine(s)");
125             }
126             }
127             else { # No symbols
128 3         115 my $why = $coverage->why_unrated;
129 3         11 my $nopublics = ( $why =~ "no public symbols defined" );
130 3   100     20 my $verbose = $ENV{HARNESS_VERBOSE} || 0;
131 3 50       10 $correct->{$module} = undef if $first_time;
132 3   66     17 $ok = $nopublics || exists $coverage->{$module};
133 3         19 $Test->ok( $ok, $msg );
134 3 100 100     1322 $Test->diag( "$module: $why" ) unless ( $nopublics && !$verbose );
135             }
136              
137 11         361 DumpFile( 't/pod_correct.yaml', $correct );
138             }
139              
140             =head2 all_modules( [@dirs] )
141              
142             Returns a list of all modules in I<$dir> and in directories below. If
143             no directories are passed, it defaults to F if F exists,
144             or F if not.
145              
146             Note that the modules are as "Foo::Bar", not "Foo/Bar.pm".
147              
148             The order of the files returned is machine-dependent. If you want them
149             sorted, you'll have to sort them yourself.
150              
151             =cut
152              
153             sub all_modules {
154 2 100   2 1 23 my @starters = @_ ? @_ : _starting_points();
155 2         9 my %starters = map { $_, 1 } @starters;
  2         13  
156              
157 2         7 my @queue = @starters;
158              
159 2         4 my @modules;
160 2         11 while (@queue) {
161 58         150 my $file = shift @queue;
162 58 100       994 if ( -d $file ) {
163 40         104 local *DH;
164 40 50       973 opendir DH, $file or next;
165 40         558 my @newfiles = readdir DH;
166 40         446 closedir DH;
167              
168 40         645 @newfiles = File::Spec->no_upwards(@newfiles);
169 40 50       90 @newfiles = grep { $_ ne "CVS" && $_ ne ".svn" } @newfiles;
  56         428  
170              
171 40         297 push @queue, map "$file/$_", @newfiles;
172             }
173 58 100       1002 if ( -f $file ) {
174 18 100       107 next unless $file =~ /\.pm$/;
175              
176 2         508 my @parts = File::Spec->splitdir($file);
177 2 50 33     25 shift @parts if @parts && exists $starters{ $parts[0] };
178 2 50 33     23 shift @parts if @parts && $parts[0] eq "lib";
179 2 50       38 $parts[-1] =~ s/\.pm$// if @parts;
180              
181             # Untaint the parts
182 2         8 for (@parts) {
183 8 50 33     562 if ( /^([a-zA-Z0-9_\.\-]+)$/ && ( $_ eq $1 ) ) {
184 8         24 $_ = $1; # Untaint the original
185             }
186             else {
187 0         0 die qq{Invalid and untaintable filename "$file"!};
188             }
189             }
190 2         10 my $module = join( "::", @parts );
191 2         14 push( @modules, $module );
192             }
193             } # while
194              
195 2         13 return @modules;
196             }
197              
198             sub _starting_points {
199 1 50   1   33 return 'blib' if -e 'blib';
200 0           return 'lib';
201             }
202              
203             =head1 AUTHOR
204              
205             Andrey Kostenko, C<< >>
206              
207             =head1 BUGS
208              
209             Please report any bugs or feature requests to C, or through
210             the web interface at L. I will be notified, and then you'll
211             automatically be notified of progress on your bug as I make changes.
212              
213              
214              
215              
216             =head1 SUPPORT
217              
218             You can find documentation for this module with the perldoc command.
219              
220             perldoc Test::Pod::Coverage::Permissive
221              
222              
223             You can also look for information at:
224              
225             =over 4
226              
227             =item * RT: CPAN's request tracker
228              
229             L
230              
231             =item * AnnoCPAN: Annotated CPAN documentation
232              
233             L
234              
235             =item * CPAN Ratings
236              
237             L
238              
239             =item * Search CPAN
240              
241             L
242              
243             =back
244              
245              
246             =head1 ACKNOWLEDGEMENTS
247              
248             Thanks to author of L. 90% of this module is a copy-paste from L.
249              
250             =head1 LICENSE AND COPYRIGHT
251              
252             Copyright 2010 Andrey Kostenko, based on Andy Lester's L
253              
254             This program is free software; you can redistribute it and/or modify it
255             under the terms of either: the GNU General Public License as published
256             by the Free Software Foundation; or the Artistic License.
257              
258             See http://dev.perl.org/licenses/ for more information.
259              
260              
261             =cut
262              
263             1; # End of Test::Pod::Coverage::Permissive