comparison src/capnproto-git-20161025/mega-test.py @ 48:9530b331f8c1

Add Cap'n Proto source
author Chris Cannam <cannam@all-day-breakfast.com>
date Tue, 25 Oct 2016 11:17:01 +0100
parents
children
comparison
equal deleted inserted replaced
47:d93140aac40b 48:9530b331f8c1
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)