File Coverage

blib/lib/Test/Prereq.pm
Criterion Covered Total %
statement 138 158 87.3
branch 27 40 67.5
condition 3 7 42.8
subroutine 30 31 96.7
pod 1 1 100.0
total 199 237 83.9


line stmt bran cond sub pod time code
1             package Test::Prereq;
2 8     8   1578701 use parent qw(Test::Builder::Module);
  8         1324  
  8         57  
3              
4 8     8   589 use strict;
  8         21  
  8         227  
5 8     8   3798 use utf8;
  8         2501  
  8         56  
6              
7 8     8   319 use v5.22;
  8         32  
8 8     8   52 use feature qw(postderef);
  8         13  
  8         1390  
9 8     8   45 no warnings qw(experimental::postderef);
  8         17  
  8         374  
10              
11 8     8   36 use warnings;
  8         11  
  8         323  
12 8     8   55 no warnings;
  8         45  
  8         575  
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             Test::Prereq - check if Makefile.PL has the right pre-requisites
19              
20             =head1 SYNOPSIS
21              
22             # if you use Makefile.PL
23             use Test::More;
24             eval "use Test::Prereq";
25             plan skip_all => "Test::Prereq required to test dependencies" if $@;
26             prereq_ok();
27              
28             # if you use Module::Build
29             use Test::More;
30             eval "use Test::Prereq::Build";
31             plan skip_all => "Test::Prereq::Build required to test dependencies" if $@;
32             prereq_ok();
33              
34             # or from the command line for a one-off check
35             perl -MTest::Prereq -eprereq_ok
36              
37             #The prerequisites test take quite some time so the following construct is
38             #recommended for non-author testers
39             use Test::More;
40             eval "use Test::Prereq::Build";
41              
42             my $msg;
43             if ($@) {
44             $msg = 'Test::Prereq::Build required to test dependencies';
45             } elsif (not $ENV{TEST_AUTHOR}) {
46             $msg = 'Author test. Set $ENV{TEST_AUTHOR} to a true value to run.';
47             }
48             plan skip_all => $msg if $msg;
49             prereq_ok();
50              
51             =head1 DESCRIPTION
52              
53             The C function examines the modules it finds in
54             F, F, and the test files it finds in F
55             (and F). It figures out which modules they use and compares
56             that list of modules to those in the C section of
57             F.
58              
59             If you use C instead, see L
60             instead.
61              
62             =head2 Warning about redefining ExtUtils::MakeMaker::WriteMakefile
63              
64             C has its own version of
65             C so it can run the F
66             and get the argument list of that function. You may see warnings
67             about this.
68              
69             =cut
70              
71 8     8   42 use vars qw($VERSION $EXCLUDE_CPANPLUS @EXPORT @prereqs);
  8         22  
  8         816  
72              
73              
74             $VERSION = '2.005';
75              
76             @EXPORT = qw( prereq_ok );
77              
78 8     8   47 use Carp qw(carp);
  8         14  
  8         591  
79 8     8   7108 use ExtUtils::MakeMaker;
  8         1035791  
  8         1360  
80 8     8   95 use File::Find;
  8         16  
  8         488  
81 8     8   4159 use Module::Extract::Use;
  8         11692  
  8         512  
