line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test::Pod::CoverageChange; |
2
|
|
|
|
|
|
|
# ABSTRACT: Test Perl files for POD coverage and syntax changes |
3
|
|
|
|
|
|
|
|
4
|
4
|
|
|
4
|
|
257052
|
use strict; |
|
4
|
|
|
|
|
25
|
|
|
4
|
|
|
|
|
95
|
|
5
|
4
|
|
|
4
|
|
18
|
use warnings; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
160
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.002'; |
8
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:DERIV'; # AUTHORITY |
9
|
|
|
|
|
|
|
|
10
|
4
|
|
|
4
|
|
1912
|
use utf8; |
|
4
|
|
|
|
|
49
|
|
|
4
|
|
|
|
|
28
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=encoding utf8 |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Test::Pod::CoverageChange - Test Perl files for POD coverage and syntax changes |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use Test::Pod::CoverageChange qw(pod_coverage_syntax_ok); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
pod_coverage_syntax_ok('lib', { |
23
|
|
|
|
|
|
|
MyModule::Bar => 3, ## expected to have 3 naked subs |
24
|
|
|
|
|
|
|
MyModule::Foo => 10, ## expected to have 10 naked subs |
25
|
|
|
|
|
|
|
MyModule::Baz => 1, ## expected to have 1 naked subs |
26
|
|
|
|
|
|
|
MyModule::Qux => 5, ## expected to have 5 naked subs |
27
|
|
|
|
|
|
|
}, [ |
28
|
|
|
|
|
|
|
We::Ignore::ThisModule, |
29
|
|
|
|
|
|
|
We::Also::Ignore::This::Module |
30
|
|
|
|
|
|
|
],[ |
31
|
|
|
|
|
|
|
'a_sub_name_to_ignore' |
32
|
|
|
|
|
|
|
qr/regexes are also acceptable/ |
33
|
|
|
|
|
|
|
]); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
C is a helper combining L and |
38
|
|
|
|
|
|
|
L to test for both POD coverage and syntax changes for a module |
39
|
|
|
|
|
|
|
distribution at once, via a single call to L. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Possible results |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=over 4 |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item * B if the file has no POD syntax or coverage error. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=item * B if latest changes increased/decreased numbers of naked subs for the packages that have allowed naked subs. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item * B if a package allowed to have naked subs has 100% POD coverage. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=item * B if a file in a given path has POD syntax error or has no POD. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=back |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Ignores packages that passed as ignored package in the c<$ignored_package> argument into the pod_coverage_syntax_ok sub. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=cut |
58
|
|
|
|
|
|
|
|
59
|
4
|
|
|
4
|
|
153
|
use Test::More; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
20
|
|
60
|
4
|
|
|
4
|
|
2538
|
use Pod::Checker; |
|
4
|
|
|
|
|
128414
|
|
|
4
|
|
|
|
|
407
|
|
61
|
4
|
|
|
4
|
|
30
|
use Pod::Coverage; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
148
|
|
62
|
4
|
|
|
4
|
|
1806
|
use File::Find::Rule; |
|
4
|
|
|
|
|
27987
|
|
|
4
|
|
|
|
|
26
|
|
63
|
4
|
|
|
4
|
|
1919
|
use Test::Pod::Coverage; |
|
4
|
|
|
|
|
4474
|
|
|
4
|
|
|
|
|
19
|
|
64
|
4
|
|
|
4
|
|
242
|
use Module::Path qw(module_path); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
180
|
|
65
|
4
|
|
|
4
|
|
22
|
use List::Util qw(any); |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
364
|
|
66
|
4
|
|
|
4
|
|
2832
|
use Path::Tiny; |
|
4
|
|
|
|
|
40075
|
|
|
4
|
|
|
|
|
225
|
|
67
|
|
|
|
|
|
|
use constant { |
68
|
4
|
|
|
|
|
283
|
POD_SYNTAX_IS_OK => 0, |
69
|
|
|
|
|
|
|
FILE_HAS_NO_POD => -1, |
70
|
4
|
|
|
4
|
|
30
|
}; |
|
4
|
|
|
|
|
6
|
|
71
|
|
|
|
|
|
|
|
72
|
4
|
|
|
4
|
|
23
|
use Exporter qw(import export_to_level); |
|
4
|
|
|
|
|
18
|
|
|
4
|
|
|
|
|
3611
|
|
73
|
|
|
|
|
|
|
our @EXPORT_OK = qw(pod_coverage_syntax_ok); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head2 pod_coverage_syntax_ok |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Checks all the modules under a given directory against POD coverage and POD syntax |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=over 4 |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item * C<$path> - path or arrayref of directories to check (recursively) |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
example: ['lib', 'other directory'] | 'lib' |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item * C<$allowed_naked_packages> - hashref of number of allowed naked subs, keyed by package name (optional) |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
example: {Package1 => 2, Package2 => 1, Package3 => 10} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item * C<$ignored_packages> - arrayref of packages that will be ignored in the checks (optional) |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
example: ['MyPackage1', 'MyPackage2', 'MyPackage3'] |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item * C<$ignored_subs> - arrayref of subnames or regexes that will be ignored in the checks (optional) |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
example: ['a_sub_name', qr/a regex/] |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=back |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=cut |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub pod_coverage_syntax_ok { |
102
|
11
|
|
|
11
|
1
|
34327
|
my %args = @_; |
103
|
11
|
|
|
|
|
47
|
my %default_values = (path => 'lib', allowed_naked_packages => {}, ignored_packages => [], ignored_subs => []); |
104
|
|
|
|
|
|
|
|
105
|
11
|
|
|
|
|
44
|
%args = (%default_values, %args); |
106
|
|
|
|
|
|
|
|
107
|
11
|
|
50
|
|
|
37
|
my $path = $args{path} // 'lib'; |
108
|
11
|
|
50
|
|
|
28
|
my $ignored_packages = $args{ignored_packages} // []; |
109
|
|
|
|
|
|
|
|
110
|
11
|
100
|
|
|
|
50
|
$path = [$path] unless ref $path eq 'ARRAY'; |
111
|
11
|
50
|
|
|
|
32
|
$ignored_packages = [$ignored_packages] unless ref $ignored_packages eq 'ARRAY'; |
112
|
|
|
|
|
|
|
|
113
|
11
|
|
|
|
|
32
|
_check_pod_coverage($path, $args{allowed_naked_packages}, $ignored_packages, $args{ignored_subs}); |
114
|
11
|
|
|
|
|
33
|
_check_pod_syntax($path, $ignored_packages); |
115
|
|
|
|
|
|
|
|
116
|
11
|
|
|
|
|
37
|
return undef; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 _check_pod_coverage |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Checks POD coverage for all the modules that exist under the given directory. |
122
|
|
|
|
|
|
|
Passes the C<$allowed_naked_packages> to L. |
123
|
|
|
|
|
|
|
Ignores the packages in the C<$ignored_packages> parameter. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=over 4 |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item * C<$path> - path or arrayref of directories to check (recursively) |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=item * C<$allowed_naked_packages> - hashref of number of allowed naked subs, keyed by package name (optional) |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item * C<$ignored_packages> - arrayref of packages that will be ignored in the checks (optional) |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=back |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=cut |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub _check_pod_coverage { |
138
|
11
|
|
50
|
11
|
|
23
|
my $path = shift // 'lib'; |
139
|
11
|
|
50
|
|
|
20
|
my $allowed_naked_packages = shift // {}; |
140
|
11
|
|
50
|
|
|
38
|
my $ignored_packages = shift // []; |
141
|
11
|
|
50
|
|
|
30
|
my $ignored_subs = shift // []; |
142
|
|
|
|
|
|
|
|
143
|
11
|
100
|
|
|
|
41
|
_check_allowed_naked_packages($allowed_naked_packages, $ignored_packages) if keys %$allowed_naked_packages; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Check for newly added packages PODs |
146
|
11
|
|
|
|
|
29
|
my @ignored_packages = (keys %$allowed_naked_packages, @$ignored_packages); |
147
|
11
|
|
|
|
|
36
|
foreach my $package (Test::Pod::Coverage::all_modules(@$path)) { |
148
|
15
|
100
|
|
15
|
|
2216
|
next if any { $_ eq $package } @ignored_packages; |
|
15
|
|
|
|
|
46
|
|
149
|
6
|
50
|
66
|
|
|
52
|
if(!pod_coverage_ok($package, {trustme => [qw(DOES META)], private => [], also_private => $ignored_subs}) |
150
|
|
|
|
|
|
|
&& _package_is_object_pad($package)){ |
151
|
0
|
|
|
|
|
0
|
diag("Package $package is an Object::Pad class, Do you miss PODs of auto-generated methods?" |
152
|
|
|
|
|
|
|
. 'it will generate method "new" and getter and setter of member fields like: |
153
|
|
|
|
|
|
|
has $XXXX :reader; #=> will create a method "XXXX" |
154
|
|
|
|
|
|
|
has $YYYY :writer: #=> will create a method "set_YYYY"'); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
11
|
|
|
|
|
6589
|
return undef; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub _package_is_object_pad{ |
163
|
2
|
|
|
2
|
|
5223
|
my $package = shift; |
164
|
2
|
|
|
|
|
5
|
my $file = module_path($package); |
165
|
2
|
|
|
|
|
166
|
return path($file)->slurp_utf8 =~ /Object::Pad/; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head2 _check_pod_syntax |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Check POD syntax for all the modules that exist under the given directory. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=over 4 |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item * C<$path> - path or arrayref of directories to check (recursively) |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=item * C<$ignored_packages> - arrayref of packages that will be ignored in the checks (optional) |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=back |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=cut |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub _check_pod_syntax { |
183
|
11
|
|
50
|
11
|
|
28
|
my $path = shift // 'lib'; |
184
|
11
|
|
50
|
|
|
25
|
my $ignored_packages = shift // []; |
185
|
|
|
|
|
|
|
|
186
|
11
|
|
|
|
|
44
|
my $Test_Builder = Test::More->builder; |
187
|
|
|
|
|
|
|
|
188
|
11
|
|
|
|
|
102
|
my @ignored_packages_full_path = (); |
189
|
11
|
|
|
|
|
25
|
for (@$ignored_packages) { |
190
|
1
|
|
|
|
|
3
|
my $file_path = module_path($_); |
191
|
1
|
50
|
|
|
|
83
|
push @ignored_packages_full_path, $file_path if defined $file_path; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
11
|
|
|
|
|
262
|
my @files_path = File::Find::Rule->file()->name('*.p[m|l]')->in(@$path); |
195
|
|
|
|
|
|
|
|
196
|
11
|
|
|
|
|
8343
|
for my $file_path (@files_path) { |
197
|
15
|
100
|
|
1
|
|
1315
|
next if any { /\Q$file_path/ } @ignored_packages_full_path; |
|
1
|
|
|
|
|
14
|
|
198
|
|
|
|
|
|
|
|
199
|
14
|
|
|
|
|
60
|
my $check_result = podchecker($file_path); |
200
|
14
|
100
|
|
|
|
18593
|
if ($check_result == POD_SYNTAX_IS_OK) { |
|
|
100
|
|
|
|
|
|
201
|
7
|
|
|
|
|
43
|
$Test_Builder->ok(1, sprintf("Pod structure is OK in the file %s.", $file_path)); |
202
|
|
|
|
|
|
|
} elsif ($check_result == FILE_HAS_NO_POD) { |
203
|
6
|
|
|
|
|
34
|
$Test_Builder->todo_skip(sprintf("There is no POD in the file %s.", $file_path)); |
204
|
|
|
|
|
|
|
} else { |
205
|
1
|
|
|
|
|
7
|
$Test_Builder->ok(0, sprintf("There are %d errors in the POD structure in the %s.", $check_result, $file_path)); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
11
|
|
|
|
|
3707
|
return undef; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head2 _check_allowed_naked_packages |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Checks passed allowed_naked_packages against existing package files. |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=over 4 |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=item * C<$allowed_naked_packages> - hashref of number of allowed naked subs, keyed by package name (optional) |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item * C<$ignored_packages> - a list of packages that will be ignored in our checks, supports arrayref (optional) |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=back |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Possible results |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=over 4 |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=item * B if the numbers of existing naked subs are equal to passed value. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=item * B if the number of existing naked subs are not equal to the passed value. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item * B if a package has 100% POD coverage and it passed as a L<$allowed_naked_package>. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=back |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub _check_allowed_naked_packages { |
239
|
6
|
|
50
|
6
|
|
14
|
my $allowed_naked_packages = shift // {}; |
240
|
6
|
|
50
|
|
|
13
|
my $ignored_packages = shift // []; |
241
|
|
|
|
|
|
|
|
242
|
6
|
|
|
|
|
30
|
my $Test_Builder = Test::More->builder; |
243
|
6
|
|
|
|
|
74
|
my $caller_test_file = (caller(2))[1]; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Check for the currently naked packages POD. |
246
|
6
|
|
|
|
|
26
|
foreach my $package (sort keys %$allowed_naked_packages) { |
247
|
8
|
50
|
|
0
|
|
52
|
next if any { /^\Q$package\E$/ } @$ignored_packages; |
|
0
|
|
|
|
|
0
|
|
248
|
|
|
|
|
|
|
|
249
|
8
|
|
|
|
|
106
|
my $pc = Pod::Coverage->new( |
250
|
|
|
|
|
|
|
package => $package, |
251
|
|
|
|
|
|
|
private => []); |
252
|
8
|
|
100
|
|
|
6050
|
my $fully_covered = defined $pc->coverage && $pc->coverage == 1; |
253
|
8
|
100
|
|
|
|
6561
|
my $coverage_percentage = defined $pc->coverage ? $pc->coverage * 100 : 0; |
254
|
8
|
|
|
|
|
1643
|
my $max_expected_naked_subs = $allowed_naked_packages->{$package}; |
255
|
8
|
|
100
|
|
|
4121
|
my $naked_subs_count = scalar $pc->naked // scalar $pc->_get_syms($package); |
256
|
|
|
|
|
|
|
|
257
|
8
|
100
|
|
|
|
630
|
if (!$fully_covered) { |
258
|
7
|
|
|
|
|
396
|
$Test_Builder->todo_skip(sprintf("You have %.2f%% POD coverage for the module '%s'.", $coverage_percentage, $package)); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
8
|
100
|
100
|
|
|
2487
|
if (!$fully_covered && $naked_subs_count < $max_expected_naked_subs) { |
|
|
100
|
100
|
|
|
|
|
262
|
1
|
|
|
|
|
8
|
$Test_Builder->ok( |
263
|
|
|
|
|
|
|
0, |
264
|
|
|
|
|
|
|
sprintf( |
265
|
|
|
|
|
|
|
"Your last changes decreased the number of naked subs in the %s package. |
266
|
|
|
|
|
|
|
Change the %s => %s in the %s file please.", $package, $package, $naked_subs_count, $caller_test_file |
267
|
|
|
|
|
|
|
)); |
268
|
1
|
|
|
|
|
988
|
next; |
269
|
|
|
|
|
|
|
} elsif (!$fully_covered && $naked_subs_count > $max_expected_naked_subs) { |
270
|
2
|
|
|
|
|
16
|
$Test_Builder->ok(0, sprintf("Your last changes increased the number of naked subs in the %s package from %s to %s. Please add pod for your new subs.", |
271
|
|
|
|
|
|
|
$package, $max_expected_naked_subs, $naked_subs_count)); |
272
|
2
|
|
|
|
|
2056
|
next; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
5
|
100
|
|
|
|
24
|
if ($fully_covered) { |
276
|
1
|
|
|
|
|
7
|
$Test_Builder->ok( |
277
|
|
|
|
|
|
|
0, |
278
|
|
|
|
|
|
|
sprintf( |
279
|
|
|
|
|
|
|
'%s modules has 100%% POD coverage. Please remove it from the %s file $allowed_naked_packages variable to fix this error.', |
280
|
|
|
|
|
|
|
$package, $caller_test_file |
281
|
|
|
|
|
|
|
)); |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
6
|
|
|
|
|
1090
|
return undef; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
1; |