blob: c6bc3bbbe3739c5d539476ba2c03da3e58ebb34a [file] [log] [blame]
# please insert nothing before this line: -*- mode: cperl; cperl-indent-level: 4; cperl-continued-statement-offset: 4; indent-tabs-mode: nil -*-
package TestApache::subprocess;
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestUtil;
use Apache2::Build;
use File::Spec::Functions qw(catfile catdir);
use IO::Select ();
use Apache2::Const -compile => 'OK';
use Config;
my $perl = Apache2::Build->build_config()->perl_config('perlpath');
my %scripts = (
argv => 'print STDOUT "@ARGV";',
env => 'print STDOUT $ENV{SubProcess}',
in_out => 'print STDOUT scalar <STDIN>;',
in_err => 'print STDERR scalar <STDIN>;',
);
sub APACHE_TEST_CONFIGURE {
my ($class, $self) = @_;
my $vars = $self->{vars};
my $target_dir = catdir $vars->{documentroot}, "util";
while (my ($file, $code) = each %scripts) {
$file = catfile $target_dir, "$file.pl";
$self->write_perlscript($file, "$code\n");
}
}
sub handler {
my $r = shift;
my $cfg = Apache::Test::config();
my $vars = $cfg->{vars};
plan $r, tests => 5, need qw(APR::PerlIO Apache2::SubProcess);
my $target_dir = catfile $vars->{documentroot}, "util";
{
# test: passing argv + void context
my $script = catfile $target_dir, "argv.pl";
my @argv = qw(foo bar);
$r->spawn_proc_prog($perl, [$script, @argv]);
# can't really test if something is still returned since it
# will be no longer void context
ok 1;
}
{
# test: passing argv + scalar context
my $script = catfile $target_dir, "argv.pl";
my @argv = qw(foo bar);
my $out_fh = $r->spawn_proc_prog($perl, [$script, @argv]);
my $output = read_data($out_fh);
ok t_cmp([split / /, $output],
\@argv,
"passing ARGV"
);
}
{
# test: passing env to subprocess through subprocess_env
my $script = catfile $target_dir, "env.pl";
my $value = "my cool proc";
$r->subprocess_env->set(SubProcess => $value);
my $out_fh = $r->spawn_proc_prog($perl, [$script]);
my $output = read_data($out_fh);
ok t_cmp($output,
$value,
"passing env via subprocess_env"
);
}
{
# test: subproc's stdin -> stdout + list context
my $script = catfile $target_dir, "in_out.pl";
my $value = "my cool proc\r\n"; # must have \n for <IN>
my ($in_fh, $out_fh, $err_fh) =
$r->spawn_proc_prog($perl, [$script]);
print $in_fh $value;
(my $output = read_data($out_fh)) =~ s/[\r\n]{1,2}/\r\n/;
ok t_cmp($output,
$value,
"testing subproc's stdin -> stdout + list context"
);
}
{
# test: subproc's stdin -> stderr + list context
my $script = catfile $target_dir, "in_err.pl";
my $value = "my stderr\r\n"; # must have \n for <IN>
my ($in_fh, $out_fh, $err_fh) =
$r->spawn_proc_prog($perl, [$script]);
print $in_fh $value;
(my $output = read_data($err_fh)) =~ s/[\r\n]{1,2}/\r\n/;
ok t_cmp($output,
$value,
"testing subproc's stdin -> stderr + list context"
);
}
# could test send_fd($out), send_fd($err), but currently it's only in
# compat.pm.
# these are wannabe's
# ok t_cmp(
# Apache2::SubProcess::spawn_proc_sub($r, $sub, \@args),
# Apache2::SUCCESS,
# "spawn a subprocess and run a subroutine in it"
# );
# ok t_cmp(
# Apache2::SubProcess::spawn_thread_prog($r, $command, \@argv),
# Apache2::SUCCESS,
# "spawn thread and run a program in it"
# );
# ok t_cmp(
# Apache2::SubProcess::spawn_thread_sub($r, $sub, \@args),
# Apache2::SUCCESS,
# "spawn thread and run a subroutine in it"
# );
Apache2::Const::OK;
}
sub read_data {
my ($fh) = @_;
my @data = ();
my $sel = IO::Select->new($fh);
# here is the catch:
#
# non-PerlIO pipe fh needs to select if the other end is not fast
# enough to send the data, since the read is non-blocking
#
# PerlIO-based pipe fh on the other hand does the select
# internally via apr_wait_for_io_or_timeout() in
# apr_file_read() (on *nix, but not on Win32).
# But you cannot call select() on the
# PerlIO-based, because its fileno() returns (-1), remember that
# apr_file_t is an opaque object, and on certain platforms
# fileno() is different from unix
#
# so we use the following wrapper: if we are under perlio we just
# go ahead and read the data, but with a short sleep first on Win32;
# if we are under non-perlio we first
# select for a few secs. (XXX: is 10 secs enough?)
#
# btw: we use perlIO only for perl 5.7+
#
if (APR::PerlIO::PERLIO_LAYERS_ARE_ENABLED() || $sel->can_read(10)) {
sleep(1) if $^O eq 'MSWin32' && APR::PerlIO::PERLIO_LAYERS_ARE_ENABLED();
@data = wantarray ? (<$fh>) : <$fh>;
}
if (wantarray) {
return @data;
}
else {
return defined $data[0] ? $data[0] : '';
}
}
1;