Initial pass at timeout for subprocessing pytest
pytest-dev/pytest#4073
This commit is contained in:
parent
29d3faed66
commit
96b2ae6654
1
setup.py
1
setup.py
|
@ -65,6 +65,7 @@ def main():
|
|||
"attrs>=17.4.0",
|
||||
"more-itertools>=4.0.0",
|
||||
"atomicwrites>=1.0",
|
||||
"monotonic",
|
||||
]
|
||||
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
|
||||
# used by tox.ini to test with pluggy master
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
import codecs
|
||||
import gc
|
||||
import monotonic
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
|
@ -482,6 +483,9 @@ class Testdir(object):
|
|||
|
||||
"""
|
||||
|
||||
class TimeoutExpired(Exception):
|
||||
pass
|
||||
|
||||
def __init__(self, request, tmpdir_factory):
|
||||
self.request = request
|
||||
self._mod_collections = WeakKeyDictionary()
|
||||
|
@ -1039,7 +1043,7 @@ class Testdir(object):
|
|||
|
||||
return popen
|
||||
|
||||
def run(self, *cmdargs):
|
||||
def run(self, *cmdargs, **kwargs):
|
||||
"""Run a command with arguments.
|
||||
|
||||
Run a process using subprocess.Popen saving the stdout and stderr.
|
||||
|
@ -1061,7 +1065,27 @@ class Testdir(object):
|
|||
popen = self.popen(
|
||||
cmdargs, stdout=f1, stderr=f2, close_fds=(sys.platform != "win32")
|
||||
)
|
||||
ret = popen.wait()
|
||||
timeout = kwargs.get('timeout')
|
||||
if timeout is None:
|
||||
ret = popen.wait()
|
||||
elif six.PY3:
|
||||
try:
|
||||
ret = popen.wait(timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
raise self.TimeoutExpired
|
||||
else:
|
||||
end = monotonic.monotonic() + timeout
|
||||
|
||||
while True:
|
||||
ret = popen.poll()
|
||||
if ret is not None:
|
||||
break
|
||||
|
||||
remaining = end - monotonic.monotonic()
|
||||
if remaining <= 0:
|
||||
raise self.TimeoutExpired()
|
||||
|
||||
time.sleep(remaining * 0.9)
|
||||
finally:
|
||||
f1.close()
|
||||
f2.close()
|
||||
|
@ -1119,7 +1143,13 @@ class Testdir(object):
|
|||
if plugins:
|
||||
args = ("-p", plugins[0]) + args
|
||||
args = self._getpytestargs() + args
|
||||
return self.run(*args)
|
||||
|
||||
if "timeout" in kwargs:
|
||||
timeout = {"timeout": kwargs["timeout"]}
|
||||
else:
|
||||
timeout = {}
|
||||
|
||||
return self.run(*args, **timeout)
|
||||
|
||||
def spawn_pytest(self, string, expect_timeout=10.0):
|
||||
"""Run pytest using pexpect.
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function
|
|||
import os
|
||||
import py.path
|
||||
import pytest
|
||||
import subprocess
|
||||
import sys
|
||||
import _pytest.pytester as pytester
|
||||
from _pytest.pytester import HookRecorder
|
||||
|
@ -401,3 +402,20 @@ def test_testdir_subprocess(testdir):
|
|||
def test_unicode_args(testdir):
|
||||
result = testdir.runpytest("-k", u"💩")
|
||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
||||
|
||||
|
||||
def test_testdir_run_no_timeout(testdir):
|
||||
testfile = testdir.makepyfile("def test_no_timeout(): pass")
|
||||
assert testdir.runpytest_subprocess(testfile).ret == EXIT_OK
|
||||
|
||||
|
||||
def test_testdir_run_timeout_expires(testdir):
|
||||
testfile = testdir.makepyfile(
|
||||
"""
|
||||
import time
|
||||
|
||||
def test_timeout():
|
||||
time.sleep(10)"""
|
||||
)
|
||||
with pytest.raises(testdir.TimeoutExpired):
|
||||
testdir.runpytest_subprocess(testfile, timeout=0.5)
|
||||
|
|
Loading…
Reference in New Issue