annotate src/capnproto-git-20161025/mega-test.py @ 83:ae30d91d2ffe

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