Skip to content

Commit ddeee2b

Browse files
committedSep 4, 2013
Improve test scripts, and add a new mega-test script which runs super-test on all platforms in parallel with progress bars.
1 parent 0613b08 commit ddeee2b

File tree

4 files changed

+191
-3
lines changed

4 files changed

+191
-3
lines changed
 

‎mega-test-quick.cfg

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
linux-gcc-4.7 1428 ./super-test.sh tmpdir capnp-gcc-4.7 quick
2+
linux-gcc-4.8 1431 ./super-test.sh tmpdir capnp-gcc-4.8 quick gcc-4.8
3+
linux-clang 1445 ./super-test.sh tmpdir capnp-clang quick clang
4+
mac 704 ./super-test.sh remote beat caffeinate quick
5+
cygwin 709 ./super-test.sh remote Kenton@flashman quick

‎mega-test.cfg

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
linux-gcc-4.7 12075 ./super-test.sh tmpdir capnp-gcc-4.7
2+
linux-gcc-4.8 10761 ./super-test.sh tmpdir capnp-gcc-4.8 gcc-4.8
3+
linux-clang 12174 ./super-test.sh tmpdir capnp-clang clang
4+
mac 5064 ./super-test.sh remote beat caffeinate
5+
cygwin 5817 ./super-test.sh remote Kenton@flashman

‎mega-test.py

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#! /usr/bin/env python
2+
3+
# MEGA TEST
4+
#
5+
# usage: mega-test.py <config>
6+
#
7+
# This runs several tests in parallel and shows progress bars for each, based on a config file.
8+
#
9+
# <config> is a file containing a list of commands to run along with the expected number of lines
10+
# they will output (to stdout and stderr combined), which is how the progress bar is calculated.
11+
# The format of the file is simply one test per line, with the line containing the test name,
12+
# the number of output lines expected, and the test command. Example:
13+
#
14+
# mytest 1523 ./my-test --foo bar
15+
# another 862 ./another-test --baz
16+
#
17+
# Each command is interpreted by `sh -euc`, therefore it is acceptable to use environment
18+
# variables and other shell syntax.
19+
#
20+
# After all tests complete, the config file will be rewritten to update the line counts to the
21+
# actual number of lines seen for all passing tests (failing tests are not updated).
22+
23+
import sys
24+
import re
25+
import os
26+
from errno import EAGAIN
27+
from fcntl import fcntl, F_GETFL, F_SETFL
28+
from select import poll, POLLIN, POLLHUP
29+
from subprocess import Popen, PIPE, STDOUT
30+
31+
CONFIG_LINE = re.compile("^([^ ]+) +([0-9]+) +(.*)$")
32+
33+
if len(sys.argv) != 2:
34+
sys.stderr.write("Wrong number of arguments.\n");
35+
sys.exit(1)
36+
37+
if not os.access("/tmp/test-output", os.F_OK):
38+
os.mkdir("/tmp/test-output")
39+
40+
config = open(sys.argv[1], 'r')
41+
42+
tests = []
43+
44+
class Test:
45+
def __init__(self, name, command, lines):
46+
self.name = name
47+
self.command = command
48+
self.lines = lines
49+
self.count = 0
50+
self.done = False
51+
52+
def start(self, poller):
53+
self.proc = Popen(["sh", "-euc", test.command], stdin=dev_null, stdout=PIPE, stderr=STDOUT)
54+
fd = self.proc.stdout.fileno()
55+
flags = fcntl(fd, F_GETFL)
56+
fcntl(fd, F_SETFL, flags | os.O_NONBLOCK)
57+
poller.register(self.proc.stdout, POLLIN)
58+
self.log = open("/tmp/test-output/" + self.name + ".log", "w")
59+
60+
def update(self):
61+
try:
62+
while True:
63+
text = self.proc.stdout.read()
64+
if text == "":
65+
self.proc.wait()
66+
self.done = True
67+
self.log.close()
68+
return True
69+
self.count += text.count("\n")
70+
self.log.write(text)
71+
except IOError as e:
72+
if e.errno == EAGAIN:
73+
return False
74+
raise
75+
76+
def print_bar(self):
77+
percent = self.count * 100 / self.lines
78+
status = "(%3d%%)" % percent
79+
80+
color_on = ""
81+
color_off = ""
82+
83+
if self.done:
84+
if self.proc.returncode == 0:
85+
color_on = "\033[0;32m"
86+
status = "PASS"
87+
else:
88+
color_on = "\033[0;31m"
89+
status = "FAIL: /tmp/test-output/%s.log" % self.name
90+
color_off = "\033[0m"
91+
92+
print "%s%-16s |%-25s| %6d/%6d %s%s " % (
93+
color_on, self.name, '=' * min(percent / 4, 25), self.count, self.lines, status, color_off)
94+
95+
def passed(self):
96+
return self.proc.returncode == 0
97+
98+
for line in config:
99+
if len(line) > 0 and not line.startswith("#"):
100+
match = CONFIG_LINE.match(line)
101+
if not match:
102+
sys.stderr.write("Invalid config syntax: %s\n" % line);
103+
sys.exit(1)
104+
test = Test(match.group(1), match.group(3), int(match.group(2)))
105+
tests.append(test)
106+
107+
config.close()
108+
109+
dev_null = open("/dev/null", "rw")
110+
poller = poll()
111+
fd_map = {}
112+
113+
for test in tests:
114+
test.start(poller)
115+
fd_map[test.proc.stdout.fileno()] = test
116+
117+
active_count = len(tests)
118+
119+
def print_bars():
120+
for test in tests:
121+
test.print_bar()
122+
123+
print_bars()
124+
125+
while active_count > 0:
126+
for (fd, event) in poller.poll():
127+
if fd_map[fd].update():
128+
active_count -= 1
129+
poller.unregister(fd)
130+
sys.stdout.write("\033[%dA\r" % len(tests))
131+
print_bars()
132+
133+
new_config = open(sys.argv[1], "w")
134+
for test in tests:
135+
if test.passed():
136+
new_config.write("%-16s %6d %s\n" % (test.name, test.count, test.command))
137+
else:
138+
new_config.write("%-16s %6d %s\n" % (test.name, test.lines, test.command))
139+
140+
for test in tests:
141+
if not test.passed():
142+
sys.exit(1)
143+
144+
sys.exit(0)

‎super-test.sh

+37-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,39 @@ while [ $# -gt 0 ]; do
1616
quick )
1717
QUICK=quick
1818
;;
19+
caffeinate )
20+
# Re-run preventing sleep.
21+
shift
22+
exec caffeinate $0 $@
23+
;;
24+
tmpdir )
25+
# Clone to a temp directory.
26+
if [ "$#" -lt 2 ]; then
27+
echo "usage: $0 tmpdir NAME [COMMAND]" >&2
28+
exit 1
29+
fi
30+
DIR=/tmp/$2
31+
shift 2
32+
if [ -e $DIR ]; then
33+
if [ "${DIR/*..*}" = "" ]; then
34+
echo "NO DO NOT PUT .. IN THERE IT'S GOING TO GO IN /tmp AND I'M GONNA DELETE IT" >&2
35+
exit 1
36+
fi
37+
if [ ! -e "$DIR/super-test.sh" ]; then
38+
echo "$DIR exists and it doesn't look like one of mine." >&2
39+
exit 1
40+
fi
41+
# make distcheck leaves non-writable files when it fails, so we need to chmod to be safe.
42+
chmod -R +w $DIR
43+
rm -rf $DIR
44+
fi
45+
git clone . $DIR
46+
if [ -e c++/gtest ]; then
47+
cp -r c++/gtest $DIR/c++/gtest
48+
fi
49+
cd $DIR
50+
exec ./super-test.sh $@
51+
;;
1952
remote )
2053
if [ "$#" -lt 2 ]; then
2154
echo "usage: $0 remote HOST [COMMAND]" >&2
@@ -29,8 +62,9 @@ while [ $# -gt 0 ]; do
2962
BRANCH=$(git rev-parse --abbrev-ref HEAD)
3063
ssh $HOST 'rm -rf tmp-test-capnp && mkdir tmp-test-capnp && git init tmp-test-capnp'
3164
git push ssh://$HOST/~/tmp-test-capnp "$BRANCH:test"
32-
ssh $HOST "cd tmp-test-capnp && git checkout test && ./super-test.sh $@ && cd .. && rm -rf tmp-test-capnp"
33-
exit 0
65+
ssh $HOST "cd tmp-test-capnp && git checkout test"
66+
scp -qr c++/gtest $HOST:~/tmp-test-capnp/c++/gtest
67+
exec ssh $HOST "cd tmp-test-capnp && ./super-test.sh $@ && cd .. && rm -rf tmp-test-capnp"
3468
;;
3569
clang )
3670
export CXX=clang++
@@ -65,7 +99,7 @@ __EOF__
6599
*************************************************************************
66100
=========================================================================
67101
__EOF__
68-
$0 remote beat $QUICK
102+
$0 remote beat caffeinate $QUICK
69103
cat << "__EOF__"
70104
=========================================================================
71105
*************************************************************************

0 commit comments

Comments
 (0)
Please sign in to comment.