Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfd6029102 | ||
|
|
3f5a8eeb7f | ||
|
|
d5b61e72d4 | ||
|
|
f224c4b00e | ||
|
|
ddefd3f453 | ||
|
|
5ce8a6ec1a | ||
|
|
fe06f27b8b | ||
|
|
86e2fb5a5a |
@@ -1,3 +1,18 @@
|
|||||||
|
pytest-xdist 1.24.0 (2018-10-18)
|
||||||
|
================================
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- `#337 <https://github.com/pytest-dev/pytest-xdist/issues/337>`_: New ``--maxprocesses`` command-line option that limits the maximum number of workers when using ``--numprocesses=auto``.
|
||||||
|
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#351 <https://github.com/pytest-dev/pytest-xdist/issues/351>`_: Fix scheduling deadlock in case of inter-test locking.
|
||||||
|
|
||||||
|
|
||||||
pytest-xdist 1.23.2 (2018-09-28)
|
pytest-xdist 1.23.2 (2018-09-28)
|
||||||
================================
|
================================
|
||||||
|
|
||||||
|
|||||||
@@ -1132,6 +1132,56 @@ class TestFileScope:
|
|||||||
assert c1 == c2
|
assert c1 == c2
|
||||||
|
|
||||||
|
|
||||||
|
class TestLocking:
|
||||||
|
_test_content = """
|
||||||
|
class TestClassName%s(object):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_class(cls):
|
||||||
|
FILE_LOCK.acquire()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def teardown_class(cls):
|
||||||
|
FILE_LOCK.release()
|
||||||
|
|
||||||
|
def test_a(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_b(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_c(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
test_file1 = """
|
||||||
|
import filelock
|
||||||
|
|
||||||
|
FILE_LOCK = filelock.FileLock("test.lock")
|
||||||
|
|
||||||
|
""" + (
|
||||||
|
(_test_content * 4) % ("A", "B", "C", "D")
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("scope", ["each", "load", "loadscope", "loadfile", "no"])
|
||||||
|
def test_single_file(self, testdir, scope):
|
||||||
|
testdir.makepyfile(test_a=self.test_file1)
|
||||||
|
result = testdir.runpytest("-n2", "--dist=%s" % scope, "-v")
|
||||||
|
result.assert_outcomes(passed=(12 if scope != "each" else 12 * 2))
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("scope", ["each", "load", "loadscope", "loadfile", "no"])
|
||||||
|
def test_multi_file(self, testdir, scope):
|
||||||
|
testdir.makepyfile(
|
||||||
|
test_a=self.test_file1,
|
||||||
|
test_b=self.test_file1,
|
||||||
|
test_c=self.test_file1,
|
||||||
|
test_d=self.test_file1,
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("-n2", "--dist=%s" % scope, "-v")
|
||||||
|
result.assert_outcomes(passed=(48 if scope != "each" else 48 * 2))
|
||||||
|
|
||||||
|
|
||||||
def parse_tests_and_workers_from_output(lines):
|
def parse_tests_and_workers_from_output(lines):
|
||||||
result = []
|
result = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ def test_dist_options(testdir):
|
|||||||
check_options(config)
|
check_options(config)
|
||||||
assert config.option.dist == "load"
|
assert config.option.dist == "load"
|
||||||
assert config.option.tx == ["popen"] * 2
|
assert config.option.tx == ["popen"] * 2
|
||||||
|
config = testdir.parseconfigure("--numprocesses", "3", "--maxprocesses", "2")
|
||||||
|
check_options(config)
|
||||||
|
assert config.option.dist == "load"
|
||||||
|
assert config.option.tx == ["popen"] * 2
|
||||||
config = testdir.parseconfigure("-d")
|
config = testdir.parseconfigure("-d")
|
||||||
check_options(config)
|
check_options(config)
|
||||||
assert config.option.dist == "load"
|
assert config.option.dist == "load"
|
||||||
|
|||||||
1
tox.ini
1
tox.ini
@@ -23,6 +23,7 @@ deps =
|
|||||||
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
|
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
|
||||||
pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
|
pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
|
||||||
pexpect: pexpect
|
pexpect: pexpect
|
||||||
|
filelock
|
||||||
platform=
|
platform=
|
||||||
pexpect: linux|darwin
|
pexpect: linux|darwin
|
||||||
commands=
|
commands=
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ def pytest_addoption(parser):
|
|||||||
"you can use 'auto' here for auto detection CPUs number on "
|
"you can use 'auto' here for auto detection CPUs number on "
|
||||||
"host system",
|
"host system",
|
||||||
)
|
)
|
||||||
|
group.addoption(
|
||||||
|
"--maxprocesses",
|
||||||
|
dest="maxprocesses",
|
||||||
|
metavar="maxprocesses",
|
||||||
|
action="store",
|
||||||
|
type=int,
|
||||||
|
help="limit the maximum number of workers to process the tests when using --numprocesses=auto",
|
||||||
|
)
|
||||||
group.addoption(
|
group.addoption(
|
||||||
"--max-worker-restart",
|
"--max-worker-restart",
|
||||||
"--max-slave-restart",
|
"--max-slave-restart",
|
||||||
@@ -172,7 +180,10 @@ def pytest_cmdline_main(config):
|
|||||||
if config.option.numprocesses:
|
if config.option.numprocesses:
|
||||||
if config.option.dist == "no":
|
if config.option.dist == "no":
|
||||||
config.option.dist = "load"
|
config.option.dist = "load"
|
||||||
config.option.tx = ["popen"] * config.option.numprocesses
|
numprocesses = config.option.numprocesses
|
||||||
|
if config.option.maxprocesses:
|
||||||
|
numprocesses = min(numprocesses, config.option.maxprocesses)
|
||||||
|
config.option.tx = ["popen"] * numprocesses
|
||||||
if config.option.distload:
|
if config.option.distload:
|
||||||
config.option.dist = "load"
|
config.option.dist = "load"
|
||||||
val = config.getvalue
|
val = config.getvalue
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ def remote_initconfig(option_dict, args):
|
|||||||
config.option.dist = "no"
|
config.option.dist = "no"
|
||||||
config.option.distload = False
|
config.option.distload = False
|
||||||
config.option.numprocesses = None
|
config.option.numprocesses = None
|
||||||
|
config.option.maxprocesses = None
|
||||||
config.args = args
|
config.args = args
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ class EachScheduling(object):
|
|||||||
if not pending:
|
if not pending:
|
||||||
pending[:] = range(len(self.node2collection[node]))
|
pending[:] = range(len(self.node2collection[node]))
|
||||||
node.send_runtest_all()
|
node.send_runtest_all()
|
||||||
|
node.shutdown()
|
||||||
else:
|
else:
|
||||||
node.send_runtest_some(pending)
|
node.send_runtest_some(pending)
|
||||||
self._started.append(node)
|
self._started.append(node)
|
||||||
|
|||||||
@@ -178,6 +178,9 @@ class LoadScheduling(object):
|
|||||||
return
|
return
|
||||||
num_send = items_per_node_max - len(node_pending)
|
num_send = items_per_node_max - len(node_pending)
|
||||||
self._send_tests(node, num_send)
|
self._send_tests(node, num_send)
|
||||||
|
else:
|
||||||
|
node.shutdown()
|
||||||
|
|
||||||
self.log("num items waiting for node:", len(self.pending))
|
self.log("num items waiting for node:", len(self.pending))
|
||||||
|
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ class LoadScopeScheduling(object):
|
|||||||
|
|
||||||
# Check that more work is available
|
# Check that more work is available
|
||||||
if not self.workqueue:
|
if not self.workqueue:
|
||||||
|
node.shutdown()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log("Number of units waiting for node:", len(self.workqueue))
|
self.log("Number of units waiting for node:", len(self.workqueue))
|
||||||
|
|||||||
Reference in New Issue
Block a user