aboutsummaryrefslogtreecommitdiffstats
path: root/testing/runner.py
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2017-12-07 14:47:36 +0100
committerChristian Tismer <tismer@stackless.com>2017-12-21 15:04:31 +0000
commit5c7e140036e26d8dad27ef8b7da984f093e97787 (patch)
tree2af91f5f903be5a6ca8ea5d1b885ea6dc6c362b7 /testing/runner.py
parent5f59ce344ffb1086b74e34fa24257b87d9325cd2 (diff)
testrunner 5: Improve the algorithm
Testrunner checks if it is running in COIN. If so, it runs each tested project 5 times and reports an error if 3 errors were found in a test function and not blacklisted. The time is measured, so we know if we can run all the tests five times or if we can run only the failed tests. At the moment, the option COIN_RERUN_FAILED_ONLY is activated by default. We can configure it by setting to false. Since this change turns the registry existence test into a flaky test (te tests generates it's missing file in the first run), the registry existence test is no more blacklisted. We simulate our own tee command now with decorated output. Task-number: PYSIDE-578 Change-Id: I6390cd50398a97a168c85c6bb778984c6a5b90fc Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'testing/runner.py')
-rw-r--r--testing/runner.py85
1 files changed, 72 insertions, 13 deletions
diff --git a/testing/runner.py b/testing/runner.py
index 421165e56..90076f582 100644
--- a/testing/runner.py
+++ b/testing/runner.py
@@ -43,6 +43,7 @@ import os
import sys
import re
import subprocess
+import inspect
from collections import namedtuple
from textwrap import dedent
@@ -52,12 +53,15 @@ from .helper import decorate, PY3, TimeoutExpired
class TestRunner(object):
- def __init__(self, log_entry, project):
+ def __init__(self, log_entry, project, index):
self.log_entry = log_entry
built_path = log_entry.build_dir
self.test_dir = os.path.join(built_path, project)
log_dir = log_entry.log_dir
- self.logfile = os.path.join(log_dir, project + ".log")
+ if index is not None:
+ self.logfile = os.path.join(log_dir, project + ".{}.log".format(index))
+ else:
+ self.logfile = os.path.join(log_dir, project + ".log")
os.environ['CTEST_OUTPUT_ON_FAILURE'] = '1'
self._setup()
@@ -97,7 +101,7 @@ class TestRunner(object):
def _setup(self):
self.ctestCommand = self._find_ctest()
- def _run(self, cmd_tuple, timeout):
+ def _run(self, cmd_tuple, label, timeout):
"""
Perform a test run in a given build
@@ -105,26 +109,73 @@ class TestRunner(object):
this script. Also, a timeout can be used.
After the change to directly using ctest, we no longer use
- "--force-new-ctest-process". Until now this han no drawbacks
- but was a littls faster.
+ "--force-new-ctest-process". Until now this has no drawbacks
+ but was a little faster.
"""
self.cmd = cmd_tuple
- shell_option = sys.platform == "win32"
+ # We no longer use the shell option. It introduces wrong handling
+ # of certain characters which are not yet correctly escaped:
+ # Especially the "^" caret char is treated as an escape, and pipe symbols
+ # without a caret are interpreted as such which leads to weirdness.
+ # Since we have all commands with explicit paths and don't use shell
+ # commands, this should work fine.
print(dedent("""\
running {cmd}
in {test_dir}
""").format(**self.__dict__))
ctest_process = subprocess.Popen(self.cmd,
cwd=self.test_dir,
- stderr=subprocess.STDOUT,
- shell=shell_option)
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ def py_tee(input, output, label):
+ '''
+ A simple (incomplete) tee command in Python
+
+ This script simply logs everything from input to output
+ while the output gets some decoration. The specific reason
+ to have this script at all is:
+
+ - it is necessary to have some decoration as prefix, since
+ we run commands several times
+
+ - collecting all output and then decorating is not nice if
+ you have to wait for a long time
+
+ The special escape is for the case of an embedded file in
+ the output.
+ '''
+ def xprint(*args, **kw):
+ print(*args, file=output, **kw)
+
+ while True:
+ line = input.readline()
+ if not line:
+ break
+ labelled = True
+ if line.startswith('BEGIN_FILE'):
+ labelled = False
+ txt = line.rstrip()
+ xprint(label, txt) if label and labelled else xprint(txt)
+ if line.startswith('END_FILE'):
+ labelled = True
+
+ tee_src = dedent("""\
+ from __future__ import print_function
+ import sys
+ {}
+ py_tee(sys.stdin, sys.stdout, '{label}')
+ """).format(dedent(inspect.getsource(py_tee)), label=label)
+ tee_cmd = (sys.executable, "-E", "-u", "-c", tee_src)
+ tee_process = subprocess.Popen(tee_cmd,
+ cwd=self.test_dir,
+ stdin=ctest_process.stdout)
try:
- comm = ctest_process.communicate
+ comm = tee_process.communicate
output = (comm(timeout=timeout) if PY3 else comm())[0]
except (TimeoutExpired, KeyboardInterrupt):
print()
- print("aborted, partial resut")
+ print("aborted, partial result")
ctest_process.kill()
outs, errs = ctest_process.communicate()
# ctest lists to a temp file. Move it to the log
@@ -138,8 +189,16 @@ class TestRunner(object):
self.partial = False
finally:
print("End of the test run")
- ctest_process.wait()
+ print()
+ tee_process.wait()
- def run(self, timeout=10 * 60):
+ def run(self, label, rerun, timeout):
cmd = self.ctestCommand, "--output-log", self.logfile
- self._run(cmd, timeout)
+ if rerun is not None:
+ # cmd += ("--rerun-failed",)
+ # For some reason, this worked never in the script file.
+ # We pass instead the test names as a regex:
+ words = "^(" + "|".join(rerun) + ")$"
+ cmd += ("--tests-regex", words)
+ self._run(cmd, label, timeout)
+# eof