Skip to content

Commit 6378d39

Browse files
Dan Lavupbrezina
authored andcommitted
markers: new preferred_topology marker
Reducing test running time and system resources, we can now mark a test with @pytest.mark.preferred_topology(KnownTopology.topology), making the topology the single and default run unless the marker is overridden. This is intended to be used for tests that do not need to cover all topologies all the time.
1 parent 10584e2 commit 6378d39

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

docs/articles/tips-and-tricks.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ inspiration for your project.
1111
tips-and-tricks/building-command-line
1212
tips-and-tricks/features-detection
1313
tips-and-tricks/pytest-fixtures
14+
tips-and-tricks/preferred-topology
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Preferred Topology
2+
==================
3+
4+
The preferred topology marker ``pytest.mark.preferred_topology`` can be
5+
used if you want the flexibility to run against all topologies, but do not
6+
need to do it all the time. An example could be Nightly builds or a CI where
7+
a single topology is adequate coverage. This will reduce the resources
8+
required to run the tests and their execution times. This is possible by using
9+
the ``pytest.mark.preferred_topology`` marker.
10+
11+
This marks the test with a default topology, deselecting any additional
12+
topologies. ``--mh-ignore-preferred-topology`` can be used to ignore the
13+
marker.
14+
15+
If more than one preferred topology has been defined, only the last topology
16+
will be used. If the preferred topology contains no value, the marker is
17+
ignored.The ``pytest.mark.preferred_topology`` marker accepts three types of
18+
values, :class:`~pytest_mh.TopologyMark` value of
19+
:class:`~pytest_mh.KnownTopologyBase` or ``str``.
20+
21+
.. code-block:: python
22+
:caption: Example: set preferred topology to IPA
23+
24+
25+
@pytest.mark.topology(KnownTopologyGroup.AnyProvider)
26+
@pytest.mark.preferred_topology(KnownTopology.IPA)
27+
def test_preferred_mark_known_topology_ipa():
28+
pass
29+
30+
31+
@pytest.mark.topology(KnownTopologyGroup.AnyProvider)
32+
@pytest.mark.preferred_topology("ipa")
33+
def test_preferred_mark_string_ipa():
34+
pass

pytest_mh/_private/plugin.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from .errors import TeardownExceptionGroup
1919
from .fixtures import MultihostFixture
2020
from .logging import MultihostLogger
21-
from .marks import TopologyMark
21+
from .marks import KnownTopologyBase, TopologyMark
2222
from .multihost import (
2323
MultihostArtifactsMode,
2424
MultihostConfig,
@@ -63,6 +63,7 @@ def __init__(self, pytest_config: pytest.Config) -> None:
6363
self.mh_collect_artifacts: MultihostArtifactsMode = pytest_config.getoption("mh_collect_artifacts")
6464
self.mh_artifacts_dir: Path = Path(pytest_config.getoption("mh_artifacts_dir"))
6565
self.mh_compress_artifacts: bool = pytest_config.getoption("mh_compress_artifacts")
66+
self.mh_ignore_preferred_topology: bool = pytest_config.getoption("mh_ignore_preferred_topology")
6667

6768
# Read --mh-collect-logs, default to --mh-collect-artifacts
6869
self.mh_collect_logs: MultihostArtifactsMode = pytest_config.getoption("mh_collect_logs")
@@ -200,6 +201,7 @@ def pytest_sessionstart(self, session: pytest.Session) -> None:
200201
self.logger.info(f" collect artifacts: {self.mh_collect_artifacts}")
201202
self.logger.info(f" artifacts directory: {self.mh_artifacts_dir}")
202203
self.logger.info(f" collect logs: {self.mh_collect_logs}")
204+
self.logger.info(f" ignore-preferred-topology: {self.mh_ignore_preferred_topology}")
203205
self.logger.info("")
204206

205207
signal(SIGINT, self.sigint_handler)
@@ -521,6 +523,32 @@ def _create_logger(self, verbose) -> logging.Logger:
521523
def _is_multihost_required(self, item: pytest.Item) -> bool:
522524
return item.get_closest_marker(name="topology") is not None
523525

526+
def _can_run_preferred_topology(self, mark: pytest.Mark, current_topology: str, item: pytest.Item) -> bool:
527+
if len(mark.args) != 1:
528+
raise ValueError(
529+
f"{item.nodeid}: Unexpected number of arguments to pytest.mark.preferred_topology: "
530+
f"got {len(mark.args)}, expected 1"
531+
)
532+
533+
arg = mark.args[0]
534+
535+
if isinstance(arg, KnownTopologyBase):
536+
name = arg.value.name
537+
elif isinstance(arg, TopologyMark):
538+
name = arg.name
539+
elif isinstance(arg, str):
540+
name = arg
541+
else:
542+
raise ValueError(
543+
f"{item.nodeid}: Unexpected type of pytest.mark.preferred_topology: "
544+
f"got {type(arg)}, expected KnownTopologyBase | TopologyMark | str"
545+
)
546+
547+
if name == current_topology:
548+
return True
549+
550+
return False
551+
524552
def _can_run_test(self, item: pytest.Item, data: MultihostItemData | None) -> bool:
525553
if data is None:
526554
return not self._is_multihost_required(item)
@@ -547,6 +575,12 @@ def _can_run_test(self, item: pytest.Item, data: MultihostItemData | None) -> bo
547575
if data.topology_mark.name not in self.mh_topology:
548576
return False
549577

578+
# Run only for preferred topology unless specific topology is requested or the marker is ignored
579+
if not self.mh_topology and not self.mh_ignore_preferred_topology:
580+
preferred_topology = item.get_closest_marker(name="preferred_topology")
581+
if preferred_topology is not None and data.topology_mark is not None:
582+
return self._can_run_preferred_topology(preferred_topology, data.topology_mark.name, item)
583+
550584
return True
551585

552586
def _clone_function(self, name: str, f: pytest.Function) -> pytest.Function:
@@ -792,6 +826,12 @@ def pytest_addoption(parser):
792826

793827
parser.addoption("--mh-lazy-ssh", action="store_true", help="Postpone connecting to host SSH until it is required")
794828

829+
parser.addoption(
830+
"--mh-ignore-preferred-topology",
831+
action="store_true",
832+
help="All topologies will run, ignore the preferred_topology marker",
833+
)
834+
795835
parser.addoption(
796836
"--mh-topology",
797837
action="append",
@@ -862,6 +902,13 @@ def pytest_configure(config: pytest.Config):
862902
"the test is skipped if condition is not met",
863903
)
864904

905+
config.addinivalue_line(
906+
"markers",
907+
"preferred_topology(topology: KnownTopologyBase | TopologyMark | str): "
908+
"mark test with a preferred topology."
909+
"Test will execute once, skipping additional topologies",
910+
)
911+
865912
config.pluginmanager.register(MultihostPlugin(config), "MultihostPlugin")
866913

867914

0 commit comments

Comments
 (0)