annotate src/capnproto-git-20161025/mega-test.py @ 157:570d27da3fb5

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