1 package TAP::Parser::Iterator::Process;
4 use vars qw($VERSION @ISA);
6 use TAP::Parser::Iterator ();
10 @ISA = 'TAP::Parser::Iterator';
12 my $IS_WIN32 = ( $^O =~ /^(MS)?Win32$/ );
16 TAP::Parser::Iterator::Process - Iterator for process-based TAP sources
28 use TAP::Parser::Iterator::Process;
30 command => ['python', 'setup.py', 'test'],
33 teardown => sub { ... },
35 my $it = TAP::Parser::Iterator::Process->new(\%args);
40 This is a simple iterator wrapper for executing external processes, used by
41 L<TAP::Parser>. Unless you're writing a plugin or subclassing, you probably
42 won't need to use this module directly.
50 Create an iterator. Expects one argument containing a hashref of the form:
52 command => \@command_to_execute
53 merge => $attempt_merge_stderr_and_stdout?
54 setup => $callback_to_setup_command
55 teardown => $callback_to_teardown_command
57 Tries to uses L<IPC::Open3> & L<IO::Select> to communicate with the spawned
58 process if they are available. Falls back onto C<open()>.
60 =head2 Instance Methods
64 Iterate through the process output, of course.
68 Iterate raw input without applying any fixes for quirky input syntax.
72 Get the wait status for this iterator's process.
76 Get the exit status for this iterator's process.
82 local $^W; # no warnings
83 # get around a catch22 in the test suite that causes failures on Win32:
84 local $SIG{__DIE__} = undef;
85 eval { require POSIX; &POSIX::WEXITSTATUS(0) };
87 *_wait2exit = sub { $_[1] >> 8 };
90 *_wait2exit = sub { POSIX::WEXITSTATUS( $_[1] ) }
96 return unless $Config{d_fork} || $IS_WIN32;
97 for my $module (qw( IPC::Open3 IO::Select )) {
108 return $got_unicode if defined $got_unicode;
109 eval 'use Encode qw(decode_utf8);';
110 $got_unicode = $@ ? 0 : 1;
115 # new() implementation supplied by TAP::Object
118 my ( $self, $args ) = @_;
120 my @command = @{ delete $args->{command} || [] }
121 or die "Must supply a command to execute";
123 $self->{command} = [@command];
125 # Private. Used to frig with chunk size during testing.
126 my $chunk_size = delete $args->{_chunk_size} || 65536;
128 my $merge = delete $args->{merge};
129 my ( $pid, $err, $sel );
131 if ( my $setup = delete $args->{setup} ) {
135 my $out = IO::Handle->new;
137 if ( $self->_use_open3 ) {
140 my $xclose = \&IPC::Open3::xclose;
141 local $^W; # no warnings
142 local *IPC::Open3::xclose = sub {
145 return if ( fileno($fh) == fileno(STDIN) );
152 $err = $merge ? '' : '>&STDERR';
155 '<&STDIN', $out, $merge ? '' : $err,
159 die "Could not execute (@command): $@" if $@;
162 # Kludge to avoid warning under 5.5
163 eval 'binmode($out, ":crlf")';
167 $err = $merge ? '' : IO::Handle->new;
168 eval { $pid = open3( '<&STDIN', $out, $err, @command ); };
169 die "Could not execute (@command): $@" if $@;
170 $sel = $merge ? undef : IO::Select->new( $out, $err );
176 = join( ' ', map { $_ =~ /\s/ ? qq{"$_"} : $_ } @command );
177 open( $out, "$command|" )
178 or die "Could not execute ($command): $!";
185 $self->{exit} = undef;
186 $self->{chunk_size} = $chunk_size;
188 if ( my $teardown = delete $args->{teardown} ) {
189 $self->{teardown} = sub {
190 $teardown->(@command);
197 =head3 C<handle_unicode>
199 Upgrade the input stream to handle UTF8.
206 if ( $self->{sel} ) {
207 if ( _get_unicode() ) {
209 # Make sure our iterator has been constructed and...
210 my $next = $self->{_next} ||= $self->_next;
212 # ...wrap it to do UTF8 casting
213 $self->{_next} = sub {
214 my $line = $next->();
215 return decode_utf8($line) if defined $line;
222 eval 'binmode($self->{out}, ":utf8")';
228 ##############################################################################
230 sub wait { shift->{wait} }
231 sub exit { shift->{exit} }
236 if ( my $out = $self->{out} ) {
237 if ( my $sel = $self->{sel} ) {
238 my $err = $self->{err};
240 my $partial = ''; # Partial line
241 my $chunk_size = $self->{chunk_size};
243 return shift @buf if @buf;
246 while ( my @ready = $sel->can_read ) {
247 for my $fh (@ready) {
248 my $got = sysread $fh, my ($chunk), $chunk_size;
253 elsif ( $fh == $err ) {
254 print STDERR $chunk; # echo STDERR
257 $chunk = $partial . $chunk;
260 # Make sure we have a complete line
261 unless ( substr( $chunk, -1, 1 ) eq "\n" ) {
262 my $nl = rindex $chunk, "\n";
268 $partial = substr( $chunk, $nl + 1 );
269 $chunk = substr( $chunk, 0, $nl );
273 push @buf, split /\n/, $chunk;
274 return shift @buf if @buf;
279 # Return partial last line
280 if ( length $partial ) {
292 if ( defined( my $line = <$out> ) ) {
311 return ( $self->{_next} ||= $self->_next )->();
319 # Avoid circular refs
320 $self->{_next} = sub {return}
323 # If we have a subprocess we need to wait for it to terminate
324 if ( defined $self->{pid} ) {
325 if ( $self->{pid} == waitpid( $self->{pid}, 0 ) ) {
330 ( delete $self->{out} )->close if $self->{out};
332 # If we have an IO::Select we also have an error handle to close.
333 if ( $self->{sel} ) {
334 ( delete $self->{err} )->close;
341 # Sometimes we get -1 on Windows. Presumably that means status not
343 $status = 0 if $IS_WIN32 && $status == -1;
345 $self->{wait} = $status;
346 $self->{exit} = $self->_wait2exit($status);
348 if ( my $teardown = $self->{teardown} ) {
355 =head3 C<get_select_handles>
357 Return a list of filehandles that may be used upstream in a select()
358 call to signal that this Iterator is ready. Iterators that are not
359 handle based should return an empty list.
363 sub get_select_handles {
365 return grep $_, ( $self->{out}, $self->{err} );
372 Originally ripped off from L<Test::Harness>.
378 L<TAP::Parser::Iterator>,