Skip to content

Commit d5b61e7

Browse files
authored
Merge pull request #351 from cybojanek/master
Issue node shutdown on empty workload
2 parents 86e2fb5 + fe06f27 commit d5b61e7

File tree

6 files changed

+57
-0
lines changed

6 files changed

+57
-0
lines changed

changelog/351.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix scheduling deadlock in case of inter-test locking.

testing/acceptance_test.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,56 @@ def test_2():
11321132
assert c1 == c2
11331133

11341134

1135+
class TestLocking:
1136+
_test_content = """
1137+
class TestClassName%s(object):
1138+
1139+
@classmethod
1140+
def setup_class(cls):
1141+
FILE_LOCK.acquire()
1142+
1143+
@classmethod
1144+
def teardown_class(cls):
1145+
FILE_LOCK.release()
1146+
1147+
def test_a(self):
1148+
pass
1149+
1150+
def test_b(self):
1151+
pass
1152+
1153+
def test_c(self):
1154+
pass
1155+
1156+
"""
1157+
1158+
test_file1 = """
1159+
import filelock
1160+
1161+
FILE_LOCK = filelock.FileLock("test.lock")
1162+
1163+
""" + (
1164+
(_test_content * 4) % ("A", "B", "C", "D")
1165+
)
1166+
1167+
@pytest.mark.parametrize("scope", ["each", "load", "loadscope", "loadfile", "no"])
1168+
def test_single_file(self, testdir, scope):
1169+
testdir.makepyfile(test_a=self.test_file1)
1170+
result = testdir.runpytest("-n2", "--dist=%s" % scope, "-v")
1171+
result.assert_outcomes(passed=(12 if scope != "each" else 12 * 2))
1172+
1173+
@pytest.mark.parametrize("scope", ["each", "load", "loadscope", "loadfile", "no"])
1174+
def test_multi_file(self, testdir, scope):
1175+
testdir.makepyfile(
1176+
test_a=self.test_file1,
1177+
test_b=self.test_file1,
1178+
test_c=self.test_file1,
1179+
test_d=self.test_file1,
1180+
)
1181+
result = testdir.runpytest("-n2", "--dist=%s" % scope, "-v")
1182+
result.assert_outcomes(passed=(48 if scope != "each" else 48 * 2))
1183+
1184+
11351185
def parse_tests_and_workers_from_output(lines):
11361186
result = []
11371187
for line in lines:

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ deps =
2323
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
2424
pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
2525
pexpect: pexpect
26+
filelock
2627
platform=
2728
pexpect: linux|darwin
2829
commands=

xdist/scheduler/each.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def schedule(self):
126126
if not pending:
127127
pending[:] = range(len(self.node2collection[node]))
128128
node.send_runtest_all()
129+
node.shutdown()
129130
else:
130131
node.send_runtest_some(pending)
131132
self._started.append(node)

xdist/scheduler/load.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ def check_schedule(self, node, duration=0):
178178
return
179179
num_send = items_per_node_max - len(node_pending)
180180
self._send_tests(node, num_send)
181+
else:
182+
node.shutdown()
183+
181184
self.log("num items waiting for node:", len(self.pending))
182185

183186
def remove_node(self, node):

xdist/scheduler/loadscope.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ def _reschedule(self, node):
306306

307307
# Check that more work is available
308308
if not self.workqueue:
309+
node.shutdown()
309310
return
310311

311312
self.log("Number of units waiting for node:", len(self.workqueue))

0 commit comments

Comments
 (0)