Skip to content

Commit 8b16c77

Browse files
Bastian-KrauseEmantor
authored andcommitted
pytestplugin/hooks: introduce pytest.mark.lg_xfail_feature
`@pytest.mark.lg_xfail_feature()` is meant to mark a test as "xfail" if the target or global env contains the specified feature flag. Signed-off-by: Bastian Krause <bst@pengutronix.de>
1 parent ce60da4 commit 8b16c77

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

doc/usage.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,44 @@ Tests requiring multiple features are also possible:
730730
def test_camera(target):
731731
pass
732732
733+
@pytest.mark.lg_xfail_feature()
734+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
735+
labgrid supports :ref:`environment-configuration-feature-flags` in the
736+
:ref:`environment-configuration`.
737+
pytest supports the ``xfail`` marker, see
738+
`pytest.mark.xfail() <https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail>`_.
739+
740+
When having more specific features, tests can be marked as ``xfail`` for a
741+
particular feature.
742+
743+
Imagine two targets have ``camera`` feature flags.
744+
One of them has the additional ``special-camera-2000`` feature flag.
745+
The other has the additional ``special-camera-3000`` feature flag.
746+
Due to a known bug on ``special-camera-3000``, the test is expected to
747+
fail.
748+
The test can be marked as ``xfail`` for that feature:
749+
750+
.. code-block:: python
751+
752+
import pytest
753+
754+
@pytest.mark.lg_feature("camera"])
755+
@pytest.mark.lg_xfail_feature(
756+
"special-camera-3000",
757+
reason="known bug xy on special-camera-3000",
758+
raises=AssertionError,
759+
strict=True,
760+
)
761+
def test_camera(target):
762+
pass
763+
764+
Features under the target and global ``features:`` keys are considered.
765+
766+
``@pytest.mark.lg_xfail_feature(feature, **kwargs)``:
767+
- ``feature`` (str) - Feature that should mark the test as ``xfail``, passed
768+
as boolean ``condition=`` to ``pytest.mark.xfail()``.
769+
- ``**kwargs`` - All kw-only args are passed to ``pytest.mark.xfail()``.
770+
733771
Test Reports
734772
~~~~~~~~~~~~
735773

labgrid/pytestplugin/hooks.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import copy
23
import logging
34
import pytest
45

@@ -72,6 +73,9 @@ def pytest_configure(config):
7273

7374
config.addinivalue_line("markers",
7475
"lg_feature: skip tests on envs/targets without given labgrid feature flags")
76+
config.addinivalue_line("markers",
77+
"lg_xfail_feature: mark tests xfail on envs/targets with given labgrid feature flag")
78+
7579
lg_log = config.option.lg_log
7680
if lg_log:
7781
ConsoleLoggingReporter(lg_log)
@@ -120,6 +124,26 @@ def pytest_collection_modifyitems(config, items):
120124
reason = f'unsupported feature(s): {", ".join(missing_feature)}'
121125
item.add_marker(pytest.mark.skip(reason=reason))
122126

127+
# pytest.mark.lg_xfail_feature
128+
lg_xfail_feature_signature = "pytest.mark.lg_xfail_feature(feature: str, *, **xfail_kwargs), xfail_kwargs as pytest.mark.xfail expects them"
129+
for marker in item.iter_markers("lg_xfail_feature"):
130+
if len(marker.args) != 1:
131+
raise pytest.UsageError(f"Unexpected number of arguments for {lg_xfail_feature_signature}")
132+
elif not isinstance(marker.args[0], str):
133+
raise pytest.UsageError(f"Unsupported 'feature' argument type {type(marker.args[0])} for {lg_xfail_feature_signature}")
134+
if "condition" in marker.kwargs:
135+
raise pytest.UsageError(f"Unsupported 'condition' argument for {lg_xfail_feature_signature}")
136+
137+
kwargs = copy.copy(marker.kwargs)
138+
reason = kwargs.pop("reason", marker.args[0])
139+
item.add_marker(
140+
pytest.mark.xfail(
141+
condition=marker.args[0] in have_feature,
142+
reason=reason,
143+
**kwargs,
144+
)
145+
)
146+
123147
@pytest.hookimpl(tryfirst=True)
124148
def pytest_runtest_setup(item):
125149
"""

tests/test_flags.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,70 @@ def test(self, env):
172172
spawn.close()
173173
# pytest command line usage error leads to exit code 4
174174
assert spawn.exitstatus == 4
175+
176+
def test_xfail_feature(tmpdir, env_feature_config):
177+
conf = env_feature_config(["test"])
178+
test = tmpdir.join("test.py")
179+
test.write(
180+
"""
181+
import pytest
182+
183+
@pytest.mark.lg_xfail_feature("test")
184+
def test(env):
185+
assert False
186+
"""
187+
)
188+
189+
with pexpect.spawn(f'pytest --lg-env {conf} {test}') as spawn:
190+
spawn.expect("1 xfailed")
191+
spawn.expect(pexpect.EOF)
192+
assert spawn.exitstatus == 0
193+
194+
def test_no_xfail_feature(tmpdir, env_feature_config):
195+
conf = env_feature_config([])
196+
test = tmpdir.join("test.py")
197+
test.write(
198+
"""
199+
import pytest
200+
201+
@pytest.mark.lg_xfail_feature("test")
202+
def test(env):
203+
assert False
204+
"""
205+
)
206+
207+
with pexpect.spawn(f'pytest --lg-env {conf} {test}') as spawn:
208+
spawn.expect("1 failed")
209+
spawn.expect(pexpect.EOF)
210+
assert spawn.exitstatus == 1
211+
212+
@pytest.mark.parametrize(
213+
"marker_args_str,error",
214+
[
215+
("", "Unexpected number of arguments"),
216+
("'too', 'many'", "Unexpected number of arguments"),
217+
("{'foo': 'bar'}", "Unsupported 'feature' argument type"),
218+
("'feature', condition='mycondition'", "Unsupported 'condition' argument"),
219+
],
220+
ids=["no args", "too many args", "unsupported arg type", "unsupported condition"]
221+
)
222+
def test_lg_xfail_feature_unexpected_args(tmpdir, env_feature_config, marker_args_str, error):
223+
# features do not matter here, simply generate a valid env config
224+
conf = env_feature_config([])
225+
test = tmpdir.join("test.py")
226+
test.write(
227+
f"""
228+
import pytest
229+
230+
@pytest.mark.lg_xfail_feature({marker_args_str})
231+
def test(self, env):
232+
assert True
233+
"""
234+
)
235+
236+
with pexpect.spawn(f'pytest --lg-env {conf} {test}') as spawn:
237+
spawn.expect(error)
238+
spawn.expect(pexpect.EOF)
239+
spawn.close()
240+
# pytest command line usage error leads to exit code 4
241+
assert spawn.exitstatus == 4

0 commit comments

Comments
 (0)