Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

run-last-failure: improve reporting #5034

Merged
merged 1 commit into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/5034.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve reporting with ``--lf`` and ``--ff`` (run-last-failure).
51 changes: 30 additions & 21 deletions src/_pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,11 @@ def __init__(self, config):
self.active = any(config.getoption(key) for key in active_keys)
self.lastfailed = config.cache.get("cache/lastfailed", {})
self._previously_failed_count = None
self._no_failures_behavior = self.config.getoption("last_failed_no_failures")
self._report_status = None

def pytest_report_collectionfinish(self):
if self.active and self.config.getoption("verbose") >= 0:
if not self._previously_failed_count:
return None
noun = "failure" if self._previously_failed_count == 1 else "failures"
suffix = " first" if self.config.getoption("failedfirst") else ""
mode = "rerun previous {count} {noun}{suffix}".format(
count=self._previously_failed_count, suffix=suffix, noun=noun
)
return "run-last-failure: %s" % mode
return "run-last-failure: %s" % self._report_status

def pytest_runtest_logreport(self, report):
if (report.when == "call" and report.passed) or report.skipped:
Expand Down Expand Up @@ -196,18 +189,35 @@ def pytest_collection_modifyitems(self, session, config, items):
else:
previously_passed.append(item)
self._previously_failed_count = len(previously_failed)

if not previously_failed:
# running a subset of all tests with recorded failures outside
# of the set of tests currently executing
return
if self.config.getoption("lf"):
items[:] = previously_failed
config.hook.pytest_deselected(items=previously_passed)
# Running a subset of all tests with recorded failures
# only outside of it.
self._report_status = "%d known failures not in selected tests" % (
len(self.lastfailed),
)
Copy link
Contributor Author

@blueyed blueyed Apr 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could have a hint to use --cache-show cache/lastfailed to see them (with #5035).

(I am having a lot there from files that do not exist anymore etc - it could also have an improved message for when no args are passed really then)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it doesn't get too verbose, I'm fine with it. Up to you. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe / rather in a followup.

else:
if self.config.getoption("lf"):
items[:] = previously_failed
config.hook.pytest_deselected(items=previously_passed)
else: # --failedfirst
items[:] = previously_failed + previously_passed

noun = (
"failure" if self._previously_failed_count == 1 else "failures"
)
suffix = " first" if self.config.getoption("failedfirst") else ""
self._report_status = "rerun previous {count} {noun}{suffix}".format(
count=self._previously_failed_count, suffix=suffix, noun=noun
)
else:
self._report_status = "no previously failed tests, "
if self.config.getoption("last_failed_no_failures") == "none":
self._report_status += "deselecting all items."
config.hook.pytest_deselected(items=items)
items[:] = []
else:
items[:] = previously_failed + previously_passed
elif self._no_failures_behavior == "none":
config.hook.pytest_deselected(items=items)
items[:] = []
self._report_status += "not deselecting items."

def pytest_sessionfinish(self, session):
config = self.config
Expand Down Expand Up @@ -303,8 +313,7 @@ def pytest_addoption(parser):
dest="last_failed_no_failures",
choices=("all", "none"),
default="all",
help="change the behavior when no test failed in the last run or no "
"information about the last failures was found in the cache",
help="which tests to run with no previously (known) failures.",
)


Expand Down
26 changes: 23 additions & 3 deletions testing/test_cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import py

import pytest
from _pytest.main import EXIT_NOTESTSCOLLECTED

pytest_plugins = ("pytester",)

Expand Down Expand Up @@ -251,7 +252,13 @@ def test_3():
result = testdir.runpytest("--lf")
result.stdout.fnmatch_lines(["*2 passed*1 desel*"])
result = testdir.runpytest("--lf")
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
result.stdout.fnmatch_lines(
[
"collected 3 items",
"run-last-failure: no previously failed tests, not deselecting items.",
"*1 failed*2 passed*",
]
)
result = testdir.runpytest("--lf", "--cache-clear")
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])

Expand Down Expand Up @@ -425,7 +432,13 @@ def test_b2():
)

result = testdir.runpytest(test_a, "--lf")
result.stdout.fnmatch_lines(["collected 2 items", "*2 passed in*"])
result.stdout.fnmatch_lines(
[
"collected 2 items",
"run-last-failure: 2 known failures not in selected tests",
"*2 passed in*",
]
)

result = testdir.runpytest(test_b, "--lf")
result.stdout.fnmatch_lines(
Expand Down Expand Up @@ -721,7 +734,14 @@ def test_2():
result = testdir.runpytest("--lf", "--lfnf", "all")
result.stdout.fnmatch_lines(["*2 passed*"])
result = testdir.runpytest("--lf", "--lfnf", "none")
result.stdout.fnmatch_lines(["*2 desel*"])
result.stdout.fnmatch_lines(
[
"collected 2 items / 2 deselected",
"run-last-failure: no previously failed tests, deselecting all items.",
"* 2 deselected in *",
]
)
assert result.ret == EXIT_NOTESTSCOLLECTED

def test_lastfailed_no_failures_behavior_empty_cache(self, testdir):
testdir.makepyfile(
Expand Down