From bf3b26b3f72d46bf64a2a6c64da03f93f24b47af Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 29 May 2019 22:43:24 +0200 Subject: [PATCH 1/4] Fix regression with --lf and non-selected failures --- src/_pytest/cacheprovider.py | 1 + testing/test_cacheprovider.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 3eaf7de28..b194f80b0 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -178,6 +178,7 @@ class LFPlugin(object): """ if ( self.active + and self._previously_failed_count and self.config.getoption("lf") and path.isfile() and self.lastfailed diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 5024701f1..da6a3da49 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -832,6 +832,26 @@ class TestLastFailed(object): ] ) + def test_lastfailed_with_unknown_failure(self, testdir): + testdir.makepyfile( + **{ + "pkg1/test_1.py": """def test_1(): assert 0""", + "pkg1/test_2.py": """def test_2(): pass""", + } + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"]) + + py.path.local("pkg1/test_1.py").remove() + result = testdir.runpytest("--lf") + result.stdout.fnmatch_lines( + [ + "collected 1 item", + "run-last-failure: 1 known failures not in selected tests", + "* 1 passed in *", + ] + ) + class TestNewFirst(object): def test_newfirst_usecase(self, testdir): From ceb4f3f701e330221f13a7ee66a2199b9b5e82e6 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 29 May 2019 23:12:45 +0200 Subject: [PATCH 2/4] fixup! Fix regression with --lf and non-selected failures --- changelog/5333.bugfix.rst | 1 + src/_pytest/cacheprovider.py | 19 ++++++++----------- testing/test_cacheprovider.py | 24 +++++++++++++++++++++++- 3 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 changelog/5333.bugfix.rst diff --git a/changelog/5333.bugfix.rst b/changelog/5333.bugfix.rst new file mode 100644 index 000000000..ac1d79585 --- /dev/null +++ b/changelog/5333.bugfix.rst @@ -0,0 +1 @@ +Fix regression with ``--lf`` not re-running all tests with known failures from non-selected tests. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index b194f80b0..b13f5e819 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -168,6 +168,7 @@ class LFPlugin(object): if result is None: rootpath = Path(self.config.rootdir) result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} + result = {x for x in result if x.exists()} self._last_failed_paths = result return result @@ -176,17 +177,13 @@ class LFPlugin(object): Ignore this file path if we are in --lf mode and it is not in the list of previously failed files. """ - if ( - self.active - and self._previously_failed_count - and self.config.getoption("lf") - and path.isfile() - and self.lastfailed - ): - skip_it = Path(path) not in self.last_failed_paths() - if skip_it: - self._skipped_files += 1 - return skip_it + if self.active and self.config.getoption("lf") and path.isfile(): + last_failed_paths = self.last_failed_paths() + if last_failed_paths: + skip_it = Path(path) not in self.last_failed_paths() + if skip_it: + self._skipped_files += 1 + return skip_it def pytest_report_collectionfinish(self): if self.active and self.config.getoption("verbose") >= 0: diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index da6a3da49..ed205d0f8 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -832,7 +832,7 @@ class TestLastFailed(object): ] ) - def test_lastfailed_with_unknown_failure(self, testdir): + def test_lastfailed_with_known_failures_not_being_selected(self, testdir): testdir.makepyfile( **{ "pkg1/test_1.py": """def test_1(): assert 0""", @@ -852,6 +852,28 @@ class TestLastFailed(object): ] ) + # Recreate file with known failure. + testdir.makepyfile(**{"pkg1/test_1.py": """def test_1(): assert 0"""}) + result = testdir.runpytest("--lf") + result.stdout.fnmatch_lines( + [ + "collected 1 item", + "run-last-failure: rerun previous 1 failure (skipped 1 file)", + "* 1 failed in *", + ] + ) + + # Remove/rename test. + testdir.makepyfile(**{"pkg1/test_1.py": """def test_renamed(): assert 0"""}) + result = testdir.runpytest("--lf") + result.stdout.fnmatch_lines( + [ + "collected 1 item", + "run-last-failure: 1 known failures not in selected tests", + "* 1 failed in *", + ] + ) + class TestNewFirst(object): def test_newfirst_usecase(self, testdir): From 480dd9e6d6dd4869a7467ae17a7bea6e5367a3c4 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 29 May 2019 23:28:58 +0200 Subject: [PATCH 3/4] last_failed_paths: improve caching --- src/_pytest/cacheprovider.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index b13f5e819..106535467 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -164,13 +164,14 @@ class LFPlugin(object): def last_failed_paths(self): """Returns a set with all Paths()s of the previously failed nodeids (cached). """ - result = getattr(self, "_last_failed_paths", None) - if result is None: + try: + return self._last_failed_paths + except AttributeError: rootpath = Path(self.config.rootdir) result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} result = {x for x in result if x.exists()} self._last_failed_paths = result - return result + return result def pytest_ignore_collect(self, path): """ From ff80464b47d095906bd3d1c68c6afc68c105ca41 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 29 May 2019 23:32:52 +0200 Subject: [PATCH 4/4] last-failed: display skipped-files msg always --- src/_pytest/cacheprovider.py | 20 ++++++++------------ testing/test_cacheprovider.py | 2 +- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 106535467..045248cb7 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -233,19 +233,15 @@ class LFPlugin(object): items[:] = previously_failed + previously_passed noun = "failure" if self._previously_failed_count == 1 else "failures" - if self._skipped_files > 0: - files_noun = "file" if self._skipped_files == 1 else "files" - skipped_files_msg = " (skipped {files} {files_noun})".format( - files=self._skipped_files, files_noun=files_noun - ) - else: - skipped_files_msg = "" suffix = " first" if self.config.getoption("failedfirst") else "" - self._report_status = "rerun previous {count} {noun}{suffix}{skipped_files}".format( - count=self._previously_failed_count, - suffix=suffix, - noun=noun, - skipped_files=skipped_files_msg, + self._report_status = "rerun previous {count} {noun}{suffix}".format( + count=self._previously_failed_count, suffix=suffix, noun=noun + ) + + if self._skipped_files > 0: + files_noun = "file" if self._skipped_files == 1 else "files" + self._report_status += " (skipped {files} {files_noun})".format( + files=self._skipped_files, files_noun=files_noun ) else: self._report_status = "no previously failed tests, " diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index ed205d0f8..6de0b3129 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -869,7 +869,7 @@ class TestLastFailed(object): result.stdout.fnmatch_lines( [ "collected 1 item", - "run-last-failure: 1 known failures not in selected tests", + "run-last-failure: 1 known failures not in selected tests (skipped 1 file)", "* 1 failed in *", ] )