| """ |
| Helper for testing. |
| """ |
|
|
| import os.path |
| import re |
| import subprocess |
| import sys |
| import threading |
| import warnings |
|
|
| import _pytest |
| import pytest |
|
|
| raises = pytest.raises |
| warns = pytest.warns |
| SkipTest = _pytest.runner.Skipped |
| skipif = pytest.mark.skipif |
| fixture = pytest.fixture |
| parametrize = pytest.mark.parametrize |
| timeout = pytest.mark.timeout |
| xfail = pytest.mark.xfail |
| param = pytest.param |
|
|
|
|
| def warnings_to_stdout(): |
| """Redirect all warnings to stdout.""" |
| showwarning_orig = warnings.showwarning |
|
|
| def showwarning(msg, cat, fname, lno, file=None, line=0): |
| showwarning_orig(msg, cat, os.path.basename(fname), line, sys.stdout) |
|
|
| warnings.showwarning = showwarning |
| |
|
|
|
|
| def check_subprocess_call(cmd, timeout=5, stdout_regex=None, stderr_regex=None): |
| """Runs a command in a subprocess with timeout in seconds. |
| |
| A SIGTERM is sent after `timeout` and if it does not terminate, a |
| SIGKILL is sent after `2 * timeout`. |
| |
| Also checks returncode is zero, stdout if stdout_regex is set, and |
| stderr if stderr_regex is set. |
| """ |
| proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|
|
| def terminate_process(): |
| """ |
| Attempt to terminate a leftover process spawned during test execution: |
| ideally this should not be needed but can help avoid clogging the CI |
| workers in case of deadlocks. |
| """ |
| warnings.warn(f"Timeout running {cmd}") |
| proc.terminate() |
|
|
| def kill_process(): |
| """ |
| Kill a leftover process spawned during test execution: ideally this |
| should not be needed but can help avoid clogging the CI workers in |
| case of deadlocks. |
| """ |
| warnings.warn(f"Timeout running {cmd}") |
| proc.kill() |
|
|
| try: |
| if timeout is not None: |
| terminate_timer = threading.Timer(timeout, terminate_process) |
| terminate_timer.start() |
| kill_timer = threading.Timer(2 * timeout, kill_process) |
| kill_timer.start() |
| stdout, stderr = proc.communicate() |
| stdout, stderr = stdout.decode(), stderr.decode() |
| if proc.returncode != 0: |
| message = ("Non-zero return code: {}.\nStdout:\n{}\nStderr:\n{}").format( |
| proc.returncode, stdout, stderr |
| ) |
| raise ValueError(message) |
|
|
| if stdout_regex is not None and not re.search(stdout_regex, stdout): |
| raise ValueError( |
| "Unexpected stdout: {!r} does not match:\n{!r}".format( |
| stdout_regex, stdout |
| ) |
| ) |
| if stderr_regex is not None and not re.search(stderr_regex, stderr): |
| raise ValueError( |
| "Unexpected stderr: {!r} does not match:\n{!r}".format( |
| stderr_regex, stderr |
| ) |
| ) |
|
|
| finally: |
| if timeout is not None: |
| terminate_timer.cancel() |
| kill_timer.cancel() |
|
|