diff --git a/changelog/257.bugfix b/changelog/257.bugfix new file mode 100644 index 0000000..6b3cde6 --- /dev/null +++ b/changelog/257.bugfix @@ -0,0 +1 @@ +Fix issue when using ``loadscope`` or ``loadfile`` where tests would fail to start if the first scope had only one test. diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 56ecc0b..697070e 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -888,6 +888,39 @@ class TestLoadScope: assert get_workers_and_test_count_by_prefix( 'test_a.py::TestB', result.outlines) in ({'gw0': 10}, {'gw1': 10}) + def test_module_single_start(self, testdir): + """Fix test suite never finishing in case all workers start with a single test (#277).""" + test_file1 = """ + import pytest + def test(): + pass + """ + test_file2 = """ + import pytest + def test_1(): + pass + def test_2(): + pass + """ + testdir.makepyfile( + test_a=test_file1, + test_b=test_file1, + test_c=test_file2 + ) + result = testdir.runpytest('-n2', '--dist=loadscope', '-v') + a = get_workers_and_test_count_by_prefix('test_a.py::test', + result.outlines) + b = get_workers_and_test_count_by_prefix('test_b.py::test', + result.outlines) + c1 = get_workers_and_test_count_by_prefix('test_c.py::test_1', + result.outlines) + c2 = get_workers_and_test_count_by_prefix('test_c.py::test_2', + result.outlines) + assert a in ({'gw0': 1}, {'gw1': 1}) + assert b in ({'gw0': 1}, {'gw1': 1}) + assert a.items() != b.items() + assert c1 == c2 + class TestFileScope: @@ -943,6 +976,39 @@ class TestFileScope: assert test_b_workers_and_test_count in ({'gw0': 10}, {'gw1': 0}) or \ test_b_workers_and_test_count in ({'gw0': 0}, {'gw1': 10}) + def test_module_single_start(self, testdir): + """Fix test suite never finishing in case all workers start with a single test (#277).""" + test_file1 = """ + import pytest + def test(): + pass + """ + test_file2 = """ + import pytest + def test_1(): + pass + def test_2(): + pass + """ + testdir.makepyfile( + test_a=test_file1, + test_b=test_file1, + test_c=test_file2 + ) + result = testdir.runpytest('-n2', '--dist=loadfile', '-v') + a = get_workers_and_test_count_by_prefix('test_a.py::test', + result.outlines) + b = get_workers_and_test_count_by_prefix('test_b.py::test', + result.outlines) + c1 = get_workers_and_test_count_by_prefix('test_c.py::test_1', + result.outlines) + c2 = get_workers_and_test_count_by_prefix('test_c.py::test_2', + result.outlines) + assert a in ({'gw0': 1}, {'gw1': 1}) + assert b in ({'gw0': 1}, {'gw1': 1}) + assert a.items() != b.items() + assert c1 == c2 + def parse_tests_and_workers_from_output(lines): result = [] diff --git a/xdist/scheduler/loadscope.py b/xdist/scheduler/loadscope.py index 4c51f7d..3c8381a 100644 --- a/xdist/scheduler/loadscope.py +++ b/xdist/scheduler/loadscope.py @@ -380,6 +380,10 @@ class LoadScopeScheduling: for node in self.nodes: self._assign_work_unit(node) + # Ensure nodes start with at least two work units if possible (#277) + for node in self.nodes: + self._reschedule(node) + # Initial distribution sent all tests, start node shutdown if not self.workqueue: for node in self.nodes: