parent
							
								
									ebc0cea226
								
							
						
					
					
						commit
						08734bdd18
					
				|  | @ -0,0 +1,2 @@ | ||||||
|  | The ``--last-failed`` (``--lf``) option got smarter and will now skip entire files if all tests | ||||||
|  | of that test file have passed in previous runs, greatly speeding up collection. | ||||||
|  | @ -158,6 +158,33 @@ class LFPlugin(object): | ||||||
|         self.lastfailed = config.cache.get("cache/lastfailed", {}) |         self.lastfailed = config.cache.get("cache/lastfailed", {}) | ||||||
|         self._previously_failed_count = None |         self._previously_failed_count = None | ||||||
|         self._report_status = None |         self._report_status = None | ||||||
|  |         self._skipped_files = 0  # count skipped files during collection due to --lf | ||||||
|  | 
 | ||||||
|  |     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: | ||||||
|  |             rootpath = Path(self.config.rootdir) | ||||||
|  |             result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} | ||||||
|  |             self._last_failed_paths = result | ||||||
|  |         return result | ||||||
|  | 
 | ||||||
|  |     def pytest_ignore_collect(self, path): | ||||||
|  |         """ | ||||||
|  |         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.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 | ||||||
| 
 | 
 | ||||||
|     def pytest_report_collectionfinish(self): |     def pytest_report_collectionfinish(self): | ||||||
|         if self.active and self.config.getoption("verbose") >= 0: |         if self.active and self.config.getoption("verbose") >= 0: | ||||||
|  | @ -206,9 +233,19 @@ class LFPlugin(object): | ||||||
|                     items[:] = previously_failed + previously_passed |                     items[:] = previously_failed + previously_passed | ||||||
| 
 | 
 | ||||||
|                 noun = "failure" if self._previously_failed_count == 1 else "failures" |                 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 "" |                 suffix = " first" if self.config.getoption("failedfirst") else "" | ||||||
|                 self._report_status = "rerun previous {count} {noun}{suffix}".format( |                 self._report_status = "rerun previous {count} {noun}{suffix}{skipped_files}".format( | ||||||
|                     count=self._previously_failed_count, suffix=suffix, noun=noun |                     count=self._previously_failed_count, | ||||||
|  |                     suffix=suffix, | ||||||
|  |                     noun=noun, | ||||||
|  |                     skipped_files=skipped_files_msg, | ||||||
|                 ) |                 ) | ||||||
|         else: |         else: | ||||||
|             self._report_status = "no previously failed tests, " |             self._report_status = "no previously failed tests, " | ||||||
|  |  | ||||||
|  | @ -445,9 +445,9 @@ class TestLastFailed(object): | ||||||
|         result = testdir.runpytest("--lf") |         result = testdir.runpytest("--lf") | ||||||
|         result.stdout.fnmatch_lines( |         result.stdout.fnmatch_lines( | ||||||
|             [ |             [ | ||||||
|                 "collected 4 items / 2 deselected / 2 selected", |                 "collected 2 items", | ||||||
|                 "run-last-failure: rerun previous 2 failures", |                 "run-last-failure: rerun previous 2 failures (skipped 1 file)", | ||||||
|                 "*2 failed, 2 deselected in*", |                 "*2 failed in*", | ||||||
|             ] |             ] | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  | @ -718,7 +718,7 @@ class TestLastFailed(object): | ||||||
|         assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"] |         assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"] | ||||||
| 
 | 
 | ||||||
|         result = testdir.runpytest("--last-failed") |         result = testdir.runpytest("--last-failed") | ||||||
|         result.stdout.fnmatch_lines(["*1 failed, 3 deselected*"]) |         result.stdout.fnmatch_lines(["*1 failed, 1 deselected*"]) | ||||||
|         assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"] |         assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"] | ||||||
| 
 | 
 | ||||||
|         # 3. fix test_foo_4, run only test_foo.py |         # 3. fix test_foo_4, run only test_foo.py | ||||||
|  | @ -779,6 +779,58 @@ class TestLastFailed(object): | ||||||
|         result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "none") |         result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "none") | ||||||
|         result.stdout.fnmatch_lines(["*2 desel*"]) |         result.stdout.fnmatch_lines(["*2 desel*"]) | ||||||
| 
 | 
 | ||||||
|  |     def test_lastfailed_skip_collection(self, testdir): | ||||||
|  |         """ | ||||||
|  |         Test --lf behavior regarding skipping collection of files that are not marked as | ||||||
|  |         failed in the cache (#5172). | ||||||
|  |         """ | ||||||
|  |         testdir.makepyfile( | ||||||
|  |             **{ | ||||||
|  |                 "pkg1/test_1.py": """ | ||||||
|  |                 import pytest | ||||||
|  | 
 | ||||||
|  |                 @pytest.mark.parametrize('i', range(3)) | ||||||
|  |                 def test_1(i): pass | ||||||
|  |             """, | ||||||
|  |                 "pkg2/test_2.py": """ | ||||||
|  |                 import pytest | ||||||
|  | 
 | ||||||
|  |                 @pytest.mark.parametrize('i', range(5)) | ||||||
|  |                 def test_1(i): | ||||||
|  |                     assert i not in (1, 3) | ||||||
|  |             """, | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         # first run: collects 8 items (test_1: 3, test_2: 5) | ||||||
|  |         result = testdir.runpytest() | ||||||
|  |         result.stdout.fnmatch_lines(["collected 8 items", "*2 failed*6 passed*"]) | ||||||
|  |         # second run: collects only 5 items from test_2, because all tests from test_1 have passed | ||||||
|  |         result = testdir.runpytest("--lf") | ||||||
|  |         result.stdout.fnmatch_lines( | ||||||
|  |             [ | ||||||
|  |                 "collected 5 items / 3 deselected / 2 selected", | ||||||
|  |                 "run-last-failure: rerun previous 2 failures (skipped 1 file)", | ||||||
|  |                 "*2 failed*3 deselected*", | ||||||
|  |             ] | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         # add another file and check if message is correct when skipping more than 1 file | ||||||
|  |         testdir.makepyfile( | ||||||
|  |             **{ | ||||||
|  |                 "pkg1/test_3.py": """ | ||||||
|  |                 def test_3(): pass | ||||||
|  |             """ | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         result = testdir.runpytest("--lf") | ||||||
|  |         result.stdout.fnmatch_lines( | ||||||
|  |             [ | ||||||
|  |                 "collected 5 items / 3 deselected / 2 selected", | ||||||
|  |                 "run-last-failure: rerun previous 2 failures (skipped 2 files)", | ||||||
|  |                 "*2 failed*3 deselected*", | ||||||
|  |             ] | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class TestNewFirst(object): | class TestNewFirst(object): | ||||||
|     def test_newfirst_usecase(self, testdir): |     def test_newfirst_usecase(self, testdir): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue