Skip to content

Commit

Permalink
fix: ensure absolute paths are relative when combined #1752
Browse files Browse the repository at this point in the history
If two data files are combined, one with absolute paths, and one with relative,
with relative_files=True in effect, the results depended on the order of
combining.

If the absolute files were seen first, they were added as absolute paths.  If
the relative files were seen first, a mapping rule was generated that would then
remap the absolute paths when they were seen.

This fix ensures that absolute paths are remapped even if they are seen first.
  • Loading branch information
nedbat committed Mar 14, 2024
1 parent 1ef020d commit 1b19799
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -33,6 +40,7 @@ Unreleased
string. Thanks, `Tanaydin Sirin <pull 1754_>`_. It is also now documented
on the :ref:`configuration page <config_report_format>`.

.. _issue 1752: https://github.com/nedbat/coveragepy/issues/1752
.. _pull 1754: https://github.com/nedbat/coveragepy/pull/1754


Expand Down
5 changes: 4 additions & 1 deletion coverage/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions coverage/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit 1b19799

Please sign in to comment.