xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Harness/lib/App/Prove.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
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