]> git.sur5r.net Git - i3/i3/blob - testcases/lib/TestWorker.pm
Merge branch 'master' into next
[i3/i3] / testcases / lib / TestWorker.pm
1 # vim:ts=4:sw=4:sts=4:expandtab
2 package TestWorker;
3 use strict; use warnings;
4 use v5.10;
5
6 use Socket qw(AF_UNIX SOCK_DGRAM PF_UNSPEC);
7 use IO::Handle; # for ->autoflush
8
9 use POSIX ();
10
11 use Errno qw(EAGAIN);
12
13 use Exporter 'import';
14 our @EXPORT = qw(worker worker_next);
15
16 use File::Basename qw(basename);
17 my @x;
18 my $options;
19
20 sub worker {
21     my ($display, $x, $outdir, $optref) = @_;
22
23     # make sure $x hangs around
24     push @x, $x;
25
26     # store the options hashref
27     $options = $optref;
28
29     socketpair(my $ipc_child, my $ipc, AF_UNIX, SOCK_DGRAM, PF_UNSPEC)
30         or die "socketpair: $!";
31
32     $ipc->autoflush(1);
33     $ipc_child->autoflush(1);
34
35     my $worker = {
36         display => $display,
37         ipc => $ipc,
38     };
39
40     my $pid = fork // die "could not fork: $!";
41
42     if ($pid == 0) {
43         close $ipc;
44         undef @complete_run::CLEANUP;
45         # reap dead test children
46         $SIG{CHLD} = sub { waitpid -1, POSIX::WNOHANG };
47
48         $worker->{ipc} = $ipc_child;
49
50         require i3test;
51         # TODO: recycle $x
52         # unfortunately this fails currently with:
53         # Could not get reply for: xcb_intern_atom_reply at X11/XCB/Atom.pm line 22.
54
55         # $i3test::x = bless $x, 'i3test::X11';
56         worker_wait($worker, $outdir);
57         exit 23;
58
59     }
60
61     close $ipc_child;
62     push @complete_run::CLEANUP, sub {
63         # signal via empty line to exit itself
64         syswrite($ipc, "\n") or kill('TERM', $pid);
65         waitpid $pid, 0;
66     };
67
68     return $worker;
69
70 }
71
72 our $EOF = "# end of file\n";
73 sub worker_wait {
74     my ($self, $outdir) = @_;
75
76     my $ipc = $self->{ipc};
77     my $ipc_fd = fileno($ipc);
78
79     while (1) {
80         my $file = $ipc->getline;
81         if (!defined($file)) {
82             next if $! == EAGAIN;
83             last;
84         }
85         chomp $file;
86
87         exit unless $file;
88
89         die "tried to launch nonexistend testfile $file: $!\n"
90             unless -e $file;
91
92         # start a new and self contained process:
93         # whatever happens in the testfile should *NOT* effect us.
94
95         my $pid = fork // die "could not fork: $!";
96         if ($pid == 0) {
97             undef @complete_run::CLEANUP;
98             local $SIG{CHLD};
99
100             $0 = $file;
101
102             POSIX::dup2($ipc_fd, 0);
103             POSIX::dup2($ipc_fd, 1);
104             POSIX::dup2(1, 2);
105
106             # get Test::Builder singleton
107             my $test = Test::Builder->new;
108
109             # Test::Builder dups stdout/stderr while loading.
110             # we need to reset them here to point to $ipc
111             $test->output(\*STDOUT);
112             $test->failure_output(\*STDERR);
113             $test->todo_output(\*STDOUT);
114
115             @ENV{qw(DISPLAY TESTNAME OUTDIR VALGRIND STRACE XTRACE COVERAGE RESTART)}
116                 = ($self->{display},
117                    basename($file),
118                    $outdir,
119                    $options->{valgrind},
120                    $options->{strace},
121                    $options->{xtrace},
122                    $options->{coverage},
123                    $options->{restart});
124
125             package main;
126             local $@;
127             do "./$file";
128             $test->ok(undef, "$@") if $@;
129
130             # XXX hack, we need to trigger the read watcher once more
131             # to signal eof to TAP::Parser
132             print $EOF;
133
134             exit 0;
135         }
136     }
137 }
138
139 sub worker_next {
140     my ($self, $file) = @_;
141
142     my $ipc = $self->{ipc};
143     syswrite $ipc, "$file\n" or die "syswrite: $!";
144 }
145
146 __PACKAGE__ __END__