File Coverage

blib/lib/TAP/Parser/Source.pm
Criterion Covered Total %
statement 101 103 98.0
branch 62 76 81.5
condition 6 9 66.6
subroutine 17 17 100.0
pod 10 10 100.0
total 196 215 91.1


line stmt bran cond sub pod time code
1             package TAP::Parser::Source;
2              
3 39     39   166459 use strict;
  39         108  
  39         1384  
4 39     39   254 use warnings;
  39         98  
  39         1617  
5              
6 39     39   274 use File::Basename qw( fileparse );
  39         96  
  39         3723  
7 39     39   956 use base 'TAP::Object';
  39         122  
  39         4383  
8              
9 39     39   575 use constant BLK_SIZE => 512;
  39         98  
  39         60449  
10              
11             =head1 NAME
12              
13             TAP::Parser::Source - a TAP source & meta data about it
14              
15             =head1 VERSION
16              
17             Version 3.40_01
18              
19             =cut
20              
21             our $VERSION = '3.40_01';
22              
23             =head1 SYNOPSIS
24              
25             use TAP::Parser::Source;
26             my $source = TAP::Parser::Source->new;
27             $source->raw( \'reference to raw TAP source' )
28             ->config( \%config )
29             ->merge( $boolean )
30             ->switches( \@switches )
31             ->test_args( \@args )
32             ->assemble_meta;
33              
34             do { ... } if $source->meta->{is_file};
35             # see assemble_meta for a full list of data available
36              
37             =head1 DESCRIPTION
38              
39             A TAP I is something that produces a stream of TAP for the parser to
40             consume, such as an executable file, a text file, an archive, an IO handle, a
41             database, etc. Cs encapsulate these I sources, and
42             provide some useful meta data about them. They are used by
43             Ls, which do whatever is required to produce &
44             capture a stream of TAP from the I source, and package it up in a
45             L for the parser to consume.
46              
47             Unless you're writing a new L, a plugin or
48             subclassing L, you probably won't need to use this module directly.
49              
50             =head1 METHODS
51              
52             =head2 Class Methods
53              
54             =head3 C
55              
56             my $source = TAP::Parser::Source->new;
57              
58             Returns a new C object.
59              
60             =cut
61              
62             # new() implementation supplied by TAP::Object
63              
64             sub _initialize {
65 362     362   1240 my ($self) = @_;
66 362         2082 $self->meta( {} );
67 362         2465 $self->config( {} );
68 362         1511 return $self;
69             }
70              
71             ##############################################################################
72              
73             =head2 Instance Methods
74              
75             =head3 C
76              
77             my $raw = $source->raw;
78             $source->raw( $some_value );
79              
80             Chaining getter/setter for the raw TAP source. This is a reference, as it may
81             contain large amounts of data (eg: raw TAP).
82              
83             =head3 C
84              
85             my $meta = $source->meta;
86             $source->meta({ %some_value });
87              
88             Chaining getter/setter for meta data about the source. This defaults to an
89             empty hashref. See L for more info.
90              
91             =head3 C
92              
93             True if the source has meta data.
94              
95             =head3 C
96              
97             my $config = $source->config;
98             $source->config({ %some_value });
99              
100             Chaining getter/setter for the source's configuration, if any has been provided
101             by the user. How it's used is up to you. This defaults to an empty hashref.
102             See L for more info.
103              
104             =head3 C
105              
106             my $merge = $source->merge;
107             $source->config( $bool );
108              
109             Chaining getter/setter for the flag that dictates whether STDOUT and STDERR
110             should be merged (where appropriate). Defaults to undef.
111              
112             =head3 C
113              
114             my $switches = $source->switches;
115             $source->config([ @switches ]);
116              
117             Chaining getter/setter for the list of command-line switches that should be
118             passed to the source (where appropriate). Defaults to undef.
119              
120             =head3 C
121              
122             my $test_args = $source->test_args;
123             $source->config([ @test_args ]);
124              
125             Chaining getter/setter for the list of command-line arguments that should be
126             passed to the source (where appropriate). Defaults to undef.
127              
128             =cut
129              
130             sub raw {
131 2398     2398 1 10027 my $self = shift;
132 2398 100       16682 return $self->{raw} unless @_;
133 333         1014 $self->{raw} = shift;
134 333         1074 return $self;
135             }
136              
137             sub meta {
138 3424     3424 1 7755 my $self = shift;
139 3424 100       15642 return $self->{meta} unless @_;
140 375         1400 $self->{meta} = shift;
141 375         1076 return $self;
142             }
143              
144             sub has_meta {
145 329 50   329 1 809 return scalar %{ shift->meta } ? 1 : 0;
  329         974  
146             }
147              
148             sub config {
149 1320     1320 1 3979 my $self = shift;
150 1320 100       6370 return $self->{config} unless @_;
151 671         2328 $self->{config} = shift;
152 671         2429 return $self;
153             }
154              
155             sub merge {
156 521     521 1 1432 my $self = shift;
157 521 100       5065 return $self->{merge} unless @_;
158 294         1031 $self->{merge} = shift;
159 294         1306 return $self;
160             }
161              
162             sub switches {
163 508     508 1 1145 my $self = shift;
164 508 100       2736 return $self->{switches} unless @_;
165 294         967 $self->{switches} = shift;
166 294         1483 return $self;
167             }
168              
169             sub test_args {
170 522     522 1 1199 my $self = shift;
171 522 100       2432 return $self->{test_args} unless @_;
172 295         934 $self->{test_args} = shift;
173 295         950 return $self;
174             }
175              
176             =head3 C
177              
178             my $meta = $source->assemble_meta;
179              
180             Gathers meta data about the L source, stashes it in L and returns
181             it as a hashref. This is done so that the Ls don't
182             have to repeat common checks. Currently this includes:
183              
184             is_scalar => $bool,
185             is_hash => $bool,
186             is_array => $bool,
187              
188             # for scalars:
189             length => $n
190             has_newlines => $bool
191              
192             # only done if the scalar looks like a filename
193             is_file => $bool,
194             is_dir => $bool,
195             is_symlink => $bool,
196             file => {
197             # only done if the scalar looks like a filename
198             basename => $string, # including ext
199             dir => $string,
200             ext => $string,
201             lc_ext => $string,
202             # system checks
203             exists => $bool,
204             stat => [ ... ], # perldoc -f stat
205             empty => $bool,
206             size => $n,
207             text => $bool,
208             binary => $bool,
209             read => $bool,
210             write => $bool,
211             execute => $bool,
212             setuid => $bool,
213             setgid => $bool,
214             sticky => $bool,
215             is_file => $bool,
216             is_dir => $bool,
217             is_symlink => $bool,
218             # only done if the file's a symlink
219             lstat => [ ... ], # perldoc -f lstat
220             # only done if the file's a readable text file
221             shebang => $first_line,
222             }
223              
224             # for arrays:
225             size => $n,
226              
227             =cut
228              
229             sub assemble_meta {
230 329     329 1 1108 my ($self) = @_;
231              
232 329 50       1428 return $self->meta if $self->has_meta;
233              
234 329         1192 my $meta = $self->meta;
235 329         1008 my $raw = $self->raw;
236              
237             # rudimentary is object test - if it's blessed it'll
238             # inherit from UNIVERSAL
239 329 100       2353 $meta->{is_object} = UNIVERSAL::isa( $raw, 'UNIVERSAL' ) ? 1 : 0;
240              
241 329 100       1434 if ( $meta->{is_object} ) {
242 4         16 $meta->{class} = ref($raw);
243             }
244             else {
245 325         1452 my $ref = lc( ref($raw) );
246 325         1949 $meta->{"is_$ref"} = 1;
247             }
248              
249 329 100       1440 if ( $meta->{is_scalar} ) {
    100          
    100          
250 299         959 my $source = $$raw;
251 299         4252 $meta->{length} = length($$raw);
252 299 100       2118 $meta->{has_newlines} = $$raw =~ /\n/ ? 1 : 0;
253              
254             # only do file checks if it looks like a filename
255 299 100 66     2323 if ( !$meta->{has_newlines} and $meta->{length} < 1024 ) {
256 233         771 my $file = {};
257 233 100       7468 $file->{exists} = -e $source ? 1 : 0;
258 233 100       1333 if ( $file->{exists} ) {
259 229         933 $meta->{file} = $file;
260              
261             # avoid extra system calls (see `perldoc -f -X`)
262 229         1632 $file->{stat} = [ stat(_) ];
263 229 50       1216 $file->{empty} = -z _ ? 1 : 0;
264 229         926 $file->{size} = -s _;
265 229 100       12645 $file->{text} = -T _ ? 1 : 0;
266 229 100       8148 $file->{binary} = -B _ ? 1 : 0;
267 229 50       1569 $file->{read} = -r _ ? 1 : 0;
268 229 50       1318 $file->{write} = -w _ ? 1 : 0;
269 229 100       1118 $file->{execute} = -x _ ? 1 : 0;
270 229 50       1019 $file->{setuid} = -u _ ? 1 : 0;
271 229 50       3491 $file->{setgid} = -g _ ? 1 : 0;
272 229 50       931 $file->{sticky} = -k _ ? 1 : 0;
273              
274 229 100       1034 $meta->{is_file} = $file->{is_file} = -f _ ? 1 : 0;
275 229 100       1267 $meta->{is_dir} = $file->{is_dir} = -d _ ? 1 : 0;
276              
277             # symlink check requires another system call
278             $meta->{is_symlink} = $file->{is_symlink}
279 229 100       3863 = -l $source ? 1 : 0;
280 229 100       1364 if ( $file->{is_symlink} ) {
281 1         9 $file->{lstat} = [ lstat(_) ];
282             }
283              
284             # put together some common info about the file
285             ( $file->{basename}, $file->{dir}, $file->{ext} )
286 229 50       15208 = map { defined $_ ? $_ : '' }
  687         3986  
287             fileparse( $source, qr/\.[^.]*/ );
288 229         1541 $file->{lc_ext} = lc( $file->{ext} );
289 229 100       1056 $file->{basename} .= $file->{ext} if $file->{ext};
290              
291 229 50 66     2265 if ( !$file->{is_dir} && $file->{read} ) {
292 228         751 eval { $file->{shebang} = $self->shebang($$raw); };
  228         1319  
293 228 50       1364 if ( my $e = $@ ) {
294 0         0 warn $e;
295             }
296             }
297             }
298             }
299             }
300             elsif ( $meta->{is_array} ) {
301 7         33 $meta->{size} = $#$raw + 1;
302             }
303             elsif ( $meta->{is_hash} ) {
304             ; # do nothing
305             }
306              
307 329         1316 return $meta;
308             }
309              
310             =head3 C
311              
312             Get the shebang line for a script file.
313              
314             my $shebang = TAP::Parser::Source->shebang( $some_script );
315              
316             May be called as a class method
317              
318             =cut
319              
320             {
321              
322             # Global shebang cache.
323             my %shebang_for;
324              
325             sub _read_shebang {
326 117     117   397 my ( $class, $file ) = @_;
327 117 50       5380 open my $fh, '<', $file or die "Can't read $file: $!\n";
328              
329             # Might be a binary file - so read a fixed number of bytes.
330 117         1948 my $got = read $fh, my ($buf), BLK_SIZE;
331 117 50       709 defined $got or die "I/O error: $!\n";
332 117 50       2907 return $1 if $buf =~ /(.*)/;
333 0         0 return;
334             }
335              
336             sub shebang {
337 228     228 1 893 my ( $class, $file ) = @_;
338             $shebang_for{$file} = $class->_read_shebang($file)
339 228 100       1514 unless exists $shebang_for{$file};
340 228         3384 return $shebang_for{$file};
341             }
342             }
343              
344             =head3 C
345              
346             my $config = $source->config_for( $class );
347              
348             Returns L for the $class given. Class names may be fully qualified
349             or abbreviated, eg:
350              
351             # these are equivalent
352             $source->config_for( 'Perl' );
353             $source->config_for( 'TAP::Parser::SourceHandler::Perl' );
354              
355             If a fully qualified $class is given, its abbreviated version is checked first.
356              
357             =cut
358              
359             sub config_for {
360 328     328 1 1170 my ( $self, $class ) = @_;
361 328         7294 my ($abbrv_class) = ( $class =~ /(?:\:\:)?(\w+)$/ );
362 328   66     1491 my $config = $self->config->{$abbrv_class} || $self->config->{$class};
363 328         1486 return $config;
364             }
365              
366             1;
367              
368             __END__