1package TAP::Parser::SourceHandler::Executable;
2
3use strict;
4use warnings;
5
6use File::Spec;
7
8use TAP::Parser::IteratorFactory   ();
9use TAP::Parser::Iterator::Process ();
10
11use base 'TAP::Parser::SourceHandler';
12
13TAP::Parser::IteratorFactory->register_handler(__PACKAGE__);
14
15=head1 NAME
16
17TAP::Parser::SourceHandler::Executable - Stream output from an executable TAP source
18
19=head1 VERSION
20
21Version 3.48
22
23=cut
24
25our $VERSION = '3.48';
26
27=head1 SYNOPSIS
28
29  use TAP::Parser::Source;
30  use TAP::Parser::SourceHandler::Executable;
31
32  my $source = TAP::Parser::Source->new->raw(['/usr/bin/ruby', 'mytest.rb']);
33  $source->assemble_meta;
34
35  my $class = 'TAP::Parser::SourceHandler::Executable';
36  my $vote  = $class->can_handle( $source );
37  my $iter  = $class->make_iterator( $source );
38
39=head1 DESCRIPTION
40
41This is an I<executable> L<TAP::Parser::SourceHandler> - it has 2 jobs:
42
431. Figure out if the L<TAP::Parser::Source> it's given is an executable
44   command (L</can_handle>).
45
462. Creates an iterator for executable commands (L</make_iterator>).
47
48Unless you're writing a plugin or subclassing L<TAP::Parser>, you
49probably won't need to use this module directly.
50
51=head1 METHODS
52
53=head2 Class Methods
54
55=head3 C<can_handle>
56
57  my $vote = $class->can_handle( $source );
58
59Only votes if $source looks like an executable file. Casts the
60following votes:
61
62  0.9  if it's a hash with an 'exec' key
63  0.8  if it's a .bat file
64  0.75 if it's got an execute bit set
65
66=cut
67
68sub can_handle {
69    my ( $class, $src ) = @_;
70    my $meta = $src->meta;
71
72    if ( $meta->{is_file} ) {
73        my $file = $meta->{file};
74
75        return 0.85 if $file->{execute} && $file->{binary};
76        return 0.8 if $file->{lc_ext} eq '.bat';
77        return 0.25 if $file->{execute};
78    }
79    elsif ( $meta->{is_hash} ) {
80        return 0.9 if $src->raw->{exec};
81    }
82
83    return 0;
84}
85
86=head3 C<make_iterator>
87
88  my $iterator = $class->make_iterator( $source );
89
90Returns a new L<TAP::Parser::Iterator::Process> for the source.
91C<$source-E<gt>raw> must be in one of the following forms:
92
93  { exec => [ @exec ] }
94
95  [ @exec ]
96
97  $file
98
99C<croak>s on error.
100
101=cut
102
103sub make_iterator {
104    my ( $class, $source ) = @_;
105    my $meta = $source->meta;
106
107    my @command;
108    if ( $meta->{is_hash} ) {
109        @command = @{ $source->raw->{exec} || [] };
110    }
111    elsif ( $meta->{is_scalar} ) {
112        @command = File::Spec->rel2abs( ${ $source->raw } )
113          if ${ $source->raw };
114    }
115    elsif ( $meta->{is_array} ) {
116        @command = @{ $source->raw };
117    }
118
119    $class->_croak('No command found in $source->raw!') unless @command;
120
121    $class->_autoflush( \*STDOUT );
122    $class->_autoflush( \*STDERR );
123
124    push @command, @{ $source->test_args || [] };
125
126    return $class->iterator_class->new(
127        {   command => \@command,
128            merge   => $source->merge
129        }
130    );
131}
132
133=head3 C<iterator_class>
134
135The class of iterator to use, override if you're sub-classing.  Defaults
136to L<TAP::Parser::Iterator::Process>.
137
138=cut
139
140use constant iterator_class => 'TAP::Parser::Iterator::Process';
141
142# Turns on autoflush for the handle passed
143sub _autoflush {
144    my ( $class, $flushed ) = @_;
145    my $old_fh = select $flushed;
146    $| = 1;
147    select $old_fh;
148}
149
1501;
151
152=head1 SUBCLASSING
153
154Please see L<TAP::Parser/SUBCLASSING> for a subclassing overview.
155
156=head2 Example
157
158  package MyRubySourceHandler;
159
160  use strict;
161
162  use Carp qw( croak );
163  use TAP::Parser::SourceHandler::Executable;
164
165  use base 'TAP::Parser::SourceHandler::Executable';
166
167  # expect $handler->(['mytest.rb', 'cmdline', 'args']);
168  sub make_iterator {
169    my ($self, $source) = @_;
170    my @test_args = @{ $source->test_args };
171    my $rb_file   = $test_args[0];
172    croak("error: Ruby file '$rb_file' not found!") unless (-f $rb_file);
173    return $self->SUPER::raw_source(['/usr/bin/ruby', @test_args]);
174  }
175
176=head1 SEE ALSO
177
178L<TAP::Object>,
179L<TAP::Parser>,
180L<TAP::Parser::IteratorFactory>,
181L<TAP::Parser::SourceHandler>,
182L<TAP::Parser::SourceHandler::Perl>,
183L<TAP::Parser::SourceHandler::File>,
184L<TAP::Parser::SourceHandler::Handle>,
185L<TAP::Parser::SourceHandler::RawTAP>
186
187=cut
188