diff --git a/CHANGES.rst b/CHANGES.rst index 1ae94d2a4..3e4589fd8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,6 +23,13 @@ upgrading your version of coverage.py. Unreleased ---------- +- Fix: in some cases, even with ``[run] relative_files=True``, a data file + could be created with absolute path names. When combined with other relative + data files, it was random whether the absolute file names would be made + relative or not. If they weren't, then a file would be listed twice in + reports, as detailed in `issue 1752`_. This is now fixed: absolute file + names are always made relative when combining. + - Fix: the last case of a match/case statement had an incorrect message if the branch was missed. It said the pattern never matched, when actually the branch is missed if the last case always matched. @@ -33,6 +40,7 @@ Unreleased string. Thanks, `Tanaydin Sirin `_. It is also now documented on the :ref:`configuration page `. +.. _issue 1752: https://github.com/nedbat/coveragepy/issues/1752 .. _pull 1754: https://github.com/nedbat/coveragepy/pull/1754 diff --git a/coverage/data.py b/coverage/data.py index 0db07d156..9513adfca 100644 --- a/coverage/data.py +++ b/coverage/data.py @@ -88,7 +88,10 @@ def combinable_files(data_file: str, data_paths: Iterable[str] | None = None) -> # We never want to combine those. files_to_combine = [fnm for fnm in files_to_combine if not fnm.endswith("-journal")] - return files_to_combine + # Sorting isn't usually needed, since it shouldn't matter what order files + # are combined, but sorting makes tests more predictable, and makes + # debugging more understandable when things go wrong. + return sorted(files_to_combine) def combine_parallel_data( diff --git a/coverage/files.py b/coverage/files.py index 71352b8eb..0dd3c4e01 100644 --- a/coverage/files.py +++ b/coverage/files.py @@ -489,6 +489,9 @@ def map(self, path: str, exists:Callable[[str], bool] = source_exists) -> str: # If we get here, no pattern matched. + if self.relative: + path = relative_filename(path) + if self.relative and not isabs_anywhere(path): # Auto-generate a pattern to implicitly match relative files parts = re.split(r"[/\\]", path) diff --git a/tests/test_api.py b/tests/test_api.py index b6ab9cda0..9f65166b9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1465,3 +1465,23 @@ def test_combine_parallel_data_keep(self) -> None: # After combining, the .coverage file & the original combined file should still be there. self.assert_exists(".coverage") self.assert_file_count(".coverage.*", 2) + + @pytest.mark.parametrize("abs_order, rel_order", [(1, 2), (2, 1)]) + def test_combine_absolute_then_relative_1752(self, abs_order: int, rel_order: int) -> None: + # https://github.com/nedbat/coveragepy/issues/1752 + # If we're combining a relative data file and an absolute data file, + # the absolutes were made relative only if the relative file name was + # encountered first. Test combining in both orders and check that the + # absolute file name is properly relative in either order. + FILE = "sub/myprog.py" + self.make_file(FILE, "a = 1") + + self.make_data_file(suffix=f"{abs_order}.abs", lines={abs_file(FILE): [1]}) + self.make_data_file(suffix=f"{rel_order}.rel", lines={FILE: [1]}) + + self.make_file(".coveragerc", "[run]\nrelative_files = True\n") + cov = coverage.Coverage() + cov.combine() + data = coverage.CoverageData() + data.read() + assert {os_sep("sub/myprog.py")} == data.measured_files()