]> rtime.felk.cvut.cz Git - wvtest.git/blob - tools/wvnulrun
Copy several helper scripts from NUL project repository
[wvtest.git] / tools / wvnulrun
1 #!/usr/bin/perl -w
2 #
3 # @file
4 # Script to supervise the execution of wvtest-based tests.
5 #
6 # It takes care of killing test (qemu or serial reader) when the test
7 # finishes or hangs.
8 #
9 # Copyright (C) 2011, 2012, Michal Sojka <sojka@os.inf.tu-dresden.de>
10 # Economic rights: Technische Universitaet Dresden (Germany)
11 #
12 # This file is part of NUL (NOVA user land).
13 #
14 # NUL is free software: you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License version
16 # 2 as published by the Free Software Foundation.
17 #
18 # NUL is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License version 2 for more details.
22 #/
23
24 use strict;
25 use IO::Pty;
26
27 # always flush
28 $| = 1;
29
30 my $istty = -t STDOUT && $ENV{'TERM'} ne "dumb";
31 my $pty = new IO::Pty;
32
33 my $pid = fork();
34 die "Cannot fork" if not defined $pid;
35 if (!$pid) {
36     # child
37     $pty->make_slave_controlling_terminal();
38 #    setpgrp(); # Terminal won't send signals to the child
39     my $slave = $pty->slave();
40     close $pty;
41     $slave->clone_winsize_from(\*STDIN) if $istty;
42     $slave->set_raw();
43
44     open(STDIN,"<&". $slave->fileno())
45       or die "Couldn't reopen STDIN for reading, $!\n";
46     open(STDOUT,">&". $slave->fileno())
47       or die "Couldn't reopen STDOUT for writing, $!\n";
48     open(STDERR,">&". $slave->fileno())
49       or die "Couldn't reopen STDERR for writing, $!\n";
50
51     close $slave;
52
53     exec(@ARGV);
54     die "Cannot exec(@ARGV): $!";
55 }
56
57 $pty->close_slave();
58 #$pty->set_raw(); # from IO::Pty "try" script. Do we need this?
59
60 sub winch {
61   $pty->slave->clone_winsize_from(\*STDIN);
62   kill WINCH => $pid if $pid;
63   $SIG{WINCH} = \&winch;
64 }
65
66 $SIG{WINCH} = \&winch if $istty;
67
68 sub bigkill($)
69 {
70     my $pid = shift;
71     ($pid > 0) || die("pid is '$pid'?!\n");
72     my $count;
73     local $SIG{CHLD} = sub { }; # this will wake us from sleep() faster
74     $count = kill -15, $pid;
75     sleep(2);
76
77     kill -9, $pid if ($pid > 1);
78
79     exit(125);
80 }
81
82 my $timeout = 100;
83
84 # parent
85 local $SIG{INT} = sub { bigkill($pid); };
86 local $SIG{TERM} = sub { bigkill($pid); };
87 local $SIG{ALRM} = sub {
88     print STDERR "! $0: Alarm timed out!  No test output for $timeout seconds.  FAILED\n";
89     bigkill($pid);
90 };
91
92 my $allstart = time();
93 my ($start, $stop);
94 my $tests_executed = 0;
95 my $tests_failed = 0;
96 my $waits_for_child = 0;
97 my $kill_ok = 0;
98 my $ignore_exit_patterns = 0;
99 my $expected_test_count = 0;
100 my $expected_test_base = 0;
101
102 sub matches_exit_pattern($)
103 {
104     return 0 if $ignore_exit_patterns;
105     if ($ENV{WVTEST_EXIT_PATTERN}) {
106         return /$ENV{WVTEST_EXIT_PATTERN}/
107     } else {
108         return
109             (/sc: done.$/ && $waits_for_child) ||
110             /resetting machine via method/ ||
111             /wvtest: done\s*$/ ||
112             / # .*System halted. *$/
113             ;
114     }
115 }
116
117 sub check_number_of_tests()
118 {
119     if ($expected_test_count) {
120         my $executed = $tests_executed - $expected_test_base;
121         my $result;
122         if ($executed == $expected_test_count) {
123             $result = "ok";
124         } else {
125             $result = "FAILED";
126             $tests_failed++;
127             print "Expected $expected_test_count tests, executed $executed tests.\n"
128         }
129         print "! $0: tests_expected == tests_executed  $result\n";
130     }
131 }
132
133 my $wvtest_output;
134 if ($ENV{WVTEST_OUTPUT}) {
135     open $wvtest_output, ">", $ENV{WVTEST_OUTPUT};
136 }
137
138 alarm($timeout);
139 while (<$pty>)
140 {
141     alarm($timeout);
142     print;
143     print $wvtest_output $_ if $wvtest_output;
144     chomp;
145     s/\r//g;
146
147     if (/^(\([0-9]+\) (#   )?)?!\s*(.*?)\s+(\S+)\s*$/) {
148         $tests_executed++;
149         $tests_failed++ if ($4 ne "ok");
150     }
151     elsif (/wvtest: timeout (\d+)\s*$/) {
152         $timeout=$1;
153         alarm($timeout);
154     }
155     elsif (/sc: wait for child/) { $waits_for_child = 1; }
156     elsif (/wvtest: ignore exit patterns/) { $ignore_exit_patterns = 1; }
157     elsif (/wvtest: expect ([0-9]+) tests/) {
158         check_number_of_tests();
159         $expected_test_count = $1;
160         $expected_test_base = $tests_executed;
161     }
162     elsif (matches_exit_pattern($_))
163     {
164         if (!$ENV{WVTEST_NOKILL}) {
165             kill 15, $pid;      # Kill novaboot or qemu
166             $kill_ok = 1;
167         } else {
168             use POSIX;
169             my $pid = fork();
170             if ($pid) {
171                 print "Keeping PID $pid alive\n";
172                 print $wvtest_output "Keeping PID $pid alive\n" if $wvtest_output;
173                 close($wvtest_output) if $wvtest_output;
174                 POSIX::_exit(0);
175             } else {
176                 # Continue printing on background
177                 close($wvtest_output) if $wvtest_output;
178                 while (<$pty>) { print; }
179                 POSIX::_exit(0);
180             }
181         }
182     }
183 }
184 my $newpid = waitpid($pid, 0);
185 if ($newpid != $pid) {
186     die("waitpid returned '$newpid', expected '$pid'\n");
187 }
188
189 my $code = $?;
190 my $ret = ($code >> 8);
191
192 if ($code && !$ret) {
193     if ($kill_ok && $code == 15) {
194         # We have killed the child - it is OK
195         $code = 0;
196     } else {
197         # return death-from-signal exits as >128.  This is what bash does if you ran
198         # the program directly.
199         $ret = $code | 128;
200     }
201 }
202
203 if ($ret != 0) {
204     print "! $0: Program '", join(" ", @ARGV), "' returned non-zero exit code ($ret)  FAILED\n";
205 }
206
207 if (!$ENV{WVTEST_EXIT_PATTERN}) {
208     printf "! $0: \$tests_executed > 0  %s\n", ($tests_executed > 0) ? "ok" : "FAILED";
209 }
210 check_number_of_tests();
211
212 if ($tests_failed > 0) { $ret = 1; }
213 if ($ret == 0 && $tests_executed == 0) { $ret = 1; }
214
215 exit $ret;