Address review
This commit is contained in:
22
README.rst
22
README.rst
@@ -96,6 +96,10 @@ distribution algorithm this with the ``--dist`` option. It takes these values:
|
|||||||
distributed to available workers as whole units. This guarantees that all
|
distributed to available workers as whole units. This guarantees that all
|
||||||
tests in a file run in the same worker.
|
tests in a file run in the same worker.
|
||||||
|
|
||||||
|
* ``--dist loadgroup``: Tests are grouped by xdist_group mark. Groups are
|
||||||
|
distributed to available workers as whole units. This guarantees that all
|
||||||
|
tests with same xdist_group name run in the same worker.
|
||||||
|
|
||||||
Making session-scoped fixtures execute only once
|
Making session-scoped fixtures execute only once
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@@ -414,3 +418,21 @@ where the configuration file was found.
|
|||||||
.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
|
.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
|
||||||
.. _`pytest-xdist repository`: https://github.com/pytest-dev/pytest-xdist
|
.. _`pytest-xdist repository`: https://github.com/pytest-dev/pytest-xdist
|
||||||
.. _`pytest`: http://pytest.org
|
.. _`pytest`: http://pytest.org
|
||||||
|
|
||||||
|
Groups tests by xdist_group mark
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
*New in version 2.4.*
|
||||||
|
|
||||||
|
Two or more tests belonging to different classes or modules can be executed in same worker through the xdist_group marker:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@pytest.mark.xdist_group(name="group1")
|
||||||
|
def test1():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestA:
|
||||||
|
@pytest.mark.xdist_group("group1")
|
||||||
|
def test2():
|
||||||
|
pass
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Create new dist option 'loadgroup'
|
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``.
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ def pytest_addoption(parser):
|
|||||||
" the same scope to any available environment.\n\n"
|
" the same scope to any available environment.\n\n"
|
||||||
"loadfile: load balance by sending test grouped by file"
|
"loadfile: load balance by sending test grouped by file"
|
||||||
" to any available environment.\n\n"
|
" to any available environment.\n\n"
|
||||||
"loadgroup: load balance by sending any pending test or test group"
|
"loadgroup: like load, but sends tests marked with 'xdist_group' to the same worker.\n\n"
|
||||||
" to any available enviroment.\n\n"
|
|
||||||
"(default) no: run tests inprocess, don't distribute."
|
"(default) no: run tests inprocess, don't distribute."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -207,7 +206,7 @@ def pytest_configure(config):
|
|||||||
config.option.forked = True
|
config.option.forked = True
|
||||||
|
|
||||||
config_line = (
|
config_line = (
|
||||||
"xgroup: specify group for tests should run in same session."
|
"xdist_group: specify group for tests should run in same session."
|
||||||
"in relation to one another. " + "Provided by pytest-xdist."
|
"in relation to one another. " + "Provided by pytest-xdist."
|
||||||
)
|
)
|
||||||
config.addinivalue_line("markers", config_line)
|
config.addinivalue_line("markers", config_line)
|
||||||
|
|||||||
@@ -120,15 +120,15 @@ class WorkerInteractor:
|
|||||||
# add the group name to nodeid as suffix if --dist=loadgroup
|
# add the group name to nodeid as suffix if --dist=loadgroup
|
||||||
if config.getvalue("loadgroup"):
|
if config.getvalue("loadgroup"):
|
||||||
for item in items:
|
for item in items:
|
||||||
try:
|
mark = item.get_closest_marker("xdist_group")
|
||||||
mark = item.get_closest_marker("xgroup")
|
if not mark:
|
||||||
except AttributeError:
|
continue
|
||||||
mark = item.get_marker("xgroup")
|
gname = (
|
||||||
|
mark.args[0]
|
||||||
if mark:
|
if len(mark.args) > 0
|
||||||
gname = mark.kwargs.get("name")
|
else mark.kwargs.get("name", "default")
|
||||||
if gname:
|
)
|
||||||
item._nodeid = "{}@{}".format(item.nodeid, gname)
|
item._nodeid = "{}@{}".format(item.nodeid, gname)
|
||||||
|
|
||||||
@pytest.hookimpl
|
@pytest.hookimpl
|
||||||
def pytest_collection_finish(self, session):
|
def pytest_collection_finish(self, session):
|
||||||
@@ -250,7 +250,7 @@ def remote_initconfig(option_dict, args):
|
|||||||
|
|
||||||
|
|
||||||
def setup_config(config, basetemp):
|
def setup_config(config, basetemp):
|
||||||
config.option.loadgroup = True if config.getvalue("dist") == "loadgroup" else False
|
config.option.loadgroup = config.getvalue("dist") == "loadgroup"
|
||||||
config.option.looponfail = False
|
config.option.looponfail = False
|
||||||
config.option.usepdb = False
|
config.option.usepdb = False
|
||||||
config.option.dist = "no"
|
config.option.dist = "no"
|
||||||
|
|||||||
@@ -3,22 +3,10 @@ from py.log import Producer
|
|||||||
|
|
||||||
|
|
||||||
class LoadGroupScheduling(LoadScopeScheduling):
|
class LoadGroupScheduling(LoadScopeScheduling):
|
||||||
"""Implement load scheduling across nodes, but grouping test only has group mark.
|
"""Implement load scheduling across nodes, but grouping test by xdist_group mark.
|
||||||
|
|
||||||
This distributes the tests collected across all nodes so each test is run
|
This class behaves very much like LoadScopeScheduling, but it groups tests by xdist_group mark
|
||||||
just once. All nodes collect and submit the list of tests and when all
|
instead of the module or class to which they belong to.
|
||||||
collections are received it is verified they are identical collections.
|
|
||||||
Then the collection gets divided up in work units, grouped by group mark
|
|
||||||
(If there is no group mark, it is itself a group.), and those work units
|
|
||||||
et submitted to nodes. Whenever a node finishes an item, it calls
|
|
||||||
``.mark_test_complete()`` which will trigger the scheduler to assign more
|
|
||||||
work units if the number of pending tests for the node falls below a low-watermark.
|
|
||||||
|
|
||||||
When created, ``numnodes`` defines how many nodes are expected to submit a
|
|
||||||
collection. This is used to know when all nodes have finished collection.
|
|
||||||
|
|
||||||
This class behaves very much like LoadScopeScheduling,
|
|
||||||
but with a itself or group(by marked) scope.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config, log=None):
|
def __init__(self, config, log=None):
|
||||||
@@ -49,10 +37,9 @@ class LoadGroupScheduling(LoadScopeScheduling):
|
|||||||
example/loadsuite/test/test_gamma.py::test_beta0@gname
|
example/loadsuite/test/test_gamma.py::test_beta0@gname
|
||||||
example/loadsuite/test/test_delta.py::Gamma1::test_gamma0@gname
|
example/loadsuite/test/test_delta.py::Gamma1::test_gamma0@gname
|
||||||
|
|
||||||
This function will group tests with the scope determined by splitting
|
This function will group tests with the scope determined by splitting the first ``@``
|
||||||
the first ``@`` from the right. That is, test will be grouped in a
|
from the right. That is, test will be grouped in a single work unit when they have
|
||||||
single work unit when they have same group name.
|
same group name. In the above example, scopes will be::
|
||||||
In the above example, scopes will be::
|
|
||||||
|
|
||||||
example/loadsuite/test/test_beta.py::test_beta0
|
example/loadsuite/test/test_beta.py::test_beta0
|
||||||
example/loadsuite/test/test_delta.py::Delta1::test_delta0
|
example/loadsuite/test/test_delta.py::Delta1::test_delta0
|
||||||
|
|||||||
@@ -1331,7 +1331,7 @@ class TestGroupScope:
|
|||||||
test_file = """
|
test_file = """
|
||||||
import pytest
|
import pytest
|
||||||
class TestA:
|
class TestA:
|
||||||
@pytest.mark.xgroup(name="xgroup")
|
@pytest.mark.xdist_group(name="xdist_group")
|
||||||
@pytest.mark.parametrize('i', range(5))
|
@pytest.mark.parametrize('i', range(5))
|
||||||
def test(self, i):
|
def test(self, i):
|
||||||
pass
|
pass
|
||||||
@@ -1371,12 +1371,12 @@ class TestGroupScope:
|
|||||||
test_a="""
|
test_a="""
|
||||||
import pytest
|
import pytest
|
||||||
class TestA:
|
class TestA:
|
||||||
@pytest.mark.xgroup(name="xgroup")
|
@pytest.mark.xdist_group(name="xdist_group")
|
||||||
@pytest.mark.parametrize('i', range(10))
|
@pytest.mark.parametrize('i', range(10))
|
||||||
def test(self, i):
|
def test(self, i):
|
||||||
pass
|
pass
|
||||||
class TestB:
|
class TestB:
|
||||||
@pytest.mark.xgroup(name="xgroup")
|
@pytest.mark.xdist_group(name="xdist_group")
|
||||||
@pytest.mark.parametrize('i', range(10))
|
@pytest.mark.parametrize('i', range(10))
|
||||||
def test(self, i):
|
def test(self, i):
|
||||||
pass
|
pass
|
||||||
@@ -1414,7 +1414,7 @@ class TestGroupScope:
|
|||||||
def test_module_single_start(self, testdir):
|
def test_module_single_start(self, testdir):
|
||||||
test_file1 = """
|
test_file1 = """
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.mark.xgroup(name="xgroup")
|
@pytest.mark.xdist_group(name="xdist_group")
|
||||||
def test():
|
def test():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
@@ -1422,7 +1422,7 @@ class TestGroupScope:
|
|||||||
import pytest
|
import pytest
|
||||||
def test_1():
|
def test_1():
|
||||||
pass
|
pass
|
||||||
@pytest.mark.xgroup(name="xgroup")
|
@pytest.mark.xdist_group(name="xdist_group")
|
||||||
def test_2():
|
def test_2():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
@@ -1434,6 +1434,25 @@ class TestGroupScope:
|
|||||||
|
|
||||||
assert a.keys() == b.keys() and b.keys() == c.keys()
|
assert a.keys() == b.keys() and b.keys() == c.keys()
|
||||||
|
|
||||||
|
def test_with_two_group_names(self, testdir):
|
||||||
|
test_file = """
|
||||||
|
import pytest
|
||||||
|
@pytest.mark.xdist_group(name="group1")
|
||||||
|
def test_1():
|
||||||
|
pass
|
||||||
|
@pytest.mark.xdist_group("group2")
|
||||||
|
def test_2():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
testdir.makepyfile(test_a=test_file, test_b=test_file)
|
||||||
|
result = testdir.runpytest("-n2", "--dist=loadgroup", "-v")
|
||||||
|
a_1 = get_workers_and_test_count_by_prefix("test_a.py::test_1", result.outlines)
|
||||||
|
a_2 = get_workers_and_test_count_by_prefix("test_a.py::test_2", result.outlines)
|
||||||
|
b_1 = get_workers_and_test_count_by_prefix("test_b.py::test_1", result.outlines)
|
||||||
|
b_2 = get_workers_and_test_count_by_prefix("test_b.py::test_2", result.outlines)
|
||||||
|
|
||||||
|
assert a_1.keys() == b_1.keys() and a_2.keys() == b_2.keys()
|
||||||
|
|
||||||
|
|
||||||
class TestLocking:
|
class TestLocking:
|
||||||
_test_content = """
|
_test_content = """
|
||||||
|
|||||||
Reference in New Issue
Block a user