diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/capnproto-git-20161025/mega-test.py	Tue Oct 25 11:17:01 2016 +0100
@@ -0,0 +1,144 @@
+#! /usr/bin/env python
+
+# MEGA TEST
+#
+# usage:  mega-test.py <config>
+#
+# This runs several tests in parallel and shows progress bars for each, based on a config file.
+#
+# <config> is a file containing a list of commands to run along with the expected number of lines
+# they will output (to stdout and stderr combined), which is how the progress bar is calculated.
+# The format of the file is simply one test per line, with the line containing the test name,
+# the number of output lines expected, and the test command.  Example:
+#
+#     mytest 1523 ./my-test --foo bar
+#     another 862 ./another-test --baz
+#
+# Each command is interpreted by `sh -euc`, therefore it is acceptable to use environment
+# variables and other shell syntax.
+#
+# After all tests complete, the config file will be rewritten to update the line counts to the
+# actual number of lines seen for all passing tests (failing tests are not updated).
+
+import sys
+import re
+import os
+from errno import EAGAIN
+from fcntl import fcntl, F_GETFL, F_SETFL
+from select import poll, POLLIN, POLLHUP
+from subprocess import Popen, PIPE, STDOUT
+
+CONFIG_LINE = re.compile("^([^ ]+) +([0-9]+) +(.*)$")
+
+if len(sys.argv) != 2:
+  sys.stderr.write("Wrong number of arguments.\n");
+  sys.exit(1)
+
+if not os.access("/tmp/test-output", os.F_OK):
+  os.mkdir("/tmp/test-output")
+
+config = open(sys.argv[1], 'r')
+
+tests = []
+
+class Test:
+  def __init__(self, name, command, lines):
+    self.name = name
+    self.command = command
+    self.lines = lines
+    self.count = 0
+    self.done = False
+
+  def start(self, poller):
+    self.proc = Popen(["sh", "-euc", test.command], stdin=dev_null, stdout=PIPE, stderr=STDOUT)
+    fd = self.proc.stdout.fileno()
+    flags = fcntl(fd, F_GETFL)
+    fcntl(fd, F_SETFL, flags | os.O_NONBLOCK)
+    poller.register(self.proc.stdout, POLLIN)
+    self.log = open("/tmp/test-output/" + self.name + ".log", "w")
+
+  def update(self):
+    try:
+      while True:
+        text = self.proc.stdout.read()
+        if text == "":
+          self.proc.wait()
+          self.done = True
+          self.log.close()
+          return True
+        self.count += text.count("\n")
+        self.log.write(text)
+    except IOError as e:
+      if e.errno == EAGAIN:
+        return False
+      raise
+
+  def print_bar(self):
+    percent = self.count * 100 / self.lines
+    status = "(%3d%%)" % percent
+
+    color_on = ""
+    color_off = ""
+
+    if self.done:
+      if self.proc.returncode == 0:
+        color_on = "\033[0;32m"
+        status = "PASS"
+      else:
+        color_on = "\033[0;31m"
+        status = "FAIL: /tmp/test-output/%s.log" % self.name
+      color_off = "\033[0m"
+
+    print "%s%-16s |%-25s| %6d/%6d %s%s    " % (
+        color_on, self.name, '=' * min(percent / 4, 25), self.count, self.lines, status, color_off)
+
+  def passed(self):
+    return self.proc.returncode == 0
+
+for line in config:
+  if len(line) > 0 and not line.startswith("#"):
+    match = CONFIG_LINE.match(line)
+    if not match:
+      sys.stderr.write("Invalid config syntax: %s\n" % line);
+      sys.exit(1)
+    test = Test(match.group(1), match.group(3), int(match.group(2)))
+    tests.append(test)
+
+config.close()
+
+dev_null = open("/dev/null", "rw")
+poller = poll()
+fd_map = {}
+
+for test in tests:
+  test.start(poller)
+  fd_map[test.proc.stdout.fileno()] = test
+
+active_count = len(tests)
+
+def print_bars():
+  for test in tests:
+    test.print_bar()
+
+print_bars()
+
+while active_count > 0:
+  for (fd, event) in poller.poll():
+    if fd_map[fd].update():
+      active_count -= 1
+      poller.unregister(fd)
+  sys.stdout.write("\033[%dA\r" % len(tests))
+  print_bars()
+
+new_config = open(sys.argv[1], "w")
+for test in tests:
+  if test.passed():
+    new_config.write("%-16s %6d %s\n" % (test.name, test.count, test.command))
+  else:
+    new_config.write("%-16s %6d %s\n" % (test.name, test.lines, test.command))
+
+for test in tests:
+  if not test.passed():
+    sys.exit(1)
+
+sys.exit(0)