File Coverage

blib/lib/TAP/Parser/SourceHandler/Perl.pm
Criterion Covered Total %
statement 119 120 99.1
branch 39 46 84.7
condition 9 12 75.0
subroutine 25 25 100.0
pod 4 4 100.0
total 196 207 94.6


line stmt bran cond sub pod time code
1             package TAP::Parser::SourceHandler::Perl;
2              
3 38     38   5986 use strict;
  38         102  
  38         1173  
4 38     38   235 use warnings;
  38         97  
  38         1204  
5 38     38   241 use Config;
  38         99  
  38         3027  
6              
7 38     38   283 use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
  38         95  
  38         2594  
8 38     38   264 use constant IS_VMS => ( $^O eq 'VMS' );
  38         94  
  38         2189  
9              
10 38     38   4907 use TAP::Parser::IteratorFactory ();
  38         134  
  38         11424  
11 38     38   6776 use TAP::Parser::Iterator::Process ();
  38         104  
  38         1132  
12 38     38   19516 use Text::ParseWords qw(shellwords);
  38         51766  
  38         2682  
13              
14 38     38   329 use base 'TAP::Parser::SourceHandler::Executable';
  38         115  
  38         48054  
15              
16             TAP::Parser::IteratorFactory->register_handler(__PACKAGE__);
17              
18             =head1 NAME
19              
20             TAP::Parser::SourceHandler::Perl - Stream TAP from a Perl executable
21              
22             =head1 VERSION
23              
24             Version 3.40_01
25              
26             =cut
27              
28             our $VERSION = '3.40_01';
29              
30             =head1 SYNOPSIS
31              
32             use TAP::Parser::Source;
33             use TAP::Parser::SourceHandler::Perl;
34              
35             my $source = TAP::Parser::Source->new->raw( \'script.pl' );
36             $source->assemble_meta;
37              
38             my $class = 'TAP::Parser::SourceHandler::Perl';
39             my $vote = $class->can_handle( $source );
40             my $iter = $class->make_iterator( $source );
41              
42             =head1 DESCRIPTION
43              
44             This is a I L - it has 2 jobs:
45              
46             1. Figure out if the L it's given is actually a Perl
47             script (L).
48              
49             2. Creates an iterator for Perl sources (L).
50              
51             Unless you're writing a plugin or subclassing L, you probably
52             won't need to use this module directly.
53              
54             =head1 METHODS
55              
56             =head2 Class Methods
57              
58             =head3 C
59              
60             my $vote = $class->can_handle( $source );
61              
62             Only votes if $source looks like a file. Casts the following votes:
63              
64             0.9 if it has a shebang ala "#!...perl"
65             0.75 if it has any shebang
66             0.8 if it's a .t file
67             0.9 if it's a .pl file
68             0.75 if it's in a 't' directory
69             0.25 by default (backwards compat)
70              
71             =cut
72              
73             sub can_handle {
74 313     313 1 1514 my ( $class, $source ) = @_;
75 313         1237 my $meta = $source->meta;
76              
77 313 100       1488 return 0 unless $meta->{is_file};
78 227         642 my $file = $meta->{file};
79              
80 227 100       952 if ( my $shebang = $file->{shebang} ) {
81 223 100       1799 return 0.9 if $shebang =~ /^#!.*\bperl/;
82              
83             # We favour Perl as the interpreter for any shebang to preserve
84             # previous semantics: we used to execute everything via Perl and
85             # relied on it to pass the shebang off to the appropriate
86             # interpreter.
87 163         773 return 0.3;
88             }
89              
90 4 100       14 return 0.8 if $file->{lc_ext} eq '.t'; # vote higher than Executable
91 3 100       12 return 0.9 if $file->{lc_ext} eq '.pl';
92              
93 2 100       18 return 0.75 if $file->{dir} =~ /^t\b/; # vote higher than Executable
94              
95             # backwards compat, always vote:
96 1         10 return 0.25;
97             }
98              
99             =head3 C
100              
101             my $iterator = $class->make_iterator( $source );
102              
103             Constructs & returns a new L for the source.
104             Assumes C<$source-Eraw> contains a reference to the perl script. Cs
105             if the file could not be found.
106              
107             The command to run is built as follows:
108              
109             $perl @switches $perl_script @test_args
110              
111             The perl command to use is determined by L. The command generated
112             is guaranteed to preserve:
113              
114             PERL5LIB
115             PERL5OPT
116             Taint Mode, if set in the script's shebang
117              
118             I the command generated will I respect any shebang line defined in
119             your Perl script. This is only a problem if you have compiled a custom version
120             of Perl or if you want to use a specific version of Perl for one test and a
121             different version for another, for example:
122              
123             #!/path/to/a/custom_perl --some --args
124             #!/usr/local/perl-5.6/bin/perl -w
125              
126             Currently you need to write a plugin to get around this.
127              
128             =cut
129              
130             sub _autoflush_stdhandles {
131 212     212   691 my ($class) = @_;
132              
133 212         1861 $class->_autoflush( \*STDOUT );
134 212         935 $class->_autoflush( \*STDERR );
135             }
136              
137             sub make_iterator {
138 212     212 1 762 my ( $class, $source ) = @_;
139 212         902 my $meta = $source->meta;
140 212         638 my $perl_script = ${ $source->raw };
  212         898  
141              
142 212 50       936 $class->_croak("Cannot find ($perl_script)") unless $meta->{is_file};
143              
144             # TODO: does this really need to be done here?
145 212         1349 $class->_autoflush_stdhandles;
146              
147 212         1565 my ( $libs, $switches )
148             = $class->_mangle_switches(
149             $class->_filter_libs( $class->_switches($source) ) );
150              
151 212         1405 $class->_run( $source, $libs, $switches );
152             }
153              
154              
155             sub _has_taint_switch {
156 365     365   1117 my( $class, $switches ) = @_;
157              
158 365 100       896 my $has_taint = grep { $_ eq "-T" || $_ eq "-t" } @{$switches};
  273         1713  
  365         1088  
159 365 100       2061 return $has_taint ? 1 : 0;
160             }
161              
162             sub _mangle_switches {
163 212     212   840 my ( $class, $libs, $switches ) = @_;
164              
165             # Taint mode ignores environment variables so we must retranslate
166             # PERL5LIB as -I switches and place PERL5OPT on the command line
167             # in order that it be seen.
168 212 100       1155 if ( $class->_has_taint_switch($switches) ) {
169 15 50       296 my @perl5lib = defined $ENV{PERL5LIB} ? split /$Config{path_sep}/, $ENV{PERL5LIB} : ();
170             return (
171             $libs,
172 15         145 [ @{$switches},
173             $class->_libs2switches([@$libs, @perl5lib]),
174 15 100       70 defined $ENV{PERL5OPT} ? shellwords( $ENV{PERL5OPT} ) : ()
175             ],
176             );
177             }
178              
179 197         903 return ( $libs, $switches );
180             }
181              
182             sub _filter_libs {
183 212     212   934 my ( $class, @switches ) = @_;
184              
185 212         5014 my $path_sep = $Config{path_sep};
186 212         2053 my $path_re = qr{$path_sep};
187              
188             # Filter out any -I switches to be handled as libs later.
189             #
190             # Nasty kludge. It might be nicer if we got the libs separately
191             # although at least this way we find any -I switches that were
192             # supplied other then as explicit libs.
193             #
194             # We filter out any names containing colons because they will break
195             # PERL5LIB
196 212         738 my @libs;
197             my @filtered_switches;
198 212         753 for (@switches) {
199 618 100 100     7807 if ( !/$path_re/ && m/ ^ ['"]? -I ['"]? (.*?) ['"]? $ /x ) {
200 529         2356 push @libs, $1;
201             }
202             else {
203 89         354 push @filtered_switches, $_;
204             }
205             }
206              
207 212         2104 return \@libs, \@filtered_switches;
208             }
209              
210             sub _iterator_hooks {
211 212     212   1876 my ( $class, $source, $libs, $switches ) = @_;
212              
213             my $setup = sub {
214 212 100 100 212   483 if ( @{$libs} and !$class->_has_taint_switch($switches) ) {
  212         1386  
215             $ENV{PERL5LIB} = join(
216 618         3007 $Config{path_sep}, grep {defined} @{$libs},
  141         621  
217             $ENV{PERL5LIB}
218 141         1623 );
219             }
220 212         1669 };
221              
222             # VMS environment variables aren't guaranteed to reset at the end of
223             # the process, so we need to put PERL5LIB back.
224 212         1026 my $previous = $ENV{PERL5LIB};
225             my $teardown = sub {
226 207 50   207   860 if ( defined $previous ) {
227 207         2868 $ENV{PERL5LIB} = $previous;
228             }
229             else {
230 0         0 delete $ENV{PERL5LIB};
231             }
232 212         1076 };
233              
234 212         1084 return ( $setup, $teardown );
235             }
236              
237             sub _run {
238 212     212   813 my ( $class, $source, $libs, $switches ) = @_;
239              
240 212 50       1134 my @command = $class->_get_command_for_switches( $source, $switches )
241             or $class->_croak("No command found!");
242              
243 212         1195 my ( $setup, $teardown ) = $class->_iterator_hooks( $source, $libs, $switches );
244              
245 212         1154 return $class->_create_iterator( $source, \@command, $setup, $teardown );
246             }
247              
248             sub _create_iterator {
249 212     212   897 my ( $class, $source, $command, $setup, $teardown ) = @_;
250              
251 212         1086 return TAP::Parser::Iterator::Process->new(
252             { command => $command,
253             merge => $source->merge,
254             setup => $setup,
255             teardown => $teardown,
256             }
257             );
258             }
259              
260             sub _get_command_for_switches {
261 212     212   954 my ( $class, $source, $switches ) = @_;
262 212         583 my $file = ${ $source->raw };
  212         977  
263 212 100       535 my @args = @{ $source->test_args || [] };
  212         792  
264 212         1228 my $command = $class->get_perl;
265              
266             # XXX don't need to quote if we treat the parts as atoms (except maybe vms)
267             #$file = qq["$file"] if ( $file =~ /\s/ ) && ( $file !~ /^".*"$/ );
268 212         671 my @command = ( $command, @{$switches}, $file, @args );
  212         1057  
269 212         1827 return @command;
270             }
271              
272             sub _libs2switches {
273 15     15   53 my $class = shift;
274 15         44 return map {"-I$_"} grep {$_} @{ $_[0] };
  113         589  
  113         306  
  15         61  
275             }
276              
277             =head3 C
278              
279             Decode any taint switches from a Perl shebang line.
280              
281             # $taint will be 't'
282             my $taint = TAP::Parser::SourceHandler::Perl->get_taint( '#!/usr/bin/perl -t' );
283              
284             # $untaint will be undefined
285             my $untaint = TAP::Parser::SourceHandler::Perl->get_taint( '#!/usr/bin/perl' );
286              
287             =cut
288              
289             sub get_taint {
290 212     212 1 901 my ( $class, $shebang ) = @_;
291             return
292 212 100 66     2363 unless defined $shebang
293             && $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/;
294 15         110 return $1;
295             }
296              
297             sub _switches {
298 212     212   808 my ( $class, $source ) = @_;
299 212         558 my $file = ${ $source->raw };
  212         1024  
300 212 100       638 my @switches = @{ $source->switches || [] };
  212         863  
301 212         879 my $shebang = $source->meta->{file}->{shebang};
302 212 50       891 return unless defined $shebang;
303              
304 212         965 my $taint = $class->get_taint($shebang);
305 212 100       941 push @switches, "-$taint" if defined $taint;
306              
307             # Quote the argument if we're VMS, since VMS will downcase anything
308             # not quoted.
309 212         473 if (IS_VMS) {
310             for (@switches) {
311             $_ = qq["$_"];
312             }
313             }
314              
315 212         1544 return @switches;
316             }
317              
318             =head3 C
319              
320             Gets the version of Perl currently running the test suite.
321              
322             =cut
323              
324             sub get_perl {
325 212     212 1 802 my $class = shift;
326 212 50       1057 return $ENV{HARNESS_PERL} if defined $ENV{HARNESS_PERL};
327 212 50 33     2201 return qq["$^X"] if IS_WIN32 && ( $^X =~ /[^\w\.\/\\]/ );
328 212         799 return $^X;
329             }
330              
331             1;
332              
333             __END__