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