File Coverage

blib/lib/Test/Strict.pm
Criterion Covered Total %
statement 173 214 80.8
branch 71 106 66.9
condition 41 70 58.5
subroutine 29 30 96.6
pod 7 7 100.0
total 321 427 75.1


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.54
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 8     8   1211849 use strict; use warnings;
  8     8   18  
  8         336  
  8         72  
  8         16  
  8         509  
66 8     8   172 use 5.006;
  8         61  
67 8     8   1712 use Test::Builder;
  8         229134  
  8         386  
68 8     8   81 use File::Spec;
  8         27  
  8         314  
69 8     8   5099 use FindBin qw($Bin);
  8         16259  
  8         4809  
70 8     8   65 use File::Find;
  8         18  
  8         514  
71 8     8   53 use Config;
  8         24  
  8         2815  
72              
73             our $COVER;
74             our $VERSION = '0.54';
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 6     6   54 my $self = shift;
99 6         17 my $caller = caller;
100              
101             {
102 8     8   55 no strict 'refs';
  8         35  
  8         37615  
  6         26  
103 6         16 *{$caller.'::strict_ok'} = \&strict_ok;
  6         36  
104 6         13 *{$caller.'::warnings_ok'} = \&warnings_ok;
  6         22  
105 6         13 *{$caller.'::syntax_ok'} = \&syntax_ok;
  6         18  
106 6         11 *{$caller.'::all_perl_files_ok'} = \&all_perl_files_ok;
  6         24  
107 6         14 *{$caller.'::all_cover_ok'} = \&all_cover_ok;
  6         17  
108             }
109              
110 6         36 $Test->exported_to($caller);
111 6         86 $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 3     3   14 my @all_files = _all_files(@_);
121 3 100       9 return grep { _is_perl_module($_) || _is_perl_script($_) } @all_files;
  46         84  
