testsuite: eliminate sleep, wait until i3 replies via IPC
This commit is contained in:
parent
10f871b57b
commit
5524785877
|
@ -29,6 +29,18 @@ use Try::Tiny;
|
|||
use Getopt::Long;
|
||||
use Time::HiRes qw(sleep);
|
||||
use X11::XCB::Connection;
|
||||
use IO::Socket::UNIX; # core
|
||||
use POSIX; # core
|
||||
use AnyEvent::Handle;
|
||||
|
||||
# open a file so that we get file descriptor 3. we will later close it in the
|
||||
# child and dup() the listening socket file descriptor to 3 to pass it to i3
|
||||
open(my $reserved, '<', '/dev/null');
|
||||
if (fileno($reserved) != 3) {
|
||||
warn "Socket file descriptor is not 3.";
|
||||
warn "Please don't start this script within a subshell of vim or something.";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# install a dummy CHLD handler to overwrite the CHLD handler of AnyEvent / EV
|
||||
# XXX: we could maybe also use a different loop than the default loop in EV?
|
||||
|
@ -72,7 +84,6 @@ for my $display (@displays) {
|
|||
};
|
||||
}
|
||||
|
||||
my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler";
|
||||
my $config = slurp('i3-test.config');
|
||||
|
||||
# 1: get a list of all testcases
|
||||
|
@ -116,21 +127,90 @@ take_job($_) for @wdisplays;
|
|||
#
|
||||
sub take_job {
|
||||
my ($display) = @_;
|
||||
|
||||
my $test = shift @testfiles;
|
||||
return unless $test;
|
||||
my $dont_start = (slurp($test) =~ /# !NO_I3_INSTANCE!/);
|
||||
my $logpath = "$outdir/i3-log-for-" . basename($test);
|
||||
|
||||
my ($fh, $tmpfile) = tempfile();
|
||||
say $fh $config;
|
||||
say $fh "ipc-socket /tmp/nested-$display";
|
||||
close($fh);
|
||||
|
||||
my $test = shift @testfiles;
|
||||
return unless $test;
|
||||
my $logpath = "$outdir/i3-log-for-" . basename($test);
|
||||
my $cmd = "export DISPLAY=$display; exec $i3cmd -c $tmpfile >$logpath 2>&1";
|
||||
my $dont_start = (slurp($test) =~ /# !NO_I3_INSTANCE!/);
|
||||
my $activate_cv = AnyEvent->condvar;
|
||||
my $start_i3 = sub {
|
||||
# remove the old unix socket
|
||||
unlink("/tmp/nested-$display-activation");
|
||||
|
||||
my $process = Proc::Background->new($cmd) unless $dont_start;
|
||||
say "[$display] Running $test with logfile $logpath";
|
||||
# pass all file descriptors up to three to the children.
|
||||
# we need to set this flag before opening the socket.
|
||||
open(my $fdtest, '<', '/dev/null');
|
||||
$^F = fileno($fdtest);
|
||||
close($fdtest);
|
||||
my $socket = IO::Socket::UNIX->new(
|
||||
Listen => 1,
|
||||
Local => "/tmp/nested-$display-activation",
|
||||
);
|
||||
|
||||
my $pid = fork;
|
||||
if (!defined($pid)) {
|
||||
die "could not fork()";
|
||||
}
|
||||
say "pid = $pid";
|
||||
if ($pid == 0) {
|
||||
say "child!";
|
||||
$ENV{LISTEN_PID} = $$;
|
||||
$ENV{LISTEN_FDS} = 1;
|
||||
$ENV{DISPLAY} = $display;
|
||||
$^F = 3;
|
||||
|
||||
say "fileno is " . fileno($socket);
|
||||
close($reserved);
|
||||
POSIX::dup2(fileno($socket), 3);
|
||||
|
||||
# now execute i3
|
||||
my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler";
|
||||
my $cmd = "exec $i3cmd -c $tmpfile >$logpath 2>&1";
|
||||
exec "/bin/sh", '-c', $cmd;
|
||||
|
||||
# if we are still here, i3 could not be found or exec failed. bail out.
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $child_watcher;
|
||||
$child_watcher = AnyEvent->child(pid => $pid, cb => sub {
|
||||
say "child died. pid = $pid";
|
||||
undef $child_watcher;
|
||||
});
|
||||
|
||||
# close the socket, the child process should be the only one which keeps a file
|
||||
# descriptor on the listening socket.
|
||||
$socket->close;
|
||||
|
||||
# We now connect (will succeed immediately) and send a request afterwards.
|
||||
# As soon as the reply is there, i3 is considered ready.
|
||||
my $cl = IO::Socket::UNIX->new(Peer => "/tmp/nested-$display-activation");
|
||||
my $hdl;
|
||||
$hdl = AnyEvent::Handle->new(fh => $cl, on_error => sub { $activate_cv->send(0) });
|
||||
|
||||
# send a get_tree message without payload
|
||||
$hdl->push_write('i3-ipc' . pack("LL", 0, 4));
|
||||
|
||||
# wait for the reply
|
||||
$hdl->push_read(chunk => 1, => sub {
|
||||
my ($h, $line) = @_;
|
||||
say "read something from i3";
|
||||
$activate_cv->send(1);
|
||||
undef $hdl;
|
||||
});
|
||||
|
||||
return $pid;
|
||||
};
|
||||
|
||||
my $pid;
|
||||
$pid = $start_i3->() unless $dont_start;
|
||||
|
||||
sleep 0.5;
|
||||
my $kill_i3 = sub {
|
||||
# Don’t bother killing i3 when we haven’t started it
|
||||
return if $dont_start;
|
||||
|
@ -148,53 +228,65 @@ sub take_job {
|
|||
}
|
||||
|
||||
say "[$display] killing i3";
|
||||
kill(9, $process->pid) or die "could not kill i3";
|
||||
kill(9, $pid) or die "could not kill i3";
|
||||
};
|
||||
|
||||
my $output;
|
||||
my $parser = TAP::Parser->new({
|
||||
exec => [ 'sh', '-c', "DISPLAY=$display /usr/bin/perl -It/lib $test" ],
|
||||
spool => IO::Scalar->new(\$output),
|
||||
merge => 1,
|
||||
# This will be called as soon as i3 is running and answered to our
|
||||
# IPC request
|
||||
$activate_cv->cb(sub {
|
||||
say "cb";
|
||||
my ($status) = $activate_cv->recv;
|
||||
say "complete-run: status = $status";
|
||||
|
||||
say "[$display] Running $test with logfile $logpath";
|
||||
|
||||
my $output;
|
||||
my $parser = TAP::Parser->new({
|
||||
exec => [ 'sh', '-c', "DISPLAY=$display /usr/bin/perl -It/lib $test" ],
|
||||
spool => IO::Scalar->new(\$output),
|
||||
merge => 1,
|
||||
});
|
||||
|
||||
my @watchers;
|
||||
my ($stdout, $stderr) = $parser->get_select_handles;
|
||||
for my $handle ($parser->get_select_handles) {
|
||||
my $w;
|
||||
$w = AnyEvent->io(
|
||||
fh => $handle,
|
||||
poll => 'r',
|
||||
cb => sub {
|
||||
# Ignore activity on stderr (unnecessary with merge => 1,
|
||||
# but let’s keep it in here if we want to use merge => 0
|
||||
# for some reason in the future).
|
||||
return if defined($stderr) and $handle == $stderr;
|
||||
|
||||
my $result = $parser->next;
|
||||
if (defined($result)) {
|
||||
# TODO: check if we should bail out
|
||||
return;
|
||||
}
|
||||
|
||||
# $result is not defined, we are done parsing
|
||||
say "[$display] $test finished";
|
||||
close($parser->delete_spool);
|
||||
$aggregator->add($test, $parser);
|
||||
push @done, [ $test, $output ];
|
||||
|
||||
$kill_i3->();
|
||||
|
||||
undef $_ for @watchers;
|
||||
if (@done == $num) {
|
||||
$cv->send;
|
||||
} else {
|
||||
take_job($display);
|
||||
}
|
||||
}
|
||||
);
|
||||
push @watchers, $w;
|
||||
}
|
||||
});
|
||||
|
||||
my @watchers;
|
||||
my ($stdout, $stderr) = $parser->get_select_handles;
|
||||
for my $handle ($parser->get_select_handles) {
|
||||
my $w;
|
||||
$w = AnyEvent->io(
|
||||
fh => $handle,
|
||||
poll => 'r',
|
||||
cb => sub {
|
||||
# Ignore activity on stderr (unnecessary with merge => 1,
|
||||
# but let’s keep it in here if we want to use merge => 0
|
||||
# for some reason in the future).
|
||||
return if defined($stderr) and $handle == $stderr;
|
||||
|
||||
my $result = $parser->next;
|
||||
if (defined($result)) {
|
||||
# TODO: check if we should bail out
|
||||
return;
|
||||
}
|
||||
|
||||
# $result is not defined, we are done parsing
|
||||
say "[$display] $test finished";
|
||||
close($parser->delete_spool);
|
||||
$aggregator->add($test, $parser);
|
||||
push @done, [ $test, $output ];
|
||||
|
||||
$kill_i3->();
|
||||
|
||||
undef $_ for @watchers;
|
||||
if (@done == $num) {
|
||||
$cv->send;
|
||||
} else {
|
||||
take_job($display);
|
||||
}
|
||||
}
|
||||
);
|
||||
push @watchers, $w;
|
||||
}
|
||||
$activate_cv->send(1) if $dont_start;
|
||||
}
|
||||
|
||||
$cv->recv;
|
||||
|
|
Loading…
Reference in New Issue