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