Merge branch 'main' into improve-high-scope-fixtures-teardown-issue-3806

This commit is contained in:
Sadra Barikbin 2023-06-04 01:09:21 +03:30 committed by GitHub
commit 6fc6b153fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 20 deletions

View File

@ -0,0 +1 @@
Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.

View File

@ -27,7 +27,7 @@ from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.python import Module
from _pytest.nodes import File
from _pytest.python import Package
from _pytest.reports import TestReport
@ -226,15 +226,23 @@ class LFPluginCollWrapper:
# Sort any lf-paths to the beginning.
lf_paths = self.lfplugin._last_failed_paths
# Use stable sort to priorize last failed.
def sort_key(node: Union[nodes.Item, nodes.Collector]) -> bool:
# Package.path is the __init__.py file, we need the directory.
if isinstance(node, Package):
path = node.path.parent
else:
path = node.path
return path in lf_paths
res.result = sorted(
res.result,
# use stable sort to priorize last failed
key=lambda x: x.path in lf_paths,
key=sort_key,
reverse=True,
)
return
elif isinstance(collector, Module):
elif isinstance(collector, File):
if collector.path in self.lfplugin._last_failed_paths:
out = yield
res = out.get_result()
@ -272,10 +280,9 @@ class LFPluginCollSkipfiles:
def pytest_make_collect_report(
self, collector: nodes.Collector
) -> Optional[CollectReport]:
# Packages are Modules, but _last_failed_paths only contains
# test-bearing paths and doesn't try to include the paths of their
# packages, so don't filter them.
if isinstance(collector, Module) and not isinstance(collector, Package):
# Packages are Files, but we only want to skip test-bearing Files,
# so don't filter Packages.
if isinstance(collector, File) and not isinstance(collector, Package):
if collector.path not in self.lfplugin._last_failed_paths:
self.lfplugin._skipped_files += 1
@ -305,9 +312,14 @@ class LFPlugin:
)
def get_last_failed_paths(self) -> Set[Path]:
"""Return a set with all Paths()s of the previously failed nodeids."""
"""Return a set with all Paths of the previously failed nodeids and
their parents."""
rootpath = self.config.rootpath
result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed}
result = set()
for nodeid in self.lastfailed:
path = rootpath / nodeid.split("::")[0]
result.add(path)
result.update(path.parents)
return {x for x in result if x.exists()}
def pytest_report_collectionfinish(self) -> Optional[str]:

View File

@ -56,7 +56,6 @@ from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
from _pytest.deprecated import INSTANCE_COLLECTOR
from _pytest.deprecated import NOSE_SUPPORT_METHOD
from _pytest.fixtures import FixtureDef
@ -698,14 +697,6 @@ class Package(Module):
func = partial(_call_with_optional_argument, teardown_module, self.obj)
self.addfinalizer(func)
def gethookproxy(self, fspath: "os.PathLike[str]"):
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.gethookproxy(fspath)
def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.isinitpath(path)
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
if direntry.name == "__pycache__":
return False

View File

@ -105,7 +105,7 @@ def tw_mock():
@pytest.fixture
def dummy_yaml_custom_test(pytester: Pytester):
def dummy_yaml_custom_test(pytester: Pytester) -> None:
"""Writes a conftest file that collects and executes a dummy yaml test.
Taken from the docs, but stripped down to the bare minimum, useful for

View File

@ -854,6 +854,33 @@ class TestLastFailed:
]
)
def test_lastfailed_skip_collection_with_nesting(self, pytester: Pytester) -> None:
"""Check that file skipping works even when the file with failures is
nested at a different level of the collection tree."""
pytester.makepyfile(
**{
"test_1.py": """
def test_1(): pass
""",
"pkg/__init__.py": "",
"pkg/test_2.py": """
def test_2(): assert False
""",
}
)
# first run
result = pytester.runpytest()
result.stdout.fnmatch_lines(["collected 2 items", "*1 failed*1 passed*"])
# second run - test_1.py is skipped.
result = pytester.runpytest("--lf")
result.stdout.fnmatch_lines(
[
"collected 1 item",
"run-last-failure: rerun previous 1 failure (skipped 1 file)",
"*= 1 failed in *",
]
)
def test_lastfailed_with_known_failures_not_being_selected(
self, pytester: Pytester
) -> None:
@ -1058,6 +1085,28 @@ class TestLastFailed:
result = pytester.runpytest("--lf")
result.assert_outcomes(failed=3)
def test_non_python_file_skipped(
self,
pytester: Pytester,
dummy_yaml_custom_test: None,
) -> None:
pytester.makepyfile(
**{
"test_bad.py": """def test_bad(): assert False""",
},
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"])
result = pytester.runpytest("--lf")
result.stdout.fnmatch_lines(
[
"collected 1 item",
"run-last-failure: rerun previous 1 failure (skipped 1 file)",
"* 1 failed in *",
]
)
class TestNewFirst:
def test_newfirst_usecase(self, pytester: Pytester) -> None: