use warnings;
use v5.10;
# the following are modules which ship with Perl (>= 5.10):
+use Pod::Usage;
+use Carp::Always;
use Cwd qw(abs_path);
use File::Basename qw(basename);
use File::Temp qw(tempfile tempdir);
use lib qw(lib);
use SocketActivation;
# the following modules are not shipped with Perl
-use EV;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::I3 qw(:all);
-use IO::Scalar; # not in core :\
-use Try::Tiny; # not in core
use X11::XCB;
-# 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?
+# We actually use AnyEvent to make sure it loads an event loop implementation.
+# Afterwards, we overwrite SIGCHLD:
+my $cv = AnyEvent->condvar;
+
+# Install a dummy CHLD handler to overwrite the CHLD handler of AnyEvent.
+# AnyEvent’s handler wait()s for every child which conflicts with TAP (TAP
+# needs to get the exit status to determine if a test is successful).
$SIG{CHLD} = sub {
};
}
my $coverage_testing = 0;
+my $valgrind = 0;
+my $help = 0;
my @displays = ();
my $result = GetOptions(
"coverage-testing" => \$coverage_testing,
+ "valgrind" => \$valgrind,
"display=s" => \@displays,
+ "help|?" => \$help,
);
+pod2usage(0) if $help;
+
@displays = split(/,/, join(',', @displays));
@displays = map { s/ //g; $_ } @displays;
my $aggregator = TAP::Parser::Aggregator->new();
$aggregator->start();
-my $cv = AnyEvent->condvar;
-
# We start tests concurrently: For each display, one test gets started. Every
# test starts another test after completing.
take_job($_) for @wdisplays;
my $time_before_start = [gettimeofday];
my $pid;
- if (!$dont_start) {
+ if ($dont_start) {
+ $activate_cv->send(1);
+ } else {
$pid = activate_i3(
unix_socket_path => "/tmp/nested-$display-activation",
display => $display,
configfile => $tmpfile,
+ outdir => $outdir,
logpath => $logpath,
+ valgrind => $valgrind,
cv => $activate_cv
);
}
my $kill_i3 = sub {
+ my $kill_cv = AnyEvent->condvar;
+
# Don’t bother killing i3 when we haven’t started it
- return if $dont_start;
+ if ($dont_start) {
+ $kill_cv->send();
+ return $kill_cv;
+ }
# When measuring code coverage, try to exit i3 cleanly (otherwise, .gcda
# files are not written) and fallback to killing it
- if ($coverage_testing) {
+ if ($coverage_testing || $valgrind) {
my $exited = 0;
- try {
- say "Exiting i3 cleanly...";
- i3("/tmp/nested-$display")->command('exit')->recv;
- $exited = 1;
- };
- return if $exited;
+ say "[$display] Exiting i3 cleanly...";
+ my $i3 = i3("/tmp/nested-$display");
+ $i3->connect->cb(sub {
+ if (!$_[0]->recv) {
+ # Could not connect to i3, just kill -9 it
+ kill(9, $pid) or die "Could not kill i3 using kill($pid)";
+ $kill_cv->send();
+ } else {
+ # Connected. Now send exit and continue once that’s acked.
+ $i3->command('exit')->cb(sub {
+ $kill_cv->send();
+ });
+ }
+ });
+ } else {
+ # No coverage testing or valgrind? Just kill -9 i3.
+ kill(9, $pid) or die "Could not kill i3 using kill($pid)";
+ $kill_cv->send();
}
- say "[$display] killing i3";
- kill(9, $pid) or die "could not kill i3";
+ return $kill_cv;
};
# This will be called as soon as i3 is running and answered to our
say "[$display] Running $test with logfile $logpath";
my $output;
+ open(my $spool, '>', \$output);
my $parser = TAP::Parser->new({
- exec => [ 'sh', '-c', qq|DISPLAY=$display LOGPATH="$logpath" /usr/bin/perl -It/lib $test| ],
- spool => IO::Scalar->new(\$output),
+ exec => [ 'sh', '-c', qq|DISPLAY=$display LOGPATH="$logpath" OUTDIR="$outdir" VALGRIND=$valgrind /usr/bin/perl -Ilib $test| ],
+ spool => $spool,
merge => 1,
});
$aggregator->add($test, $parser);
push @done, [ $test, $output ];
- $kill_i3->();
+ my $exitcv = $kill_i3->();
+ $exitcv->cb(sub {
- undef $_ for @watchers;
- if (@done == $num) {
- $cv->send;
- } else {
- take_job($display);
- }
+ undef $_ for @watchers;
+ if (@done == $num) {
+ $cv->send;
+ } else {
+ take_job($display);
+ }
+ });
}
);
push @watchers, $w;
}
});
-
- $activate_cv->send(1) if $dont_start;
}
$cv->recv;
# 4: print summary
$harness->summary($aggregator);
+
+__END__
+
+=head1 NAME
+
+complete-run.pl - Run the i3 testsuite
+
+=head1 SYNOPSIS
+
+complete-run.pl [files...]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--display>
+
+Specifies which X11 display should be used. Can be specified multiple times and
+will parallelize the tests:
+
+ # Run tests on the second X server
+ ./complete-run.pl -d :1
+
+ # Run four tests in parallel on some Xdummy servers
+ ./complete-run.pl -d :1,:2,:3,:4
+
+=item B<--valgrind>
+
+Runs i3 under valgrind to find memory problems. The output will be available in
+C<latest/valgrind.log>.
+
+=item B<--coverage-testing>
+
+Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.