1 package TAP::Parser::Iterator::Process;
9 use base 'TAP::Parser::Iterator';
11 my $IS_WIN32 = ( $^O =~ /^(MS)?Win32$/ );
15 TAP::Parser::Iterator::Process - Iterator for process-based TAP sources
23 our $VERSION = '3.42';
27 use TAP::Parser::Iterator::Process;
29 command => ['python', 'setup.py', 'test'],
32 teardown => sub { ... },
34 my $it = TAP::Parser::Iterator::Process->new(\%args);
39 This is a simple iterator wrapper for executing external processes, used by
40 L<TAP::Parser>. Unless you're writing a plugin or subclassing, you probably
41 won't need to use this module directly.
49 Create an iterator. Expects one argument containing a hashref of the form:
51 command => \@command_to_execute
52 merge => $attempt_merge_stderr_and_stdout?
53 setup => $callback_to_setup_command
54 teardown => $callback_to_teardown_command
56 Tries to uses L<IPC::Open3> & L<IO::Select> to communicate with the spawned
57 process if they are available. Falls back onto C<open()>.
59 =head2 Instance Methods
63 Iterate through the process output, of course.
67 Iterate raw input without applying any fixes for quirky input syntax.
71 Get the wait status for this iterator's process.
75 Get the exit status for this iterator's process.
81 no warnings 'uninitialized';
82 # get around a catch22 in the test suite that causes failures on Win32:
83 local $SIG{__DIE__} = undef;
84 eval { require POSIX; &POSIX::WEXITSTATUS(0) };
86 *_wait2exit = sub { $_[1] >> 8 };
89 *_wait2exit = sub { POSIX::WEXITSTATUS( $_[1] ) }
95 return unless $Config{d_fork} || $IS_WIN32;
96 for my $module (qw( IPC::Open3 IO::Select )) {
107 return $got_unicode if defined $got_unicode;
108 eval 'use Encode qw(decode_utf8);';
109 $got_unicode = $@ ? 0 : 1;
114 # new() implementation supplied by TAP::Object
117 my ( $self, $args ) = @_;
119 my @command = @{ delete $args->{command} || [] }
120 or die "Must supply a command to execute";
122 $self->{command} = [@command];
124 # Private. Used to frig with chunk size during testing.
125 my $chunk_size = delete $args->{_chunk_size} || 65536;
127 my $merge = delete $args->{merge};
128 my ( $pid, $err, $sel );
130 if ( my $setup = delete $args->{setup} ) {
134 my $out = IO::Handle->new;
136 if ( $self->_use_open3 ) {
139 my $xclose = \&IPC::Open3::xclose;
141 local *IPC::Open3::xclose = sub {
144 return if ( fileno($fh) == fileno(STDIN) );
151 $err = $merge ? '' : '>&STDERR';
154 '<&STDIN', $out, $merge ? '' : $err,
158 die "Could not execute (@command): $@" if $@;
160 binmode($out, ":crlf");
164 $err = $merge ? '' : IO::Handle->new;
165 eval { $pid = open3( '<&STDIN', $out, $err, @command ); };
166 die "Could not execute (@command): $@" if $@;
167 $sel = $merge ? undef : IO::Select->new( $out, $err );
173 = join( ' ', map { $_ =~ /\s/ ? qq{"$_"} : $_ } @command );
174 open( $out, "$command|" )
175 or die "Could not execute ($command): $!";
182 $self->{exit} = undef;
183 $self->{chunk_size} = $chunk_size;
185 if ( my $teardown = delete $args->{teardown} ) {
186 $self->{teardown} = sub {
187 $teardown->(@command);
194 =head3 C<handle_unicode>
196 Upgrade the input stream to handle UTF8.
203 if ( $self->{sel} ) {
204 if ( _get_unicode() ) {
206 # Make sure our iterator has been constructed and...
207 my $next = $self->{_next} ||= $self->_next;
209 # ...wrap it to do UTF8 casting
210 $self->{_next} = sub {
211 my $line = $next->();
212 return decode_utf8($line) if defined $line;
219 eval 'binmode($self->{out}, ":utf8")';
225 ##############################################################################
227 sub wait { shift->{wait} }
228 sub exit { shift->{exit} }
233 if ( my $out = $self->{out} ) {
234 if ( my $sel = $self->{sel} ) {
235 my $err = $self->{err};
237 my $partial = ''; # Partial line
238 my $chunk_size = $self->{chunk_size};
240 return shift @buf if @buf;
243 while ( my @ready = $sel->can_read ) {
244 for my $fh (@ready) {
245 my $got = sysread $fh, my ($chunk), $chunk_size;
250 elsif ( $fh == $err ) {
251 print STDERR $chunk; # echo STDERR
254 $chunk = $partial . $chunk;
257 # Make sure we have a complete line
258 unless ( substr( $chunk, -1, 1 ) eq "\n" ) {
259 my $nl = rindex $chunk, "\n";
265 $partial = substr( $chunk, $nl + 1 );
266 $chunk = substr( $chunk, 0, $nl );
270 push @buf, split /\n/, $chunk;
271 return shift @buf if @buf;
276 # Return partial last line
277 if ( length $partial ) {
289 if ( defined( my $line = <$out> ) ) {
308 return ( $self->{_next} ||= $self->_next )->();
316 # Avoid circular refs
317 $self->{_next} = sub {return}
320 # If we have a subprocess we need to wait for it to terminate
321 if ( defined $self->{pid} ) {
322 if ( $self->{pid} == waitpid( $self->{pid}, 0 ) ) {
327 ( delete $self->{out} )->close if $self->{out};
329 # If we have an IO::Select we also have an error handle to close.
330 if ( $self->{sel} ) {
331 ( delete $self->{err} )->close;
338 # Sometimes we get -1 on Windows. Presumably that means status not
340 $status = 0 if $IS_WIN32 && $status == -1;
342 $self->{wait} = $status;
343 $self->{exit} = $self->_wait2exit($status);
345 if ( my $teardown = $self->{teardown} ) {
352 =head3 C<get_select_handles>
354 Return a list of filehandles that may be used upstream in a select()
355 call to signal that this Iterator is ready. Iterators that are not
356 handle based should return an empty list.
360 sub get_select_handles {
362 return grep $_, ( $self->{out}, $self->{err} );
369 Originally ripped off from L<Test::Harness>.
375 L<TAP::Parser::Iterator>,