]> git.sur5r.net Git - i3/i3/commitdiff
tests: Implement multi-monitor tests using Xdmx
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 24 Dec 2011 14:34:28 +0000 (15:34 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Mon, 26 Dec 2011 19:48:57 +0000 (20:48 +0100)
testcases/complete-run.pl
testcases/lib/SocketActivation.pm
testcases/lib/StartXDummy.pm
testcases/t/500-multi-monitor.t [new file with mode: 0644]

index cb7b5c8c107fb4bb83ff29746afc027b61a4a036..3475a45cb9a854d42f5b71e7902884d10758a9fa 100755 (executable)
@@ -69,8 +69,17 @@ my @testfiles = @ARGV;
 
 my $numtests = scalar @testfiles;
 
+# When the user specifies displays, we don’t run multi-monitor tests at all
+# (because we don’t know which displaynumber is the X-Server with multiple
+# monitors).
+my $multidpy = undef;
+
 # No displays specified, let’s start some Xdummy instances.
-@displays = start_xdummy($parallel, $numtests) if @displays == 0;
+if (@displays == 0) {
+    my $dpyref;
+    ($dpyref, $multidpy) = start_xdummy($parallel, $numtests);
+    @displays = @$dpyref;
+}
 
 # 1: create an output directory for this test-run
 my $outdir = "testsuite-";
@@ -87,7 +96,7 @@ symlink("$outdir", "latest") or die "Could not symlink latest to $outdir";
 # 2: keep the connection open so that i3 is not the only client. this prevents
 #    the X server from exiting (Xdummy will restart it, but not quick enough
 #    sometimes)
-my @worker;
+my @single_worker;
 for my $display (@displays) {
     my $screen;
     my $x = X11::XCB::Connection->new(display => $display);
@@ -95,7 +104,17 @@ for my $display (@displays) {
         die "Could not connect to display $display\n";
     } else {
         # start a TestWorker for each display
-        push @worker, worker($display, $x, $outdir);
+        push @single_worker, worker($display, $x, $outdir);
+    }
+}
+
+my @multi_worker;
+if (defined($multidpy)) {
+    my $x = X11::XCB::Connection->new(display => $multidpy);
+    if ($x->has_error) {
+        die "Could not connect to multi-monitor display $multidpy\n";
+    } else {
+        push @multi_worker, worker($multidpy, $x, $outdir);
     }
 }
 
@@ -127,18 +146,30 @@ my @done;
 my $num = @testfiles;
 my $harness = TAP::Harness->new({ });
 
+my @single_monitor_tests = grep { m,^t/([0-9]+)-, && $1 < 500 } @testfiles;
+my @multi_monitor_tests = grep { m,^t/([0-9]+)-, && $1 >= 500 } @testfiles;
+
 my $aggregator = TAP::Parser::Aggregator->new();
 $aggregator->start();
 
-status_init(displays => \@displays, tests => $num);
+status_init(displays => [ @displays, $multidpy ], tests => $num);
 
-my $cv = AE::cv;
+my $single_cv = AE::cv;
+my $multi_cv = AE::cv;
 
 # We start tests concurrently: For each display, one test gets started. Every
 # test starts another test after completing.
-for (@worker) { $cv->begin; take_job($_) }
+for (@single_worker) {
+    $single_cv->begin;
+    take_job($_, $single_cv, \@single_monitor_tests);
+}
+for (@multi_worker) {
+    $multi_cv->begin;
+    take_job($_, $multi_cv, \@multi_monitor_tests);
+}
 
-$cv->recv;
+$single_cv->recv;
+$multi_cv->recv;
 
 $aggregator->stop();
 
@@ -198,9 +229,9 @@ exit 0;
 # triggered to finish testing.
 #
 sub take_job {
-    my ($worker) = @_;
+    my ($worker, $cv, $tests) = @_;
 
-    my $test = shift @testfiles
+    my $test = shift @$tests
         or return $cv->end;
 
     my $display = $worker->{display};
@@ -269,7 +300,7 @@ sub take_job {
             push @done, [ $test, $output ];
 
             undef $w;
-            take_job($worker);
+            take_job($worker, $cv, $tests);
         }
     );
 }
index 0dadad750edeccf4c28a505a1f83aae226a52eca..d09d33fbd1c72c79b217ad6ba7a98e3f5e5cee34 100644 (file)
@@ -85,8 +85,10 @@ sub activate_i3 {
 
         # Construct the command to launch i3. Use maximum debug level, disable
         # the interactive signalhandler to make it crash immediately instead.
-        # Also disable logging to SHM since we want to redirect the logs anyways.
-        my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler --shmlog-size=0";
+        # Also disable logging to SHM since we redirect the logs anyways.
+        # Force Xinerama because we use Xdmx for multi-monitor tests.
+        my $i3cmd = abs_path("../i3") . q| -V -d all --disable-signalhandler| .
+                                        q| --shmlog-size=0 --force-xinerama|;
 
         # For convenience:
         my $outdir = $args{outdir};
index d9d2fd129cc6a1e99620291222f44d4b8af02570..3df782004b2fb4ff58fdf1dac41376aa41c442d8 100644 (file)
@@ -9,6 +9,8 @@ use v5.10;
 
 our @EXPORT = qw(start_xdummy);
 
+my $x_socketpath = '/tmp/.X11-unix/X';
+
 # reads in a whole file
 sub slurp {
     open(my $fh, '<', shift) or return '';
@@ -16,6 +18,42 @@ sub slurp {
     <$fh>;
 }
 
+# forks an Xdummy or Xdmx process
+sub fork_xserver {
+    my $displaynum = shift;
+    my $pid = fork();
+    die "Could not fork: $!" unless defined($pid);
+    if ($pid == 0) {
+        # Child, close stdout/stderr, then start Xdummy.
+        close STDOUT;
+        close STDERR;
+
+        exec @_;
+        exit 1;
+    }
+    push(@complete_run::CLEANUP, sub {
+        kill(15, $pid);
+        # Unlink the X11 socket, Xdmx seems to leave it there.
+        unlink($x_socketpath . $displaynum);
+    });
+
+    return $x_socketpath . $displaynum;
+}
+
+# Blocks until the socket paths specified in the given array reference actually
+# exist.
+sub wait_for_x {
+    my ($sockets_waiting) = @_;
+
+    # Wait until Xdmx actually runs. Pretty ugly solution, but as long as we
+    # can’t socket-activate X11…
+    while (1) {
+        @$sockets_waiting = grep { ! -S $_ } @$sockets_waiting;
+        last unless @$sockets_waiting;
+        sleep 0.1;
+    }
+}
+
 =head2 start_xdummy($parallel)
 
 Starts C<$parallel> (or number of cores * 2 if undef) Xdummy processes (see
@@ -24,8 +62,6 @@ the Xdummy processes and a list of PIDs of the processes.
 
 =cut
 
-my $x_socketpath = '/tmp/.X11-unix/X';
-
 sub start_xdummy {
     my ($parallel, $numtests) = @_;
 
@@ -39,11 +75,17 @@ sub start_xdummy {
     # If /proc/cpuinfo does not exist, we fall back to 2 cores.
     $num_cores ||= 2;
 
-    $parallel ||= $num_cores * 2;
+    # If unset, we use num_cores * 2, plus two extra xdummys to combine to a
+    # multi-monitor setup using Xdmx.
+    $parallel ||= ($num_cores * 2) + 2;
 
     # If we are running a small number of tests, don’t over-parallelize.
     $parallel = $numtests if $numtests < $parallel;
 
+    # Ensure we have at least 1 X-Server plus two X-Servers for multi-monitor
+    # tests.
+    $parallel = 3 if $parallel < 3;
+
     # 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) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
@@ -52,37 +94,33 @@ sub start_xdummy {
     say "Starting $parallel Xdummy instances, starting at :$displaynum...";
 
     my @sockets_waiting;
-    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.
-            close STDOUT;
-            close STDERR;
-            # make sure this display isn’t in use yet
-            $displaynum++ while -e ($x_socketpath . $displaynum);
-
-            # We use -config /dev/null to prevent Xdummy from using the system
-            # Xorg configuration. The tests should be independant from the
-            # actual system X configuration.
-            exec './Xdummy', ":$displaynum", '-config', '/dev/null';
-            exit 1;
-        }
-        push(@complete_run::CLEANUP, sub { kill(15, $pid) });
+    for (1 .. $parallel) {
+        # We use -config /dev/null to prevent Xdummy from using the system
+        # Xorg configuration. The tests should be independant from the
+        # actual system X configuration.
+        my $socket = fork_xserver($displaynum, './Xdummy', ":$displaynum",
+                '-config', '/dev/null');
         push(@displays, ":$displaynum");
-        push(@sockets_waiting, $x_socketpath . $displaynum);
+        push(@sockets_waiting, $socket);
         $displaynum++;
     }
 
-    # Wait until the X11 sockets actually appear. Pretty ugly solution, but as
-    # long as we can’t socket-activate X11…
-    while (1) {
-        @sockets_waiting = grep { ! -S $_ } @sockets_waiting;
-        last unless @sockets_waiting;
-        sleep 0.1;
-    }
+    wait_for_x(\@sockets_waiting);
+
+    # Now combine the last two displays to a multi-monitor display using Xdmx
+    my $first = pop @displays;
+    my $second = pop @displays;
+
+    # make sure this display isn’t in use yet
+    $displaynum++ while -e ($x_socketpath . $displaynum);
+    say 'starting xdmx on display :' . $displaynum;
+
+    my $multidpy = ":$displaynum";
+    my $socket = fork_xserver($displaynum, 'Xdmx', '+xinerama', '-xinput',
+            'local', '-display', $first, '-display', $second, '-ac', $multidpy);
+    wait_for_x([ $socket ]);
 
-    return @displays;
+    return \@displays, $multidpy;
 }
 
 1
diff --git a/testcases/t/500-multi-monitor.t b/testcases/t/500-multi-monitor.t
new file mode 100644 (file)
index 0000000..1f42f0b
--- /dev/null
@@ -0,0 +1,21 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Tests that the provided X-Server to the t/5??-*.t tests is actually providing
+# multiple monitors.
+#
+use i3test;
+
+my $i3 = i3(get_socket_path());
+
+####################
+# Request tree
+####################
+
+my $tree = $i3->get_tree->recv;
+
+my @outputs = map { $_->{name} } @{$tree->{nodes}};
+is_deeply(\@outputs, [ '__i3', 'xinerama-0', 'xinerama-1' ],
+          'multi-monitor outputs ok');
+
+done_testing;