1b39c5158Smillertpackage App::Prove; 2b39c5158Smillert 3b39c5158Smillertuse strict; 46fb12b70Safresh1use warnings; 5b39c5158Smillert 6b8851fccSafresh1use TAP::Harness::Env; 76fb12b70Safresh1use Text::ParseWords qw(shellwords); 8b39c5158Smillertuse File::Spec; 9b39c5158Smillertuse Getopt::Long; 10b39c5158Smillertuse App::Prove::State; 11b39c5158Smillertuse Carp; 12b39c5158Smillert 136fb12b70Safresh1use base 'TAP::Object'; 146fb12b70Safresh1 15b39c5158Smillert=head1 NAME 16b39c5158Smillert 17b39c5158SmillertApp::Prove - Implements the C<prove> command. 18b39c5158Smillert 19b39c5158Smillert=head1 VERSION 20b39c5158Smillert 21*3d61058aSafresh1Version 3.48 22b39c5158Smillert 23b39c5158Smillert=cut 24b39c5158Smillert 25*3d61058aSafresh1our $VERSION = '3.48'; 26b39c5158Smillert 27b39c5158Smillert=head1 DESCRIPTION 28b39c5158Smillert 29b39c5158SmillertL<Test::Harness> provides a command, C<prove>, which runs a TAP based 30b39c5158Smillerttest suite and prints a report. The C<prove> command is a minimal 31b39c5158Smillertwrapper around an instance of this module. 32b39c5158Smillert 33b39c5158Smillert=head1 SYNOPSIS 34b39c5158Smillert 35b39c5158Smillert use App::Prove; 36b39c5158Smillert 37b39c5158Smillert my $app = App::Prove->new; 38b39c5158Smillert $app->process_args(@ARGV); 39b39c5158Smillert $app->run; 40b39c5158Smillert 41b39c5158Smillert=cut 42b39c5158Smillert 43b39c5158Smillertuse constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ ); 44b39c5158Smillertuse constant IS_VMS => $^O eq 'VMS'; 45b39c5158Smillertuse constant IS_UNIXY => !( IS_VMS || IS_WIN32 ); 46b39c5158Smillert 47b39c5158Smillertuse constant STATE_FILE => IS_UNIXY ? '.prove' : '_prove'; 48b39c5158Smillertuse constant RC_FILE => IS_UNIXY ? '.proverc' : '_proverc'; 49b39c5158Smillert 50b39c5158Smillertuse constant PLUGINS => 'App::Prove::Plugin'; 51b39c5158Smillert 52b39c5158Smillertmy @ATTR; 53b39c5158Smillert 54b39c5158SmillertBEGIN { 55b39c5158Smillert @ATTR = qw( 56b39c5158Smillert archive argv blib show_count color directives exec failures comments 57b39c5158Smillert formatter harness includes modules plugins jobs lib merge parse quiet 58b39c5158Smillert really_quiet recurse backwards shuffle taint_fail taint_warn timer 59b39c5158Smillert verbose warnings_fail warnings_warn show_help show_man show_version 60898184e3Ssthen state_class test_args state dry extensions ignore_exit rules state_manager 61898184e3Ssthen normalize sources tapversion trap 629f11ffb7Safresh1 statefile 63b39c5158Smillert ); 64b39c5158Smillert __PACKAGE__->mk_methods(@ATTR); 65b39c5158Smillert} 66b39c5158Smillert 67b39c5158Smillert=head1 METHODS 68b39c5158Smillert 69b39c5158Smillert=head2 Class Methods 70b39c5158Smillert 71b39c5158Smillert=head3 C<new> 72b39c5158Smillert 73b39c5158SmillertCreate a new C<App::Prove>. Optionally a hash ref of attribute 74b39c5158Smillertinitializers may be passed. 75b39c5158Smillert 76b39c5158Smillert=cut 77b39c5158Smillert 78b39c5158Smillert# new() implementation supplied by TAP::Object 79b39c5158Smillert 80b39c5158Smillertsub _initialize { 81b39c5158Smillert my $self = shift; 82b39c5158Smillert my $args = shift || {}; 83b39c5158Smillert 84898184e3Ssthen my @is_array = qw( 85898184e3Ssthen argv rc_opts includes modules state plugins rules sources 86898184e3Ssthen ); 87898184e3Ssthen 88b39c5158Smillert # setup defaults: 89898184e3Ssthen for my $key (@is_array) { 90b39c5158Smillert $self->{$key} = []; 91b39c5158Smillert } 92b39c5158Smillert 93b39c5158Smillert for my $attr (@ATTR) { 94b39c5158Smillert if ( exists $args->{$attr} ) { 95b39c5158Smillert 96b39c5158Smillert # TODO: Some validation here 97b39c5158Smillert $self->{$attr} = $args->{$attr}; 98b39c5158Smillert } 99b39c5158Smillert } 100b39c5158Smillert 101b39c5158Smillert $self->state_class('App::Prove::State'); 102b39c5158Smillert return $self; 103b39c5158Smillert} 104b39c5158Smillert 105b39c5158Smillert=head3 C<state_class> 106b39c5158Smillert 107b39c5158SmillertGetter/setter for the name of the class used for maintaining state. This 108b39c5158Smillertclass should either subclass from C<App::Prove::State> or provide an identical 109b39c5158Smillertinterface. 110b39c5158Smillert 111b39c5158Smillert=head3 C<state_manager> 112b39c5158Smillert 113b39c5158SmillertGetter/setter for the instance of the C<state_class>. 114b39c5158Smillert 115b39c5158Smillert=cut 116b39c5158Smillert 117b39c5158Smillert=head3 C<add_rc_file> 118b39c5158Smillert 119b39c5158Smillert $prove->add_rc_file('myproj/.proverc'); 120b39c5158Smillert 121b39c5158SmillertCalled before C<process_args> to prepend the contents of an rc file to 122b39c5158Smillertthe options. 123b39c5158Smillert 124b39c5158Smillert=cut 125b39c5158Smillert 126b39c5158Smillertsub add_rc_file { 127b39c5158Smillert my ( $self, $rc_file ) = @_; 128b39c5158Smillert 129b39c5158Smillert local *RC; 130b39c5158Smillert open RC, "<$rc_file" or croak "Can't read $rc_file ($!)"; 131b39c5158Smillert while ( defined( my $line = <RC> ) ) { 132b39c5158Smillert push @{ $self->{rc_opts} }, 133b39c5158Smillert grep { defined and not /^#/ } 134b39c5158Smillert $line =~ m{ ' ([^']*) ' | " ([^"]*) " | (\#.*) | (\S+) }xg; 135b39c5158Smillert } 136b39c5158Smillert close RC; 137b39c5158Smillert} 138b39c5158Smillert 139b39c5158Smillert=head3 C<process_args> 140b39c5158Smillert 141b39c5158Smillert $prove->process_args(@args); 142b39c5158Smillert 143b39c5158SmillertProcesses the command-line arguments. Attributes will be set 144b39c5158Smillertappropriately. Any filenames may be found in the C<argv> attribute. 145b39c5158Smillert 146b39c5158SmillertDies on invalid arguments. 147b39c5158Smillert 148b39c5158Smillert=cut 149b39c5158Smillert 150b39c5158Smillertsub process_args { 151b39c5158Smillert my $self = shift; 152b39c5158Smillert 153b39c5158Smillert my @rc = RC_FILE; 154b39c5158Smillert unshift @rc, glob '~/' . RC_FILE if IS_UNIXY; 155b39c5158Smillert 156b39c5158Smillert # Preprocess meta-args. 157b39c5158Smillert my @args; 158b39c5158Smillert while ( defined( my $arg = shift ) ) { 159b39c5158Smillert if ( $arg eq '--norc' ) { 160b39c5158Smillert @rc = (); 161b39c5158Smillert } 162b39c5158Smillert elsif ( $arg eq '--rc' ) { 163b39c5158Smillert defined( my $rc = shift ) 164b39c5158Smillert or croak "Missing argument to --rc"; 165b39c5158Smillert push @rc, $rc; 166b39c5158Smillert } 167b39c5158Smillert elsif ( $arg =~ m{^--rc=(.+)$} ) { 168b39c5158Smillert push @rc, $1; 169b39c5158Smillert } 170b39c5158Smillert else { 171b39c5158Smillert push @args, $arg; 172b39c5158Smillert } 173b39c5158Smillert } 174b39c5158Smillert 175b39c5158Smillert # Everything after the arisdottle '::' gets passed as args to 176b39c5158Smillert # test programs. 177b39c5158Smillert if ( defined( my $stop_at = _first_pos( '::', @args ) ) ) { 178b39c5158Smillert my @test_args = splice @args, $stop_at; 179b39c5158Smillert shift @test_args; 180b39c5158Smillert $self->{test_args} = \@test_args; 181b39c5158Smillert } 182b39c5158Smillert 183b39c5158Smillert # Grab options from RC files 184b39c5158Smillert $self->add_rc_file($_) for grep -f, @rc; 185b39c5158Smillert unshift @args, @{ $self->{rc_opts} }; 186b39c5158Smillert 187b39c5158Smillert if ( my @bad = map {"-$_"} grep {/^-(man|help)$/} @args ) { 188b39c5158Smillert die "Long options should be written with two dashes: ", 189b39c5158Smillert join( ', ', @bad ), "\n"; 190b39c5158Smillert } 191b39c5158Smillert 192b39c5158Smillert # And finally... 193b39c5158Smillert 194b39c5158Smillert { 195b39c5158Smillert local @ARGV = @args; 196898184e3Ssthen Getopt::Long::Configure(qw(no_ignore_case bundling pass_through)); 197b39c5158Smillert 198b39c5158Smillert # Don't add coderefs to GetOptions 199b39c5158Smillert GetOptions( 200b39c5158Smillert 'v|verbose' => \$self->{verbose}, 201b39c5158Smillert 'f|failures' => \$self->{failures}, 202b39c5158Smillert 'o|comments' => \$self->{comments}, 203b39c5158Smillert 'l|lib' => \$self->{lib}, 204b39c5158Smillert 'b|blib' => \$self->{blib}, 205b39c5158Smillert 's|shuffle' => \$self->{shuffle}, 206b39c5158Smillert 'color!' => \$self->{color}, 207b39c5158Smillert 'colour!' => \$self->{color}, 208b39c5158Smillert 'count!' => \$self->{show_count}, 209b39c5158Smillert 'c' => \$self->{color}, 210b39c5158Smillert 'D|dry' => \$self->{dry}, 211898184e3Ssthen 'ext=s@' => sub { 212898184e3Ssthen my ( $opt, $val ) = @_; 21391f110e0Safresh1 214898184e3Ssthen # Workaround for Getopt::Long 2.25 handling of 215898184e3Ssthen # multivalue options 216898184e3Ssthen push @{ $self->{extensions} ||= [] }, $val; 217898184e3Ssthen }, 218b39c5158Smillert 'harness=s' => \$self->{harness}, 219b39c5158Smillert 'ignore-exit' => \$self->{ignore_exit}, 220898184e3Ssthen 'source=s@' => $self->{sources}, 221b39c5158Smillert 'formatter=s' => \$self->{formatter}, 222b39c5158Smillert 'r|recurse' => \$self->{recurse}, 223b39c5158Smillert 'reverse' => \$self->{backwards}, 224b39c5158Smillert 'p|parse' => \$self->{parse}, 225b39c5158Smillert 'q|quiet' => \$self->{quiet}, 226b39c5158Smillert 'Q|QUIET' => \$self->{really_quiet}, 227b39c5158Smillert 'e|exec=s' => \$self->{exec}, 228b39c5158Smillert 'm|merge' => \$self->{merge}, 229b39c5158Smillert 'I=s@' => $self->{includes}, 230b39c5158Smillert 'M=s@' => $self->{modules}, 231b39c5158Smillert 'P=s@' => $self->{plugins}, 232b39c5158Smillert 'state=s@' => $self->{state}, 2339f11ffb7Safresh1 'statefile=s' => \$self->{statefile}, 234b39c5158Smillert 'directives' => \$self->{directives}, 235b39c5158Smillert 'h|help|?' => \$self->{show_help}, 236b39c5158Smillert 'H|man' => \$self->{show_man}, 237b39c5158Smillert 'V|version' => \$self->{show_version}, 238b39c5158Smillert 'a|archive=s' => \$self->{archive}, 239b39c5158Smillert 'j|jobs=i' => \$self->{jobs}, 240b39c5158Smillert 'timer' => \$self->{timer}, 241b39c5158Smillert 'T' => \$self->{taint_fail}, 242b39c5158Smillert 't' => \$self->{taint_warn}, 243b39c5158Smillert 'W' => \$self->{warnings_fail}, 244b39c5158Smillert 'w' => \$self->{warnings_warn}, 245b39c5158Smillert 'normalize' => \$self->{normalize}, 246b39c5158Smillert 'rules=s@' => $self->{rules}, 247898184e3Ssthen 'tapversion=s' => \$self->{tapversion}, 248898184e3Ssthen 'trap' => \$self->{trap}, 249b39c5158Smillert ) or croak('Unable to continue'); 250b39c5158Smillert 251b39c5158Smillert # Stash the remainder of argv for later 252b39c5158Smillert $self->{argv} = [@ARGV]; 253b39c5158Smillert } 254b39c5158Smillert 255b39c5158Smillert return; 256b39c5158Smillert} 257b39c5158Smillert 258b39c5158Smillertsub _first_pos { 259b39c5158Smillert my $want = shift; 260b39c5158Smillert for ( 0 .. $#_ ) { 261b39c5158Smillert return $_ if $_[$_] eq $want; 262b39c5158Smillert } 263b39c5158Smillert return; 264b39c5158Smillert} 265b39c5158Smillert 266b39c5158Smillertsub _help { 267b39c5158Smillert my ( $self, $verbosity ) = @_; 268b39c5158Smillert 269b39c5158Smillert eval('use Pod::Usage 1.12 ()'); 270b39c5158Smillert if ( my $err = $@ ) { 271b39c5158Smillert die 'Please install Pod::Usage for the --help option ' 272b39c5158Smillert . '(or try `perldoc prove`.)' 273b39c5158Smillert . "\n ($@)"; 274b39c5158Smillert } 275b39c5158Smillert 276b39c5158Smillert Pod::Usage::pod2usage( { -verbose => $verbosity } ); 277b39c5158Smillert 278b39c5158Smillert return; 279b39c5158Smillert} 280b39c5158Smillert 281b39c5158Smillertsub _color_default { 282b39c5158Smillert my $self = shift; 283b39c5158Smillert 2849f11ffb7Safresh1 return -t STDOUT && !$ENV{HARNESS_NOTTY}; 285b39c5158Smillert} 286b39c5158Smillert 287b39c5158Smillertsub _get_args { 288b39c5158Smillert my $self = shift; 289b39c5158Smillert 290b39c5158Smillert my %args; 291b39c5158Smillert 292898184e3Ssthen $args{trap} = 1 if $self->trap; 293898184e3Ssthen 294b39c5158Smillert if ( defined $self->color ? $self->color : $self->_color_default ) { 295b39c5158Smillert $args{color} = 1; 296b39c5158Smillert } 297b39c5158Smillert if ( !defined $self->show_count ) { 298b39c5158Smillert $args{show_count} = 1; 299b39c5158Smillert } 300b39c5158Smillert else { 301b39c5158Smillert $args{show_count} = $self->show_count; 302b39c5158Smillert } 303b39c5158Smillert 304b39c5158Smillert if ( $self->archive ) { 305b39c5158Smillert $self->require_harness( archive => 'TAP::Harness::Archive' ); 306b39c5158Smillert $args{archive} = $self->archive; 307b39c5158Smillert } 308b39c5158Smillert 309b39c5158Smillert if ( my $jobs = $self->jobs ) { 310b39c5158Smillert $args{jobs} = $jobs; 311b39c5158Smillert } 312b39c5158Smillert 313b39c5158Smillert if ( my $harness_opt = $self->harness ) { 314b39c5158Smillert $self->require_harness( harness => $harness_opt ); 315b39c5158Smillert } 316b39c5158Smillert 317b39c5158Smillert if ( my $formatter = $self->formatter ) { 318b39c5158Smillert $args{formatter_class} = $formatter; 319b39c5158Smillert } 320b39c5158Smillert 321898184e3Ssthen for my $handler ( @{ $self->sources } ) { 322898184e3Ssthen my ( $name, $config ) = $self->_parse_source($handler); 323898184e3Ssthen $args{sources}->{$name} = $config; 324898184e3Ssthen } 325898184e3Ssthen 326b39c5158Smillert if ( $self->ignore_exit ) { 327b39c5158Smillert $args{ignore_exit} = 1; 328b39c5158Smillert } 329b39c5158Smillert 330b39c5158Smillert if ( $self->taint_fail && $self->taint_warn ) { 331b39c5158Smillert die '-t and -T are mutually exclusive'; 332b39c5158Smillert } 333b39c5158Smillert 334b39c5158Smillert if ( $self->warnings_fail && $self->warnings_warn ) { 335b39c5158Smillert die '-w and -W are mutually exclusive'; 336b39c5158Smillert } 337b39c5158Smillert 338b39c5158Smillert for my $a (qw( lib switches )) { 339b39c5158Smillert my $method = "_get_$a"; 340b39c5158Smillert my $val = $self->$method(); 341b39c5158Smillert $args{$a} = $val if defined $val; 342b39c5158Smillert } 343b39c5158Smillert 344b39c5158Smillert # Handle verbose, quiet, really_quiet flags 345b39c5158Smillert my %verb_map = ( verbose => 1, quiet => -1, really_quiet => -2, ); 346b39c5158Smillert 347eac174f2Safresh1 my @verb_adj = map { $self->$_() ? $verb_map{$_} : () } 348b39c5158Smillert keys %verb_map; 349b39c5158Smillert 350b39c5158Smillert die "Only one of verbose, quiet or really_quiet should be specified\n" 351b39c5158Smillert if @verb_adj > 1; 352b39c5158Smillert 353eac174f2Safresh1 $args{verbosity} = shift @verb_adj if @verb_adj; 354b39c5158Smillert 355b39c5158Smillert for my $a (qw( merge failures comments timer directives normalize )) { 356b39c5158Smillert $args{$a} = 1 if $self->$a(); 357b39c5158Smillert } 358b39c5158Smillert 359b39c5158Smillert $args{errors} = 1 if $self->parse; 360b39c5158Smillert 361b39c5158Smillert # defined but zero-length exec runs test files as binaries 362b39c5158Smillert $args{exec} = [ split( /\s+/, $self->exec ) ] 363b39c5158Smillert if ( defined( $self->exec ) ); 364b39c5158Smillert 365898184e3Ssthen $args{version} = $self->tapversion if defined( $self->tapversion ); 366898184e3Ssthen 367b39c5158Smillert if ( defined( my $test_args = $self->test_args ) ) { 368b39c5158Smillert $args{test_args} = $test_args; 369b39c5158Smillert } 370b39c5158Smillert 371b39c5158Smillert if ( @{ $self->rules } ) { 372b39c5158Smillert my @rules; 373b39c5158Smillert for ( @{ $self->rules } ) { 374b39c5158Smillert if (/^par=(.*)/) { 375b39c5158Smillert push @rules, $1; 376b39c5158Smillert } 377b39c5158Smillert elsif (/^seq=(.*)/) { 378b39c5158Smillert push @rules, { seq => $1 }; 379b39c5158Smillert } 380b39c5158Smillert } 381b39c5158Smillert $args{rules} = { par => [@rules] }; 382b39c5158Smillert } 383b8851fccSafresh1 $args{harness_class} = $self->{harness_class} if $self->{harness_class}; 384b39c5158Smillert 385b8851fccSafresh1 return \%args; 386b39c5158Smillert} 387b39c5158Smillert 388b39c5158Smillertsub _find_module { 389b39c5158Smillert my ( $self, $class, @search ) = @_; 390b39c5158Smillert 391b39c5158Smillert croak "Bad module name $class" 392b39c5158Smillert unless $class =~ /^ \w+ (?: :: \w+ ) *$/x; 393b39c5158Smillert 394b39c5158Smillert for my $pfx (@search) { 395b39c5158Smillert my $name = join( '::', $pfx, $class ); 396b39c5158Smillert eval "require $name"; 397b39c5158Smillert return $name unless $@; 398b39c5158Smillert } 399b39c5158Smillert 400b39c5158Smillert eval "require $class"; 401b39c5158Smillert return $class unless $@; 402b39c5158Smillert return; 403b39c5158Smillert} 404b39c5158Smillert 405b39c5158Smillertsub _load_extension { 406b39c5158Smillert my ( $self, $name, @search ) = @_; 407b39c5158Smillert 408b39c5158Smillert my @args = (); 409b39c5158Smillert if ( $name =~ /^(.*?)=(.*)/ ) { 410b39c5158Smillert $name = $1; 411b39c5158Smillert @args = split( /,/, $2 ); 412b39c5158Smillert } 413b39c5158Smillert 414b39c5158Smillert if ( my $class = $self->_find_module( $name, @search ) ) { 415b39c5158Smillert if ( $class->can('load') ) { 416b39c5158Smillert $class->load( { app_prove => $self, args => [@args] } ); 417b39c5158Smillert } 418b39c5158Smillert } 419b39c5158Smillert else { 420b39c5158Smillert croak "Can't load module $name"; 421b39c5158Smillert } 422b39c5158Smillert} 423b39c5158Smillert 424b39c5158Smillertsub _load_extensions { 425b39c5158Smillert my ( $self, $ext, @search ) = @_; 426b39c5158Smillert $self->_load_extension( $_, @search ) for @$ext; 427b39c5158Smillert} 428b39c5158Smillert 429898184e3Ssthensub _parse_source { 430898184e3Ssthen my ( $self, $handler ) = @_; 431898184e3Ssthen 432898184e3Ssthen # Load any options. 433898184e3Ssthen ( my $opt_name = lc $handler ) =~ s/::/-/g; 434898184e3Ssthen local @ARGV = @{ $self->{argv} }; 435898184e3Ssthen my %config; 436898184e3Ssthen Getopt::Long::GetOptions( 437898184e3Ssthen "$opt_name-option=s%" => sub { 438898184e3Ssthen my ( $name, $k, $v ) = @_; 439898184e3Ssthen if ( $v =~ /(?<!\\)=/ ) { 440898184e3Ssthen 441898184e3Ssthen # It's a hash option. 442898184e3Ssthen croak "Option $name must be consistently used as a hash" 443898184e3Ssthen if exists $config{$k} && ref $config{$k} ne 'HASH'; 444898184e3Ssthen $config{$k} ||= {}; 445898184e3Ssthen my ( $hk, $hv ) = split /(?<!\\)=/, $v, 2; 446898184e3Ssthen $config{$k}{$hk} = $hv; 447898184e3Ssthen } 448898184e3Ssthen else { 449898184e3Ssthen $v =~ s/\\=/=/g; 450898184e3Ssthen if ( exists $config{$k} ) { 451898184e3Ssthen $config{$k} = [ $config{$k} ] 452898184e3Ssthen unless ref $config{$k} eq 'ARRAY'; 453898184e3Ssthen push @{ $config{$k} } => $v; 454898184e3Ssthen } 455898184e3Ssthen else { 456898184e3Ssthen $config{$k} = $v; 457898184e3Ssthen } 458898184e3Ssthen } 459898184e3Ssthen } 460898184e3Ssthen ); 461898184e3Ssthen $self->{argv} = \@ARGV; 462898184e3Ssthen return ( $handler, \%config ); 463898184e3Ssthen} 464898184e3Ssthen 465b39c5158Smillert=head3 C<run> 466b39c5158Smillert 467b39c5158SmillertPerform whatever actions the command line args specified. The C<prove> 468b39c5158Smillertcommand line tool consists of the following code: 469b39c5158Smillert 470b39c5158Smillert use App::Prove; 471b39c5158Smillert 472b39c5158Smillert my $app = App::Prove->new; 473b39c5158Smillert $app->process_args(@ARGV); 474b39c5158Smillert exit( $app->run ? 0 : 1 ); # if you need the exit code 475b39c5158Smillert 476b39c5158Smillert=cut 477b39c5158Smillert 478b39c5158Smillertsub run { 479b39c5158Smillert my $self = shift; 480b39c5158Smillert 481b39c5158Smillert unless ( $self->state_manager ) { 482b39c5158Smillert $self->state_manager( 4839f11ffb7Safresh1 $self->state_class->new( { store => $self->statefile || STATE_FILE } ) ); 484b39c5158Smillert } 485b39c5158Smillert 486b39c5158Smillert if ( $self->show_help ) { 487b39c5158Smillert $self->_help(1); 488b39c5158Smillert } 489b39c5158Smillert elsif ( $self->show_man ) { 490b39c5158Smillert $self->_help(2); 491b39c5158Smillert } 492b39c5158Smillert elsif ( $self->show_version ) { 493b39c5158Smillert $self->print_version; 494b39c5158Smillert } 495b39c5158Smillert elsif ( $self->dry ) { 496b39c5158Smillert print "$_\n" for $self->_get_tests; 497b39c5158Smillert } 498b39c5158Smillert else { 499b39c5158Smillert 500b39c5158Smillert $self->_load_extensions( $self->modules ); 501b39c5158Smillert $self->_load_extensions( $self->plugins, PLUGINS ); 502b39c5158Smillert 503b39c5158Smillert local $ENV{TEST_VERBOSE} = 1 if $self->verbose; 504b39c5158Smillert 505b39c5158Smillert return $self->_runtests( $self->_get_args, $self->_get_tests ); 506b39c5158Smillert } 507b39c5158Smillert 508b39c5158Smillert return 1; 509b39c5158Smillert} 510b39c5158Smillert 511b39c5158Smillertsub _get_tests { 512b39c5158Smillert my $self = shift; 513b39c5158Smillert 514b39c5158Smillert my $state = $self->state_manager; 515898184e3Ssthen my $ext = $self->extensions; 516898184e3Ssthen $state->extensions($ext) if defined $ext; 517b39c5158Smillert if ( defined( my $state_switch = $self->state ) ) { 518b39c5158Smillert $state->apply_switch(@$state_switch); 519b39c5158Smillert } 520b39c5158Smillert 521b39c5158Smillert my @tests = $state->get_tests( $self->recurse, @{ $self->argv } ); 522b39c5158Smillert 523b39c5158Smillert $self->_shuffle(@tests) if $self->shuffle; 524b39c5158Smillert @tests = reverse @tests if $self->backwards; 525b39c5158Smillert 526b39c5158Smillert return @tests; 527b39c5158Smillert} 528b39c5158Smillert 529b39c5158Smillertsub _runtests { 530b8851fccSafresh1 my ( $self, $args, @tests ) = @_; 531b8851fccSafresh1 my $harness = TAP::Harness::Env->create($args); 532b39c5158Smillert 533b39c5158Smillert my $state = $self->state_manager; 534b39c5158Smillert 535b39c5158Smillert $harness->callback( 536b39c5158Smillert after_test => sub { 537b39c5158Smillert $state->observe_test(@_); 538b39c5158Smillert } 539b39c5158Smillert ); 540b39c5158Smillert 541b39c5158Smillert $harness->callback( 542b39c5158Smillert after_runtests => sub { 543b39c5158Smillert $state->commit(@_); 544b39c5158Smillert } 545b39c5158Smillert ); 546b39c5158Smillert 547b39c5158Smillert my $aggregator = $harness->runtests(@tests); 548b39c5158Smillert 549b39c5158Smillert return !$aggregator->has_errors; 550b39c5158Smillert} 551b39c5158Smillert 552b39c5158Smillertsub _get_switches { 553b39c5158Smillert my $self = shift; 554b39c5158Smillert my @switches; 555b39c5158Smillert 556b39c5158Smillert # notes that -T or -t must be at the front of the switches! 557b39c5158Smillert if ( $self->taint_fail ) { 558b39c5158Smillert push @switches, '-T'; 559b39c5158Smillert } 560b39c5158Smillert elsif ( $self->taint_warn ) { 561b39c5158Smillert push @switches, '-t'; 562b39c5158Smillert } 563b39c5158Smillert if ( $self->warnings_fail ) { 564b39c5158Smillert push @switches, '-W'; 565b39c5158Smillert } 566b39c5158Smillert elsif ( $self->warnings_warn ) { 567b39c5158Smillert push @switches, '-w'; 568b39c5158Smillert } 569b39c5158Smillert 570b39c5158Smillert return @switches ? \@switches : (); 571b39c5158Smillert} 572b39c5158Smillert 573b39c5158Smillertsub _get_lib { 574b39c5158Smillert my $self = shift; 575b39c5158Smillert my @libs; 576b39c5158Smillert if ( $self->lib ) { 577b39c5158Smillert push @libs, 'lib'; 578b39c5158Smillert } 579b39c5158Smillert if ( $self->blib ) { 580b39c5158Smillert push @libs, 'blib/lib', 'blib/arch'; 581b39c5158Smillert } 582b39c5158Smillert if ( @{ $self->includes } ) { 583b39c5158Smillert push @libs, @{ $self->includes }; 584b39c5158Smillert } 585b39c5158Smillert 586b39c5158Smillert #24926 587b39c5158Smillert @libs = map { File::Spec->rel2abs($_) } @libs; 588b39c5158Smillert 589b39c5158Smillert # Huh? 590b39c5158Smillert return @libs ? \@libs : (); 591b39c5158Smillert} 592b39c5158Smillert 593b39c5158Smillertsub _shuffle { 594b39c5158Smillert my $self = shift; 595b39c5158Smillert 596b39c5158Smillert # Fisher-Yates shuffle 597b39c5158Smillert my $i = @_; 598b39c5158Smillert while ($i) { 599b39c5158Smillert my $j = rand $i--; 600b39c5158Smillert @_[ $i, $j ] = @_[ $j, $i ]; 601b39c5158Smillert } 602b39c5158Smillert return; 603b39c5158Smillert} 604b39c5158Smillert 605b39c5158Smillert=head3 C<require_harness> 606b39c5158Smillert 607b39c5158SmillertLoad a harness replacement class. 608b39c5158Smillert 609b39c5158Smillert $prove->require_harness($for => $class_name); 610b39c5158Smillert 611b39c5158Smillert=cut 612b39c5158Smillert 613b39c5158Smillertsub require_harness { 614b39c5158Smillert my ( $self, $for, $class ) = @_; 615b39c5158Smillert 616b39c5158Smillert my ($class_name) = $class =~ /^(\w+(?:::\w+)*)/; 617b39c5158Smillert 618b39c5158Smillert # Emulate Perl's -MModule=arg1,arg2 behaviour 619b39c5158Smillert $class =~ s!^(\w+(?:::\w+)*)=(.*)$!$1 split(/,/,q{$2})!; 620b39c5158Smillert 621b39c5158Smillert eval("use $class;"); 622b39c5158Smillert die "$class_name is required to use the --$for feature: $@" if $@; 623b39c5158Smillert 624b39c5158Smillert $self->{harness_class} = $class_name; 625b39c5158Smillert 626b39c5158Smillert return; 627b39c5158Smillert} 628b39c5158Smillert 629b39c5158Smillert=head3 C<print_version> 630b39c5158Smillert 631b39c5158SmillertDisplay the version numbers of the loaded L<TAP::Harness> and the 632b39c5158Smillertcurrent Perl. 633b39c5158Smillert 634b39c5158Smillert=cut 635b39c5158Smillert 636b39c5158Smillertsub print_version { 637b39c5158Smillert my $self = shift; 638b8851fccSafresh1 require TAP::Harness; 639b39c5158Smillert printf( 640b39c5158Smillert "TAP::Harness v%s and Perl v%vd\n", 641b39c5158Smillert $TAP::Harness::VERSION, $^V 642b39c5158Smillert ); 643b39c5158Smillert 644b39c5158Smillert return; 645b39c5158Smillert} 646b39c5158Smillert 647b39c5158Smillert1; 648b39c5158Smillert 649b39c5158Smillert# vim:ts=4:sw=4:et:sta 650b39c5158Smillert 651b39c5158Smillert__END__ 652b39c5158Smillert 653b39c5158Smillert=head2 Attributes 654b39c5158Smillert 655b39c5158SmillertAfter command line parsing the following attributes reflect the values 656b39c5158Smillertof the corresponding command line switches. They may be altered before 657b39c5158Smillertcalling C<run>. 658b39c5158Smillert 659b39c5158Smillert=over 660b39c5158Smillert 661b39c5158Smillert=item C<archive> 662b39c5158Smillert 663b39c5158Smillert=item C<argv> 664b39c5158Smillert 665b39c5158Smillert=item C<backwards> 666b39c5158Smillert 667b39c5158Smillert=item C<blib> 668b39c5158Smillert 669b39c5158Smillert=item C<color> 670b39c5158Smillert 671b39c5158Smillert=item C<directives> 672b39c5158Smillert 673b39c5158Smillert=item C<dry> 674b39c5158Smillert 675b39c5158Smillert=item C<exec> 676b39c5158Smillert 677898184e3Ssthen=item C<extensions> 678b39c5158Smillert 679b39c5158Smillert=item C<failures> 680b39c5158Smillert 681b39c5158Smillert=item C<comments> 682b39c5158Smillert 683b39c5158Smillert=item C<formatter> 684b39c5158Smillert 685b39c5158Smillert=item C<harness> 686b39c5158Smillert 687b39c5158Smillert=item C<ignore_exit> 688b39c5158Smillert 689b39c5158Smillert=item C<includes> 690b39c5158Smillert 691b39c5158Smillert=item C<jobs> 692b39c5158Smillert 693b39c5158Smillert=item C<lib> 694b39c5158Smillert 695b39c5158Smillert=item C<merge> 696b39c5158Smillert 697b39c5158Smillert=item C<modules> 698b39c5158Smillert 699b39c5158Smillert=item C<parse> 700b39c5158Smillert 701b39c5158Smillert=item C<plugins> 702b39c5158Smillert 703b39c5158Smillert=item C<quiet> 704b39c5158Smillert 705b39c5158Smillert=item C<really_quiet> 706b39c5158Smillert 707b39c5158Smillert=item C<recurse> 708b39c5158Smillert 709b39c5158Smillert=item C<rules> 710b39c5158Smillert 711b39c5158Smillert=item C<show_count> 712b39c5158Smillert 713b39c5158Smillert=item C<show_help> 714b39c5158Smillert 715b39c5158Smillert=item C<show_man> 716b39c5158Smillert 717b39c5158Smillert=item C<show_version> 718b39c5158Smillert 719b39c5158Smillert=item C<shuffle> 720b39c5158Smillert 721b39c5158Smillert=item C<state> 722b39c5158Smillert 723b39c5158Smillert=item C<state_class> 724b39c5158Smillert 725b39c5158Smillert=item C<taint_fail> 726b39c5158Smillert 727b39c5158Smillert=item C<taint_warn> 728b39c5158Smillert 729b39c5158Smillert=item C<test_args> 730b39c5158Smillert 731b39c5158Smillert=item C<timer> 732b39c5158Smillert 733b39c5158Smillert=item C<verbose> 734b39c5158Smillert 735b39c5158Smillert=item C<warnings_fail> 736b39c5158Smillert 737b39c5158Smillert=item C<warnings_warn> 738b39c5158Smillert 739898184e3Ssthen=item C<tapversion> 740898184e3Ssthen 741898184e3Ssthen=item C<trap> 742898184e3Ssthen 743b39c5158Smillert=back 744b39c5158Smillert 745b39c5158Smillert=head1 PLUGINS 746b39c5158Smillert 747b39c5158SmillertC<App::Prove> provides support for 3rd-party plugins. These are currently 748b39c5158Smillertloaded at run-time, I<after> arguments have been parsed (so you can not 749b39c5158Smillertchange the way arguments are processed, sorry), typically with the 750b39c5158SmillertC<< -PI<plugin> >> switch, eg: 751b39c5158Smillert 752b39c5158Smillert prove -PMyPlugin 753b39c5158Smillert 754b39c5158SmillertThis will search for a module named C<App::Prove::Plugin::MyPlugin>, or failing 755b39c5158Smillertthat, C<MyPlugin>. If the plugin can't be found, C<prove> will complain & exit. 756b39c5158Smillert 757b39c5158SmillertYou can pass an argument to your plugin by appending an C<=> after the plugin 758b39c5158Smillertname, eg C<-PMyPlugin=foo>. You can pass multiple arguments using commas: 759b39c5158Smillert 760b39c5158Smillert prove -PMyPlugin=foo,bar,baz 761b39c5158Smillert 762b39c5158SmillertThese are passed in to your plugin's C<load()> class method (if it has one), 763b39c5158Smillertalong with a reference to the C<App::Prove> object that is invoking your plugin: 764b39c5158Smillert 765b39c5158Smillert sub load { 766b39c5158Smillert my ($class, $p) = @_; 767b39c5158Smillert 768b39c5158Smillert my @args = @{ $p->{args} }; 769b39c5158Smillert # @args will contain ( 'foo', 'bar', 'baz' ) 770b39c5158Smillert $p->{app_prove}->do_something; 771b39c5158Smillert ... 772b39c5158Smillert } 773b39c5158Smillert 774b39c5158Smillert=head2 Sample Plugin 775b39c5158Smillert 776b39c5158SmillertHere's a sample plugin, for your reference: 777b39c5158Smillert 778b39c5158Smillert package App::Prove::Plugin::Foo; 779b39c5158Smillert 780b39c5158Smillert # Sample plugin, try running with: 781b39c5158Smillert # prove -PFoo=bar -r -j3 782b39c5158Smillert # prove -PFoo -Q 783b39c5158Smillert # prove -PFoo=bar,My::Formatter 784b39c5158Smillert 785b39c5158Smillert use strict; 786b39c5158Smillert use warnings; 787b39c5158Smillert 788b39c5158Smillert sub load { 789b39c5158Smillert my ($class, $p) = @_; 790b39c5158Smillert my @args = @{ $p->{args} }; 791b39c5158Smillert my $app = $p->{app_prove}; 792b39c5158Smillert 793b39c5158Smillert print "loading plugin: $class, args: ", join(', ', @args ), "\n"; 794b39c5158Smillert 795b39c5158Smillert # turn on verbosity 796b39c5158Smillert $app->verbose( 1 ); 797b39c5158Smillert 798b39c5158Smillert # set the formatter? 799b39c5158Smillert $app->formatter( $args[1] ) if @args > 1; 800b39c5158Smillert 801b39c5158Smillert # print some of App::Prove's state: 802b39c5158Smillert for my $attr (qw( jobs quiet really_quiet recurse verbose )) { 803b39c5158Smillert my $val = $app->$attr; 804b39c5158Smillert $val = 'undef' unless defined( $val ); 805b39c5158Smillert print "$attr: $val\n"; 806b39c5158Smillert } 807b39c5158Smillert 808b39c5158Smillert return 1; 809b39c5158Smillert } 810b39c5158Smillert 811b39c5158Smillert 1; 812b39c5158Smillert 813b39c5158Smillert=head1 SEE ALSO 814b39c5158Smillert 815b39c5158SmillertL<prove>, L<TAP::Harness> 816b39c5158Smillert 817b39c5158Smillert=cut 818