Skip to content

Commit 0d4636b

Browse files
authored
Merge pull request #5034 from blueyed/run-last-failure-report
run-last-failure: improve reporting
2 parents a65edf6 + 1f5a61e commit 0d4636b

File tree

3 files changed

+54
-24
lines changed

3 files changed

+54
-24
lines changed

changelog/5034.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve reporting with ``--lf`` and ``--ff`` (run-last-failure).

src/_pytest/cacheprovider.py

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -157,18 +157,11 @@ def __init__(self, config):
157157
self.active = any(config.getoption(key) for key in active_keys)
158158
self.lastfailed = config.cache.get("cache/lastfailed", {})
159159
self._previously_failed_count = None
160-
self._no_failures_behavior = self.config.getoption("last_failed_no_failures")
160+
self._report_status = None
161161

162162
def pytest_report_collectionfinish(self):
163163
if self.active and self.config.getoption("verbose") >= 0:
164-
if not self._previously_failed_count:
165-
return None
166-
noun = "failure" if self._previously_failed_count == 1 else "failures"
167-
suffix = " first" if self.config.getoption("failedfirst") else ""
168-
mode = "rerun previous {count} {noun}{suffix}".format(
169-
count=self._previously_failed_count, suffix=suffix, noun=noun
170-
)
171-
return "run-last-failure: %s" % mode
164+
return "run-last-failure: %s" % self._report_status
172165

173166
def pytest_runtest_logreport(self, report):
174167
if (report.when == "call" and report.passed) or report.skipped:
@@ -196,18 +189,35 @@ def pytest_collection_modifyitems(self, session, config, items):
196189
else:
197190
previously_passed.append(item)
198191
self._previously_failed_count = len(previously_failed)
192+
199193
if not previously_failed:
200-
# running a subset of all tests with recorded failures outside
201-
# of the set of tests currently executing
202-
return
203-
if self.config.getoption("lf"):
204-
items[:] = previously_failed
205-
config.hook.pytest_deselected(items=previously_passed)
194+
# Running a subset of all tests with recorded failures
195+
# only outside of it.
196+
self._report_status = "%d known failures not in selected tests" % (
197+
len(self.lastfailed),
198+
)
199+
else:
200+
if self.config.getoption("lf"):
201+
items[:] = previously_failed
202+
config.hook.pytest_deselected(items=previously_passed)
203+
else: # --failedfirst
204+
items[:] = previously_failed + previously_passed
205+
206+
noun = (
207+
"failure" if self._previously_failed_count == 1 else "failures"
208+
)
209+
suffix = " first" if self.config.getoption("failedfirst") else ""
210+
self._report_status = "rerun previous {count} {noun}{suffix}".format(
211+
count=self._previously_failed_count, suffix=suffix, noun=noun
212+
)
213+
else:
214+
self._report_status = "no previously failed tests, "
215+
if self.config.getoption("last_failed_no_failures") == "none":
216+
self._report_status += "deselecting all items."
217+
config.hook.pytest_deselected(items=items)
218+
items[:] = []
206219
else:
207-
items[:] = previously_failed + previously_passed
208-
elif self._no_failures_behavior == "none":
209-
config.hook.pytest_deselected(items=items)
210-
items[:] = []
220+
self._report_status += "not deselecting items."
211221

212222
def pytest_sessionfinish(self, session):
213223
config = self.config
@@ -303,8 +313,7 @@ def pytest_addoption(parser):
303313
dest="last_failed_no_failures",
304314
choices=("all", "none"),
305315
default="all",
306-
help="change the behavior when no test failed in the last run or no "
307-
"information about the last failures was found in the cache",
316+
help="which tests to run with no previously (known) failures.",
308317
)
309318

310319

testing/test_cacheprovider.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import py
1111

1212
import pytest
13+
from _pytest.main import EXIT_NOTESTSCOLLECTED
1314

1415
pytest_plugins = ("pytester",)
1516

@@ -251,7 +252,13 @@ def test_3():
251252
result = testdir.runpytest("--lf")
252253
result.stdout.fnmatch_lines(["*2 passed*1 desel*"])
253254
result = testdir.runpytest("--lf")
254-
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
255+
result.stdout.fnmatch_lines(
256+
[
257+
"collected 3 items",
258+
"run-last-failure: no previously failed tests, not deselecting items.",
259+
"*1 failed*2 passed*",
260+
]
261+
)
255262
result = testdir.runpytest("--lf", "--cache-clear")
256263
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
257264

@@ -425,7 +432,13 @@ def test_b2():
425432
)
426433

427434
result = testdir.runpytest(test_a, "--lf")
428-
result.stdout.fnmatch_lines(["collected 2 items", "*2 passed in*"])
435+
result.stdout.fnmatch_lines(
436+
[
437+
"collected 2 items",
438+
"run-last-failure: 2 known failures not in selected tests",
439+
"*2 passed in*",
440+
]
441+
)
429442

430443
result = testdir.runpytest(test_b, "--lf")
431444
result.stdout.fnmatch_lines(
@@ -721,7 +734,14 @@ def test_2():
721734
result = testdir.runpytest("--lf", "--lfnf", "all")
722735
result.stdout.fnmatch_lines(["*2 passed*"])
723736
result = testdir.runpytest("--lf", "--lfnf", "none")
724-
result.stdout.fnmatch_lines(["*2 desel*"])
737+
result.stdout.fnmatch_lines(
738+
[
739+
"collected 2 items / 2 deselected",
740+
"run-last-failure: no previously failed tests, deselecting all items.",
741+
"* 2 deselected in *",
742+
]
743+
)
744+
assert result.ret == EXIT_NOTESTSCOLLECTED
725745

726746
def test_lastfailed_no_failures_behavior_empty_cache(self, testdir):
727747
testdir.makepyfile(

0 commit comments

Comments
 (0)