Skip to content

Commit cb67b1f

Browse files
committed
feat: implicitly combine relative paths
1 parent c28544c commit cb67b1f

File tree

4 files changed

+45
-11
lines changed

4 files changed

+45
-11
lines changed

coverage/control.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -741,16 +741,14 @@ def combine(self, data_paths=None, strict=False, keep=False):
741741
self._post_init()
742742
self.get_data()
743743

744-
aliases = None
745-
if self.config.paths:
746-
aliases = PathAliases(
747-
debugfn=(self._debug.write if self._debug.should("pathmap") else None),
748-
relative=self.config.relative_files,
749-
)
750-
for paths in self.config.paths.values():
751-
result = paths[0]
752-
for pattern in paths[1:]:
753-
aliases.add(pattern, result)
744+
aliases = PathAliases(
745+
debugfn=(self._debug.write if self._debug.should("pathmap") else None),
746+
relative=self.config.relative_files,
747+
)
748+
for paths in self.config.paths.values():
749+
result = paths[0]
750+
for pattern in paths[1:]:
751+
aliases.add(pattern, result)
754752

755753
combine_parallel_data(
756754
self._data,

coverage/files.py

+14
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,20 @@ def map(self, path):
409409
f"producing {new!r}"
410410
)
411411
return new
412+
413+
# If we get here, no pattern matched.
414+
415+
if self.relative and not isabs_anywhere(path):
416+
parts = re.split(r"[/\\]", path)
417+
if len(parts) > 1:
418+
dir1 = parts[0]
419+
pattern = f"*/{dir1}"
420+
regex = rf"^(.*[\\/])?{re.escape(dir1)}[\\/]"
421+
result = f"{dir1}{os.sep}"
422+
self.debugfn(f"Generating rule: {pattern!r} -> {result!r} using regex {regex!r}")
423+
self.aliases.append((pattern, re.compile(regex), result))
424+
return self.map(path)
425+
412426
self.debugfn(f"No rules match, path {path!r} is unchanged")
413427
return path
414428

coverage/sqldata.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,9 @@ def update(self, other_data, aliases=None):
596596
"""Update this data with data from several other :class:`CoverageData` instances.
597597
598598
If `aliases` is provided, it's a `PathAliases` object that is used to
599-
re-map paths to match the local machine's.
599+
re-map paths to match the local machine's. Note: `aliases` is None
600+
only when called directly from the test suite.
601+
600602
"""
601603
if self._debug.should("dataop"):
602604
self._debug.write("Updating with data from {!r}".format(

tests/test_files.py

+20
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,26 @@ def test_relative_linux_on_windows(self, paths):
416416
r"project\module\tests\file.py",
417417
)
418418

419+
@pytest.mark.skipif(env.WINDOWS, reason="This test assumes Unix file system")
420+
def test_implicit_relative_windows_on_linux(self):
421+
# https://github.com/nedbat/coveragepy/issues/991
422+
aliases = PathAliases(relative=True)
423+
self.assert_mapped(
424+
aliases,
425+
r"project\module\tests\file.py",
426+
r"project/module/tests/file.py",
427+
)
428+
429+
@pytest.mark.skipif(not env.WINDOWS, reason="This test assumes Windows file system")
430+
def test_implicit_relative_linux_on_windows(self):
431+
# https://github.com/nedbat/coveragepy/issues/991
432+
aliases = PathAliases(relative=True)
433+
self.assert_mapped(
434+
aliases,
435+
r"project/module/tests/file.py",
436+
r"project\module\tests\file.py",
437+
)
438+
419439
def test_multiple_wildcard(self, rel_yn):
420440
aliases = PathAliases(relative=rel_yn)
421441
aliases.add('/home/jenkins/*/a/*/b/*/django', './django')

0 commit comments

Comments
 (0)