diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index 2f05f703..915a7e02 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -1,13 +1,6 @@ #!/usr/bin/env perl # vim:ts=4:sw=4:expandtab -# # © 2010-2011 Michael Stapelberg and contributors -# -# syntax: ./complete-run.pl --display :1 --display :2 -# to run the test suite on the X11 displays :1 and :2 -# use 'Xdummy :1' and 'Xdummy :2' before to start two -# headless X11 servers -# use strict; use warnings; @@ -28,6 +21,7 @@ use TAP::Parser::Aggregator; # these are shipped with the testsuite use lib qw(lib); use SocketActivation; +use StartXDummy; # the following modules are not shipped with Perl use AnyEvent; use AnyEvent::Handle; @@ -46,7 +40,7 @@ $SIG{CHLD} = sub { # reads in a whole file sub slurp { - open my $fh, '<', shift; + open(my $fh, '<', shift); local $/; <$fh>; } @@ -54,21 +48,32 @@ sub slurp { my $coverage_testing = 0; my $valgrind = 0; my $help = 0; +# Number of tests to run in parallel. Important to know how many Xdummy +# instances we need to start (unless @displays are given). Defaults to +# num_cores * 2. +my $parallel = undef; my @displays = (); +my @childpids = (); my $result = GetOptions( "coverage-testing" => \$coverage_testing, "valgrind" => \$valgrind, "display=s" => \@displays, + "parallel=i" => \$parallel, "help|?" => \$help, ); -pod2usage(0) if $help; +pod2usage(-verbose => 2, -exitcode => 0) if $help; @displays = split(/,/, join(',', @displays)); @displays = map { s/ //g; $_ } @displays; -@displays = qw(:1) if @displays == 0; +# No displays specified, let’s start some Xdummy instances. +if (@displays == 0) { + my ($displays, $pids) = start_xdummy($parallel); + @displays = @$displays; + @childpids = @$pids; +} # connect to all displays for two reasons: # 1: check if the display actually works @@ -88,6 +93,8 @@ for my $display (@displays) { } } +die "No usable displays found" if @wdisplays == 0; + my $config = slurp('i3-test.config'); # 1: get a list of all testcases @@ -268,6 +275,9 @@ $cv->recv; $aggregator->stop(); +# Disable buffering to make sure the output and summary appear before we exit. +$| = 1; + for (@done) { my ($test, $output) = @$_; say "output for $test:"; @@ -277,6 +287,8 @@ for (@done) { # 4: print summary $harness->summary($aggregator); +kill(15, $_) for @childpids; + __END__ =head1 NAME @@ -287,6 +299,15 @@ complete-run.pl - Run the i3 testsuite complete-run.pl [files...] +=head1 EXAMPLE + +To run the whole testsuite on a reasonable number of Xdummy instances (your +running X11 will not be touched), run: + ./complete-run.pl + +To run only a specific test (useful when developing a new feature), run: + ./complete-run t/100-fullscreen.t + =head1 OPTIONS =over 8 @@ -302,6 +323,9 @@ will parallelize the tests: # Run four tests in parallel on some Xdummy servers ./complete-run.pl -d :1,:2,:3,:4 +Note that it is not necessary to specify this anymore. If omitted, +complete-run.pl will start (num_cores * 2) Xdummy instances. + =item B<--valgrind> Runs i3 under valgrind to find memory problems. The output will be available in @@ -310,3 +334,11 @@ C. =item B<--coverage-testing> Exits i3 cleanly (instead of kill -9) to make coverage testing work properly. + +=item B<--parallel> + +Number of Xdummy instances to start (if you don’t want to start num_cores * 2 +instances for some reason). + + # Run all tests on a single Xdummy instance + ./complete-run.pl -p 1 diff --git a/testcases/lib/SocketActivation.pm b/testcases/lib/SocketActivation.pm index 47a709d5..31b3ba89 100644 --- a/testcases/lib/SocketActivation.pm +++ b/testcases/lib/SocketActivation.pm @@ -1,6 +1,8 @@ package SocketActivation; # vim:ts=4:sw=4:expandtab +use strict; +use warnings; use IO::Socket::UNIX; # core use Cwd qw(abs_path); # core use POSIX; # core diff --git a/testcases/lib/StartXDummy.pm b/testcases/lib/StartXDummy.pm new file mode 100644 index 00000000..8657ba9d --- /dev/null +++ b/testcases/lib/StartXDummy.pm @@ -0,0 +1,80 @@ +package StartXDummy; +# vim:ts=4:sw=4:expandtab + +use strict; +use warnings; +use POSIX (); +use Exporter 'import'; +use Time::HiRes qw(sleep); +use v5.10; + +our @EXPORT = qw(start_xdummy); + +# reads in a whole file +sub slurp { + open(my $fh, '<', shift) or return ''; + local $/; + <$fh>; +} + +=head2 start_xdummy($parallel) + +Starts C<$parallel> (or number of cores * 2 if undef) Xdummy processes (see +the file ./Xdummy) and returns two arrayrefs: a list of X11 display numbers to +the Xdummy processes and a list of PIDs of the processes. + +=cut +sub start_xdummy { + my ($parallel) = @_; + + my @displays = (); + my @childpids = (); + + # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have + # _SC_NPROCESSORS_CONF. + my $cpuinfo = slurp('/proc/cpuinfo'); + my $num_cores = scalar grep { /model name/ } split("\n", $cpuinfo); + # If /proc/cpuinfo does not exist, we fall back to 2 cores. + $num_cores ||= 2; + + $parallel ||= $num_cores * 2; + + # First get the last used display number, then increment it by one. + # Effectively falls back to 1 if no X server is running. + my ($displaynum) = reverse ('0', sort ); + $displaynum =~ s/.*(\d)$/$1/; + $displaynum++; + + say "Starting $parallel Xdummy instances, starting at :$displaynum..."; + + for my $idx (0 .. ($parallel-1)) { + my $pid = fork(); + die "Could not fork: $!" unless defined($pid); + if ($pid == 0) { + # Child, close stdout/stderr, then start Xdummy. + POSIX::close(0); + POSIX::close(2); + exec './Xdummy', ":$displaynum", '-config', '/dev/null'; + exit 1; + } + push(@childpids, $pid); + push(@displays, ":$displaynum"); + $displaynum++; + } + + # Wait until the X11 sockets actually appear. Pretty ugly solution, but as + # long as we can’t socket-activate X11… + my $sockets_ready; + do { + $sockets_ready = 1; + for (@displays) { + my $path = "/tmp/.X11-unix/X" . substr($_, 1); + $sockets_ready = 0 unless -S $path; + } + sleep 0.1; + } until $sockets_ready; + + return \@displays, \@childpids; +} + +1