]> git.sur5r.net Git - i3/i3/blobdiff - testcases/complete-run.pl
complete-run: Bugfix: return condvar when $dont_start is true
[i3/i3] / testcases / complete-run.pl
index d427d70a05fab2b1121d1498ecc6a828c0eee153..2f05f703e2a695622f26b2d505c72b8db4a861ed 100755 (executable)
@@ -13,6 +13,8 @@ use strict;
 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);
@@ -27,16 +29,18 @@ use TAP::Parser::Aggregator;
 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 {
 };
 
@@ -48,13 +52,19 @@ sub slurp {
 }
 
 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;
 
@@ -103,8 +113,6 @@ my $harness = TAP::Harness->new({ });
 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;
@@ -136,12 +144,16 @@ sub take_job {
     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
         );
 
@@ -153,23 +165,39 @@ sub take_job {
     }
 
     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
@@ -187,9 +215,10 @@ sub take_job {
         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,
         });
 
@@ -218,21 +247,21 @@ sub take_job {
                     $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;
@@ -247,3 +276,37 @@ for (@done) {
 
 # 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.