File Coverage

blib/lib/Test/Strict.pm
Criterion Covered Total %
statement 172 214 80.3
branch 69 106 65.0
condition 42 70 60.0
subroutine 29 30 96.6
pod 7 7 100.0
total 319 427 74.7


line stmt bran cond sub pod time code
1             package Test::Strict;
2              
3             =head1 NAME
4              
5             Test::Strict - Check syntax, presence of use strict; and test coverage
6              
7             =head1 VERSION
8              
9             Version 0.52
10              
11             =head1 SYNOPSIS
12              
13             C lets you check the syntax, presence of C
14             and presence C in your perl code.
15             It report its results in standard L fashion:
16              
17             use Test::Strict tests => 3;
18             syntax_ok( 'bin/myscript.pl' );
19             strict_ok( 'My::Module', "use strict; in My::Module" );
20             warnings_ok( 'lib/My/Module.pm' );
21              
22             Module authors can include the following in a t/strict.t
23             and have C automatically find and check
24             all perl files in a module distribution:
25              
26             use Test::Strict;
27             all_perl_files_ok(); # Syntax ok and use strict;
28              
29             or
30              
31             use Test::Strict;
32             all_perl_files_ok( @mydirs );
33              
34             C can also enforce a minimum test coverage
35             the test suite should reach.
36             Module authors can include the following in a t/cover.t
37             and have C automatically check the test coverage:
38              
39             use Test::Strict;
40             all_cover_ok( 80 ); # at least 80% coverage
41              
42             or
43              
44             use Test::Strict;
45             all_cover_ok( 80, 't/' );
46              
47             =head1 DESCRIPTION
48              
49             The most basic test one can write is "does it compile ?".
50             This module tests if the code compiles and play nice with L modules.
51              
52             Another good practice this module can test is to "use strict;" in all perl files.
53              
54             By setting a minimum test coverage through C, a code author
55             can ensure his code is tested above a preset level of I throughout the development cycle.
56              
57             Along with L, this module can provide the first tests to setup for a module author.
58              
59             This module should be able to run under the -T flag for perl >= 5.6.
60             All paths are untainted with the following pattern: C
61             controlled by C<$Test::Strict::UNTAINT_PATTERN>.
62              
63             =cut
64              
65 5     5   227660 use strict; use warnings;
  5     5   34  
  5         122  
  5         21  
  5         7  
  5         128  
66 5     5   121 use 5.006;
  5         14  
67 5     5   25 use Test::Builder;
  5         8  
  5         96  
68 5     5   20 use File::Spec;
  5         13  
  5         144  
69 5     5   2016 use FindBin qw($Bin);
  5         4525  
  5         574  
70 5     5   30 use File::Find;
  5         9  
  5         218  
71 5     5   24 use Config;
  5         9  
  5         1204  
72              
73             our $COVER;
74             our $VERSION = '0.52';
75             our $PERL = $^X || 'perl';
76             our $COVERAGE_THRESHOLD = 50; # 50%
77             our $UNTAINT_PATTERN = qr|^(.*)$|;
78             our $PERL_PATTERN = qr/^#!.*perl/;
79             our $CAN_USE_WARNINGS = ($] >= 5.006);
80             our $TEST_SYNTAX = 1; # Check compile
81             our $TEST_STRICT = 1; # Check use strict;
82             our $TEST_WARNINGS = 0; # Check use warnings;
83             our $TEST_SKIP = []; # List of files to skip check
84             our $DEVEL_COVER_OPTIONS = '+ignore,".Test.Strict\b"';
85             our $DEVEL_COVER_DB = 'cover_db';
86             my $IS_WINDOWS = $^O =~ /MSwin/i;
87              
88             my $Test = Test::Builder->new;
89             my $updir = File::Spec->updir();
90             my %file_find_arg = ($] <= 5.006) ? ()
91             : (
92             untaint => 1,
93             untaint_pattern => $UNTAINT_PATTERN,
94             untaint_skip => 1,
95             );
96              
97             sub import {
98 4     4   28 my $self = shift;
99 4         8 my $caller = caller;
100              
101             {
102 5     5   62 no strict 'refs';
  5         10  
  5         12706  
  4         5  
103 4         9 *{$caller.'::strict_ok'} = \&strict_ok;
  4         18  
104 4         8 *{$caller.'::warnings_ok'} = \&warnings_ok;
  4         11  
105 4         6 *{$caller.'::syntax_ok'} = \&syntax_ok;
  4         11  
106 4         7 *{$caller.'::all_perl_files_ok'} = \&all_perl_files_ok;
  4         11  
107 4         7 *{$caller.'::all_cover_ok'} = \&all_cover_ok;
  4         9  
108             }
109              
110 4         16 $Test->exported_to($caller);
111 4         37 $Test->plan(@_);
112             }
113              
114             ##
115             ## _all_perl_files( @dirs )
116             ## Returns a list of perl files in @dir
117             ## if @dir is not provided, it searches from one dir level above
118             ##
119             sub _all_perl_files {
120 2     2   9 my @all_files = _all_files(@_);
121 2 100       6 return grep { _is_perl_module($_) || _is_perl_script($_) } @all_files;
  42         80  
122             }
123              
124             sub _all_files {
125 2 100   2   23 my @base_dirs = @_ ? @_
126             : File::Spec->catdir($Bin, $updir);
127 2         4 my @found;
128             my $want_sub = sub {
129             #return if ($File::Find::dir =~ m![\\/]?CVS[\\/]|[\\/]?.svn[\\/]!); # Filter out cvs or subversion dirs/
130             #return if ($File::Find::dir =~ m![\\/]?blib[\\/]libdoc$!); # Filter out pod doc in dist
131             #return if ($File::Find::dir =~ m![\\/]?blib[\\/]man\d$!); # Filter out pod doc in dist
132 66 100 66 66   1307 if (-d $File::Find::name &&
      100        
133             ($_ eq 'CVS' || $_ eq '.svn' || $_ eq '.git' || # Filter out cvs or git or subversion dirs
134             $File::Find::name =~ m!(?:^|[\\/])blib[\\/]libdoc$! || # Filter out pod doc in dist
135             $File::Find::name =~ m!(?:^|[\\/])blib[\\/]man\d$!) # Filter out pod doc in dist
136             ) {
137 2         5 $File::Find::prune = 1;
138 2         13 return;
139             }
140              
141 64 100 66     2241 return unless (-f $File::Find::name && -r _);
142 43 50       114 return if ($File::Find::name =~ m!\.#.+?[\d\.]+$!); # Filter out CVS backup files (.#file.revision)
143 43         665 push @found, File::Spec->canonpath( File::Spec->no_upwards( $File::Find::name ) );
144 2         16 };
145              
146 2         24 my $find_arg = {
147             %file_find_arg,
148             wanted => $want_sub,
149             no_chdir => 1,
150             };
151 2         281 find( $find_arg, @base_dirs); # Find all potential file candidates
152              
153 2   50     7 my $files_to_skip = $TEST_SKIP || [];
154 2         5 my %skip = map { $_ => undef } @$files_to_skip;
  1         5  
155 2         5 return grep { ! exists $skip{$_} } @found; # Exclude files to skip
  43         70  
156             }
157              
158             =head1 FUNCTIONS
159              
160             =head2 syntax_ok( $file [, $text] )
161              
162             Run a syntax check on C<$file> by running C with an external perl interpreter.
163             The external perl interpreter path is stored in C<$Test::Strict::PERL> which can be modified.
164             You may prefer C from L to syntax test a module.
165             For a module, the path (lib/My/Module.pm) or the name (My::Module) can be both used.
166              
167             =cut
168              
169             sub syntax_ok {
170 20     20 1 76 my $file = shift;
171 20   66     116 my $test_txt = shift || "Syntax check $file";
172              
173 20         69 $file = _module_to_path($file);
174 20 50 33     433 unless (-f $file && -r _) {
175 0         0 $Test->ok( 0, $test_txt );
176 0         0 $Test->diag( "File $file not found or not readable" );
177 0         0 return;
178             }
179              
180 20         84 my $is_script = _is_perl_script($file);
181              
182             # Set the environment to compile the script or module
183 20         321 require Config;
184 20   50     867 my $inc = join($Config::Config{path_sep}, @INC) || '';
185 20         94 $file = _untaint($file);
186 20         54 my $perl_bin = _untaint($PERL);
187 20 50       126 local $ENV{PATH} = _untaint($ENV{PATH}) if $ENV{PATH};
188              
189             # Add the -t -T switches if they are set in the #! line
190 20         36 my $switch = '';
191 20 100 100     84 $switch = _taint_switch($file) || '' if $is_script;
192              
193             # Compile and check for errors
194 20         34 my $eval = do {
195 20         108 local $ENV{PERL5LIB} = $inc;
196 20         1318307 `$perl_bin -c$switch \"$file\" 2>&1`;
197             };
198 20         454 $file = quotemeta($file);
199 20         2162 my $ok = $eval =~ qr!$file syntax OK!ms;
200 20         686 $Test->ok($ok, $test_txt);
201 20 50       30195 unless ($ok) {
202 0         0 $Test->diag( $eval );
203             }
204 20         440 return $ok;
205             }
206              
207             =head2 strict_ok( $file [, $text] )
208              
209             Check if C<$file> contains a C statement.
210             C and C are also considered valid.
211             use Modern::Perl is also accepted.
212              
213             This is a pretty naive test which may be fooled in some edge cases.
214             For a module, the path (lib/My/Module.pm) or the name (My::Module) can be both used.
215              
216             =cut
217              
218             sub strict_ok {
219 26     26 1 7792 my $file = shift;
220 26   66     310 my $test_txt = shift || "use strict $file";
221 26         268 $file = _module_to_path($file);
222 26 50       1452 open my $fh, '<', $file or do { $Test->ok(0, $test_txt); $Test->diag("Could not open $file: $!"); return; };
  0         0  
  0         0  
  0         0  
223 26         183 my $ok = _strict_ok($fh);
224 26         134 $Test->ok($ok, $test_txt);
225 26         7327 return $ok;
226             }
227              
228             sub _module_rx {
229 39     39   281 my (@module_names) = @_;
230 39         1566 my $names = join '|', map quotemeta, reverse sort @module_names;
231             # TODO: improve this matching (e.g. see TODO test)
232 39         2867 return qr/\buse\s+(?:$names)(?:[;\s]|$)/;
233             }
234              
235             sub _strict_ok {
236 28     28   456202 my ($in) = @_;
237 28         103 my $strict_module_rx = _module_rx( modules_enabling_strict() );
238 28         87 local $_;
239 28         46 my $pod;
240 28         539 while (<$in>) {
241 257 100       1304 next if (/^\s*#/); # Skip comments
242 239 100       502 $pod = 0, next if /^=(cut|back|end)/;
243 233 100       428 $pod = 1, next if /^=\S+/;
244 218 100       365 next if $pod; # skip pod
245 47 50       89 last if (/^\s*(__END__|__DATA__)/); # End of code
246 47 100       560 return 1 if $_ =~ $strict_module_rx;
247 23 100 100     106 if (/\buse\s+(5\.\d+)/ and $1 >= 5.012) {
248 2         108 return 1;
249             }
250 21 100 66     91 if (/\buse\s+v5\.(\d+)/ and $1 >= 12) {
251 1         9 return 1;
252             }
253             }
254 1         18 return;
255             }
256              
257             =head2 modules_enabling_strict
258              
259             Experimental. Returning a list of modules and pragmata that enable strict.
260             To modify this list, change C<@Test::Strict::MODULES_ENABLING_STRICT>.
261              
262             List taken from L v95
263              
264             =cut
265              
266             our @MODULES_ENABLING_STRICT = qw(
267             strict
268             Any::Moose
269             Catmandu::Sane
270             Class::Spiffy
271             Coat
272             common::sense
273             Dancer
274             HTML::FormHandler::Moose
275             HTML::FormHandler::Moose::Role
276             Mo
277             Modern::Perl
278             Mojo::Base
279             Moo
280             Moo::Role
281             MooX
282             Moose
283             Moose::Exporter
284             Moose::Role
285             MooseX::Declare
286             MooseX::Role::Parameterized
287             MooseX::Types
288             Mouse
289             Mouse::Role
290             perl5
291             perl5i::1
292             perl5i::2
293             perl5i::latest
294             Role::Tiny
295             Spiffy
296             strictures
297             Test::Most
298             Test::Roo
299             Test::Roo::Role
300             );
301              
302 28     28 1 760 sub modules_enabling_strict { return @MODULES_ENABLING_STRICT }
303              
304             =head2 modules_enabling_warnings
305              
306             Experimental. Returning a list of modules and pragmata that enable warnings
307             To modify this list, change C<@Test::Strict::MODULES_ENABLING_WARNINGS>.
308              
309             List taken from L v95
310              
311             =cut
312              
313             our @MODULES_ENABLING_WARNINGS = qw(
314             warnings
315             Any::Moose
316             Catmandu::Sane
317             Class::Spiffy
318             Coat
319             common::sense
320             Dancer
321             HTML::FormHandler::Moose
322             HTML::FormHandler::Moose::Role
323             Mo
324             Modern::Perl
325             Mojo::Base
326             Moo
327             Moo::Role
328             MooX
329             Moose
330             Moose::Exporter
331             Moose::Role
332             MooseX::Declare
333             MooseX::Role::Parameterized
334             MooseX::Types
335             Mouse
336             Mouse::Role
337             perl5
338             perl5i::1
339             perl5i::2
340             perl5i::latest
341             Role::Tiny
342             Spiffy
343             strictures
344             Test::Most
345             Test::Roo
346             Test::Roo::Role
347             );
348              
349 11     11 1 122 sub modules_enabling_warnings { return @MODULES_ENABLING_WARNINGS }
350              
351             =head2 warnings_ok( $file [, $text] )
352              
353             Check if warnings have been turned on.
354              
355             If C<$file> is a module, check if it contains a C or C
356             or C or C statement. use Modern::Perl is also accepted.
357             If the perl version is <= 5.6, this test is skipped (C appeared in perl 5.6).
358              
359             If C<$file> is a script, check if it starts with C<#!...perl -w>.
360             If the -w is not found and perl is >= 5.6, check for a C or C
361             or C or C statement. use Modern::Perl is also accepted.
362              
363             This is a pretty naive test which may be fooled in some edge cases.
364             For a module, the path (lib/My/Module.pm) or the name (My::Module) can be both used.
365              
366             =cut
367              
368             sub warnings_ok {
369 11     11 1 4124 my $file = shift;
370 11   66     128 my $test_txt = shift || "use warnings $file";
371              
372 11         93 $file = _module_to_path($file);
373 11         51 my $is_module = _is_perl_module( $file );
374 11         44 my $is_script = _is_perl_script( $file );
375 11 50 100     65 if (!$is_script and $is_module and ! $CAN_USE_WARNINGS) {
      66        
376 0         0 $Test->skip();
377 0         0 $Test->diag("This version of perl ($]) does not have use warnings - perl 5.6 or higher is required");
378 0         0 return;
379             }
380              
381 11 50       356 open my $fh, '<', $file or do { $Test->ok(0, $test_txt); $Test->diag("Could not open $file: $!"); return; };
  0         0  
  0         0  
  0         0  
382 11         113 my $ok = _warnings_ok($is_script, $fh);
383 11         54 $Test->ok($ok, $test_txt);
384 11         3154 return $ok
385             }
386              
387             # TODO unite with _strict_ok
388             sub _warnings_ok {
389 11     11   48 my ($is_script, $in) = @_;
390 11         25 my $warnings_module_rx = _module_rx( modules_enabling_warnings() );
391 11         48 local $_;
392 11         168 while (<$in>) {
393 13 100 100     215 if ($. == 1 and $is_script and $_ =~ $PERL_PATTERN) {
      100        
394 6 100       35 if (/\s+-\w*[wW]/) {
395 5         25 return 1;
396             }
397             }
398 8 50       15 last unless $CAN_USE_WARNINGS;
399 8 100       22 next if (/^\s*#/); # Skip comments
400 7 50       27 next if (/^\s*=.+/ .. /^\s*=(cut|back|end)/); # Skip pod
401 7 50       14 last if (/^\s*(__END__|__DATA__)/); # End of code
402 7 100       70 return 1 if $_ =~ $warnings_module_rx;
403             }
404 0         0 return;
405             }
406              
407             =head2 all_perl_files_ok( [ @directories ] )
408              
409             Applies C and C to all perl files found in C<@directories> (and sub directories).
410             If no <@directories> is given, the starting point is one level above the current running script,
411             that should cover all the files of a typical CPAN distribution.
412             A perl file is *.pl or *.pm or *.t or a file starting with C<#!...perl>
413              
414             If the test plan is defined:
415              
416             use Test::Strict tests => 18;
417             all_perl_files_ok();
418              
419             the total number of files tested must be specified.
420              
421             You can control which tests are run on each perl site through:
422              
423             $Test::Strict::TEST_SYNTAX (default = 1)
424             $Test::Strict::TEST_STRICT (default = 1)
425             $Test::Strict::TEST_WARNINGS (default = 0)
426             $Test::Strict::TEST_SKIP (default = []) "Trusted" files to skip
427              
428             =cut
429              
430             sub all_perl_files_ok {
431 2     2 1 10796 my @files = _all_perl_files( @_ );
432              
433 2         10 _make_plan();
434 2         198 foreach my $file ( @files ) {
435 17 50       184 syntax_ok( $file ) if $TEST_SYNTAX;
436 17 50       252 strict_ok( $file ) if $TEST_STRICT;
437 17 100       163 warnings_ok( $file ) if $TEST_WARNINGS;
438             }
439             }
440              
441             =head2 all_cover_ok( [coverage_threshold [, @t_dirs]] )
442              
443             This will run all the tests in @t_dirs
444             (or current script's directory if @t_dirs is undef)
445             under L
446             and calculate the global test coverage of the code loaded by the tests.
447             If the test coverage is greater or equal than C, it is a pass,
448             otherwise it's a fail. The default coverage threshold is 50
449             (meaning 50% of the code loaded has been covered by test).
450              
451             The threshold can be modified through C<$Test::Strict::COVERAGE_THRESHOLD>.
452              
453             You may want to select which files are selected for code
454             coverage through C<$Test::Strict::DEVEL_COVER_OPTIONS>,
455             see L for the list of available options.
456             The default is '+ignore,"/Test/Strict\b"'.
457              
458             The path to C utility can be modified through C<$Test::Strict::COVER>.
459              
460             The 50% threshold is a completely arbitrary value, which should not be considered
461             as a good enough coverage.
462              
463             The total coverage is the return value of C.
464              
465             =cut
466              
467             sub all_cover_ok {
468 0     0 1 0 my $cover_bin = _cover_path();
469 0 0       0 die "ERROR: Cover binary not found, please install Devel::Cover.\n"
470             unless (defined $cover_bin);
471              
472 0   0     0 my $threshold = shift || $COVERAGE_THRESHOLD;
473 0 0 0     0 my @dirs = @_ ? @_
474             : (File::Spec->splitpath( $0 ))[1] || '.';
475 0   0     0 my @all_files = grep { ! /$0$/o && $0 !~ /$_$/ }
476 0         0 grep { _is_perl_script($_) }
  0         0  
477             _all_files(@dirs);
478 0         0 _make_plan();
479              
480 0         0 my $perl_bin = _untaint($PERL);
481 0 0       0 local $ENV{PATH} = _untaint($ENV{PATH}) if $ENV{PATH};
482 0 0 0     0 if ($IS_WINDOWS and ! -d $DEVEL_COVER_DB) {
483 0 0       0 mkdir $DEVEL_COVER_DB or warn "$DEVEL_COVER_DB: $!";
484             }
485              
486 0         0 my $res = `$cover_bin -delete 2>&1`;
487 0 0       0 if ($?) {
488 0         0 $Test->skip();
489 0         0 $Test->diag("Cover at $cover_bin got error $?: $res");
490 0         0 return;
491             }
492 0         0 foreach my $file ( @all_files ) {
493 0         0 $file = _untaint($file);
494 0         0 `$perl_bin -MDevel::Cover=$DEVEL_COVER_OPTIONS $file`;
495 0         0 $Test->ok(! $?, "Coverage captured from $file" );
496             }
497 0         0 $Test->ok(my $cover = `$cover_bin 2>&1`, "Got cover");
498              
499 0         0 my ($total) = ($cover =~ /^\s*Total.+?([\d\.]+)\s*$/m);
500 0         0 $Test->ok( $total >= $threshold, "coverage = ${total}% > ${threshold}%");
501 0         0 return $total;
502             }
503              
504             sub _is_perl_module {
505 56 100   56   269 return 0 if $_[0] =~ /\~$/;
506 55 100       344 $_[0] =~ /\.pm$/i || $_[0] =~ /::/;
507             }
508              
509              
510             sub _is_perl_script {
511 74     74   201 my $file = shift;
512              
513 74 100       276 return 0 if $file =~ /\~$/;
514 73 100       292 return 1 if $file =~ /\.pl$/i;
515 58 100       203 return 1 if $file =~ /\.t$/;
516 34 50       925 open my $fh, '<', $file or return;
517 34         434 my $first = <$fh>;
518 34 50 66     263 return 1 if defined $first && ($first =~ $PERL_PATTERN);
519 34         433 return;
520             }
521              
522             ##
523             ## Returns the taint switches -tT in the #! line of a perl script
524             ##
525             sub _taint_switch {
526 16     16   28 my $file = shift;
527              
528 16 50       503 open my $fh, '<', $file or return;
529 16         210 my $first = <$fh>;
530 16 100       335 $first =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ or return;
531 2         35 return $1;
532             }
533              
534             ##
535             ## Return the path of a module
536             ##
537             sub _module_to_path {
538 57     57   116 my $file = shift;
539              
540 57         215 my @parts = split /::/, $file;
541 57         703 my $module = File::Spec->catfile(@parts) . '.pm';
542 57         306 foreach my $dir (@INC) {
543 609         4161 my $candidate = File::Spec->catfile($dir, $module);
544 609 50 66     6629 next unless (-e $candidate && -f _ && -r _);
      66        
545 2         18 return $candidate;
546             }
547 55         199 return $file; # non existing file - error is catched elsewhere
548             }
549              
550              
551             sub _cover_path {
552 1 50   1   72 return $COVER if defined $COVER;
553              
554 1 50       5 my $os_separator = $IS_WINDOWS ? ';' : ':';
555 1         17 foreach ((split /$os_separator/, $ENV{PATH}), @Config{qw(bin sitedir scriptdir)} ) {
556 12   100     140 my $path = $_ || '.';
557 12         79 my $path_cover = File::Spec->catfile($path, 'cover');
558 12 50       26 if ($IS_WINDOWS) {
559 0 0 0     0 next unless (-f $path_cover && -r _);
560             }
561             else {
562 12 50       169 next unless -x $path_cover;
563             }
564 0         0 return $COVER = _untaint($path_cover);
565             }
566 1         7 return;
567             }
568              
569              
570             sub _make_plan {
571 2 50   2   13 unless ($Test->has_plan) {
572 0         0 $Test->plan( 'no_plan' );
573             }
574 2         255 $Test->expected_tests;
575             }
576              
577             sub _untaint {
578 60     60   109 my @untainted = map {($_ =~ $UNTAINT_PATTERN)} @_;
  60         494  
579             wantarray ? @untainted
580 60 50       351 : $untainted[0];
581             }
582              
583             =head1 CAVEATS
584              
585             For C to work properly, it is strongly advised to install the most recent version of L
586             and use perl 5.8.1 or above.
587             In the case of a C scenario, C re-run all the tests in a separate perl interpreter,
588             this may lead to some side effects.
589              
590             =head1 SEE ALSO
591              
592             L, L. L, L
593              
594             =head1 REPOSITORY
595              
596             L
597              
598             =head1 AUTHOR
599              
600             Pierre Denis, C<< >>.
601              
602             =head1 MAINTAINER
603              
604             L
605              
606             Currently maintained by Mohammad S Anwar (MANWAR), C<< >>
607              
608             =head1 COPYRIGHT
609              
610             Copyright 2005, 2010 Pierre Denis, All Rights Reserved.
611              
612             You may use, modify, and distribute this package under the
613             same terms as Perl itself.
614              
615             =cut
616              
617             1;