Skip to content

Commit 9d7ba5b

Browse files
darwintreeToad2186
andauthored
Add --no-loadscope-reorder and --loadscope-reorder options (#1217)
Based on #1098. Closes #1098 --------- Co-authored-by: Toan Vuong <toan.vuong@hyperscience.com>
1 parent 532f07f commit 9d7ba5b

File tree

4 files changed

+58
-5
lines changed

4 files changed

+58
-5
lines changed

changelog/1083.improvement.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add ``--no-loadscope-reorder`` and ``--loadscope-reorder`` option to control whether to automatically reorder tests in loadscope for tests where relative ordering matters. This only applies when using ``loadscope``.
2+
3+
For example, [test_file_1, test_file_2, ..., test_file_n] are given as input test files, if ``--no-loadscope-reorder`` is used, for either worker, the ``test_file_a`` will be executed before ``test_file_b`` only if ``a < b``.
4+
5+
The default behavior is to reorder the tests to maximize the number of tests that can be executed in parallel.

src/xdist/plugin.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,32 @@ def pytest_addoption(parser: pytest.Parser) -> None:
127127
"(default) no: Run tests inprocess, don't distribute."
128128
),
129129
)
130+
group.addoption(
131+
"--loadscope-reorder",
132+
dest="loadscopereorder",
133+
action="store_true",
134+
default=True,
135+
help=(
136+
"Pytest-xdist will default reorder tests by number of tests per scope "
137+
"when used in conjunction with loadscope.\n"
138+
"This option will enable loadscope reorder which will improve the "
139+
"parallelism of the test suite.\n"
140+
"However, the partial order of tests might not be retained.\n"
141+
),
142+
)
143+
group.addoption(
144+
"--no-loadscope-reorder",
145+
dest="loadscopereorder",
146+
action="store_false",
147+
help=(
148+
"Pytest-xdist will default reorder tests by number of tests per scope "
149+
"when used in conjunction with loadscope.\n"
150+
"This option will disable loadscope reorder, "
151+
"and the partial order of tests can be retained.\n"
152+
"This is useful when pytest-xdist is used together with "
153+
"other plugins that specify tests in a specific order."
154+
),
155+
)
130156
group.addoption(
131157
"--tx",
132158
dest="tx",

src/xdist/scheduler/loadscope.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,15 @@ def schedule(self) -> None:
371371
work_unit = unsorted_workqueue.setdefault(scope, {})
372372
work_unit[nodeid] = False
373373

374-
# Insert tests scopes into work queue ordered by number of tests.
375-
for scope, nodeids in sorted(
376-
unsorted_workqueue.items(), key=lambda item: -len(item[1])
377-
):
378-
self.workqueue[scope] = nodeids
374+
if self.config.option.loadscopereorder:
375+
# Insert tests scopes into work queue ordered by number of tests.
376+
for scope, nodeids in sorted(
377+
unsorted_workqueue.items(), key=lambda item: -len(item[1])
378+
):
379+
self.workqueue[scope] = nodeids
380+
else:
381+
for scope, nodeids in unsorted_workqueue.items():
382+
self.workqueue[scope] = nodeids
379383

380384
# Avoid having more workers than work
381385
extra_nodes = len(self.nodes) - len(self.workqueue)

testing/acceptance_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,24 @@ def test(i):
12541254
"test_b.py::test", result.outlines
12551255
) == {"gw0": 20}
12561256

1257+
def test_workqueue_ordered_by_input(self, pytester: pytest.Pytester) -> None:
1258+
test_file = """
1259+
import pytest
1260+
@pytest.mark.parametrize('i', range({}))
1261+
def test(i):
1262+
pass
1263+
"""
1264+
pytester.makepyfile(test_a=test_file.format(10), test_b=test_file.format(20))
1265+
result = pytester.runpytest(
1266+
"-n2", "--dist=loadscope", "--no-loadscope-reorder", "-v"
1267+
)
1268+
assert get_workers_and_test_count_by_prefix(
1269+
"test_a.py::test", result.outlines
1270+
) == {"gw0": 10}
1271+
assert get_workers_and_test_count_by_prefix(
1272+
"test_b.py::test", result.outlines
1273+
) == {"gw1": 20}
1274+
12571275
def test_module_single_start(self, pytester: pytest.Pytester) -> None:
12581276
"""Fix test suite never finishing in case all workers start with a single test (#277)."""
12591277
test_file1 = """

0 commit comments

Comments
 (0)