From 001670969b7d127ec3c78a0d8b761c2cecde1a81 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 29 May 2016 21:47:46 +0200 Subject: [PATCH] tests: Ignore segfaults on pytest exit In various situations (especially on OS X), pytest segfaults on exit probably due to Qt/PyQt bugs. We now have a wrapper script which ignores those segfaults if pytest did run successfully. --- scripts/dev/run_pytest.py | 58 +++++++++++++++++++++++++++++++++++++++ tests/conftest.py | 11 ++++++++ tox.ini | 6 ++-- 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 scripts/dev/run_pytest.py diff --git a/scripts/dev/run_pytest.py b/scripts/dev/run_pytest.py new file mode 100644 index 000000000..228a068e0 --- /dev/null +++ b/scripts/dev/run_pytest.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2016 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""Wrapper around pytest to ignore segfaults on exit.""" + +import os +import sys +import subprocess +import signal + + +if __name__ == '__main__': + script_path = os.path.abspath(os.path.dirname(__file__)) + pytest_status_file = os.path.join(script_path, '..', '..', '.cache', + 'pytest_status') + + try: + os.remove(pytest_status_file) + except FileNotFoundError: + pass + + try: + subprocess.check_call([sys.executable, '-m', 'pytest'] + sys.argv[1:]) + except subprocess.CalledProcessError as exc: + is_segfault = exc.returncode in [128 + signal.SIGSEGV, -signal.SIGSEGV] + if is_segfault and os.path.exists(pytest_status_file): + print("Ignoring segfault on exit!") + with open(pytest_status_file, 'r', encoding='ascii') as f: + exit_status = int(f.read()) + else: + exit_status = exc.returncode + else: + exit_status = 0 + + try: + os.remove(pytest_status_file) + except FileNotFoundError: + pass + + sys.exit(exit_status) diff --git a/tests/conftest.py b/tests/conftest.py index 39e88e96c..06f686fbd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -163,3 +163,14 @@ def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() setattr(item, "rep_" + rep.when, rep) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_sessionfinish(exitstatus): + """Create a file to tell run_pytest.py how pytest exited.""" + outcome = yield + outcome.get_result() + status_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), + '..', '.cache', 'pytest_status') + with open(status_file, 'w', encoding='ascii') as f: + f.write(str(exitstatus)) diff --git a/tox.ini b/tox.ini index 82c33a969..1ac2364ae 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ deps = -r{toxinidir}/misc/requirements/requirements-tests.txt commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m pytest {posargs:tests} + {envpython} scripts/dev/run_pytest.py {posargs:tests} [testenv:py35-cov] basepython = python3.5 @@ -26,7 +26,7 @@ passenv = {[testenv]passenv} deps = {[testenv]deps} commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m pytest --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} + {envpython} scripts/dev/run_pytest.py --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} {envpython} scripts/dev/check_coverage.py {posargs} [testenv:py34-cov] @@ -36,7 +36,7 @@ passenv = {[testenv]passenv} deps = {[testenv]deps} commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m pytest --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} + {envpython} scripts/dev/run_pytest.py --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests} {envpython} scripts/dev/check_coverage.py {posargs} [testenv:mkvenv]