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 Getopt::Long;
|
||||||
use Time::HiRes qw(sleep);
|
use Time::HiRes qw(sleep);
|
||||||
use X11::XCB::Connection;
|
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
|
# 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?
|
# 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');
|
my $config = slurp('i3-test.config');
|
||||||
|
|
||||||
# 1: get a list of all testcases
|
# 1: get a list of all testcases
|
||||||
|
@ -116,21 +127,90 @@ take_job($_) for @wdisplays;
|
||||||
#
|
#
|
||||||
sub take_job {
|
sub take_job {
|
||||||
my ($display) = @_;
|
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();
|
my ($fh, $tmpfile) = tempfile();
|
||||||
say $fh $config;
|
say $fh $config;
|
||||||
say $fh "ipc-socket /tmp/nested-$display";
|
say $fh "ipc-socket /tmp/nested-$display";
|
||||||
close($fh);
|
close($fh);
|
||||||
|
|
||||||
my $test = shift @testfiles;
|
my $activate_cv = AnyEvent->condvar;
|
||||||
return unless $test;
|
my $start_i3 = sub {
|
||||||
my $logpath = "$outdir/i3-log-for-" . basename($test);
|
# remove the old unix socket
|
||||||
my $cmd = "export DISPLAY=$display; exec $i3cmd -c $tmpfile >$logpath 2>&1";
|
unlink("/tmp/nested-$display-activation");
|
||||||
my $dont_start = (slurp($test) =~ /# !NO_I3_INSTANCE!/);
|
|
||||||
|
|
||||||
my $process = Proc::Background->new($cmd) unless $dont_start;
|
# pass all file descriptors up to three to the children.
|
||||||
say "[$display] Running $test with logfile $logpath";
|
# 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 {
|
my $kill_i3 = sub {
|
||||||
# Don’t bother killing i3 when we haven’t started it
|
# Don’t bother killing i3 when we haven’t started it
|
||||||
return if $dont_start;
|
return if $dont_start;
|
||||||
|
@ -148,53 +228,65 @@ sub take_job {
|
||||||
}
|
}
|
||||||
|
|
||||||
say "[$display] killing i3";
|
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;
|
# This will be called as soon as i3 is running and answered to our
|
||||||
my $parser = TAP::Parser->new({
|
# IPC request
|
||||||
exec => [ 'sh', '-c', "DISPLAY=$display /usr/bin/perl -It/lib $test" ],
|
$activate_cv->cb(sub {
|
||||||
spool => IO::Scalar->new(\$output),
|
say "cb";
|
||||||
merge => 1,
|
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;
|
$activate_cv->send(1) if $dont_start;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$cv->recv;
|
$cv->recv;
|
||||||
|
|
Loading…
Reference in New Issue