File Coverage

blib/lib/App/Prove.pm
Criterion Covered Total %
statement 214 276 77.5
branch 86 130 66.1
condition 12 22 54.5
subroutine 34 39 87.1
pod 5 5 100.0
total 351 472 74.3


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