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