Skip to content

Commit 62e50d0

Browse files
committed
Address review
1 parent 1b5d6b6 commit 62e50d0

File tree

6 files changed

+65
-38
lines changed

6 files changed

+65
-38
lines changed

README.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ distribution algorithm this with the ``--dist`` option. It takes these values:
9696
distributed to available workers as whole units. This guarantees that all
9797
tests in a file run in the same worker.
9898

99+
* ``--dist loadgroup``: Tests are grouped by xdist_group mark. Groups are
100+
distributed to available workers as whole units. This guarantees that all
101+
tests with same xdist_group name run in the same worker.
102+
99103
Making session-scoped fixtures execute only once
100104
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
101105

@@ -414,3 +418,21 @@ where the configuration file was found.
414418
.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
415419
.. _`pytest-xdist repository`: https://github.com/pytest-dev/pytest-xdist
416420
.. _`pytest`: http://pytest.org
421+
422+
Groups tests by xdist_group mark
423+
---------------------------------
424+
425+
*New in version 2.4.*
426+
427+
Two or more tests belonging to different classes or modules can be executed in same worker through the xdist_group marker:
428+
429+
.. code-block:: python
430+
431+
@pytest.mark.xdist_group(name="group1")
432+
def test1():
433+
pass
434+
435+
class TestA:
436+
@pytest.mark.xdist_group("group1")
437+
def test2():
438+
pass

changelog/733.feature.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Create new dist option 'loadgroup'
1+
New ``--dist=loadgroup`` option, which ensures all tests marked with ``@pytest.mark.xdist_group`` run in the same session/worker. Other tests run distributed as in ``--dist=load``.

src/xdist/plugin.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ def pytest_addoption(parser):
9898
" the same scope to any available environment.\n\n"
9999
"loadfile: load balance by sending test grouped by file"
100100
" to any available environment.\n\n"
101-
"loadgroup: load balance by sending any pending test or test group"
102-
" to any available enviroment.\n\n"
101+
"loadgroup: like load, but sends tests marked with 'xdist_group' to the same worker.\n\n"
103102
"(default) no: run tests inprocess, don't distribute."
104103
),
105104
)
@@ -207,7 +206,7 @@ def pytest_configure(config):
207206
config.option.forked = True
208207

209208
config_line = (
210-
"xgroup: specify group for tests should run in same session."
209+
"xdist_group: specify group for tests should run in same session."
211210
"in relation to one another. " + "Provided by pytest-xdist."
212211
)
213212
config.addinivalue_line("markers", config_line)

src/xdist/remote.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,15 @@ def pytest_collection_modifyitems(self, session, config, items):
120120
# add the group name to nodeid as suffix if --dist=loadgroup
121121
if config.getvalue("loadgroup"):
122122
for item in items:
123-
try:
124-
mark = item.get_closest_marker("xgroup")
125-
except AttributeError:
126-
mark = item.get_marker("xgroup")
127-
128-
if mark:
129-
gname = mark.kwargs.get("name")
130-
if gname:
131-
item._nodeid = "{}@{}".format(item.nodeid, gname)
123+
mark = item.get_closest_marker("xdist_group")
124+
if not mark:
125+
continue
126+
gname = (
127+
mark.args[0]
128+
if len(mark.args) > 0
129+
else mark.kwargs.get("name", "default")
130+
)
131+
item._nodeid = "{}@{}".format(item.nodeid, gname)
132132

133133
@pytest.hookimpl
134134
def pytest_collection_finish(self, session):
@@ -250,7 +250,7 @@ def remote_initconfig(option_dict, args):
250250

251251

252252
def setup_config(config, basetemp):
253-
config.option.loadgroup = True if config.getvalue("dist") == "loadgroup" else False
253+
config.option.loadgroup = config.getvalue("dist") == "loadgroup"
254254
config.option.looponfail = False
255255
config.option.usepdb = False
256256
config.option.dist = "no"

src/xdist/scheduler/loadgroup.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,10 @@
33

44

