Skip to content

Commit

Permalink
Merge pull request #3295 from brianmaissy/feature/last-failed-no-fail…
Browse files Browse the repository at this point in the history
…ures-behavior

implemented --last-failed-no-failures
  • Loading branch information
nicoddemus authored Mar 20, 2018
2 parents f61d052 + d2e533b commit d03e389
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 18 deletions.
47 changes: 29 additions & 18 deletions _pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,12 @@ 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')

def pytest_report_collectionfinish(self):
if self.active:
if not self._previously_failed_count:
mode = "run all (no recorded failures)"
mode = "run {} (no recorded failures)".format(self._no_failures_behavior)
else:
noun = 'failure' if self._previously_failed_count == 1 else 'failures'
suffix = " first" if self.config.getoption(
Expand Down Expand Up @@ -144,24 +145,28 @@ def pytest_collectreport(self, report):
self.lastfailed[report.nodeid] = True

def pytest_collection_modifyitems(self, session, config, items):
if self.active and self.lastfailed:
previously_failed = []
previously_passed = []
for item in items:
if item.nodeid in self.lastfailed:
previously_failed.append(item)
if self.active:
if self.lastfailed:
previously_failed = []
previously_passed = []
for item in items:
if item.nodeid in self.lastfailed:
previously_failed.append(item)
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)
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)
else:
items[:] = previously_failed + previously_passed
items[:] = previously_failed + previously_passed
elif self._no_failures_behavior == 'none':
config.hook.pytest_deselected(items=items)
items[:] = []

def pytest_sessionfinish(self, session):
config = self.config
Expand Down Expand Up @@ -230,6 +235,12 @@ def pytest_addoption(parser):
parser.addini(
"cache_dir", default='.pytest_cache',
help="cache directory path.")
group.addoption(
'--lfnf', '--last-failed-no-failures', action='store',
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'
)


def pytest_cmdline_main(config):
Expand Down
1 change: 1 addition & 0 deletions changelog/3139.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New ``--last-failed-no-failures`` command-line option that allows to specify the behavior of the cache plugin's ```--last-failed`` feature when no tests failed in the last run (or no cache was found): ``none`` or ``all`` (the default).
10 changes: 10 additions & 0 deletions doc/en/cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ New ``--nf``, ``--new-first`` options: run new tests first followed by the rest
of the tests, in both cases tests are also sorted by the file modified time,
with more recent files coming first.

Behavior when no tests failed in the last run
---------------------------------------------

When no tests failed in the last run, or when no cached ``lastfailed`` data was
found, ``pytest`` can be configured either to run all of the tests or no tests,
using the ``--last-failed-no-failures`` option, which takes one of the following values::

pytest --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed-no-failures none # run no tests and exit

The new config.cache object
--------------------------------

Expand Down
30 changes: 30 additions & 0 deletions testing/test_cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,36 @@ def test_foo_4():
result.stdout.fnmatch_lines('*4 passed*')
assert self.get_cached_last_failed(testdir) == []

def test_lastfailed_no_failures_behavior_all_passed(self, testdir):
testdir.makepyfile("""
def test_1():
assert True
def test_2():
assert True
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines(["*2 passed*"])
result = testdir.runpytest("--lf")
result.stdout.fnmatch_lines(["*2 passed*"])
result = testdir.runpytest("--lf", "--lfnf", "all")
result.stdout.fnmatch_lines(["*2 passed*"])
result = testdir.runpytest("--lf", "--lfnf", "none")
result.stdout.fnmatch_lines(["*2 desel*"])

def test_lastfailed_no_failures_behavior_empty_cache(self, testdir):
testdir.makepyfile("""
def test_1():
assert True
def test_2():
assert False
""")
result = testdir.runpytest("--lf", "--cache-clear")
result.stdout.fnmatch_lines(["*1 failed*1 passed*"])
result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "all")
result.stdout.fnmatch_lines(["*1 failed*1 passed*"])
result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "none")
result.stdout.fnmatch_lines(["*2 desel*"])


class TestNewFirst(object):
def test_newfirst_usecase(self, testdir):
Expand Down

0 comments on commit d03e389

Please sign in to comment.