]> rtime.felk.cvut.cz Git - frescor/frsh-forb.git/blob - src/wvtest/wvtestrun
wvtestrun: Visually delimit the output of failed command
[frescor/frsh-forb.git] / src / wvtest / wvtestrun
1 #!/usr/bin/perl -w
2 #
3 # WvTest:
4 #   Copyright (C)2007-2009 Versabanq Innovations Inc. and contributors.
5 #       Licensed under the GNU Library General Public License, version 2.
6 #       See the included file named LICENSE for license information.
7 #
8 use strict;
9 use Time::HiRes qw(time);
10
11 # always flush
12 $| = 1;
13
14 if (@ARGV < 1) {
15     print STDERR "Usage: $0 <command line...>\n";
16     exit 127;
17 }
18
19 print STDERR "Testing \"all\" in @ARGV:\n";
20
21 my $pid = open(my $fh, "-|");
22 if (!$pid) {
23     # child
24     setpgrp();
25     open STDERR, '>&STDOUT' or die("Can't dup stdout: $!\n");
26     exec(@ARGV);
27     exit 126; # just in case
28 }
29
30 my $istty = -t STDOUT && $ENV{'TERM'} ne "dumb";
31 my @log = ();
32 my ($gpasses, $gfails) = (0,0);
33
34 sub bigkill($)
35 {
36     my $pid = shift;
37
38     if (@log) {
39         print "\n" . join("\n", @log) . "\n";
40     }
41
42     print STDERR "\n! Killed by signal    FAILED\n";
43
44     ($pid > 0) || die("pid is '$pid'?!\n");
45
46     local $SIG{CHLD} = sub { }; # this will wake us from sleep() faster
47     kill 15, $pid;
48     sleep(2);
49
50     if ($pid > 1) {
51         kill 9, -$pid;
52     }
53     kill 9, $pid;
54
55     exit(125);
56 }
57
58 # parent
59 local $SIG{INT} = sub { bigkill($pid); };
60 local $SIG{TERM} = sub { bigkill($pid); };
61 local $SIG{ALRM} = sub {
62     print STDERR "Alarm timed out!  No test results for too long.\n";
63     bigkill($pid);
64 };
65
66 my $column = `tput cols` - 10;
67
68 sub colourize($)
69 {
70     my $result = shift;
71     my $pass = ($result eq "ok");
72
73     if ($istty) {
74         my $colour = $pass ? "\e[32;1m" : "\e[31;1m";
75         my $column = "\e[${column}G";
76         return "$column$colour$result\e[0m";
77     } else {
78         return $result;
79     }
80 }
81
82 sub mstime($$$)
83 {
84     my ($floatsec, $warntime, $badtime) = @_;
85     my $ms = int($floatsec * 1000);
86     my $str = sprintf("%d.%03ds", $ms/1000, $ms % 1000);
87
88     if ($istty && $ms > $badtime) {
89         return "\e[31;1m$str\e[0m";
90     } elsif ($istty && $ms > $warntime) {
91         return "\e[33;1m$str\e[0m";
92     } else {
93         return "$str";
94     }
95 }
96
97 sub resultline($$)
98 {
99     my ($name, $result) = @_;
100     return sprintf("! %-65s %s", $name, colourize($result));
101 }
102
103 my $allstart = time();
104 my ($start, $stop);
105
106 sub endsect()
107 {
108     $stop = time();
109     if ($start) {
110         printf " %s %s\n", mstime($stop - $start, 500, 1000), colourize("ok");
111     }
112 }
113
114 while (<$fh>)
115 {
116     chomp;
117     s/\r//g;
118
119     if (/^\s*Testing "(.*)" in (.*):\s*$/)
120     {
121         alarm(120);
122         my ($sect, $file) = ($1, $2);
123
124         endsect();
125
126         printf("! %s  %s: ", $file, $sect);
127         @log = ();
128         push @log, "-"x78;
129         $start = $stop;
130     }
131     elsif (/^!\s*(.*?)\s+(\S+)\s*$/)
132     {
133         alarm(120);
134
135         my ($name, $result) = ($1, $2);
136         my $pass = ($result eq "ok");
137
138         if (!$start) {
139             printf("\n! Startup: ");
140             $start = time();
141         }
142
143         push @log, resultline($name, $result);
144
145         if (!$pass) {
146             $gfails++;
147             if (@log) {
148                 print "\n" . join("\n", @log) . "\n";
149                 @log = ();
150             }
151         } else {
152             $gpasses++;
153             print ".";
154         }
155     }
156     else
157     {
158         push @log, $_;
159     }
160 }
161
162 endsect();
163
164 my $newpid = waitpid($pid, 0);
165 if ($newpid != $pid) {
166     die("waitpid returned '$newpid', expected '$pid'\n");
167 }
168
169 my $code = $?;
170 my $ret = ($code >> 8);
171
172 # return death-from-signal exits as >128.  This is what bash does if you ran
173 # the program directly.
174 if ($code && !$ret) { $ret = $code | 128; }
175
176 if ($ret && @log) {
177     print "\n" . join("\n", @log) . "\n";
178 }
179
180 if ($code != 0) {
181     print resultline("Program returned non-zero exit code ($ret)", "FAILED");
182 }
183
184 my $gtotal = $gpasses+$gfails;
185 printf("\nWvTest: %d test%s, %d failure%s, total time %s.\n",
186     $gtotal, $gtotal==1 ? "" : "s",
187     $gfails, $gfails==1 ? "" : "s",
188     mstime(time() - $allstart, 2000, 5000));
189 print STDERR "\nWvTest result code: $ret\n";
190 exit( $ret ? $ret : ($gfails ? 125 : 0) );