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