]> git.sur5r.net Git - i3/i3/commitdiff
tests: Refactor the socket activation into lib/SocketActivation.pm
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 4 Oct 2011 19:55:29 +0000 (20:55 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 4 Oct 2011 22:30:30 +0000 (23:30 +0100)
testcases/complete-run.pl
testcases/lib/SocketActivation.pm [new file with mode: 0644]

index 763a4f92900697fb684fcc623853f850a5596879..d427d70a05fab2b1121d1498ecc6a828c0eee153 100755 (executable)
@@ -23,6 +23,9 @@ use Time::HiRes qw(sleep gettimeofday tv_interval);
 use TAP::Harness;
 use TAP::Parser;
 use TAP::Parser::Aggregator;
+# these are shipped with the testsuite
+use lib qw(lib);
+use SocketActivation;
 # the following modules are not shipped with Perl
 use EV;
 use AnyEvent;
@@ -131,73 +134,23 @@ sub take_job {
 
     my $activate_cv = AnyEvent->condvar;
     my $time_before_start = [gettimeofday];
-    my $start_i3 = sub {
-        # remove the old unix socket
-        unlink("/tmp/nested-$display-activation");
-
-        # pass all file descriptors up to three to the children.
-        # we need to set this flag before opening the socket.
-        open(my $fdtest, '<', '/dev/null');
-        $^F = fileno($fdtest);
-        close($fdtest);
-        my $socket = IO::Socket::UNIX->new(
-            Listen => 1,
-            Local => "/tmp/nested-$display-activation",
-        );
 
-        my $pid = fork;
-        if (!defined($pid)) {
-            die "could not fork()";
-        }
-        if ($pid == 0) {
-            $ENV{LISTEN_PID} = $$;
-            $ENV{LISTEN_FDS} = 1;
-            $ENV{DISPLAY} = $display;
-            $^F = 3;
-
-            POSIX::close(3);
-            POSIX::dup2(fileno($socket), 3);
-
-            # now execute i3
-            my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler";
-            my $cmd = "exec $i3cmd -c $tmpfile >$logpath 2>&1";
-            exec "/bin/sh", '-c', $cmd;
-
-            # if we are still here, i3 could not be found or exec failed. bail out.
-            exit 1;
-        }
+    my $pid;
+    if (!$dont_start) {
+        $pid = activate_i3(
+            unix_socket_path => "/tmp/nested-$display-activation",
+            display => $display,
+            configfile => $tmpfile,
+            logpath => $logpath,
+            cv => $activate_cv
+        );
 
         my $child_watcher;
         $child_watcher = AnyEvent->child(pid => $pid, cb => sub {
             say "child died. pid = $pid";
             undef $child_watcher;
         });
-
-        # close the socket, the child process should be the only one which keeps a file
-        # descriptor on the listening socket.
-        $socket->close;
-
-        # We now connect (will succeed immediately) and send a request afterwards.
-        # As soon as the reply is there, i3 is considered ready.
-        my $cl = IO::Socket::UNIX->new(Peer => "/tmp/nested-$display-activation");
-        my $hdl;
-        $hdl = AnyEvent::Handle->new(fh => $cl, on_error => sub { $activate_cv->send(0) });
-
-        # send a get_tree message without payload
-        $hdl->push_write('i3-ipc' . pack("LL", 0, 4));
-
-        # wait for the reply
-        $hdl->push_read(chunk => 1, => sub {
-            my ($h, $line) = @_;
-            $activate_cv->send(1);
-            undef $hdl;
-        });
-
-        return $pid;
-    };
-
-    my $pid;
-    $pid = $start_i3->() unless $dont_start;
+    }
 
     my $kill_i3 = sub {
         # Don’t bother killing i3 when we haven’t started it
diff --git a/testcases/lib/SocketActivation.pm b/testcases/lib/SocketActivation.pm
new file mode 100644 (file)
index 0000000..92e514d
--- /dev/null
@@ -0,0 +1,99 @@
+package SocketActivation;
+# vim:ts=4:sw=4:expandtab
+
+use IO::Socket::UNIX; # core
+use Cwd qw(abs_path); # core
+use POSIX; # core
+use AnyEvent::Handle; # not core
+use Exporter 'import';
+use v5.10;
+
+our @EXPORT = qw(activate_i3);
+
+#
+# Starts i3 using socket activation. Creates a listening socket (with bind +
+# listen) which is then passed to i3, who in turn calls accept and handles the
+# requests.
+#
+# Since the kernel buffers the connect, the parent process can connect to the
+# socket immediately after forking. It then sends a request and waits until it
+# gets an answer. Obviously, i3 has to be initialized to actually answer the
+# request.
+#
+# This way, we can wait *precisely* the amount of time which i3 waits to get
+# ready, which is a *HUGE* speed gain (and a lot more robust) in comparison to
+# using sleep() with a fixed amount of time.
+#
+# unix_socket_path: Location of the socket to use for the activation
+# display: X11 $ENV{DISPLAY}
+# configfile: path to the configuration file to use
+# logpath: path to the logfile to which i3 will append
+# cv: an AnyEvent->condvar which will be triggered once i3 is ready
+#
+sub activate_i3 {
+    my %args = @_;
+
+    # remove the old unix socket
+    unlink($args{unix_socket_path});
+
+    # pass all file descriptors up to three to the children.
+    # we need to set this flag before opening the socket.
+    open(my $fdtest, '<', '/dev/null');
+    $^F = fileno($fdtest);
+    close($fdtest);
+    my $socket = IO::Socket::UNIX->new(
+        Listen => 1,
+        Local => $args{unix_socket_path},
+    );
+
+    my $pid = fork;
+    if (!defined($pid)) {
+        die "could not fork()";
+    }
+    if ($pid == 0) {
+        $ENV{LISTEN_PID} = $$;
+        $ENV{LISTEN_FDS} = 1;
+        $ENV{DISPLAY} = $args{display};
+        $^F = 3;
+
+        POSIX::close(3);
+        POSIX::dup2(fileno($socket), 3);
+
+        # now execute i3
+        my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler";
+        my $cmd = "exec $i3cmd -c $args{configfile} >$args{logpath} 2>&1";
+        exec "/bin/sh", '-c', $cmd;
+
+        # if we are still here, i3 could not be found or exec failed. bail out.
+        exit 1;
+    }
+
+    # close the socket, the child process should be the only one which keeps a file
+    # descriptor on the listening socket.
+    $socket->close;
+
+    # We now connect (will succeed immediately) and send a request afterwards.
+    # As soon as the reply is there, i3 is considered ready.
+    my $cl = IO::Socket::UNIX->new(Peer => $args{unix_socket_path});
+    my $hdl;
+    $hdl = AnyEvent::Handle->new(
+        fh => $cl,
+        on_error => sub {
+            $hdl->destroy;
+            $args{cv}->send(0);
+        });
+
+    # send a get_tree message without payload
+    $hdl->push_write('i3-ipc' . pack("LL", 0, 4));
+
+    # wait for the reply
+    $hdl->push_read(chunk => 1, => sub {
+        my ($h, $line) = @_;
+        $args{cv}->send(1);
+        undef $hdl;
+    });
+
+    return $pid;
+}
+
+1