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