Skip to content

Commit

Permalink
Merge pull request #11059 from bluetech/lf-skip-across
Browse files Browse the repository at this point in the history
cacheprovider: fix file-skipping functionality across packages
  • Loading branch information
bluetech authored Jun 3, 2023
2 parents 6c9b277 + c76ae74 commit 4b823a4
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
26 changes: 19 additions & 7 deletions src/_pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,18 @@ def pytest_make_collect_report(self, collector: nodes.Collector):
# 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
Expand Down Expand Up @@ -272,9 +280,8 @@ def __init__(self, lfplugin: "LFPlugin") -> None:
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.
# Packages are Modules, but we only want to skip test-bearing Modules,
# so don't filter Packages.
if isinstance(collector, Module) and not isinstance(collector, Package):
if collector.path not in self.lfplugin._last_failed_paths:
self.lfplugin._skipped_files += 1
Expand Down Expand Up @@ -305,9 +312,14 @@ def __init__(self, config: Config) -> None:
)

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]:
Expand Down
27 changes: 27 additions & 0 deletions testing/test_cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,33 @@ def test_3(): pass
]
)

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:
Expand Down

0 comments on commit 4b823a4

Please sign in to comment.