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   52880 use strict;
  6         10  
  6         169  
4 6     6   24 use warnings;
  6         8  
  6         184  
5              
6 6     6   2294 use TAP::Harness::Env;
  6         10  
  6         166  
7 6     6   28 use Text::ParseWords qw(shellwords);
  6         7  
  6         212  
8 6     6   22 use File::Spec;
  6         7  
  6         94  
9 6     6   3726 use Getopt::Long;
  6         60517  
  6         31  
10 6     6   2934 use App::Prove::State;
  6         16  
  6         188  
11 6     6   33 use Carp;
  6         8  
  6         359  
12              
13 6     6   26 use base 'TAP::Object';
  6         76  
  6         616  
14              
15             =head1 NAME
16              
17             App::Prove - Implements the C command.
18              
19             =head1 VERSION
20              
21             Version 3.38
22              
23             =cut
24              
25             our $VERSION = '3.38';
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         7  
  6         309  
44 6     6   50 use constant IS_VMS => $^O eq 'VMS';
  6         10  
  6         323  
45 6     6   41 use constant IS_UNIXY => !( IS_VMS || IS_WIN32 );
  6         13  
  6         285  
46              
47 6     6   22 use constant STATE_FILE => IS_UNIXY ? '.prove' : '_prove';
  6         9  
  6         262  
48 6     6   21 use constant RC_FILE => IS_UNIXY ? '.proverc' : '_proverc';
  6         7  
  6         227  
49              
50 6     6   24 use constant PLUGINS => 'App::Prove::Plugin';
  6         7  
  6         474  
51              
52             my @ATTR;
53              
54             BEGIN {
55 6     6   45 @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         62 __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   69 my $self = shift;
81 64   100     188 my $args = shift || {};
82              
83 64         180 my @is_array = qw(
84             argv rc_opts includes modules state plugins rules sources
85             );
86              
87             # setup defaults:
88 64         86 for my $key (@is_array) {
89 512         627 $self->{$key} = [];
90             }
91              
92 64         91 for my $attr (@ATTR) {
93 2816 100       3467 if ( exists $args->{$attr} ) {
94              
95             # TODO: Some validation here
96 98         136 $self->{$attr} = $args->{$attr};
97             }
98             }
99              
100 64         165 $self->state_class('App::Prove::State');
101 64         144 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 649 my ( $self, $rc_file ) = @_;
127              
128 2         4 local *RC;
129 2 50       70 open RC, "<$rc_file" or croak "Can't read $rc_file ($!)";
130 2         29 while ( defined( my $line = ) ) {
131 9         47 push @{ $self->{rc_opts} },
132 9 100       6 grep { defined and not /^#/ }
  60         117  
133             $line =~ m{ ' ([^']*) ' | " ([^"]*) " | (\#.*) | (\S+) }xg;
134             }
135 2         16 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 21175 my $self = shift;
151              
152 39         120 my @rc = RC_FILE;
153 39         846 unshift @rc, glob '~/' . RC_FILE if IS_UNIXY;
154              
155             # Preprocess meta-args.
156 39         56 my @args;
157 39         110 while ( defined( my $arg = shift ) ) {
158 126 100       282 if ( $arg eq '--norc' ) {
    50          
    50          
159 39         111 @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         168 push @args, $arg;
171             }
172             }
173              
174             # Everything after the arisdottle '::' gets passed as args to
175             # test programs.
176 39 100       85 if ( defined( my $stop_at = _first_pos( '::', @args ) ) ) {
177 1         7 my @test_args = splice @args, $stop_at;
178 1         3 shift @test_args;
179 1         8 $self->{test_args} = \@test_args;
180             }
181              
182             # Grab options from RC files
183 39         65 $self->add_rc_file($_) for grep -f, @rc;
184 39         28 unshift @args, @{ $self->{rc_opts} };
  39         64  
185              
186 39 50       49 if ( my @bad = map {"-$_"} grep {/^-(man|help)$/} @args ) {
  0         0  
  85         215  
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         33 local @ARGV = @args;
  39         84  
195 39         124 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   2925 my ( $opt, $val ) = @_;
212              
213             # Workaround for Getopt::Long 2.25 handling of
214             # multivalue options
215 3   100     3 push @{ $self->{extensions} ||= [] }, $val;
  3         16  
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       2518 ) or croak('Unable to continue');
248              
249             # Stash the remainder of argv for later
250 39         54521 $self->{argv} = [@ARGV];
251             }
252              
253 39         100 return;
254             }
255              
256             sub _first_pos {
257 39     39   45 my $want = shift;
258 39         97 for ( 0 .. $#_ ) {
259 84 100       156 return $_ if $_[$_] eq $want;
260             }
261 38         76 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     30 return -t STDOUT && !$ENV{HARNESS_NOTTY} && !IS_WIN32;
283             }
284              
285             sub _get_args {
286 60     60   57 my $self = shift;
287              
288 60         64 my %args;
289              
290 60 50       119 $args{trap} = 1 if $self->trap;
291              
292 60 100       121 if ( defined $self->color ? $self->color : $self->_color_default ) {
    100          
293 2         5 $args{color} = 1;
294             }
295 60 50       225 if ( !defined $self->show_count ) {
296 60         110 $args{show_count} = 1;
297             }
298             else {
299 0         0 $args{show_count} = $self->show_count;
300             }
301              
302 60 50       130 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       113 if ( my $jobs = $self->jobs ) {
308 0         0 $args{jobs} = $jobs;
309             }
310              
311 60 50       109 if ( my $harness_opt = $self->harness ) {
312 0         0 $self->require_harness( harness => $harness_opt );
313             }
314              
315 60 100       123 if ( my $formatter = $self->formatter ) {
316 1         2 $args{formatter_class} = $formatter;
317             }
318              
319 60         43 for my $handler ( @{ $self->sources } ) {
  60         110  
320 1         9 my ( $name, $config ) = $self->_parse_source($handler);
321 1         3 $args{sources}->{$name} = $config;
322             }
323              
324 60 50       119 if ( $self->ignore_exit ) {
325 0         0 $args{ignore_exit} = 1;
326             }
327              
328 60 50 66     122 if ( $self->taint_fail && $self->taint_warn ) {
329 0         0 die '-t and -T are mutually exclusive';
330             }
331              
332 60 50 66     111 if ( $self->warnings_fail && $self->warnings_warn ) {
333 0         0 die '-w and -W are mutually exclusive';
334             }
335              
336 60         79 for my $a (qw( lib switches )) {
337 120         177 my $method = "_get_$a";
338 120         264 my $val = $self->$method();
339 120 100       220 $args{$a} = $val if defined $val;
340             }
341              
342             # Handle verbose, quiet, really_quiet flags
343 60         160 my %verb_map = ( verbose => 1, quiet => -1, really_quiet => -2, );
344              
345 60 100       133 my @verb_adj = grep {$_} map { $self->$_() ? $verb_map{$_} : 0 }
  180         178  
  180         315  
346             keys %verb_map;
347              
348 60 50       126 die "Only one of verbose, quiet or really_quiet should be specified\n"
349             if @verb_adj > 1;
350              
351 60   100     203 $args{verbosity} = shift @verb_adj || 0;
352              
353 60         80 for my $a (qw( merge failures comments timer directives normalize )) {
354 360 100       607 $args{$a} = 1 if $self->$a();
355             }
356              
357 60 100       135 $args{errors} = 1 if $self->parse;
358              
359             # defined but zero-length exec runs test files as binaries
360 60 100       128 $args{exec} = [ split( /\s+/, $self->exec ) ]
361             if ( defined( $self->exec ) );
362              
363 60 50       127 $args{version} = $self->tapversion if defined( $self->tapversion );
364              
365 60 100       113 if ( defined( my $test_args = $self->test_args ) ) {
366 1         5 $args{test_args} = $test_args;
367             }
368              
369 60 50       52 if ( @{ $self->rules } ) {
  60         123  
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       104 $args{harness_class} = $self->{harness_class} if $self->{harness_class};
382              
383 60         195 return \%args;
384             }
385              
386             sub _find_module {
387 5     5   6 my ( $self, $class, @search ) = @_;
388              
389 5 50       24 croak "Bad module name $class"
390             unless $class =~ /^ \w+ (?: :: \w+ ) *$/x;
391              
392 5         6 for my $pfx (@search) {
393 4         8 my $name = join( '::', $pfx, $class );
394 4         261 eval "require $name";
395 4 100       221 return $name unless $@;
396             }
397              
398 2         102 eval "require $class";
399 2 50       93 return $class unless $@;
400 0         0 return;
401             }
402              
403             sub _load_extension {
404 5     5   8 my ( $self, $name, @search ) = @_;
405              
406 5         6 my @args = ();
407 5 100       19 if ( $name =~ /^(.*?)=(.*)/ ) {
408 2         5 $name = $1;
409 2         5 @args = split( /,/, $2 );
410             }
411              
412 5 50       11 if ( my $class = $self->_find_module( $name, @search ) ) {
413 5         21 $class->import(@args);
414 5 100       55 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   162 my ( $self, $ext, @search ) = @_;
425 120         197 $self->_load_extension( $_, @search ) for @$ext;
426             }
427              
428             sub _parse_source {
429 1     1   2 my ( $self, $handler ) = @_;
430              
431             # Load any options.
432 1         3 ( my $opt_name = lc $handler ) =~ s/::/-/g;
433 1         6 local @ARGV = @{ $self->{argv} };
  1         3  
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         8 );
460 1         103 $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 15852 my $self = shift;
479              
480 60 50       151 unless ( $self->state_manager ) {
481 60         139 $self->state_manager(
482             $self->state_class->new( { store => STATE_FILE } ) );
483             }
484              
485 60 50       161 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         134 $self->_load_extensions( $self->modules );
500 60         123 $self->_load_extensions( $self->plugins, PLUGINS );
501              
502 60 100       134 local $ENV{TEST_VERBOSE} = 1 if $self->verbose;
503              
504 60         182 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   59 my $self = shift;
512              
513 60         112 my $state = $self->state_manager;
514 60         99 my $ext = $self->extensions;
515 60 100       104 $state->extensions($ext) if defined $ext;
516 60 50       109 if ( defined( my $state_switch = $self->state ) ) {
517 60         155 $state->apply_switch(@$state_switch);
518             }
519              
520 60         146 my @tests = $state->get_tests( $self->recurse, @{ $self->argv } );
  60         106  
521              
522 60 100       145 $self->_shuffle(@tests) if $self->shuffle;
523 60 100       133 @tests = reverse @tests if $self->backwards;
524              
525 60         217 return @tests;
526             }
527              
528             sub _runtests {
529 2     2   3 my ( $self, $args, @tests ) = @_;
530 2         17 my $harness = TAP::Harness::Env->create($args);
531              
532 2         10 my $state = $self->state_manager;
533              
534             $harness->callback(
535             after_test => sub {
536 2     2   18 $state->observe_test(@_);
537             }
538 2         20 );
539              
540             $harness->callback(
541             after_runtests => sub {
542 2     2   8 $state->commit(@_);
543             }
544 2         22 );
545              
546 2         7 my $aggregator = $harness->runtests(@tests);
547              
548 2         5 return !$aggregator->has_errors;
549             }
550              
551             sub _get_switches {
552 60     60   51 my $self = shift;
553 60         46 my @switches;
554              
555             # notes that -T or -t must be at the front of the switches!
556 60 100       109 if ( $self->taint_fail ) {
    100          
557 1         2 push @switches, '-T';
558             }
559             elsif ( $self->taint_warn ) {
560 1         3 push @switches, '-t';
561             }
562 60 100       105 if ( $self->warnings_fail ) {
    100          
563 1         2 push @switches, '-W';
564             }
565             elsif ( $self->warnings_warn ) {
566 1         3 push @switches, '-w';
567             }
568              
569 60 100       168 return @switches ? \@switches : ();
570             }
571              
572             sub _get_lib {
573 60     60   69 my $self = shift;
574 60         71 my @libs;
575 60 100       118 if ( $self->lib ) {
576 3         7 push @libs, 'lib';
577             }
578 60 100       124 if ( $self->blib ) {
579 3         7 push @libs, 'blib/lib', 'blib/arch';
580             }
581 60 100       55 if ( @{ $self->includes } ) {
  60         99  
582 1         2 push @libs, @{ $self->includes };
  1         3  
583             }
584              
585             #24926
586 60         107 @libs = map { File::Spec->rel2abs($_) } @libs;
  12         196  
587              
588             # Huh?
589 60 100       127 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__