]> git.sur5r.net Git - i3/i3/blob - testcases/lib/StartXServer.pm
Merge branch 'master' into next
[i3/i3] / testcases / lib / StartXServer.pm
1 package StartXServer;
2 # vim:ts=4:sw=4:expandtab
3
4 use strict;
5 use warnings;
6 use Exporter 'import';
7 use Time::HiRes qw(sleep);
8 use v5.10;
9
10 our @EXPORT = qw(start_xserver);
11
12 my @pids;
13 my $x_socketpath = '/tmp/.X11-unix/X';
14
15 # reads in a whole file
16 sub slurp {
17     open(my $fh, '<', shift) or return '';
18     local $/;
19     <$fh>;
20 }
21
22 # forks an X server process
23 sub fork_xserver {
24     my $keep_xserver_output = shift;
25     my $displaynum = shift;
26     my $pid = fork();
27     die "Could not fork: $!" unless defined($pid);
28     if ($pid == 0) {
29         # Child, close stdout/stderr, then start Xephyr
30         if (!$keep_xserver_output) {
31             close STDOUT;
32             close STDERR;
33         }
34
35         exec @_;
36         exit 1;
37     }
38     push(@complete_run::CLEANUP, sub {
39         kill(15, $pid);
40         # Unlink the X11 socket, Xdmx seems to leave it there.
41         unlink($x_socketpath . $displaynum);
42     });
43
44     push @pids, $pid;
45
46     return $x_socketpath . $displaynum;
47 }
48
49 # Blocks until the socket paths specified in the given array reference actually
50 # exist.
51 sub wait_for_x {
52     my ($sockets_waiting) = @_;
53
54     # Wait until Xdmx actually runs. Pretty ugly solution, but as long as we
55     # can’t socket-activate X11…
56     while (1) {
57         @$sockets_waiting = grep { ! -S $_ } @$sockets_waiting;
58         last unless @$sockets_waiting;
59         sleep 0.1;
60     }
61 }
62
63 =head2 start_xserver($parallel)
64
65 Starts C<$parallel> (or number of cores * 2 if undef) Xephyr processes (see
66 http://www.freedesktop.org/wiki/Software/Xephyr/) and returns two arrayrefs: a
67 list of X11 display numbers to the Xephyr processes and a list of PIDs of the
68 processes.
69
70 =cut
71
72 sub start_xserver {
73     my ($parallel, $numtests, $keep_xserver_output) = @_;
74
75     my @displays = ();
76     my @childpids = ();
77
78     $SIG{CHLD} = sub {
79         my $child = waitpid -1, POSIX::WNOHANG;
80         @pids = grep { $_ != $child } @pids;
81         return unless @pids == 0;
82         print STDERR "All X server processes died.\n";
83         print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n";
84         exit 1;
85     };
86
87     # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have
88     # _SC_NPROCESSORS_CONF.
89     my $cpuinfo = slurp('/proc/cpuinfo');
90     my $num_cores = scalar grep { /model name/ } split("\n", $cpuinfo);
91     # If /proc/cpuinfo does not exist, we fall back to 2 cores.
92     $num_cores ||= 2;
93
94     # If unset, we use num_cores * 2.
95     $parallel ||= ($num_cores * 2);
96
97     # If we are running a small number of tests, don’t over-parallelize.
98     $parallel = $numtests if $numtests < $parallel;
99
100     # First get the last used display number, then increment it by one.
101     # Effectively falls back to 1 if no X server is running.
102     my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
103     $displaynum++;
104
105     say "Starting $parallel Xephyr instances, starting at :$displaynum...";
106
107     my @sockets_waiting;
108     for (1 .. $parallel) {
109         my $socket = fork_xserver($keep_xserver_output, $displaynum,
110                 'Xephyr', ":$displaynum", '-screen', '1280x800',
111                 '-nolisten', 'tcp');
112         push(@displays, ":$displaynum");
113         push(@sockets_waiting, $socket);
114         $displaynum++;
115     }
116
117     wait_for_x(\@sockets_waiting);
118
119     return @displays;
120 }
121
122 1