7 use Text::ParseWords qw(shellwords);
10 use App::Prove::State;
13 use parent 'TAP::Object';
17 App::Prove - Implements the C<prove> command.
25 our $VERSION = '3.29';
29 L<Test::Harness> provides a command, C<prove>, which runs a TAP based
30 test suite and prints a report. The C<prove> command is a minimal
31 wrapper around an instance of this module.
37 my $app = App::Prove->new;
38 $app->process_args(@ARGV);
43 use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
44 use constant IS_VMS => $^O eq 'VMS';
45 use constant IS_UNIXY => !( IS_VMS || IS_WIN32 );
47 use constant STATE_FILE => IS_UNIXY ? '.prove' : '_prove';
48 use constant RC_FILE => IS_UNIXY ? '.proverc' : '_proverc';
50 use constant PLUGINS => 'App::Prove::Plugin';
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
63 __PACKAGE__->mk_methods(@ATTR);
72 Create a new C<App::Prove>. Optionally a hash ref of attribute
73 initializers may be passed.
77 # new() implementation supplied by TAP::Object
81 my $args = shift || {};
84 argv rc_opts includes modules state plugins rules sources
88 for my $key (@is_array) {
91 $self->{harness_class} = 'TAP::Harness';
93 for my $attr (@ATTR) {
94 if ( exists $args->{$attr} ) {
96 # TODO: Some validation here
97 $self->{$attr} = $args->{$attr};
101 my %env_provides_default = (
102 HARNESS_TIMER => 'timer',
105 while ( my ( $env, $attr ) = each %env_provides_default ) {
106 $self->{$attr} = 1 if $ENV{$env};
108 $self->state_class('App::Prove::State');
112 =head3 C<state_class>
114 Getter/setter for the name of the class used for maintaining state. This
115 class should either subclass from C<App::Prove::State> or provide an identical
118 =head3 C<state_manager>
120 Getter/setter for the instance of the C<state_class>.
124 =head3 C<add_rc_file>
126 $prove->add_rc_file('myproj/.proverc');
128 Called before C<process_args> to prepend the contents of an rc file to
134 my ( $self, $rc_file ) = @_;
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;
146 =head3 C<process_args>
148 $prove->process_args(@args);
150 Processes the command-line arguments. Attributes will be set
151 appropriately. Any filenames may be found in the C<argv> attribute.
153 Dies on invalid arguments.
161 unshift @rc, glob '~/' . RC_FILE if IS_UNIXY;
163 # Preprocess meta-args.
165 while ( defined( my $arg = shift ) ) {
166 if ( $arg eq '--norc' ) {
169 elsif ( $arg eq '--rc' ) {
170 defined( my $rc = shift )
171 or croak "Missing argument to --rc";
174 elsif ( $arg =~ m{^--rc=(.+)$} ) {
182 # Everything after the arisdottle '::' gets passed as args to
184 if ( defined( my $stop_at = _first_pos( '::', @args ) ) ) {
185 my @test_args = splice @args, $stop_at;
187 $self->{test_args} = \@test_args;
190 # Grab options from RC files
191 $self->add_rc_file($_) for grep -f, @rc;
192 unshift @args, @{ $self->{rc_opts} };
194 if ( my @bad = map {"-$_"} grep {/^-(man|help)$/} @args ) {
195 die "Long options should be written with two dashes: ",
196 join( ', ', @bad ), "\n";
203 Getopt::Long::Configure(qw(no_ignore_case bundling pass_through));
205 # Don't add coderefs to 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},
219 my ( $opt, $val ) = @_;
221 # Workaround for Getopt::Long 2.25 handling of
223 push @{ $self->{extensions} ||= [] }, $val;
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');
257 # Stash the remainder of argv for later
258 $self->{argv} = [@ARGV];
267 return $_ if $_[$_] eq $want;
273 my ( $self, $verbosity ) = @_;
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`.)'
282 Pod::Usage::pod2usage( { -verbose => $verbosity } );
290 return -t STDOUT && !$ENV{HARNESS_NOTTY} && !IS_WIN32;
298 $args{trap} = 1 if $self->trap;
300 if ( defined $self->color ? $self->color : $self->_color_default ) {
303 if ( !defined $self->show_count ) {
304 $args{show_count} = 1;
307 $args{show_count} = $self->show_count;
310 if ( $self->archive ) {
311 $self->require_harness( archive => 'TAP::Harness::Archive' );
312 $args{archive} = $self->archive;
315 if ( my $jobs = $self->jobs ) {
319 if ( my $harness_opt = $self->harness ) {
320 $self->require_harness( harness => $harness_opt );
323 if ( my $formatter = $self->formatter ) {
324 $args{formatter_class} = $formatter;
327 for my $handler ( @{ $self->sources } ) {
328 my ( $name, $config ) = $self->_parse_source($handler);
329 $args{sources}->{$name} = $config;
332 if ( $self->ignore_exit ) {
333 $args{ignore_exit} = 1;
336 if ( $self->taint_fail && $self->taint_warn ) {
337 die '-t and -T are mutually exclusive';
340 if ( $self->warnings_fail && $self->warnings_warn ) {
341 die '-w and -W are mutually exclusive';
344 for my $a (qw( lib switches )) {
345 my $method = "_get_$a";
346 my $val = $self->$method();
347 $args{$a} = $val if defined $val;
350 # Handle verbose, quiet, really_quiet flags
351 my %verb_map = ( verbose => 1, quiet => -1, really_quiet => -2, );
353 my @verb_adj = grep {$_} map { $self->$_() ? $verb_map{$_} : 0 }
356 die "Only one of verbose, quiet or really_quiet should be specified\n"
359 $args{verbosity} = shift @verb_adj || 0;
361 for my $a (qw( merge failures comments timer directives normalize )) {
362 $args{$a} = 1 if $self->$a();
365 $args{errors} = 1 if $self->parse;
367 # defined but zero-length exec runs test files as binaries
368 $args{exec} = [ split( /\s+/, $self->exec ) ]
369 if ( defined( $self->exec ) );
371 $args{version} = $self->tapversion if defined( $self->tapversion );
373 if ( defined( my $test_args = $self->test_args ) ) {
374 $args{test_args} = $test_args;
377 if ( @{ $self->rules } ) {
379 for ( @{ $self->rules } ) {
383 elsif (/^seq=(.*)/) {
384 push @rules, { seq => $1 };
387 $args{rules} = { par => [@rules] };
390 return ( \%args, $self->{harness_class} );
394 my ( $self, $class, @search ) = @_;
396 croak "Bad module name $class"
397 unless $class =~ /^ \w+ (?: :: \w+ ) *$/x;
399 for my $pfx (@search) {
400 my $name = join( '::', $pfx, $class );
401 eval "require $name";
402 return $name unless $@;
405 eval "require $class";
406 return $class unless $@;
410 sub _load_extension {
411 my ( $self, $name, @search ) = @_;
414 if ( $name =~ /^(.*?)=(.*)/ ) {
416 @args = split( /,/, $2 );
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] } );
426 croak "Can't load module $name";
430 sub _load_extensions {
431 my ( $self, $ext, @search ) = @_;
432 $self->_load_extension( $_, @search ) for @$ext;
436 my ( $self, $handler ) = @_;
439 ( my $opt_name = lc $handler ) =~ s/::/-/g;
440 local @ARGV = @{ $self->{argv} };
442 Getopt::Long::GetOptions(
443 "$opt_name-option=s%" => sub {
444 my ( $name, $k, $v ) = @_;
445 if ( $v =~ /(?<!\\)=/ ) {
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';
451 my ( $hk, $hv ) = split /(?<!\\)=/, $v, 2;
452 $config{$k}{$hk} = $hv;
456 if ( exists $config{$k} ) {
457 $config{$k} = [ $config{$k} ]
458 unless ref $config{$k} eq 'ARRAY';
459 push @{ $config{$k} } => $v;
467 $self->{argv} = \@ARGV;
468 return ( $handler, \%config );
473 Perform whatever actions the command line args specified. The C<prove>
474 command line tool consists of the following code:
478 my $app = App::Prove->new;
479 $app->process_args(@ARGV);
480 exit( $app->run ? 0 : 1 ); # if you need the exit code
487 unless ( $self->state_manager ) {
488 $self->state_manager(
489 $self->state_class->new( { store => STATE_FILE } ) );
492 if ( $self->show_help ) {
495 elsif ( $self->show_man ) {
498 elsif ( $self->show_version ) {
499 $self->print_version;
501 elsif ( $self->dry ) {
502 print "$_\n" for $self->_get_tests;
506 $self->_load_extensions( $self->modules );
507 $self->_load_extensions( $self->plugins, PLUGINS );
509 local $ENV{TEST_VERBOSE} = 1 if $self->verbose;
511 return $self->_runtests( $self->_get_args, $self->_get_tests );
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);
527 my @tests = $state->get_tests( $self->recurse, @{ $self->argv } );
529 $self->_shuffle(@tests) if $self->shuffle;
530 @tests = reverse @tests if $self->backwards;
536 my ( $self, $args, $harness_class, @tests ) = @_;
537 my $harness = $harness_class->new($args);
539 my $state = $self->state_manager;
543 $state->observe_test(@_);
548 after_runtests => sub {
553 my $aggregator = $harness->runtests(@tests);
555 return !$aggregator->has_errors;
562 # notes that -T or -t must be at the front of the switches!
563 if ( $self->taint_fail ) {
564 push @switches, '-T';
566 elsif ( $self->taint_warn ) {
567 push @switches, '-t';
569 if ( $self->warnings_fail ) {
570 push @switches, '-W';
572 elsif ( $self->warnings_warn ) {
573 push @switches, '-w';
576 push @switches, shellwords( $ENV{HARNESS_PERL_SWITCHES} ) if defined $ENV{HARNESS_PERL_SWITCHES};
578 return @switches ? \@switches : ();
588 push @libs, 'blib/lib', 'blib/arch';
590 if ( @{ $self->includes } ) {
591 push @libs, @{ $self->includes };
595 @libs = map { File::Spec->rel2abs($_) } @libs;
598 return @libs ? \@libs : ();
604 # Fisher-Yates shuffle
608 @_[ $i, $j ] = @_[ $j, $i ];
613 =head3 C<require_harness>
615 Load a harness replacement class.
617 $prove->require_harness($for => $class_name);
621 sub require_harness {
622 my ( $self, $for, $class ) = @_;
624 my ($class_name) = $class =~ /^(\w+(?:::\w+)*)/;
626 # Emulate Perl's -MModule=arg1,arg2 behaviour
627 $class =~ s!^(\w+(?:::\w+)*)=(.*)$!$1 split(/,/,q{$2})!;
630 die "$class_name is required to use the --$for feature: $@" if $@;
632 $self->{harness_class} = $class_name;
637 =head3 C<print_version>
639 Display the version numbers of the loaded L<TAP::Harness> and the
647 "TAP::Harness v%s and Perl v%vd\n",
648 $TAP::Harness::VERSION, $^V
656 # vim:ts=4:sw=4:et:sta
662 After command line parsing the following attributes reflect the values
663 of the corresponding command line switches. They may be altered before
712 =item C<really_quiet>
724 =item C<show_version>
742 =item C<warnings_fail>
744 =item C<warnings_warn>
754 C<App::Prove> provides support for 3rd-party plugins. These are currently
755 loaded at run-time, I<after> arguments have been parsed (so you can not
756 change the way arguments are processed, sorry), typically with the
757 C<< -PI<plugin> >> switch, eg:
761 This will search for a module named C<App::Prove::Plugin::MyPlugin>, or failing
762 that, C<MyPlugin>. If the plugin can't be found, C<prove> will complain & exit.
764 You can pass an argument to your plugin by appending an C<=> after the plugin
765 name, eg C<-PMyPlugin=foo>. You can pass multiple arguments using commas:
767 prove -PMyPlugin=foo,bar,baz
769 These are passed in to your plugin's C<load()> class method (if it has one),
770 along with a reference to the C<App::Prove> object that is invoking your plugin:
773 my ($class, $p) = @_;
775 my @args = @{ $p->{args} };
776 # @args will contain ( 'foo', 'bar', 'baz' )
777 $p->{app_prove}->do_something;
781 Note that the user's arguments are also passed to your plugin's C<import()>
782 function as a list, eg:
785 my ($class, @args) = @_;
786 # @args will contain ( 'foo', 'bar', 'baz' )
790 This is for backwards compatibility, and may be deprecated in the future.
794 Here's a sample plugin, for your reference:
796 package App::Prove::Plugin::Foo;
798 # Sample plugin, try running with:
799 # prove -PFoo=bar -r -j3
801 # prove -PFoo=bar,My::Formatter
807 my ($class, $p) = @_;
808 my @args = @{ $p->{args} };
809 my $app = $p->{app_prove};
811 print "loading plugin: $class, args: ", join(', ', @args ), "\n";
817 $app->formatter( $args[1] ) if @args > 1;
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";
833 L<prove>, L<TAP::Harness>