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",
|
"attrs>=17.4.0",
|
||||||
"more-itertools>=4.0.0",
|
"more-itertools>=4.0.0",
|
||||||
"atomicwrites>=1.0",
|
"atomicwrites>=1.0",
|
||||||
|
"monotonic",
|
||||||
]
|
]
|
||||||
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
|
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
|
||||||
# used by tox.ini to test with pluggy master
|
# used by tox.ini to test with pluggy master
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import gc
|
import gc
|
||||||
|
import monotonic
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
@ -482,6 +483,9 @@ class Testdir(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class TimeoutExpired(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self, request, tmpdir_factory):
|
def __init__(self, request, tmpdir_factory):
|
||||||
self.request = request
|
self.request = request
|
||||||
self._mod_collections = WeakKeyDictionary()
|
self._mod_collections = WeakKeyDictionary()
|
||||||
|
@ -1039,7 +1043,7 @@ class Testdir(object):
|
||||||
|
|
||||||
return popen
|
return popen
|
||||||
|
|
||||||
def run(self, *cmdargs):
|
def run(self, *cmdargs, **kwargs):
|
||||||
"""Run a command with arguments.
|
"""Run a command with arguments.
|
||||||
|
|
||||||
Run a process using subprocess.Popen saving the stdout and stderr.
|
Run a process using subprocess.Popen saving the stdout and stderr.
|
||||||
|
@ -1061,7 +1065,27 @@ class Testdir(object):
|
||||||
popen = self.popen(
|
popen = self.popen(
|
||||||
cmdargs, stdout=f1, stderr=f2, close_fds=(sys.platform != "win32")
|
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:
|
finally:
|
||||||
f1.close()
|
f1.close()
|
||||||
f2.close()
|
f2.close()
|
||||||
|
@ -1119,7 +1143,13 @@ class Testdir(object):
|
||||||
if plugins:
|
if plugins:
|
||||||
args = ("-p", plugins[0]) + args
|
args = ("-p", plugins[0]) + args
|
||||||
args = self._getpytestargs() + 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):
|
def spawn_pytest(self, string, expect_timeout=10.0):
|
||||||
"""Run pytest using pexpect.
|
"""Run pytest using pexpect.
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function
|
||||||
import os
|
import os
|
||||||
import py.path
|
import py.path
|
||||||
import pytest
|
import pytest
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import _pytest.pytester as pytester
|
import _pytest.pytester as pytester
|
||||||
from _pytest.pytester import HookRecorder
|
from _pytest.pytester import HookRecorder
|
||||||
|
@ -401,3 +402,20 @@ def test_testdir_subprocess(testdir):
|
||||||
def test_unicode_args(testdir):
|
def test_unicode_args(testdir):
|
||||||
result = testdir.runpytest("-k", u"💩")
|
result = testdir.runpytest("-k", u"💩")
|
||||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
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