Merge branch 'pytest-dev:main' into okken_12231_xfail_tb

This commit is contained in:
Brian Okken 2024-05-04 15:36:36 -07:00 committed by GitHub
commit 8551173751
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 96 additions and 7 deletions

View File

@ -36,6 +36,7 @@ Andrey Paramonov
Andrzej Klajnert Andrzej Klajnert
Andrzej Ostrowski Andrzej Ostrowski
Andy Freeland Andy Freeland
Anita Hammer
Anthon van der Neut Anthon van der Neut
Anthony Shaw Anthony Shaw
Anthony Sottile Anthony Sottile

View File

@ -0,0 +1,4 @@
Fix reporting of teardown errors in higher-scoped fixtures when using `--maxfail` or `--stepwise`.
Originally added in pytest 8.0.0, but reverted in 8.0.2 due to a regression in pytest-xdist.
This regression was fixed in pytest-xdist 3.6.1.

View File

@ -0,0 +1 @@
Keyboard interrupts and system exits are now properly handled during the test collection.

View File

@ -134,6 +134,10 @@ def runtestprotocol(
show_test_item(item) show_test_item(item)
if not item.config.getoption("setuponly", False): if not item.config.getoption("setuponly", False):
reports.append(call_and_report(item, "call", log)) reports.append(call_and_report(item, "call", log))
# If the session is about to fail or stop, teardown everything - this is
# necessary to correctly report fixture teardown errors (see #11706)
if item.session.shouldfail or item.session.shouldstop:
nextitem = None
reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) reports.append(call_and_report(item, "teardown", log, nextitem=nextitem))
# After all teardown hooks have been called # After all teardown hooks have been called
# want funcargs and request info to go away. # want funcargs and request info to go away.
@ -389,7 +393,9 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
return list(collector.collect()) return list(collector.collect())
call = CallInfo.from_call(collect, "collect") call = CallInfo.from_call(
collect, "collect", reraise=(KeyboardInterrupt, SystemExit)
)
longrepr: Union[None, Tuple[str, int, str], str, TerminalRepr] = None longrepr: Union[None, Tuple[str, int, str], str, TerminalRepr] = None
if not call.excinfo: if not call.excinfo:
outcome: Literal["passed", "skipped", "failed"] = "passed" outcome: Literal["passed", "skipped", "failed"] = "passed"

View File

@ -1,9 +1,7 @@
anyio[curio,trio]==4.3.0 anyio[curio,trio]==4.3.0
django==5.0.4 django==5.0.4
pytest-asyncio==0.23.6 pytest-asyncio==0.23.6
# Temporarily not installed until pytest-bdd is fixed: pytest-bdd==7.1.2
# https://github.com/pytest-dev/pytest/pull/11785
# pytest-bdd==7.0.1
pytest-cov==5.0.0 pytest-cov==5.0.0
pytest-django==4.8.0 pytest-django==4.8.0
pytest-flakes==4.0.5 pytest-flakes==4.0.5

View File

@ -7,6 +7,7 @@ import sys
import tempfile import tempfile
import textwrap import textwrap
from typing import List from typing import List
from typing import Type
from _pytest.assertion.util import running_on_ci from _pytest.assertion.util import running_on_ci
from _pytest.config import ExitCode from _pytest.config import ExitCode
@ -1856,3 +1857,33 @@ def test_do_not_collect_symlink_siblings(
# Ensure we collect it only once if we pass the symlinked directory. # Ensure we collect it only once if we pass the symlinked directory.
result = pytester.runpytest(symlink_path, "-sv") result = pytester.runpytest(symlink_path, "-sv")
result.assert_outcomes(passed=1) result.assert_outcomes(passed=1)
@pytest.mark.parametrize(
"exception_class, msg",
[
(KeyboardInterrupt, "*!!! KeyboardInterrupt !!!*"),
(SystemExit, "INTERNALERROR> SystemExit"),
],
)
def test_respect_system_exceptions(
pytester: Pytester,
exception_class: Type[BaseException],
msg: str,
):
head = "Before exception"
tail = "After exception"
ensure_file(pytester.path / "test_eggs.py").write_text(
f"print('{head}')", encoding="UTF-8"
)
ensure_file(pytester.path / "test_ham.py").write_text(
f"raise {exception_class.__name__}()", encoding="UTF-8"
)
ensure_file(pytester.path / "test_spam.py").write_text(
f"print('{tail}')", encoding="UTF-8"
)
result = pytester.runpytest_subprocess("-s")
result.stdout.fnmatch_lines([f"*{head}*"])
result.stdout.fnmatch_lines([msg])
result.stdout.no_fnmatch_line(f"*{tail}*")

View File

@ -1216,3 +1216,53 @@ def test_pytest_version_env_var(pytester: Pytester, monkeypatch: MonkeyPatch) ->
result = pytester.runpytest_inprocess() result = pytester.runpytest_inprocess()
assert result.ret == ExitCode.OK assert result.ret == ExitCode.OK
assert os.environ["PYTEST_VERSION"] == "old version" assert os.environ["PYTEST_VERSION"] == "old version"
def test_teardown_session_failed(pytester: Pytester) -> None:
"""Test that higher-scoped fixture teardowns run in the context of the last
item after the test session bails early due to --maxfail.
Regression test for #11706.
"""
pytester.makepyfile(
"""
import pytest
@pytest.fixture(scope="module")
def baz():
yield
pytest.fail("This is a failing teardown")
def test_foo(baz):
pytest.fail("This is a failing test")
def test_bar(): pass
"""
)
result = pytester.runpytest("--maxfail=1")
result.assert_outcomes(failed=1, errors=1)
def test_teardown_session_stopped(pytester: Pytester) -> None:
"""Test that higher-scoped fixture teardowns run in the context of the last
item after the test session bails early due to --stepwise.
Regression test for #11706.
"""
pytester.makepyfile(
"""
import pytest
@pytest.fixture(scope="module")
def baz():
yield
pytest.fail("This is a failing teardown")
def test_foo(baz):
pytest.fail("This is a failing test")
def test_bar(): pass
"""
)
result = pytester.runpytest("--stepwise")
result.assert_outcomes(failed=1, errors=1)

View File

@ -134,11 +134,9 @@ changedir = testing/plugins_integration
deps = -rtesting/plugins_integration/requirements.txt deps = -rtesting/plugins_integration/requirements.txt
setenv = setenv =
PYTHONPATH=. PYTHONPATH=.
# Command temporarily removed until pytest-bdd is fixed:
# https://github.com/pytest-dev/pytest/pull/11785
# pytest bdd_wallet.py
commands = commands =
pip check pip check
pytest bdd_wallet.py
pytest --cov=. simple_integration.py pytest --cov=. simple_integration.py
pytest --ds=django_settings simple_integration.py pytest --ds=django_settings simple_integration.py
pytest --html=simple.html simple_integration.py pytest --html=simple.html simple_integration.py