]> Sergey Matveev's repositories - public-inbox.git/commitdiff
t/common: start_script replaces spawn_listener
authorEric Wong <e@80x24.org>
Sun, 24 Nov 2019 00:22:31 +0000 (00:22 +0000)
committerEric Wong <e@80x24.org>
Sun, 24 Nov 2019 21:46:49 +0000 (21:46 +0000)
We can shave several hundred milliseconds off tests which spawn
daemons by preloading and avoiding startup time for common
modules which are already loaded in the parent process.

This also gives ENV{TAIL} support to all tests which support
daemons which log to stdout/stderr.

13 files changed:
t/common.perl
t/git-http-backend.t
t/httpd-corner.t
t/httpd-https.t
t/httpd-unix.t
t/httpd.t
t/nntpd-tls.t
t/nntpd-validate.t
t/nntpd.t
t/perf-nntpd.t
t/v2mirror.t
t/v2writable.t
t/www_listing.t

index c56930804fa8c362a214dbf96a931428c680ccb0..2126a76106e94e4c5abebf9ceca1096c949e9184 100644 (file)
@@ -30,30 +30,6 @@ sub tcp_connect {
        $s;
 }
 
-sub spawn_listener {
-       my ($env, $cmd, $socks) = @_;
-       my $pid = fork;
-       defined $pid or die "fork failed: $!\n";
-       if ($pid == 0) {
-               # pretend to be systemd (cf. sd_listen_fds(3))
-               my $fd = 3; # 3 == SD_LISTEN_FDS_START
-               foreach my $s (@$socks) {
-                       my $fl = fcntl($s, F_GETFD, 0);
-                       if (($fl & FD_CLOEXEC) != FD_CLOEXEC) {
-                               warn "got FD:".fileno($s)." w/o CLOEXEC\n";
-                       }
-                       fcntl($s, F_SETFD, $fl &= ~FD_CLOEXEC);
-                       dup2(fileno($s), $fd++) or die "dup2 failed: $!\n";
-               }
-               $ENV{LISTEN_PID} = $$;
-               $ENV{LISTEN_FDS} = scalar @$socks;
-               %ENV = (%ENV, %$env) if $env;
-               exec @$cmd;
-               die "FAIL: ",join(' ', @$cmd), ": $!\n";
-       }
-       $pid;
-}
-
 sub require_git ($;$) {
        my ($req, $maybe) = @_;
        my ($req_maj, $req_min) = split(/\./, $req);
@@ -68,7 +44,6 @@ sub require_git ($;$) {
        1;
 }
 
-my %cached_scripts;
 sub key2script ($) {
        my ($key) = @_;
        return $key if $key =~ m!\A/!;
@@ -105,11 +80,10 @@ sub run_script_exit (;$) {
        die RUN_SCRIPT_EXIT;
 }
 
-sub run_script ($;$$) {
-       my ($cmd, $env, $opt) = @_;
-       my ($key, @argv) = @$cmd;
-       my $run_mode = $ENV{TEST_RUN_MODE} // $opt->{run_mode} // 1;
-       my $sub = $run_mode == 0 ? undef : ($cached_scripts{$key} //= do {
+my %cached_scripts;
+sub key2sub ($) {
+       my ($key) = @_;
+       $cached_scripts{$key} //= do {
                my $f = key2script($key);
                open my $fh, '<', $f or die "open $f: $!";
                my $str = do { local $/; <$fh> };
@@ -129,8 +103,34 @@ $str
 1;
 EOF
                $pkg->can('main');
-       }); # do
+       }
+}
 
+sub _run_sub ($$$) {
+       my ($sub, $key, $argv) = @_;
+       local @ARGV = @$argv;
+       $run_script_exit_code = undef;
+       my $exit_code = eval { $sub->(@$argv) };
+       if ($@ eq RUN_SCRIPT_EXIT) {
+               $@ = '';
+               $exit_code = $run_script_exit_code;
+               $? = ($exit_code << 8);
+       } elsif (defined($exit_code)) {
+               $? = ($exit_code << 8);
+       } elsif ($@) { # mimic die() behavior when uncaught
+               warn "E: eval-ed $key: $@\n";
+               $? = ($! << 8) if $!;
+               $? = (255 << 8) if $? == 0;
+       } else {
+               die "BUG: eval-ed $key: no exit code or \$@\n";
+       }
+}
+
+sub run_script ($;$$) {
+       my ($cmd, $env, $opt) = @_;
+       my ($key, @argv) = @$cmd;
+       my $run_mode = $ENV{TEST_RUN_MODE} // $opt->{run_mode} // 1;
+       my $sub = $run_mode == 0 ? undef : key2sub($key);
        my $fhref = [];
        my $spawn_opt = {};
        for my $fd (0..2) {
@@ -162,22 +162,7 @@ EOF
                local %ENV = $env ? (%ENV, %$env) : %ENV;
                local %SIG = %SIG;
                _prepare_redirects($fhref);
-               local @ARGV = @argv;
-               $run_script_exit_code = undef;
-               my $exit_code = eval { $sub->(@argv) };
-               if ($@ eq RUN_SCRIPT_EXIT) {
-                       $@ = '';
-                       $exit_code = $run_script_exit_code;
-                       $? = ($exit_code << 8);
-               } elsif (defined($exit_code)) {
-                       $? = ($exit_code << 8);
-               } elsif ($@) { # mimic die() behavior when uncaught
-                       warn "E: eval-ed $key: $@\n";
-                       $? = ($! << 8) if $!;
-                       $? = (255 << 8) if $? == 0;
-               } else {
-                       die "BUG: eval-ed $key: no exit code or \$@\n";
-               }
+               _run_sub($sub, $key, \@argv);
        }
 
        # slurp the redirects back into user-supplied strings
@@ -191,4 +176,99 @@ EOF
        $? == 0;
 }
 
+sub wait_for_tail () { sleep(2) }
+
+sub start_script {
+       my ($cmd, $env, $opt) = @_;
+       my ($key, @argv) = @$cmd;
+       my $run_mode = $ENV{TEST_RUN_MODE} // $opt->{run_mode} // 1;
+       my $sub = $run_mode == 0 ? undef : key2sub($key);
+       my $tail_pid;
+       if (my $tail_cmd = $ENV{TAIL}) {
+               my @paths;
+               for (@argv) {
+                       next unless /\A--std(?:err|out)=(.+)\z/;
+                       push @paths, $1;
+               }
+               if (@paths) {
+                       defined($tail_pid = fork) or die "fork: $!\n";
+                       if ($tail_pid == 0) {
+                               # make sure files exist, first
+                               open my $fh, '>>', $_ for @paths;
+                               open(STDOUT, '>&STDERR') or die "1>&2: $!";
+                               exec(split(' ', $tail_cmd), @paths);
+                               die "$tail_cmd failed: $!";
+                       }
+                       wait_for_tail();
+               }
+       }
+       defined(my $pid = fork) or die "fork: $!\n";
+       if ($pid == 0) {
+               # pretend to be systemd (cf. sd_listen_fds(3))
+               # 3 == SD_LISTEN_FDS_START
+               my $fd;
+               for ($fd = 0; 1; $fd++) {
+                       my $s = $opt->{$fd};
+                       last if $fd >= 3 && !defined($s);
+                       next unless $s;
+                       my $fl = fcntl($s, F_GETFD, 0);
+                       if (($fl & FD_CLOEXEC) != FD_CLOEXEC) {
+                               warn "got FD:".fileno($s)." w/o CLOEXEC\n";
+                       }
+                       fcntl($s, F_SETFD, $fl &= ~FD_CLOEXEC);
+                       dup2(fileno($s), $fd) or die "dup2 failed: $!\n";
+               }
+               %ENV = (%ENV, %$env) if $env;
+               my $fds = $fd - 3;
+               if ($fds > 0) {
+                       $ENV{LISTEN_PID} = $$;
+                       $ENV{LISTEN_FDS} = $fds;
+               }
+               $0 = join(' ', @$cmd);
+               if ($sub) {
+                       _run_sub($sub, $key, \@argv);
+                       POSIX::_exit($? >> 8);
+               } else {
+                       exec(key2script($key), @argv);
+                       die "FAIL: ",join(' ', $key, @argv), ": $!\n";
+               }
+       }
+       TestProcess->new($pid, $tail_pid);
+}
+
+package TestProcess;
+use strict;
+
+# prevent new threads from inheriting these objects
+sub CLONE_SKIP { 1 }
+
+sub new {
+       my ($klass, $pid, $tail_pid) = @_;
+       bless { pid => $pid, tail_pid => $tail_pid, owner => $$ }, $klass;
+}
+
+sub kill {
+       my ($self, $sig) = @_;
+       CORE::kill($sig // 'TERM', $self->{pid});
+}
+
+sub join {
+       my ($self) = @_;
+       my $pid = delete $self->{pid} or return;
+       my $ret = waitpid($pid, 0);
+       defined($ret) or die "waitpid($pid): $!";
+       $ret == $pid or die "waitpid($pid) != $ret";
+}
+
+sub DESTROY {
+       my ($self) = @_;
+       return if $self->{owner} != $$;
+       if (my $tail = delete $self->{tail_pid}) {
+               ::wait_for_tail();
+               CORE::kill('TERM', $tail);
+       }
+       my $pid = delete $self->{pid} or return;
+       CORE::kill('TERM', $pid);
+}
+
 1;
index c2a0465335c5b7cf94ffa782553995703b7dd50d..c4dc09a1009a77519f3db5e92d630ded310e2d13 100644 (file)
@@ -22,12 +22,10 @@ my $psgi = "./t/git-http-backend.psgi";
 my $tmpdir = tempdir('pi-git-http-backend-XXXXXX', TMPDIR => 1, CLEANUP => 1);
 my $err = "$tmpdir/stderr.log";
 my $out = "$tmpdir/stdout.log";
-my $httpd = 'blib/script/public-inbox-httpd';
 my $sock = tcp_server();
 my $host = $sock->sockhost;
 my $port = $sock->sockport;
-my $pid;
-END { kill 'TERM', $pid if defined $pid };
+my $td;
 
 my $get_maxrss = sub {
         my $http = Net::HTTP->new(Host => "$host:$port");
@@ -44,9 +42,8 @@ my $get_maxrss = sub {
 
 {
        ok($sock, 'sock created');
-       my $cmd = [ $httpd, '-W0', "--stdout=$out", "--stderr=$err", $psgi ];
-       ok(defined($pid = spawn_listener(undef, $cmd, [$sock])),
-          'forked httpd process successfully');
+       my $cmd = [ '-httpd', '-W0', "--stdout=$out", "--stderr=$err", $psgi ];
+       $td = start_script($cmd, undef, { 3 => $sock });
 }
 my $mem_a = $get_maxrss->();
 
@@ -113,9 +110,8 @@ SKIP: {
 }
 
 {
-       ok(kill('TERM', $pid), 'killed httpd');
-       $pid = undef;
-       waitpid(-1, 0);
+       ok($td->kill, 'killed httpd');
+       $td->join;
 }
 
 done_testing();
index cc36c7e10c87fc90d4477d2c59b1f3340caeb4ad..eca77d7fc6dd0f9e600f736d1ef6e534400f67a5 100644 (file)
@@ -26,7 +26,6 @@ my $fifo = "$tmpdir/fifo";
 ok(defined mkfifo($fifo, 0777), 'created FIFO');
 my $err = "$tmpdir/stderr.log";
 my $out = "$tmpdir/stdout.log";
-my $httpd = 'blib/script/public-inbox-httpd';
 my $psgi = "./t/httpd-corner.psgi";
 my $sock = tcp_server() or die;
 
@@ -64,13 +63,11 @@ sub unix_server ($) {
 my $upath = "$tmpdir/s";
 my $unix = unix_server($upath);
 ok($unix, 'UNIX socket created');
-my $pid;
-END { kill 'TERM', $pid if defined $pid };
+my $td;
 my $spawn_httpd = sub {
        my (@args) = @_;
-       my $cmd = [ $httpd, @args, "--stdout=$out", "--stderr=$err", $psgi ];
-       $pid = spawn_listener(undef, $cmd, [ $sock, $unix ]);
-       ok(defined $pid, 'forked httpd process successfully');
+       my $cmd = [ '-httpd', @args, "--stdout=$out", "--stderr=$err", $psgi ];
+       $td = start_script($cmd, undef, { 3 => $sock, 4 => $unix });
 };
 
 $spawn_httpd->();
@@ -213,16 +210,14 @@ sub conn_for {
        open my $f, '>', $fifo or die "open $fifo: $!\n";
        $f->autoflush(1);
        ok(print($f "hello\n"), 'wrote something to fifo');
-       my $kpid = $pid;
-       $pid = undef;
-       is(kill('TERM', $kpid), 1, 'started graceful shutdown');
+       is($td->kill, 1, 'started graceful shutdown');
        ok(print($f "world\n"), 'wrote else to fifo');
        close $f or die "close fifo: $!\n";
        $conn->read(my $buf, 8192);
        my ($head, $body) = split(/\r\n\r\n/, $buf, 2);
        like($head, qr!\AHTTP/1\.[01] 200 OK!, 'got 200 for slow-header');
        is($body, "hello\nworld\n", 'read expected body');
-       is(waitpid($kpid, 0), $kpid, 'reaped httpd');
+       $td->join;
        is($?, 0, 'no error');
        $spawn_httpd->('-W0');
 }
@@ -244,15 +239,13 @@ sub conn_for {
                $conn->sysread($buf, 8192);
                is($buf, $c, 'got trickle for reading');
        }
-       my $kpid = $pid;
-       $pid = undef;
-       is(kill('TERM', $kpid), 1, 'started graceful shutdown');
+       is($td->kill, 1, 'started graceful shutdown');
        ok(print($f "world\n"), 'wrote else to fifo');
        close $f or die "close fifo: $!\n";
        $conn->sysread($buf, 8192);
        is($buf, "world\n", 'read expected body');
        is($conn->sysread($buf, 8192), 0, 'got EOF from server');
-       is(waitpid($kpid, 0), $kpid, 'reaped httpd');
+       $td->join;
        is($?, 0, 'no error');
        $spawn_httpd->('-W0');
 }
@@ -346,9 +339,7 @@ SKIP: {
        $conn->write("Content-Length: $len\r\n");
        delay();
        $conn->write("\r\n");
-       my $kpid = $pid;
-       $pid = undef;
-       is(kill('TERM', $kpid), 1, 'started graceful shutdown');
+       is($td->kill, 1, 'started graceful shutdown');
        delay();
        my $n = 0;
        foreach my $c ('a'..'z') {
@@ -356,7 +347,7 @@ SKIP: {
        }
        is($n, $len, 'wrote alphabet');
        $check_self->($conn);
-       is(waitpid($kpid, 0), $kpid, 'reaped httpd');
+       $td->join;
        is($?, 0, 'no error');
        $spawn_httpd->('-W0');
 }
@@ -553,12 +544,29 @@ SKIP: {
        defined(my $x = getsockopt($sock, SOL_SOCKET, $var)) or die;
        is($x, $accf_arg, 'SO_ACCEPTFILTER unchanged if previously set');
 };
+
 SKIP: {
        skip 'only testing lsof(8) output on Linux', 1 if $^O ne 'linux';
        skip 'no lsof in PATH', 1 unless which('lsof');
-       my @lsof = `lsof -p $pid`;
+       my @lsof = `lsof -p $td->{pid}`;
        is_deeply([grep(/\bdeleted\b/, @lsof)], [], 'no lingering deleted inputs');
-       is_deeply([grep(/\bpipe\b/, @lsof)], [], 'no extra pipes with -W0');
+
+       # filter out pipes inherited from the parent
+       my @this = `lsof -p $$`;
+       my $bad;
+       sub extract_inodes {
+               map {;
+                       my @f = split(' ', $_);
+                       my $inode = $f[-2];
+                       $bad = $_ if $inode !~ /\A[0-9]+\z/;
+                       $inode => 1;
+               } grep (/\bpipe\b/, @_);
+       }
+       my %child = extract_inodes(@lsof);
+       my %parent = extract_inodes(@this);
+       skip("inode not in expected format: $bad", 1) if defined($bad);
+       delete @child{(keys %parent)};
+       is_deeply([], [keys %child], 'no extra pipes with -W0');
 };
 
 done_testing();
index 22c62bf412034efce62cacb6a6e269ebb24be5c3..81a111088bbcc3ca34eb77a16f443053ec793e82 100644 (file)
@@ -23,14 +23,8 @@ my $psgi = "./t/httpd-corner.psgi";
 my $tmpdir = tempdir('pi-httpd-https-XXXXXX', TMPDIR => 1, CLEANUP => 1);
 my $err = "$tmpdir/stderr.log";
 my $out = "$tmpdir/stdout.log";
-my $httpd = 'blib/script/public-inbox-httpd';
 my $https = tcp_server();
-my ($pid, $tail_pid);
-END {
-       foreach ($pid, $tail_pid) {
-               kill 'TERM', $_ if defined $_;
-       }
-};
+my $td;
 my $https_addr = $https->sockhost . ':' . $https->sockport;
 
 for my $args (
@@ -39,15 +33,9 @@ for my $args (
        for ($out, $err) {
                open my $fh, '>', $_ or die "truncate: $!";
        }
-       if (my $tail_cmd = $ENV{TAIL}) { # don't assume GNU tail
-               $tail_pid = fork;
-               if (defined $tail_pid && $tail_pid == 0) {
-                       exec(split(' ', $tail_cmd), $out, $err);
-               }
-       }
-       my $cmd = [ $httpd, '-W0', @$args,
+       my $cmd = [ '-httpd', '-W0', @$args,
                        "--stdout=$out", "--stderr=$err", $psgi ];
-       $pid = spawn_listener(undef, $cmd, [ $https ]);
+       $td = start_script($cmd, undef, { 3 => $https });
        my %o = (
                SSL_hostname => 'server.local',
                SSL_verifycn_name => 'server.local',
@@ -119,15 +107,9 @@ for my $args (
        };
 
        $c = undef;
-       kill('TERM', $pid);
-       is($pid, waitpid($pid, 0), 'httpd exited successfully');
+       $td->kill;
+       $td->join;
        is($?, 0, 'no error in exited process');
-       $pid = undef;
-       if (defined $tail_pid) {
-               kill 'TERM', $tail_pid;
-               waitpid($tail_pid, 0);
-               $tail_pid = undef;
-       }
 }
 done_testing();
 1;
index 2c91828154cd880b12177e5a566240df2724634b..5ec70fd8f1836f74e7ccd7c239115e6db81eb74a 100644 (file)
@@ -4,6 +4,8 @@
 use strict;
 use warnings;
 use Test::More;
+require './t/common.perl';
+use Errno qw(EADDRINUSE);
 
 foreach my $mod (qw(Plack::Util Plack::Builder HTTP::Date HTTP::Status)) {
        eval "require $mod";
@@ -14,23 +16,16 @@ use File::Temp qw/tempdir/;
 use IO::Socket::UNIX;
 my $tmpdir = tempdir('httpd-unix-XXXXXX', TMPDIR => 1, CLEANUP => 1);
 my $unix = "$tmpdir/unix.sock";
-my $httpd = 'blib/script/public-inbox-httpd';
 my $psgi = './t/httpd-corner.psgi';
 my $out = "$tmpdir/out.log";
 my $err = "$tmpdir/err.log";
-
-my $pid;
-END { kill 'TERM', $pid if defined $pid };
+my $td;
 
 my $spawn_httpd = sub {
        my (@args) = @_;
        push @args, '-W0';
-       $pid = fork;
-       if ($pid == 0) {
-               exec $httpd, @args, "--stdout=$out", "--stderr=$err", $psgi;
-               die "FAIL: $!\n";
-       }
-       ok(defined $pid, 'forked httpd process successfully');
+       my $cmd = [ '-httpd', @args, "--stdout=$out", "--stderr=$err", $psgi ];
+       $td = start_script($cmd);
 };
 
 {
@@ -65,15 +60,18 @@ sub check_sock ($) {
 check_sock($unix);
 
 { # do not clobber existing socket
-       my $fpid = fork;
-       if ($fpid == 0) {
-               open STDOUT, '>>', "$tmpdir/1" or die "redirect failed: $!";
-               open STDERR, '>>', "$tmpdir/2" or die "redirect failed: $!";
-               exec $httpd, '-l', $unix, '-W0', $psgi;
-               die "FAIL: $!\n";
-       }
-       is($fpid, waitpid($fpid, 0), 'second httpd exits');
-       isnt($?, 0, 'httpd failed with failure to bind');
+       my %err = ( 'linux' => EADDRINUSE );
+       open my $out, '>>', "$tmpdir/1" or die "redirect failed: $!";
+       open my $err, '>>', "$tmpdir/2" or die "redirect failed: $!";
+       my $cmd = ['-httpd', '-l', $unix, '-W0', $psgi];
+       my $ftd = start_script($cmd, undef, { 1 => $out, 2 => $err });
+       $ftd->join;
+       isnt($?, 0, 'httpd failure set $?');
+       SKIP: {
+               my $ec = $err{$^O} or
+                       skip("not sure if $^O fails with EADDRINUSE", 1);
+               is($? >> 8, $ec, 'httpd failed with EADDRINUSE');
+       };
        open my $fh, "$tmpdir/2" or die "failed to open $tmpdir/2: $!";
        local $/;
        my $e = <$fh>;
@@ -82,10 +80,8 @@ check_sock($unix);
 }
 
 {
-       my $kpid = $pid;
-       $pid = undef;
-       is(kill('TERM', $kpid), 1, 'terminate existing process');
-       is(waitpid($kpid, 0), $kpid, 'existing httpd terminated');
+       is($td->kill, 1, 'terminate existing process');
+       $td->join;
        is($?, 0, 'existing httpd exited successfully');
        ok(-S $unix, 'unix socket still exists');
 }
@@ -96,9 +92,8 @@ SKIP: {
 
        # wait for daemonization
        $spawn_httpd->("-l$unix", '-D', '-P', "$tmpdir/pid");
-       my $kpid = $pid;
-       $pid = undef;
-       is(waitpid($kpid, 0), $kpid, 'existing httpd terminated');
+       $td->join;
+       is($?, 0, 'daemonized process OK');
        check_sock($unix);
 
        ok(-f "$tmpdir/pid", 'pid file written');
index e7527ed64f2ae6815ccc87e2aec8bae5fb219621..ce8063b28672863fa97fa73f52b9f8b1d3f3d75e 100644 (file)
--- a/t/httpd.t
+++ b/t/httpd.t
@@ -21,13 +21,11 @@ my $maindir = "$tmpdir/main.git";
 my $group = 'test-httpd';
 my $addr = $group . '@example.com';
 my $cfgpfx = "publicinbox.$group";
-my $httpd = 'blib/script/public-inbox-httpd';
 my $sock = tcp_server();
-my $pid;
+my $td;
 use_ok 'PublicInbox::Git';
 use_ok 'PublicInbox::Import';
 use_ok 'Email::MIME';
-END { kill 'TERM', $pid if defined $pid };
 {
        local $ENV{HOME} = $home;
        my $cmd = [ '-init', $group, $maindir, 'http://example.com/', $addr ];
@@ -52,8 +50,8 @@ EOF
                $im->done($mime);
        }
        ok($sock, 'sock created');
-       $cmd = [ $httpd, '-W0', "--stdout=$out", "--stderr=$err" ];
-       $pid = spawn_listener(undef, $cmd, [$sock]);
+       $cmd = [ '-httpd', '-W0', "--stdout=$out", "--stderr=$err" ];
+       $td = start_script($cmd, undef, { 3 => $sock });
        my $host = $sock->sockhost;
        my $port = $sock->sockport;
        my $conn = tcp_connect($sock);
@@ -78,9 +76,8 @@ EOF
                        "http://$host:$port/$group", "$tmpdir/dumb.git"),
                0, 'clone successful');
 
-       ok(kill('TERM', $pid), 'killed httpd');
-       $pid = undef;
-       waitpid(-1, 0);
+       ok($td->kill, 'killed httpd');
+       $td->join;
 
        is(system('git', "--git-dir=$tmpdir/clone.git",
                  qw(fsck --no-verbose)), 0,
index 4e71e82d7f57bc34a372eb8dbc6722ce04f6b354..5d170b785fcf0938b1eb6ed0c3a2e0455ef95f60 100644 (file)
@@ -41,16 +41,8 @@ my $inboxdir = "$tmpdir";
 my $pi_config = "$tmpdir/pi_config";
 my $group = 'test-nntpd-tls';
 my $addr = $group . '@example.com';
-my $nntpd = 'blib/script/public-inbox-nntpd';
 my $starttls = tcp_server();
 my $nntps = tcp_server();
-my ($pid, $tail_pid);
-END {
-       foreach ($pid, $tail_pid) {
-               kill 'TERM', $_ if defined $_;
-       }
-};
-
 my $ibx = PublicInbox::Inbox->new({
        inboxdir => $inboxdir,
        name => 'nntpd-tls',
@@ -91,6 +83,7 @@ EOF
 my $nntps_addr = $nntps->sockhost . ':' . $nntps->sockport;
 my $starttls_addr = $starttls->sockhost . ':' . $starttls->sockport;
 my $env = { PI_CONFIG => $pi_config };
+my $td;
 
 for my $args (
        [ "--cert=$cert", "--key=$key",
@@ -100,14 +93,8 @@ for my $args (
        for ($out, $err) {
                open my $fh, '>', $_ or die "truncate: $!";
        }
-       if (my $tail_cmd = $ENV{TAIL}) { # don't assume GNU tail
-               $tail_pid = fork;
-               if (defined $tail_pid && $tail_pid == 0) {
-                       exec(split(' ', $tail_cmd), $out, $err);
-               }
-       }
-       my $cmd = [ $nntpd, '-W0', @$args, "--stdout=$out", "--stderr=$err" ];
-       $pid = spawn_listener($env, $cmd, [ $starttls, $nntps ]);
+       my $cmd = [ '-nntpd', '-W0', @$args, "--stdout=$out", "--stderr=$err" ];
+       $td = start_script($cmd, $env, { 3 => $starttls, 4 => $nntps });
        my %o = (
                SSL_hostname => 'server.local',
                SSL_verifycn_name => 'server.local',
@@ -211,21 +198,15 @@ for my $args (
        };
 
        $c = undef;
-       kill('TERM', $pid);
-       is($pid, waitpid($pid, 0), 'nntpd exited successfully');
+       $td->kill;
+       $td->join;
        is($?, 0, 'no error in exited process');
-       $pid = undef;
        my $eout = eval {
                open my $fh, '<', $err or die "open $err failed: $!";
                local $/;
                <$fh>;
        };
        unlike($eout, qr/wide/i, 'no Wide character warnings');
-       if (defined $tail_pid) {
-               kill 'TERM', $tail_pid;
-               waitpid($tail_pid, 0);
-               $tail_pid = undef;
-       }
 }
 done_testing();
 
index de0243946497a88976b7843476c62d28ee6d4644..e3c10d9c3338f43f78851a2e911173e06d0bb59b 100644 (file)
@@ -10,9 +10,15 @@ use Symbol qw(gensym);
 use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
 my $inbox_dir = $ENV{GIANT_INBOX_DIR};
 plan skip_all => "GIANT_INBOX_DIR not defined for $0" unless $inbox_dir;
+if (my $m = $ENV{TEST_RUN_MODE}) {
+       plan skip_all => "threads conflict w/ TEST_RUN_MODE=$m";
+}
 my $mid = $ENV{TEST_MID};
 
 # This test is also an excuse for me to experiment with Perl threads :P
+# TODO: get rid of threads, I was reading an old threads(3perl) manpage
+# and missed the WARNING in the newer ones about it being "discouraged"
+# in perlpolicy(1).
 unless (eval 'use threads; 1') {
        plan skip_all => "$0 requires a threaded perl" if $@;
 }
@@ -37,13 +43,8 @@ if ($test_tls && !-r $key || !-r $cert) {
 require './t/common.perl';
 my $keep_tmp = !!$ENV{TEST_KEEP_TMP};
 my $tmpdir = tempdir('nntpd-validate-XXXXXX',TMPDIR => 1,CLEANUP => $keep_tmp);
-my (%OPT, $pid, $tail_pid, $host_port, $group);
+my (%OPT, $td, $host_port, $group);
 my $batch = 1000;
-END {
-       foreach ($pid, $tail_pid) {
-               kill 'TERM', $_ if defined $_;
-       }
-};
 if (($ENV{NNTP_TEST_URL} // '') =~ m!\Anntp://([^/]+)/([^/]+)\z!) {
        ($host_port, $group) = ($1, $2);
        $host_port .= ":119" unless index($host_port, ':') > 0;
@@ -149,7 +150,6 @@ sub make_local_server {
        $group = 'inbox.test.perf.nntpd';
        my $ibx = { inboxdir => $inbox_dir, newsgroup => $group };
        $ibx = PublicInbox::Inbox->new($ibx);
-       my $nntpd = 'blib/script/public-inbox-nntpd';
        my $pi_config = "$tmpdir/config";
        {
                open my $fh, '>', $pi_config or die "open($pi_config): $!";
@@ -165,20 +165,13 @@ sub make_local_server {
        for ($out, $err) {
                open my $fh, '>', $_ or die "truncate: $!";
        }
-       if (my $tail_cmd = $ENV{TAIL}) { # don't assume GNU tail
-               $tail_pid = fork;
-               if (defined $tail_pid && $tail_pid == 0) {
-                       open STDOUT, '>&STDERR' or die ">&2 failed: $!";
-                       exec(split(' ', $tail_cmd), $out, $err);
-               }
-       }
        my $sock = tcp_server();
        ok($sock, 'sock created');
        $host_port = $sock->sockhost . ':' . $sock->sockport;
 
        # not using multiple workers, here, since we want to increase
        # the chance of tripping concurrency bugs within PublicInbox/NNTP*.pm
-       my $cmd = [ $nntpd, "--stdout=$out", "--stderr=$err", '-W0' ];
+       my $cmd = [ '-nntpd', "--stdout=$out", "--stderr=$err", '-W0' ];
        push @$cmd, "-lnntp://$host_port";
        if ($test_tls) {
                push @$cmd, "--cert=$cert", "--key=$key";
@@ -190,7 +183,9 @@ sub make_local_server {
                );
        }
        print STDERR "# CMD ". join(' ', @$cmd). "\n";
-       $pid = spawn_listener({ PI_CONFIG => $pi_config }, $cmd, [$sock]);
+       my $env = { PI_CONFIG => $pi_config };
+       # perl threads and run_mode != 0 don't get along
+       $td = start_script($cmd, $env, { run_mode => 0, 3 => $sock });
 }
 
 package DigestPipe;
index 4795dc008d58feb841215b5ca2dd9d8ffbc5f4a8..3c928610de242d1455c2a06091fe8dcb9625dae8 100644 (file)
--- a/t/nntpd.t
+++ b/t/nntpd.t
@@ -29,7 +29,6 @@ my $out = "$tmpdir/stdout.log";
 my $inboxdir = "$tmpdir/main.git";
 my $group = 'test-nntpd';
 my $addr = $group . '@example.com';
-my $nntpd = 'blib/script/public-inbox-nntpd';
 SKIP: {
        skip "git 2.6+ required for V2Writable", 1 if $version == 1;
        use_ok 'PublicInbox::V2Writable';
@@ -37,9 +36,8 @@ SKIP: {
 
 my %opts;
 my $sock = tcp_server();
-my $pid;
+my $td;
 my $len;
-END { kill 'TERM', $pid if defined $pid };
 
 my $ibx = {
        inboxdir => $inboxdir,
@@ -90,9 +88,8 @@ EOF
        }
 
        ok($sock, 'sock created');
-       my $cmd = [ $nntpd, '-W0', "--stdout=$out", "--stderr=$err" ];
-       $pid = spawn_listener(undef, $cmd, [ $sock ]);
-       ok(defined $pid, 'forked nntpd process successfully');
+       my $cmd = [ '-nntpd', '-W0', "--stdout=$out", "--stderr=$err" ];
+       $td = start_script($cmd, undef, { 3 => $sock });
        my $host_port = $sock->sockhost . ':' . $sock->sockport;
        my $n = Net::NNTP->new($host_port);
        my $list = $n->list;
@@ -306,7 +303,7 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000
                is($? >> 8, 0, 'no errors');
        }
        SKIP: {
-               my @of = `lsof -p $pid 2>/dev/null`;
+               my @of = `lsof -p $td->{pid} 2>/dev/null`;
                skip('lsof broken', 1) if (!scalar(@of) || $?);
                my @xap = grep m!Search/Xapian!, @of;
                is_deeply(\@xap, [], 'Xapian not loaded in nntpd');
@@ -315,7 +312,7 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000
                setsockopt($s, IPPROTO_TCP, TCP_NODELAY, 1);
                syswrite($s, 'HDR List-id 1-');
                select(undef, undef, undef, 0.15);
-               ok(kill('TERM', $pid), 'killed nntpd');
+               ok($td->kill, 'killed nntpd');
                select(undef, undef, undef, 0.15);
                syswrite($s, "\r\n");
                $buf = '';
@@ -329,7 +326,7 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000
        }
 
        $n = $s = undef;
-       is($pid, waitpid($pid, 0), 'nntpd exited successfully');
+       $td->join;
        my $eout = eval {
                local $/;
                open my $fh, '<', $err or die "open $err failed: $!";
@@ -339,6 +336,7 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000
        unlike($eout, qr/wide/i, 'no Wide character warnings');
 }
 
+$td = undef;
 done_testing();
 
 sub read_til_dot {
index 7abf2249ef32386ad7677fff22272b2e417d216e..c7d2eaffe9b5b1a787fe38c314fd569c1f69dd15 100644 (file)
@@ -10,18 +10,9 @@ use Net::NNTP;
 my $pi_dir = $ENV{GIANT_PI_DIR};
 plan skip_all => "GIANT_PI_DIR not defined for $0" unless $pi_dir;
 eval { require PublicInbox::Search };
-my ($host_port, $group, %opts, $s, $pid);
+my ($host_port, $group, %opts, $s, $td);
 require './t/common.perl';
 
-END {
-       if ($s) {
-               $s->print("QUIT\r\n");
-               $s->getline;
-               $s = undef;
-       }
-       kill 'TERM', $pid if defined $pid;
-};
-
 if (($ENV{NNTP_TEST_URL} || '') =~ m!\Anntp://([^/]+)/([^/]+)\z!) {
        ($host_port, $group) = ($1, $2);
        $host_port .= ":119" unless index($host_port, ':') > 0;
@@ -29,7 +20,6 @@ if (($ENV{NNTP_TEST_URL} || '') =~ m!\Anntp://([^/]+)/([^/]+)\z!) {
        $group = 'inbox.test.perf.nntpd';
        my $ibx = { inboxdir => $pi_dir, newsgroup => $group };
        $ibx = PublicInbox::Inbox->new($ibx);
-       my $nntpd = 'blib/script/public-inbox-nntpd';
        my $tmpdir = tempdir('perf-nntpd-XXXXXX', TMPDIR => 1, CLEANUP => 1);
 
        my $pi_config = "$tmpdir/config";
@@ -46,8 +36,8 @@ if (($ENV{NNTP_TEST_URL} || '') =~ m!\Anntp://([^/]+)/([^/]+)\z!) {
 
        my $sock = tcp_server();
        ok($sock, 'sock created');
-       my $cmd = [ $nntpd, '-W0' ];
-       $pid = spawn_listener({ PI_CONFIG => $pi_config }, $cmd, [$sock]);
+       my $cmd = [ '-nntpd', '-W0' ];
+       $td = start_script($cmd, { PI_CONFIG => $pi_config }, { 3 => $sock });
        $host_port = $sock->sockhost . ':' . $sock->sockport;
 }
 %opts = (
@@ -110,6 +100,12 @@ $t = timeit(1, sub {
 });
 diag 'newnews took: ' . timestr($t) . " for $n";
 
+if ($s) {
+       $s->print("QUIT\r\n");
+       $s->getline;
+}
+
+
 done_testing();
 
 1;
index 2c7f6a8418f23e07858e079a872f8193f0cd66ac..1a39ce4982488cfe98eaaafb22e3c52aa41180ab 100644 (file)
@@ -21,7 +21,6 @@ use PublicInbox::MIME;
 use PublicInbox::Config;
 # FIXME: too much setup
 my $tmpdir = tempdir('pi-v2mirror-XXXXXX', TMPDIR => 1, CLEANUP => 1);
-my $script = 'blib/script/public-inbox';
 my $pi_config = "$tmpdir/config";
 {
        open my $fh, '>', $pi_config or die "open($pi_config): $!";
@@ -60,19 +59,10 @@ ok($epoch_max > 0, "multiple epochs");
 $v2w->done;
 $ibx->cleanup;
 
-my ($sock, $pid);
-
-# TODO: replace this with ->DESTROY:
-my $owner_pid = $$;
-END { kill('TERM', $pid) if defined($pid) && $owner_pid == $$ };
-
-$! = 0;
-$sock = tcp_server();
+my $sock = tcp_server();
 ok($sock, 'sock created');
-my $httpd = "$script-httpd";
-my $cmd = [ $httpd, '-W0', "--stdout=$tmpdir/out", "--stderr=$tmpdir/err" ];
-ok(defined($pid = spawn_listener(undef, $cmd, [ $sock ])),
-       'spawned httpd process successfully');
+my $cmd = [ '-httpd', '-W0', "--stdout=$tmpdir/out", "--stderr=$tmpdir/err" ];
+my $td = start_script($cmd, undef, { 3 => $sock });
 my ($host, $port) = ($sock->sockhost, $sock->sockport);
 $sock = undef;
 
@@ -194,9 +184,8 @@ is($mibx->git->check($to_purge), undef, 'unindex+prune successful in mirror');
        is(scalar($mset->items), 0, '1@example.com no longer visible in mirror');
 }
 
-ok(kill('TERM', $pid), 'killed httpd');
-$pid = undef;
-waitpid(-1, 0);
+ok($td->kill, 'killed httpd');
+$td->join;
 
 done_testing();
 
index 28420bb92419a4259707cae1cf4a67d8bf292aa9..4bb6d733d4e27582af2a4e9fa071edd39a034433 100644 (file)
@@ -163,12 +163,10 @@ EOF
        close $fh or die "close: $!\n";
        my $sock = tcp_server();
        ok($sock, 'sock created');
-       my $pid;
        my $len;
-       END { kill 'TERM', $pid if defined $pid };
-       my $nntpd = 'blib/script/public-inbox-nntpd';
-       my $cmd = [ $nntpd, '-W0', "--stdout=$out", "--stderr=$err" ];
-       $pid = spawn_listener({ PI_CONFIG => $pi_config }, $cmd, [ $sock ]);
+       my $cmd = [ '-nntpd', '-W0', "--stdout=$out", "--stderr=$err" ];
+       my $env = { PI_CONFIG => $pi_config };
+       my $td = start_script($cmd, $env, { 3 => $sock });
        my $host_port = $sock->sockhost . ':' . $sock->sockport;
        my $n = Net::NNTP->new($host_port);
        $n->group($group);
index 61a059e56cf7aa2b88046d8d81c5f757954d5ea9..9cde357514acbf0651fbec30ca51797d66cabd65 100644 (file)
@@ -64,15 +64,13 @@ sub tiny_test {
                'epoch 1 in description');
 }
 
-my $pid;
-END { kill 'TERM', $pid if defined $pid };
+my $td;
 SKIP: {
        my $err = "$tmpdir/stderr.log";
        my $out = "$tmpdir/stdout.log";
        my $alt = "$tmpdir/alt.git";
        my $cfgfile = "$tmpdir/config";
        my $v2 = "$tmpdir/v2";
-       my $httpd = 'blib/script/public-inbox-httpd';
        my $sock = tcp_server();
        ok($sock, 'sock created');
        my ($host, $port) = ($sock->sockhost, $sock->sockport);
@@ -106,8 +104,8 @@ SKIP: {
 
        close $fh or die;
        my $env = { PI_CONFIG => $cfgfile };
-       my $cmd = [ $httpd, '-W0', "--stdout=$out", "--stderr=$err" ];
-       $pid = spawn_listener($env, $cmd, [$sock]);
+       my $cmd = [ '-httpd', '-W0', "--stdout=$out", "--stderr=$err" ];
+       $td = start_script($cmd, $env, { 3 => $sock });
        $sock = undef;
 
        tiny_test($host, $port);