Skip to content

Commit d11938b

Browse files
fix(ciapp/pytest): do not access XFAIL reason unsafely (#3277) (#3282)
* do not attempt to access wasxfail unsafely * add changelog * add unit tests * improve logic and fix tests * Apply suggestions from code review Co-authored-by: Kyle Verhoog <[email protected]> Co-authored-by: Kyle Verhoog <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit 927ff22) Co-authored-by: Juan Antonio Fernández de Alba <[email protected]>
1 parent 52be34a commit d11938b

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

ddtrace/contrib/pytest/plugin.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,28 +187,31 @@ def pytest_runtest_makereport(item, call):
187187
xfail = hasattr(result, "wasxfail") or "xfail" in result.keywords
188188
has_skip_keyword = any(x in result.keywords for x in ["skip", "skipif", "skipped"])
189189

190+
# If run with --runxfail flag, tests behave as if they were not marked with xfail,
191+
# that's why no test.XFAIL_REASON or test.RESULT tags will be added.
190192
if result.skipped:
191193
if xfail and not has_skip_keyword:
192194
# XFail tests that fail are recorded skipped by pytest, should be passed instead
193-
span.set_tag(test.RESULT, test.Status.XFAIL.value)
194-
span.set_tag(test.XFAIL_REASON, result.wasxfail)
195195
span.set_tag(test.STATUS, test.Status.PASS.value)
196+
if not item.config.option.runxfail:
197+
span.set_tag(test.RESULT, test.Status.XFAIL.value)
198+
span.set_tag(test.XFAIL_REASON, getattr(result, "wasxfail", "XFail"))
196199
else:
197200
span.set_tag(test.STATUS, test.Status.SKIP.value)
198201
reason = _extract_reason(call)
199202
if reason is not None:
200203
span.set_tag(test.SKIP_REASON, reason)
201204
elif result.passed:
202205
span.set_tag(test.STATUS, test.Status.PASS.value)
203-
if xfail and not has_skip_keyword:
206+
if xfail and not has_skip_keyword and not item.config.option.runxfail:
204207
# XPass (strict=False) are recorded passed by pytest
205208
span.set_tag(test.XFAIL_REASON, getattr(result, "wasxfail", "XFail"))
206209
span.set_tag(test.RESULT, test.Status.XPASS.value)
207210
else:
208-
if xfail and not has_skip_keyword:
211+
span.set_tag(test.STATUS, test.Status.FAIL.value)
212+
if xfail and not has_skip_keyword and not item.config.option.runxfail:
209213
# XPass (strict=True) are recorded failed by pytest, longrepr contains reason
210-
span.set_tag(test.XFAIL_REASON, result.longrepr)
214+
span.set_tag(test.XFAIL_REASON, getattr(result, "longrepr", "XFail"))
211215
span.set_tag(test.RESULT, test.Status.XPASS.value)
212-
span.set_tag(test.STATUS, test.Status.FAIL.value)
213216
if call.excinfo:
214217
span.set_exc_info(call.excinfo.type, call.excinfo.value, call.excinfo.tb)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
pytest: fix unsafe access to xfail reason.

tests/contrib/pytest/test_pytest.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,44 @@ def test_xfail_conditional():
325325
assert spans[1].get_tag(test.RESULT) == test.Status.XFAIL.value
326326
assert spans[1].get_tag(test.XFAIL_REASON) == "test should xfail"
327327

328+
def test_xfail_runxfail_fails(self):
329+
"""Test xfail with --runxfail flags should not crash when failing."""
330+
py_file = self.testdir.makepyfile(
331+
"""
332+
import pytest
333+
334+
@pytest.mark.xfail(reason='should fail')
335+
def test_should_fail():
336+
assert 0
337+
338+
"""
339+
)
340+
file_name = os.path.basename(py_file.strpath)
341+
self.inline_run("--ddtrace", "--runxfail", file_name)
342+
spans = self.pop_spans()
343+
344+
assert len(spans) == 1
345+
assert spans[0].get_tag(test.STATUS) == test.Status.FAIL.value
346+
347+
def test_xfail_runxfail_passes(self):
348+
"""Test xfail with --runxfail flags should not crash when passing."""
349+
py_file = self.testdir.makepyfile(
350+
"""
351+
import pytest
352+
353+
@pytest.mark.xfail(reason='should fail')
354+
def test_should_pass():
355+
assert 1
356+
357+
"""
358+
)
359+
file_name = os.path.basename(py_file.strpath)
360+
self.inline_run("--ddtrace", "--runxfail", file_name)
361+
spans = self.pop_spans()
362+
363+
assert len(spans) == 1
364+
assert spans[0].get_tag(test.STATUS) == test.Status.PASS.value
365+
328366
def test_xpass_not_strict(self):
329367
"""Test xpass (unexpected passing) with strict=False, should be marked as pass."""
330368
py_file = self.testdir.makepyfile(

0 commit comments

Comments
 (0)