File Coverage

blib/lib/App/Prove.pm
Criterion Covered Total %
statement 214 276 77.5
branch 86 130 66.1
condition 11 20 55.0
subroutine 34 39 87.1
pod 5 5 100.0
total 350 470 74.4


line stmt bran cond sub pod time code
1             package App::Prove;
2              
3 6     6   59337 use strict;
  6         11  
  6         180  
4 6     6   23 use warnings;
  6         7  
  6         172  
5              
6 6     6   2280 use TAP::Harness::Env;
  6         12  
  6         252  
7 6     6   39 use Text::ParseWords qw(shellwords);
  6         6  
  6         261  
8 6     6   24 use File::Spec;
  6         8  
  6         115  
9 6     6   4208 use Getopt::Long;
  6         68809  
  6         33  
10 6     6   3663 use App::Prove::State;
  6         18  
  6         260  
11 6     6   45 use Carp;
  6         12  
  6         489  
12              
13 6     6   31 use base 'TAP::Object';
  6         81  
  6         708  
14              
15             =head1 NAME
16              
17             App::Prove - Implements the C command.
18              
19             =head1 VERSION
20              
21             Version 3.39
22              
23             =cut
24              
25             our $VERSION = '3.39';
26              
27             =head1 DESCRIPTION
28              
29             L provides a command, C, which runs a TAP based
30             test suite and prints a report. The C command is a minimal
31             wrapper 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              
43 6     6   28 use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
  6         9  
  6         431  
44 6     6   22 use constant IS_VMS => $^O eq 'VMS';
  6         8  
  6         313  
45 6     6   30 use constant IS_UNIXY => !( IS_VMS || IS_WIN32 );
  6         10  
  6         281  
46              
47 6     6   24 use constant STATE_FILE => IS_UNIXY ? '.prove' : '_prove';
  6         9  
  6         273  
48 6     6   22 use constant RC_FILE => IS_UNIXY ? '.proverc' : '_proverc';
  6         8  
  6         251  
49              
50 6     6   24 use constant PLUGINS => 'App::Prove::Plugin';
  6         7  
  6         491  
51              
52             my @ATTR;
53              
54             BEGIN {
55 6     6   46 @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 6         74 __PACKAGE__->mk_methods(@ATTR);
64             }
65              
66             =head1 METHODS
67              
68             =head2 Class Methods
69              
70             =head3 C
71              
72             Create a new C. Optionally a hash ref of attribute
73             initializers may be passed.
74              
75             =cut
76              
77             # new() implementation supplied by TAP::Object
78              
79             sub _initialize {
80 64     64   99 my $self = shift;
81 64   100     281 my $args = shift || {};
82              
83 64         347 my @is_array = qw(
84             argv rc_opts includes modules state plugins rules sources
85             );
86              
87             # setup defaults:
88 64         132 for my $key (@is_array) {
89 512         831 $self->{$key} = [];
90             }
91              
92 64         174 for my $attr (@ATTR) {
93 2816 100       3848 if ( exists $args->{$attr} ) {
94              
95             # TODO: Some validation here
96 98         168 $self->{$attr} = $args->{$attr};
97             }
98             }
99              
100 64         250 $self->state_class('App::Prove::State');
101 64         218 return $self;
102             }
103              
104             =head3 C
105              
106             Getter/setter for the name of the class used for maintaining state. This
107             class should either subclass from C or provide an identical
108             interface.
109              
110             =head3 C
111              
112             Getter/setter for the instance of the C.
113              
114             =cut
115              
116             =head3 C
117              
118             $prove->add_rc_file('myproj/.proverc');
119              
120             Called before C to prepend the contents of an rc file to
121             the options.
122              
123             =cut
124              
125             sub add_rc_file {
126 2     2 1 1255 my ( $self, $rc_file ) = @_;
127              
128 2         6 local *RC;
129 2 50       123 open RC, "<$rc_file" or croak "Can't read $rc_file ($!)";
130 2         41 while ( defined( my $line = ) ) {
131 9         62 push @{ $self->{rc_opts} },
132 9 100       7 grep { defined and not /^#/ }
  60         461  
133             $line =~ m{ ' ([^']*) ' | " ([^"]*) " | (\#.*) | (\S+) }xg;
134             }
135 2         47 close RC;
136             }
137              
138             =head3 C
139              
140             $prove->process_args(@args);
141              
142             Processes the command-line arguments. Attributes will be set
143             appropriately. Any filenames may be found in the C attribute.
144              
145             Dies on invalid arguments.
146              
147             =cut
148              
149             sub process_args {
150 39     39 1 34361 my $self = shift;
151              
152 39         128 my @rc = RC_FILE;
153 39         1734 unshift @rc, glob '~/' . RC_FILE if IS_UNIXY;
154              
155             # Preprocess meta-args.
156 39         160 my @args;
157 39         164 while ( defined( my $arg = shift ) ) {
158 126 100       413 if ( $arg eq '--norc' ) {
    50          
    50          
159 39         137 @rc = ();
160             }
161             elsif ( $arg eq '--rc' ) {
162 0 0       0 defined( my $rc = shift )
163             or croak "Missing argument to --rc";
164 0         0 push @rc, $rc;
165             }
166             elsif ( $arg =~ m{^--rc=(.+)$} ) {
167 0         0 push @rc, $1;
168             }
169             else {
170 87         213 push @args, $arg;
171             }
172             }
173              
174             # Everything after the arisdottle '::' gets passed as args to
175             # test programs.
176 39 100       148 if ( defined( my $stop_at = _first_pos( '::', @args ) ) ) {
177 1         71 my @test_args = splice @args, $stop_at;
178 1         3 shift @test_args;
179 1         7 $self->{test_args} = \@test_args;
180             }
181              
182             # Grab options from RC files
183 39         133 $self->add_rc_file($_) for grep -f, @rc;
184 39         75 unshift @args, @{ $self->{rc_opts} };
  39         94  
185              
186 39 50       96 if ( my @bad = map {"-$_"} grep {/^-(man|help)$/} @args ) {
  0         0  
  85         324  
187 0         0 die "Long options should be written with two dashes: ",
188             join( ', ', @bad ), "\n";
189             }
190              
191             # And finally...
192              
193             {
194 39         51 local @ARGV = @args;
  39         136  
195 39         249 Getopt::Long::Configure(qw(no_ignore_case bundling pass_through));
196              
197             # Don't add coderefs to GetOptions
198             GetOptions(
199             'v|verbose' => \$self->{verbose},
200             'f|failures' => \$self->{failures},
201             'o|comments' => \$self->{comments},
202             'l|lib' => \$self->{lib},
203             'b|blib' => \$self->{blib},
204             's|shuffle' => \$self->{shuffle},
205             'color!' => \$self->{color},
206             'colour!' => \$self->{color},
207             'count!' => \$self->{show_count},
208             'c' => \$self->{color},
209             'D|dry' => \$self->{dry},
210             'ext=s@' => sub {
211 3     3   3090 my ( $opt, $val ) = @_;
212              
213             # Workaround for Getopt::Long 2.25 handling of
214             # multivalue options
215 3   100     4 push @{ $self->{extensions} ||= [] }, $val;
  3         18  
216             },
217             'harness=s' => \$self->{harness},
218             'ignore-exit' => \$self->{ignore_exit},
219             'source=s@' => $self->{sources},
220             'formatter=s' => \$self->{formatter},
221             'r|recurse' => \$self->{recurse},
222             'reverse' => \$self->{backwards},
223             'p|parse' => \$self->{parse},
224             'q|quiet' => \$self->{quiet},
225             'Q|QUIET' => \$self->{really_quiet},
226             'e|exec=s' => \$self->{exec},
227             'm|merge' => \$self->{merge},
228             'I=s@' => $self->{includes},
229             'M=s@' => $self->{modules},
230             'P=s@' => $self->{plugins},
231             'state=s@' => $self->{state},
232             'directives' => \$self->{directives},
233             'h|help|?' => \$self->{show_help},
234             'H|man' => \$self->{show_man},
235             'V|version' => \$self->{show_version},
236             'a|archive=s' => \$self->{archive},
237             'j|jobs=i' => \$self->{jobs},
238             'timer' => \$self->{timer},
239             'T' => \$self->{taint_fail},
240             't' => \$self->{taint_warn},
241             'W' => \$self->{warnings_fail},
242             'w' => \$self->{warnings_warn},
243             'normalize' => \$self->{normalize},
244             'rules=s@' => $self->{rules},
245             'tapversion=s' => \$self->{tapversion},
246             'trap' => \$self->{trap},
247 39 50       3758 ) or croak('Unable to continue');
248              
249             # Stash the remainder of argv for later
250 39         68409 $self->{argv} = [@ARGV];
251             }
252              
253 39         151 return;
254             }
255              
256             sub _first_pos {
257 39     39   66 my $want = shift;
258 39         168 for ( 0 .. $#_ ) {
259 84 100       231 return $_ if $_[$_] eq $want;
260             }
261 38         131 return;
262             }
263              
264             sub _help {
265 0     0   0 my ( $self, $verbosity ) = @_;
266              
267 0         0 eval('use Pod::Usage 1.12 ()');
268 0 0       0 if ( my $err = $@ ) {
269 0         0 die 'Please install Pod::Usage for the --help option '
270             . '(or try `perldoc prove`.)'
271             . "\n ($@)";
272             }
273              
274 0         0 Pod::Usage::pod2usage( { -verbose => $verbosity } );
275              
276 0         0 return;
277             }
278              
279             sub _color_default {
280 3     3   8 my $self = shift;
281              
282 3   33     38 return -t STDOUT && !$ENV{HARNESS_NOTTY} && !IS_WIN32;
283             }
284              
285             sub _get_args {
286 60     60   84 my $self = shift;
287              
288 60         87 my %args;
289              
290 60 50       165 $args{trap} = 1 if $self->trap;
291              
292 60 100       185 if ( defined $self->color ? $self->color : $self->_color_default ) {
    100          
293 2         7 $args{color} = 1;
294             }
295 60 50       356 if ( !defined $self->show_count ) {
296 60         206 $args{show_count} = 1;
297             }
298             else {
299 0         0 $args{show_count} = $self->show_count;
300             }
301              
302 60 50       195 if ( $self->archive ) {
303 0         0 $self->require_harness( archive => 'TAP::Harness::Archive' );
304 0         0 $args{archive} = $self->archive;
305             }
306              
307 60 50       160 if ( my $jobs = $self->jobs ) {
308 0         0 $args{jobs} = $jobs;
309             }
310              
311 60 50       165 if ( my $harness_opt = $self->harness ) {
312 0         0 $self->require_harness( harness => $harness_opt );
313             }
314              
315 60 100       144 if ( my $formatter = $self->formatter ) {
316 1         3 $args{formatter_class} = $formatter;
317             }
318              
319 60         84 for my $handler ( @{ $self->sources } ) {
  60         154  
320 1         11 my ( $name, $config ) = $self->_parse_source($handler);
321 1         4 $args{sources}->{$name} = $config;
322             }
323              
324 60 50       213 if ( $self->ignore_exit ) {
325 0         0 $args{ignore_exit} = 1;
326             }
327              
328 60 50 66     150 if ( $self->taint_fail && $self->taint_warn ) {
329 0         0 die '-t and -T are mutually exclusive';
330             }
331              
332 60 50 66     175 if ( $self->warnings_fail && $self->warnings_warn ) {
333 0         0 die '-w and -W are mutually exclusive';
334             }
335              
336 60         133 for my $a (qw( lib switches )) {
337 120         265 my $method = "_get_$a";
338 120         436 my $val = $self->$method();
339 120 100       324 $args{$a} = $val if defined $val;
340             }
341              
342             # Handle verbose, quiet, really_quiet flags
343 60         273 my %verb_map = ( verbose => 1, quiet => -1, really_quiet => -2, );
344              
345 60 100       220 my @verb_adj = grep {$_} map { $self->$_() ? $verb_map{$_} : 0 }
  180         312  
  180         398  
346             keys %verb_map;
347              
348 60 50       182 die "Only one of verbose, quiet or really_quiet should be specified\n"
349             if @verb_adj > 1;
350              
351 60   100     276 $args{verbosity} = shift @verb_adj || 0;
352              
353 60         130 for my $a (qw( merge failures comments timer directives normalize )) {
354 360 100       961 $args{$a} = 1 if $self->$a();
355             }
356              
357 60 100       205 $args{errors} = 1 if $self->parse;
358              
359             # defined but zero-length exec runs test files as binaries
360 60 100       195 $args{exec} = [ split( /\s+/, $self->exec ) ]
361             if ( defined( $self->exec ) );
362              
363 60 50       174 $args{version} = $self->tapversion if defined( $self->tapversion );
364              
365 60 100       136 if ( defined( my $test_args = $self->test_args ) ) {
366 1         2 $args{test_args} = $test_args;
367             }
368              
369 60 50       68 if ( @{ $self->rules } ) {
  60         150  
370 0         0 my @rules;
371 0         0 for ( @{ $self->rules } ) {
  0         0  
372 0 0       0 if (/^par=(.*)/) {
    0          
373 0         0 push @rules, $1;
374             }
375             elsif (/^seq=(.*)/) {
376 0         0 push @rules, { seq => $1 };
377             }
378             }
379 0         0 $args{rules} = { par => [@rules] };
380             }
381 60 50       188 $args{harness_class} = $self->{harness_class} if $self->{harness_class};
382              
383 60         268 return \%args;
384             }
385              
386             sub _find_module {
387 5     5   11 my ( $self, $class, @search ) = @_;
388              
389 5 50       35 croak "Bad module name $class"
390             unless $class =~ /^ \w+ (?: :: \w+ ) *$/x;
391              
392 5         10 for my $pfx (@search) {
393 4         17 my $name = join( '::', $pfx, $class );
394 4         534 eval "require $name";
395 4 100       360 return $name unless $@;
396             }
397              
398 2         161 eval "require $class";
399 2 50       100 return $class unless $@;
400 0         0 return;
401             }
402              
403             sub _load_extension {
404 5     5   10 my ( $self, $name, @search ) = @_;
405              
406 5         12 my @args = ();
407 5 100       33 if ( $name =~ /^(.*?)=(.*)/ ) {
408 2         9 $name = $1;
409 2         12 @args = split( /,/, $2 );
410             }
411              
412 5 50       21 if ( my $class = $self->_find_module( $name, @search ) ) {
413 5         41 $class->import(@args);
414 5 100       90 if ( $class->can('load') ) {
415 1         6 $class->load( { app_prove => $self, args => [@args] } );
416             }
417             }
418             else {
419 0         0 croak "Can't load module $name";
420             }
421             }
422              
423             sub _load_extensions {
424 120     120   240 my ( $self, $ext, @search ) = @_;
425 120         315 $self->_load_extension( $_, @search ) for @$ext;
426             }
427              
428             sub _parse_source {
429 1     1   12 my ( $self, $handler ) = @_;
430              
431             # Load any options.
432 1         5 ( my $opt_name = lc $handler ) =~ s/::/-/g;
433 1         2 local @ARGV = @{ $self->{argv} };
  1         5  
434 1         1 my %config;
435             Getopt::Long::GetOptions(
436             "$opt_name-option=s%" => sub {
437 0     0   0 my ( $name, $k, $v ) = @_;
438 0 0       0 if ( $v =~ /(?
439              
440             # It's a hash option.
441             croak "Option $name must be consistently used as a hash"
442 0 0 0     0 if exists $config{$k} && ref $config{$k} ne 'HASH';
443 0   0     0 $config{$k} ||= {};
444 0         0 my ( $hk, $hv ) = split /(?
445 0         0 $config{$k}{$hk} = $hv;
446             }
447             else {
448 0         0 $v =~ s/\\=/=/g;
449 0 0       0 if ( exists $config{$k} ) {
450             $config{$k} = [ $config{$k} ]
451 0 0       0 unless ref $config{$k} eq 'ARRAY';
452 0         0 push @{ $config{$k} } => $v;
  0         0  
453             }
454             else {
455 0         0 $config{$k} = $v;
456             }
457             }
458             }
459 1         11 );
460 1         159 $self->{argv} = \@ARGV;
461 1         3 return ( $handler, \%config );
462             }
463              
464             =head3 C
465              
466             Perform whatever actions the command line args specified. The C
467             command line tool consists of the following code:
468              
469             use App::Prove;
470              
471             my $app = App::Prove->new;
472             $app->process_args(@ARGV);
473             exit( $app->run ? 0 : 1 ); # if you need the exit code
474              
475             =cut
476              
477             sub run {
478 60     60 1 17960 my $self = shift;
479              
480 60 50       273 unless ( $self->state_manager ) {
481 60         214 $self->state_manager(
482             $self->state_class->new( { store => STATE_FILE } ) );
483             }
484              
485 60 50       236 if ( $self->show_help ) {
    50          
    50          
    50          
486 0         0 $self->_help(1);
487             }
488             elsif ( $self->show_man ) {
489 0         0 $self->_help(2);
490             }
491             elsif ( $self->show_version ) {
492 0         0 $self->print_version;
493             }
494             elsif ( $self->dry ) {
495 0         0 print "$_\n" for $self->_get_tests;
496             }
497             else {
498              
499 60         198 $self->_load_extensions( $self->modules );
500 60         268 $self->_load_extensions( $self->plugins, PLUGINS );
501              
502 60 100       188 local $ENV{TEST_VERBOSE} = 1 if $self->verbose;
503              
504 60         222 return $self->_runtests( $self->_get_args, $self->_get_tests );
505             }
506              
507 0         0 return 1;
508             }
509              
510             sub _get_tests {
511 60     60   94 my $self = shift;
512              
513 60         131 my $state = $self->state_manager;
514 60         166 my $ext = $self->extensions;
515 60 100       153 $state->extensions($ext) if defined $ext;
516 60 50       229 if ( defined( my $state_switch = $self->state ) ) {
517 60         254 $state->apply_switch(@$state_switch);
518             }
519              
520 60         208 my @tests = $state->get_tests( $self->recurse, @{ $self->argv } );
  60         182  
521              
522 60 100       199 $self->_shuffle(@tests) if $self->shuffle;
523 60 100       189 @tests = reverse @tests if $self->backwards;
524              
525 60         327 return @tests;
526             }
527              
528             sub _runtests {
529 2     2   5 my ( $self, $args, @tests ) = @_;
530 2         22 my $harness = TAP::Harness::Env->create($args);
531              
532 2         14 my $state = $self->state_manager;
533              
534             $harness->callback(
535             after_test => sub {
536 2     2   23 $state->observe_test(@_);
537             }
538 2         30 );
539              
540             $harness->callback(
541             after_runtests => sub {
542 2     2   12 $state->commit(@_);
543             }
544 2         14 );
545              
546 2         11 my $aggregator = $harness->runtests(@tests);
547              
548 2         7 return !$aggregator->has_errors;
549             }
550              
551             sub _get_switches {
552 60     60   89 my $self = shift;
553 60         75 my @switches;
554              
555             # notes that -T or -t must be at the front of the switches!
556 60 100       162 if ( $self->taint_fail ) {
    100          
557 1         3 push @switches, '-T';
558             }
559             elsif ( $self->taint_warn ) {
560 1         3 push @switches, '-t';
561             }
562 60 100       135 if ( $self->warnings_fail ) {
    100          
563 1         3 push @switches, '-W';
564             }
565             elsif ( $self->warnings_warn ) {
566 1         3 push @switches, '-w';
567             }
568              
569 60 100       165 return @switches ? \@switches : ();
570             }
571              
572             sub _get_lib {
573 60     60   79 my $self = shift;
574 60         89 my @libs;
575 60 100       164 if ( $self->lib ) {
576 3         9 push @libs, 'lib';
577             }
578 60 100       208 if ( $self->blib ) {
579 3         10 push @libs, 'blib/lib', 'blib/arch';
580             }
581 60 100       98 if ( @{ $self->includes } ) {
  60         155  
582 1         3 push @libs, @{ $self->includes };
  1         2  
583             }
584              
585             #24926
586 60         165 @libs = map { File::Spec->rel2abs($_) } @libs;
  12         325  
587              
588             # Huh?
589 60 100       205 return @libs ? \@libs : ();
590             }
591              
592             sub _shuffle {
593 0     0     my $self = shift;
594              
595             # Fisher-Yates shuffle
596 0           my $i = @_;
597 0           while ($i) {
598 0           my $j = rand $i--;
599 0           @_[ $i, $j ] = @_[ $j, $i ];
600             }
601 0           return;
602             }
603              
604             =head3 C
605              
606             Load a harness replacement class.
607              
608             $prove->require_harness($for => $class_name);
609              
610             =cut
611              
612             sub require_harness {
613 0     0 1   my ( $self, $for, $class ) = @_;
614              
615 0           my ($class_name) = $class =~ /^(\w+(?:::\w+)*)/;
616              
617             # Emulate Perl's -MModule=arg1,arg2 behaviour
618 0           $class =~ s!^(\w+(?:::\w+)*)=(.*)$!$1 split(/,/,q{$2})!;
619              
620 0           eval("use $class;");
621 0 0         die "$class_name is required to use the --$for feature: $@" if $@;
622              
623 0           $self->{harness_class} = $class_name;
624              
625 0           return;
626             }
627              
628             =head3 C
629              
630             Display the version numbers of the loaded L and the
631             current Perl.
632              
633             =cut
634              
635             sub print_version {
636 0     0 1   my $self = shift;
637 0           require TAP::Harness;
638 0           printf(
639             "TAP::Harness v%s and Perl v%vd\n",
640             $TAP::Harness::VERSION, $^V
641             );
642              
643 0           return;
644             }
645              
646             1;
647              
648             # vim:ts=4:sw=4:et:sta
649              
650             __END__