82              
83             my $Test = __PACKAGE__->builder;
84              
85             {
86 8     8   53 no warnings;
  8         13  
  8         2546  
87              
88             * ExtUtils::MakeMaker::WriteMakefile = sub {
89 4     4   68 my %hash = @_;
90              
91 4         17 my $name = $hash{NAME};
92             my %prereqs =
93 16 100       73 map { defined $_ ? %$_ : () }
94 4         18 @hash{qw(PREREQ_PM BUILD_REQUIRES CONFIGURE_REQUIRES TEST_REQUIRES)};
95              
96 4         41 @Test::Prereq::prereqs = sort keys %prereqs;
97              
98 4         56 1;
99             }
100             }
101              
102             #unless( caller ) { prereq_ok() }
103              
104             =head1 FUNCTIONS
105              
106             =over 4
107              
108             =item prereq_ok( [ NAME [, SKIP_ARRAY] ] )
109              
110             Tests F to ensure all non-core module dependencies are in
111             C. If you haven't set a testing plan already,
112             C creates a plan of one test.
113              
114             Valid versions come from C (which uses C<$]>).
115              
116             #!/usr/bin/perl
117             use Module::CoreList;
118             print map "$_\n", sort keys %Module::CoreList::version;
119              
120             C attempts to remove modules found in F and libraries
121             found in F from the reported prerequisites.
122              
123             The optional third argument is an array reference to a list of names
124             that C should ignore. You might want to use this if your
125             tests do funny things with C.
126              
127             Versions prior to 1.038 would use CPAN.pm to virtually include
128             prerequisites in distributions that you declared explicitly. This
129             isn't really a good idea. Some modules have moved to different
130             distributions, so you should just specify all the modules that you use
131             instead of relying on a particular distribution to provide them. Not
132             only that, expanding distributions with CPAN.pm takes forever.
133              
134             If you want the old behavior, set the C
135             environment variable to a true value.
136              
137             =cut
138              
139             my $default_version = $];
140             my $version = $];
141              
142             sub prereq_ok {
143 2 100   2 1 319626 $Test->plan( tests => 1 ) unless $Test->has_plan;
144 2         1190 __PACKAGE__->_prereq_check( @_ );
145             }
146              
147             sub import {
148 8     8   73 my $self = shift;
149 8         20 my $caller = caller;
150 8     8   101 no strict 'refs';
  8         24  
  8         16943  
151 8         19 *{$caller.'::prereq_ok'} = \&prereq_ok;
  8         56  
152              
153 8         46 $Test->exported_to($caller);
154 8         114 $Test->plan(@_);
155             }
156              
157             sub _prereq_check {
158 2     2   6 my $class = shift;
159              
160 2   50     14 my $name = shift // 'Prereq test';
161 2   50     12 my $skip = shift // [];
162              
163 2 50       9 unless( ref $skip eq ref [] ) {
164 0         0 carp( 'The second parameter to prereq_ok must be an array reference!' );
165 0         0 return;
166             }
167              
168             # get the declared prereqs from the Makefile.PL
169 2         13 my $prereqs = $class->_get_prereqs();
170 2 50       9 unless( $prereqs ) {
171 0         0 $class->_not_ok( "\t" .
172             $class->_master_file . " did not return a true value.\n" );
173 0         0 return 0;
174             }
175              
176 2         18 my $loaded = $class->_get_loaded_modules();
177              
178 2 50       12 unless( $loaded ) {
179 0         0 $class->_not_ok( "\tCouldn't look up the modules for some reasons.\n" ,
180             "\tDo the blib/lib and t directories exist?\n",
181             );
182 0         0 return 0;
183             }
184              
185             # remove modules found in PREREQ_PM
186 2         7 foreach my $module ( @$prereqs ) {
187 34         62 delete $loaded->{$module};
188             }
189              
190             # remove modules found in distribution
191 2         20 my $distro = $class->_get_dist_modules( 'blib/lib' );
192 2         7 foreach my $module ( $distro->@* ) {
193 4         13 delete $loaded->{$module};
194             }
195              
196             # remove modules found in test directory
197 2         18 $distro = $class->_get_test_libraries();
198 2         7 foreach my $module ( $distro->@* ) {
199 0         0 delete $loaded->{$module};
200             }
201              
202             # remove modules in the skip array
203 2         6 foreach my $module ( $skip->@* ) {
204 0         0 delete $loaded->{$module};
205             }
206              
207 2 50       8 if( $EXCLUDE_CPANPLUS ) {
208 0         0 foreach my $module ( keys %$loaded ) {
209 0 0       0 next unless $module =~ m/^CPANPLUS::/;
210 0         0 delete $loaded->{$module};
211             }
212             }
213              
214 2 50       9 if( keys %$loaded ) { # stuff left in %loaded, oops!
215             $class->_not_ok( "Found some modules that didn't show up in PREREQ_PM or *_REQUIRES\n",
216 0         0 map { "\t$_\n" } sort keys %$loaded );
  0         0  
217             }
218             else {
219 2         23 $Test->ok( 1, $name );
220             }
221              
222 2         2027 return 1;
223             }
224              
225             sub _not_ok {
226 0     0   0 my( $self, $name, @message ) = @_;
227              
228 0         0 $Test->ok( 0, $name );
229 0         0 $Test->diag( join "", @message );
230             }
231              
232 4     4   27 sub _master_file { 'Makefile.PL' }
233              
234             sub _get_prereqs {
235 5     5   269833 my $class = shift;
236 5         59 my $file = $class->_master_file;
237              
238 5         19 delete $INC{$file}; # make sure we load it again
239              
240             {
241 5         12 local $^W = 0;
  5         62  
242              
243 5 100       3410 unless( do "./$file" ) {
244 1         24 print STDERR "_get_prereqs: Error loading $file: $@\n";
245 1         10 return;
246             }
247 4         24 delete $INC{$file}; # pretend we were never here
248             }
249              
250 4         31 my @modules = sort @Test::Prereq::prereqs;
251 4         14 @Test::Prereq::prereqs = ();
252 4         14 return \@modules;
253             }
254              
255             # get all the loaded modules. we'll filter this later
256             sub _get_loaded_modules {
257 3     3   1718 my $class = shift;
258              
259             # return unless( defined $_[0] and defined $_[1] );
260             # return unless( -d $_[0] and -d $_[1] );
261              
262 3         8 my( @libs, @t, @scripts );
263              
264 3 100   30   489 File::Find::find( sub { push @libs, $File::Find::name if m/\.pm$/ }, 'blib/lib' )
  30 50       2038  
265             if -e 'blib/lib';
266 3 100   33   243 File::Find::find( sub { push @t, $File::Find::name if m/\.t$/ }, 't' )
  33 50       670  
267             if -e 't';
268 3 100   6   171 File::Find::find( sub { push @scripts, $File::Find::name if -f $_ }, 'blib/script' )
  6 50       278  
269             if -e 'blib/script';
270              
271 3         15 my @found = ();
272 3         9 foreach my $file ( @libs, @t, @scripts ) {
273 36         88 push @found, @{ $class->_get_from_file( $file ) };
  36         416  
274             }
275              
276 3         11 return { map { $_, 1 } @found };
  51         178  
277             }
278              
279             sub _get_test_libraries {
280 2     2   7 my $class = shift;
281              
282 2         5 my $dirsep = "/";
283              
284 2         25 my @found = ();
285              
286 2 50   22   171 File::Find::find( sub { push @found, $File::Find::name if m/\.p(l|m)$/ }, 't' );
  22         745  
287              
288             my @files =
289             map {
290 2         15 my $x = $_;
  0         0  
291 0         0 $x =~ s/^.*$dirsep//;
292 0         0 $x =~ s|$dirsep|::|g;
293 0         0 $x;
294             }
295             @found;
296              
297 2 50       26 push @files, 'test.pl' if -e 'test.pl';
298              
299 2         10 return \@files;
300             }
301              
302             sub _get_dist_modules {
303 2     2   7 my $class = shift;
304              
305 2 50 33     72 return unless( defined $_[0] and -d $_[0] );
306              
307 2         10 my $dirsep = "/";
308              
309 2         5 my @found = ();
310              
311 2 100   20   224 File::Find::find( sub { push @found, $File::Find::name if m/\.pm$/ }, $_[0] );
  20         1731  
312              
313             my @files =
314             map {
315 2         17 my $x = $_;
  4         11  
316 4         86 $x =~ s/^$_[0]($dirsep)?//;
317 4         21 $x =~ s/\.pm$//;
318 4         35 $x =~ s|$dirsep|::|g;
319 4         16 $x;
320             }
321             @found;
322              
323 2         11 return \@files;
324             }
325              
326             sub _get_from_file {
327 38     38   189255 state $extor = Module::Extract::Use->new;
328 38         243 my( $class, $file ) = @_;
329              
330 38         186 my $modules = $extor->get_modules_with_details( $file );
331              
332             # We also depend on the super classes, which might not be
333             # part of the distro
334             my @imports =
335             map {
336 38         8591791 state $can_import = { map { $_, 1 } qw(base parent) };
  126         563  
  8         46  
337 126 100       378 exists $can_import->{$_->module}
338             ?
339             $_->imports->@*
340             :
341             ();
342             } $modules->@*;
343              
344             my @modules =
345 133         285 grep { state %Seen; ! $Seen{$_}++ } (
  133         480  
346             @imports,
347 38         284 map { $_->module } $modules->@*
  126         976  
348             );
349              
350 38         491 return \@modules;
351             }
352              
353             =back
354              
355             =head1 TO DO
356              
357             =over 4
358              
359             =item * set up a couple fake module distributions to test
360              
361             =item * warn about things that show up in C unnecessarily
362              
363             =back
364              
365             =head1 SOURCE AVAILABILITY
366              
367             This source is in Github:
368              
369             http://github.com/briandfoy/test-prereq
370              
371             =head1 CONTRIBUTORS
372              
373             Many thanks to:
374              
375             Andy Lester, Slavin Rezić, Randal Schwartz, Iain Truskett, Dylan Martin
376              
377             =head1 AUTHOR
378              
379             brian d foy, C<< >>
380              
381             =head1 COPYRIGHT and LICENSE
382              
383             Copyright © 2002-2025, brian d foy . All rights reserved.
384             This software is available under the Artistic License 2.
385              
386             =cut
387              
388             1;