55
class LoadGroupScheduling(LoadScopeScheduling):
6-
"""Implement load scheduling across nodes, but grouping test only has group mark.
6+
"""Implement load scheduling across nodes, but grouping test by xdist_group mark.
77
8-
This distributes the tests collected across all nodes so each test is run
9-
just once. All nodes collect and submit the list of tests and when all
10-
collections are received it is verified they are identical collections.
11-
Then the collection gets divided up in work units, grouped by group mark
12-
(If there is no group mark, it is itself a group.), and those work units
13-
et submitted to nodes. Whenever a node finishes an item, it calls
14-
``.mark_test_complete()`` which will trigger the scheduler to assign more
15-
work units if the number of pending tests for the node falls below a low-watermark.
16-
17-
When created, ``numnodes`` defines how many nodes are expected to submit a
18-
collection. This is used to know when all nodes have finished collection.
19-
20-
This class behaves very much like LoadScopeScheduling,
21-
but with a itself or group(by marked) scope.
8+
This class behaves very much like LoadScopeScheduling, but it groups tests by xdist_group mark
9+
instead of the module or class to which they belong to.
2210
"""
2311

2412
def __init__(self, config, log=None):
@@ -49,10 +37,9 @@ def _split_scope(self, nodeid):
4937
example/loadsuite/test/test_gamma.py::test_beta0@gname
5038
example/loadsuite/test/test_delta.py::Gamma1::test_gamma0@gname
5139
52-
This function will group tests with the scope determined by splitting
53-
the first ``@`` from the right. That is, test will be grouped in a
54-
single work unit when they have same group name.
55-
In the above example, scopes will be::
40+
This function will group tests with the scope determined by splitting the first ``@``
41+
from the right. That is, test will be grouped in a single work unit when they have
42+
same group name. In the above example, scopes will be::
5643
5744
example/loadsuite/test/test_beta.py::test_beta0
5845
example/loadsuite/test/test_delta.py::Delta1::test_delta0

testing/acceptance_test.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,7 @@ def test_by_module(self, testdir):
13311331
test_file = """
13321332
import pytest
13331333
class TestA:
1334-
@pytest.mark.xgroup(name="xgroup")
1334+
@pytest.mark.xdist_group(name="xdist_group")
13351335
@pytest.mark.parametrize('i', range(5))
13361336
def test(self, i):
13371337
pass
@@ -1371,12 +1371,12 @@ def test_by_class(self, testdir):
13711371
test_a="""
13721372
import pytest
13731373
class TestA:
1374-
@pytest.mark.xgroup(name="xgroup")
1374+
@pytest.mark.xdist_group(name="xdist_group")
13751375
@pytest.mark.parametrize('i', range(10))
13761376
def test(self, i):
13771377
pass
13781378
class TestB:
1379-
@pytest.mark.xgroup(name="xgroup")
1379+
@pytest.mark.xdist_group(name="xdist_group")
13801380
@pytest.mark.parametrize('i', range(10))
13811381
def test(self, i):
13821382
pass
@@ -1414,15 +1414,15 @@ def test(self, i):
14141414
def test_module_single_start(self, testdir):
14151415
test_file1 = """
14161416
import pytest
1417-
@pytest.mark.xgroup(name="xgroup")
1417+
@pytest.mark.xdist_group(name="xdist_group")
14181418
def test():
14191419
pass
14201420
"""
14211421
test_file2 = """
14221422
import pytest
14231423
def test_1():
14241424
pass
1425-
@pytest.mark.xgroup(name="xgroup")
1425+
@pytest.mark.xdist_group(name="xdist_group")
14261426
def test_2():
14271427
pass
14281428
"""
@@ -1434,6 +1434,25 @@ def test_2():
14341434

14351435
assert a.keys() == b.keys() and b.keys() == c.keys()
14361436

1437+
def test_with_two_group_names(self, testdir):
1438+
test_file = """
1439+
import pytest
1440+
@pytest.mark.xdist_group(name="group1")
1441+
def test_1():
1442+
pass
1443+
@pytest.mark.xdist_group("group2")
1444+
def test_2():
1445+
pass
1446+
"""
1447+
testdir.makepyfile(test_a=test_file, test_b=test_file)
1448+
result = testdir.runpytest("-n2", "--dist=loadgroup", "-v")
1449+
a_1 = get_workers_and_test_count_by_prefix("test_a.py::test_1", result.outlines)
1450+
a_2 = get_workers_and_test_count_by_prefix("test_a.py::test_2", result.outlines)
1451+
b_1 = get_workers_and_test_count_by_prefix("test_b.py::test_1", result.outlines)
1452+
b_2 = get_workers_and_test_count_by_prefix("test_b.py::test_2", result.outlines)
1453+
1454+
assert a_1.keys() == b_1.keys() and a_2.keys() == b_2.keys()
1455+
14371456

14381457
class TestLocking:
14391458
_test_content = """

0 commit comments

Comments
 (0)