Skip to content

Commit 1717d11

Browse files
authored
fix: workaround absolute paths appearing in coverage data (#524)
Issue reported here: aspect-build/bazel-examples#404 (comment) Now looks like ``` SF:src/__init__.py DA:1,1 DA:2,1 ``` Similar to https://github.com/bazelbuild/rules_python/blob/466da1d9710289bfb01061b9be7bb124132996e0/python/private/python_bootstrap_template.txt#L336-L346 but ours is a bit more principled since we don't break the abstraction of what coveragepy happens to do with the paths. ### Changes are visible to end-users: no ### Test plan - Manual testing; please provide instructions so we can reproduce: Just `bazel coverage src:test` and inspect the .dat file
1 parent 9270b4d commit 1717d11

File tree

5 files changed

+27
-4
lines changed

5 files changed

+27
-4
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
uses: zgosalvez/github-actions-report-lcov@5989987f8058a03137e90bc16f9c0baaac5e069a # v4.1.22
7777
with:
7878
title-prefix: "e2e/use_release folder:"
79+
working-directory: e2e/use_release
7980
# Point to the already-merged data file Bazel produces with --combined_report=lcov
8081
# Follows https://bazel.build/configure/coverage#running_coverage
8182
coverage-files: "${{ env.output_path }}/_coverage/_coverage_report.dat"

e2e/use_release/.coveragerc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[run]
2+
# NB: this ought to put relative paths into the report files.
3+
# However Bazel actions run in a sandbox with symlinks, and coveragepy follows those out of the sandbox
4+
# resulting in the wrong paths.
5+
# See https://github.com/nedbat/coveragepy/issues/963 and https://github.com/aspect-build/rules_py/pull/524
6+
relative_files = False

e2e/use_release/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
load("@rules_uv//uv:pip.bzl", "pip_compile")
22

3+
exports_files([".coveragerc"])
4+
35
pip_compile(name = "pip_compile")

e2e/use_release/src/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ py_binary(
1212
# TODO(alex): land https://github.com/aspect-build/rules_py/pull/401 and shorten this
1313
py_pytest_main(
1414
name = "__test__",
15+
data = ["//:.coveragerc"],
1516
deps = [
1617
"@pip//coverage",
1718
"@pip//pytest",

py/private/pytest.py.tmpl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ from typing import List
2020
import pytest
2121
# None means coverage wasn't enabled
2222
cov = None
23+
# For workaround of https://github.com/nedbat/coveragepy/issues/963
24+
coveragepy_absfile_mapping = {}
2325

2426
# Since our py_test had InstrumentedFilesInfo, we know Bazel will hand us this environment variable.
2527
# https://bazel.build/rules/lib/providers/InstrumentedFilesInfo
@@ -28,9 +30,13 @@ if "COVERAGE_MANIFEST" in os.environ:
2830
import coverage
2931
# The lines are files that matched the --instrumentation_filter flag
3032
with open(os.getenv("COVERAGE_MANIFEST"), "r") as mf:
31-
cov = coverage.Coverage(include = mf.read().splitlines())
33+
manifest_entries = mf.read().splitlines()
34+
cov = coverage.Coverage(include = manifest_entries)
35+
# coveragepy incorrectly converts our entries by following symlinks
36+
# record a mapping of their conversion so we can undo it later in reporting the coverage
37+
coveragepy_absfile_mapping = {coverage.files.abs_file(mfe): mfe for mfe in manifest_entries}
3238
cov.start()
33-
except Exception as e:
39+
except ModuleNotFoundError as e:
3440
print("WARNING: python coverage setup failed. Do you need to include the 'coverage' library as a dependency of py_pytest_main?", e)
3541
pass
3642

@@ -95,15 +101,22 @@ if __name__ == "__main__":
95101
# https://bazel.build/configure/coverage
96102
coverage_output_file = os.getenv("COVERAGE_OUTPUT_FILE")
97103

98-
# Workaround https://github.com/bazelbuild/bazel/issues/25118
99-
# by removing 'end line number' from FN: records
100104
unfixed_dat = coverage_output_file + ".tmp"
101105
cov.lcov_report(outfile = unfixed_dat)
102106
cov.save()
103107

104108
with open(unfixed_dat, "r") as unfixed:
105109
with open(coverage_output_file, "w") as output_file:
106110
for line in unfixed:
111+
# Workaround https://github.com/nedbat/coveragepy/issues/963
112+
# by mapping SF: records to un-do the symlink-following
113+
if line.startswith('SF:'):
114+
sourcefile = line[3:].rstrip()
115+
if sourcefile in coveragepy_absfile_mapping:
116+
output_file.write(f"SF:{coveragepy_absfile_mapping[sourcefile]}\n")
117+
continue
118+
# Workaround https://github.com/bazelbuild/bazel/issues/25118
119+
# by removing 'end line number' from FN: records
107120
if line.startswith('FN:'):
108121
parts = line[3:].split(",") # Remove 'FN:' and split by commas
109122
if len(parts) == 3:

0 commit comments

Comments
 (0)