Test-coverage-report
#!/usr/bin/perl
#=============================================================================== # REVISION: $Id$ # DESCRIPTION: Build & display test coverage report # AUTHOR: Alexander Simakov, <xdr [dot] box [at] Gmail>
# http://alexander-simakov.blogspot.com/ # LICENSE: Public domain #===============================================================================
use strict; use warnings;
our $VERSION = qw($Revision$) [1];
use Readonly; use English qw( -no_match_vars ); use Getopt::Long 2.24 qw(:config no_auto_abbrev no_ignore_case); use Pod::Usage; use IO::Prompt; use File::Temp qw(tempdir); use File::Basename; use Carp;
#use Smart::Comments;
Readonly my $DEFAULT_PROVE_CMD => '/usr/bin/prove'; Readonly my $DEFAULT_PROVE_ARGS => q{};
Readonly my $DEFAULT_COVER_CMD => '/usr/bin/cover'; ## no critic (RequireInterpolationOfMetachars) Readonly my $DEFAULT_COVER_ARGS => q{-ignore_re '[.]t$'}; ## use critic
Readonly my $DEFAULT_BROWSER_CMD => q{}; Readonly my $DEFAULT_BROWSER_ARGS => q{};
sub get_options { my $options = { 'prove-cmd' => $DEFAULT_PROVE_CMD, 'prove-args' => $DEFAULT_PROVE_ARGS, 'cover-cmd' => $DEFAULT_COVER_CMD, 'cover-args' => $DEFAULT_COVER_ARGS, 'browser-cmd' => $DEFAULT_BROWSER_CMD, 'browser-args' => $DEFAULT_BROWSER_ARGS, };
my $options_okay = GetOptions( $options, 'input-file|f=s', # Input .t or .pm file 'prove-cmd|p=s', # Which prove command to use 'prove-args|P=s', # prove args 'cover-cmd|c=s', # Which cover command 'cover-args|C=s', # cover args 'browser-cmd|b=s', # Which browser to use 'browser-args|B=s', # Browser args 'output-dir|d=s', # Output directory 'help|?', # Show brief help message 'man', # Show full documentation );
# More meaningful names for pod2usage's -verbose parameter Readonly my $SHOW_USAGE_ONLY => 0; Readonly my $SHOW_BRIEF_HELP_MESSAGE => 1; Readonly my $SHOW_FULL_MANUAL => 2;
# Show appropriate help message if ( !$options_okay ) { pod2usage( -exitval => 2, -verbose => $SHOW_USAGE_ONLY ); }
if ( $options->{'help'} ) { pod2usage( -exitval => 0, -verbose => $SHOW_BRIEF_HELP_MESSAGE ); }
if ( $options->{'man'} ) { pod2usage( -exitval => 0, -verbose => $SHOW_FULL_MANUAL ); }
# Check required options foreach my $option (qw( input-file browser-cmd prove-cmd cover-cmd )) { if ( !$options->{$option} ) { pod2usage( -message => "Option $option is required", -exitval => 2, -verbose => $SHOW_USAGE_ONLY, ); } }
### options: $options return $options; }
sub create_tmp_dir { my $output_dir = shift; my $input_file = shift;
my $basename = basename( $input_file, qw(.pm .t) ); ### basename: $basename
my $tmp_dir; if ($output_dir) { $tmp_dir = tempdir( "$basename-XXXX", DIR => $output_dir, CLEANUP => 0, ); } else { $tmp_dir = tempdir( "$basename-XXXX", TMPDIR => 1, CLEANUP => 0, ); } ### tmp_dir: $tmp_dir
return $tmp_dir; }
sub enable_coverage_report { my $output_dir = shift;
$ENV{'HARNESS_PERL_SWITCHES'} = "-MDevel::Cover=-db,$output_dir";
return; }
sub prove { my $input_file = shift; my $prove_cmd = shift; my $prove_args = shift;
system "$prove_cmd $input_file $prove_args";
return if $CHILD_ERROR == 0; croak 'Cannot prove the test'; }
sub generate_coverage_report { my $output_dir = shift; my $cover_cmd = shift; my $cover_args = shift;
system "$cover_cmd $cover_args $output_dir";
return if $CHILD_ERROR == 0; croak 'Cannot generate coverage report'; }
sub open_browser { my $url = shift; my $browser_cmd = shift; my $browser_args = shift;
system "$browser_cmd $browser_args $url";
return if $CHILD_ERROR == 0; croak 'Cannot open browser'; }
sub cleanup_dir { my $dir = shift;
system "rm -frv '$dir'";
return; }
sub confirm_cleanup { my $output_dir = shift;
my $msg = "Coverage report is generated in '$output_dir'. " . 'Press \'Y\' (default) to cleanup this directory or \'N\' ' . 'if you want to keep it.';
my $answer = prompt( $msg, -default => 'Y', -YN, -one_char );
if ( $answer eq 'Y' ) { cleanup_dir($output_dir); }
return; }
sub build_coverage_report { my $options = shift;
my $tmp_dir = create_tmp_dir( $options->{'output-dir'}, $options->{'input-file'} );
enable_coverage_report($tmp_dir);
eval { prove( $options->{'input-file'}, $options->{'prove-cmd'}, $options->{'prove-args'}, );
generate_coverage_report( $tmp_dir, $options->{'cover-cmd'}, $options->{'cover-args'}, );
open_browser( "$tmp_dir/coverage.html", $options->{'browser-cmd'}, $options->{'browser-args'}, ); };
if ($EVAL_ERROR) { print "$EVAL_ERROR\n"; cleanup_dir($tmp_dir);
exit 1; }
confirm_cleanup($tmp_dir);
return; }
sub main { my $options = get_options();
build_coverage_report($options);
return; }
main();
__END__ =head1 NAME test-coverage-report.pl - Build & display test coverage report =head1 SYNOPSIS test-coverage-report.pl [options]  Options:    --input-file|-f Input .t or .pm file    --prove-cmd|- p Which prove command to use    --prove-args|-P prove args    --cover-cmd|-c Which cover command    --cover-args|-C cover args    --browser-cmd|-b Which browser to use    --browser-args|-B Browser args    --output-dir|-d Output directory    --help|-? Show brief help message    --man Show full documentation =head1 DESCRIPTION Run tests, build coverage report and open web-browser. =cut