122             }
123              
124             sub _all_files {
125 3 100   3   40 my @base_dirs = @_ ? @_
126             : File::Spec->catdir($Bin, $updir);
127 3         23 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 72 100 66 72   877 if (-d $File::Find::name &&
      100        
133             ($File::Find::name =~ m!(?:^|[\\/])(?:CVS|\.svn|\.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         3 $File::Find::prune = 1;
138 2         10 return;
139             }
140              
141 70 100 66     2903 return unless (-f $File::Find::name && -r _);
142 47 50       110 return if ($File::Find::name =~ m!\.#.+?[\d\.]+$!); # Filter out CVS backup files (.#file.revision)
143 47         1045 push @found, File::Spec->canonpath( File::Spec->no_upwards( $File::Find::name ) );
144 3         26 };
145              
146 3         35 my $find_arg = {
147             %file_find_arg,
148             wanted => $want_sub,
149             no_chdir => 1,
150             };
151 3         577 find( $find_arg, @base_dirs); # Find all potential file candidates
152              
153 3   50     16 my $files_to_skip = $TEST_SKIP || [];
154 3         18 my %skip = map { $_ => undef } @$files_to_skip;
  1         7  
155 3         19 return grep { ! exists $skip{$_} } @found; # Exclude files to skip
  47         127  
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 23     23 1 90 my $file = shift;
171 23   66     251 my $test_txt = shift || "Syntax check $file";
172              
173 23         80 $file = _module_to_path($file);
174 23 50 33     1507 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 23         109 my $is_script = _is_perl_script($file);
181              
182             # Set the environment to compile the script or module
183 23         378 require Config;
184 23   50     1066 my $inc = join($Config::Config{path_sep}, @INC) || '';
185 23         135 $file = _untaint($file);
186 23         53 my $perl_bin = _untaint($PERL);
187 23 50       115 local $ENV{PATH} = _untaint($ENV{PATH}) if $ENV{PATH};
188              
189             # Add the -t -T switches if they are set in the #! line
190 23         108 my $switch = '';
191 23 100 100     96 $switch = _taint_switch($file) || '' if $is_script;
192              
193             # Compile and check for errors
194 23         56 my $eval = do {
195 23         320 local $ENV{PERL5LIB} = $inc;
196 23         12947559 `$perl_bin -c$switch \"$file\" 2>&1`;
197             };
198 23         550 $file = quotemeta($file);
199 23         3384 my $ok = $eval =~ qr!$file syntax OK!ms;
200 23         903 $Test->ok($ok, $test_txt);
201 23 50       35727 unless ($ok) {
202 0         0 $Test->diag( $eval );
203             }
204 23         566 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 29     29 1 16503 my $file = shift;
220 29   66     242 my $test_txt = shift || "use strict $file";
221 29         286 $file = _module_to_path($file);
222 29 50       1937 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 29         280 my $ok = _strict_ok($fh);
224 29         188 $Test->ok($ok, $test_txt);
225 29         16273 return $ok;
226             }
227              
228             sub _module_rx {
229 47     47   408 my (@module_names) = @_;
230 47         2903 my $names = join '|', map quotemeta, reverse sort @module_names;
231             # TODO: improve this matching (e.g. see TODO test)
232 47         10322 return qr/\buse\s+(?:$names)(?:[;\s]|$)/;
233             }
234              
235             sub _strict_ok {
236 31     31   7304377 my ($in) = @_;
237 31         164 my $strict_module_rx = _module_rx( modules_enabling_strict() );
238 31         176 local $_;
239 31         96 my $pod;
240 31         889 while (<$in>) {
241 261 100       1638 next if (/^\s*#/); # Skip comments
242 242 100       549 $pod = 0, next if /^=(cut|back|end)/;
243 236 100       502 $pod = 1, next if /^=\S+/;
244 221 100       506 next if $pod; # skip pod
245 50 50       145 last if (/^\s*(__END__|__DATA__)/); # End of code
246 50 100       764 return 1 if $_ =~ $strict_module_rx;
247 23 100 100     185 if (/\buse\s+(5\.\d+)/ and $1 >= 5.012) {
248 2         17 return 1;
249             }
250 21 100 66     135 if (/\buse\s+v5\.(\d+)/ and $1 >= 12) {
251 1         6 return 1;
252             }
253             }
254 1         24 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 31     31 1 1065 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 16     16 1 284 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 16     16 1 182575 my $file = shift;
370 16   66     186 my $test_txt = shift || "use warnings $file";
371              
372 16         98 $file = _module_to_path($file);
373 16         121 my $is_module = _is_perl_module( $file );
374 16         90 my $is_script = _is_perl_script( $file );
375 16 50 100     132 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 16 50       816 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 16         65 my $ok = _warnings_ok($is_script, $fh);
383 16         129 $Test->ok($ok, $test_txt);
384 16         11759 return $ok
385             }
386              
387             # TODO unite with _strict_ok
388             sub _warnings_ok {
389 16     16   66 my ($is_script, $in) = @_;
390 16         49 my $warnings_module_rx = _module_rx( modules_enabling_warnings() );
391 16         102 local $_;
392 16         782 while (<$in>) {
393 38 100 100     541 if ($. == 1 and $is_script and $_ =~ $PERL_PATTERN) {
      100        
394 7 100       57 if (/\s+-\w*[wW]/) {
395 6         32 return 1;
396             }
397             }
398 32 50       75 last unless $CAN_USE_WARNINGS;
399 32 100       104 next if (/^\s*#/); # Skip comments
400 30 100       114 next if (/^\s*=.+/ .. /^\s*=(cut|back|end)/); # Skip pod
401 21 50       53 last if (/^\s*(__END__|__DATA__)/); # End of code
402 21 100       279 return 1 if $_ =~ $warnings_module_rx;
403             }
404 3         16 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 3     3 1 221045 my @files = _all_perl_files( @_ );
432              
433 3         15 _make_plan();
434 3         563 foreach my $file ( @files ) {
435 20 50       168 syntax_ok( $file ) if $TEST_SYNTAX;
436 20 50       205 strict_ok( $file ) if $TEST_STRICT;
437 20 100       268 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 65 100   65   576 return 0 if $_[0] =~ /\~$/;
506 64 100       489 $_[0] =~ /\.pm$/i || $_[0] =~ /::/;
507             }
508              
509              
510             sub _is_perl_script {
511 85     85   160184 my $file = shift;
512              
513 85 100       411 return 0 if $file =~ /\~$/;
514 84 100       2754 return 1 if $file =~ /\.pl$/i;
515 59 100       172 return 1 if $file =~ /\.t$/;
516 37 50       1212 open my $fh, '<', $file or return;
517 37         2692 my $first = <$fh>;
518 37 50 66     319 return 1 if defined $first && ($first =~ $PERL_PATTERN);
519 37         497 return;
520             }
521              
522             ##
523             ## Returns the taint switches -tT in the #! line of a perl script
524             ##
525             sub _taint_switch {
526 18     18   39 my $file = shift;
527              
528 18 50       905 open my $fh, '<', $file or return;
529 18         533 my $first = <$fh>;
530 18 100       4346 $first =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ or return;
531 2         81 return $1;
532             }
533              
534             ##
535             ## Return the path of a module
536             ##
537             sub _module_to_path {
538 68     68   201 my $file = shift;
539              
540 68         364 my @parts = split /::/, $file;
541 68         1142 my $module = File::Spec->catfile(@parts) . '.pm';
542 68         344 foreach my $dir (@INC) {
543 649         4965 my $candidate = File::Spec->catfile($dir, $module);
544 649 50 66     16881 next unless (-e $candidate && -f _ && -r _);
      66        
545 2         26 return $candidate;
546             }
547 66         277 return $file; # non existing file - error is catched elsewhere
548             }
549              
550              
551             sub _cover_path {
552 1 50   1   254611 return $COVER if defined $COVER;
553              
554 1 50       7 my $os_separator = $IS_WINDOWS ? ';' : ':';
555 1         26 foreach ((split /$os_separator/, $ENV{PATH}), @Config{qw(bin sitedir scriptdir)} ) {
556 3   50     12 my $path = $_ || '.';
557 3         43 my $path_cover = File::Spec->catfile($path, 'cover');
558 3 50       11 if ($IS_WINDOWS) {
559 0 0 0     0 next unless (-f $path_cover && -r _);
560             }
561             else {
562 3 100       121 next unless -x $path_cover;
563             }
564 1         8 return $COVER = _untaint($path_cover);
565             }
566 0         0 return;
567             }
568              
569              
570             sub _make_plan {
571 3 50   3   29 unless ($Test->has_plan) {
572 0         0 $Test->plan( 'no_plan' );
573             }
574 3         837 $Test->expected_tests;
575             }
576              
577             sub _untaint {
578 70     70   135 my @untainted = map {($_ =~ $UNTAINT_PATTERN)} @_;
  70         667  
579             wantarray ? @untainted
580 70 50       393 : $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;