Mercurial > hg > sv-dependency-builds
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) |