Skip to content

Commit 9270b4d

Browse files
authored
fix: workaround Bazel lcov parsing bug (#521)
bazelbuild/bazel#25118 was causing our coverage reports to fail to parse into combined report. Related to #520 which was missing testing that the report actually worked. fyi @dizzy57
1 parent eb45eca commit 9270b4d

File tree

3 files changed

+32
-12
lines changed

3 files changed

+32
-12
lines changed

.github/workflows/ci.yaml

+5-4
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,18 @@ jobs:
6767
steps:
6868
- uses: actions/checkout@v4
6969
- run: ./minimal_download_test.sh
70-
- run: bazel coverage //src/...
70+
- run: bazel coverage --combined_report=lcov //src/...
7171
- uses: hrishikesh-kadam/setup-lcov@6c1aa0cc9e1c02f9f58f01ac599f1064ccc83470 # v1
7272
# The github-actions-report-lcov doesn't follow symlinks, so get an absolute path
73-
- run: echo "bazel_testlogs=$(bazel info bazel-testlogs)" >> $GITHUB_ENV
73+
- run: echo "output_path=$(bazel info output_path)" >> $GITHUB_ENV
7474
- name: Report code coverage
7575
if: github.event.pull_request.head.repo.fork == false # Forks always have read-only tokens
7676
uses: zgosalvez/github-actions-report-lcov@5989987f8058a03137e90bc16f9c0baaac5e069a # v4.1.22
7777
with:
7878
title-prefix: "e2e/use_release folder:"
79-
working-directory: ${{ env.bazel_testlogs }}
80-
coverage-files: "**/coverage.dat"
79+
# Point to the already-merged data file Bazel produces with --combined_report=lcov
80+
# Follows https://bazel.build/configure/coverage#running_coverage
81+
coverage-files: "${{ env.output_path }}/_coverage/_coverage_report.dat"
8182
github-token: ${{ secrets.GITHUB_TOKEN }}
8283
update-comment: true
8384

py/private/py_binary.bzl

+10-7
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,6 @@ A collision can occur when multiple packages providing the same file are install
175175
"_allowlist_function_transition": attr.label(
176176
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
177177
),
178-
# Magic attribute to make coverage work. There's no
179-
# docs about this; see TestActionBuilder.java
180-
"_lcov_merger": attr.label(
181-
default = configuration_field(fragment = "coverage", name = "output_generator"),
182-
executable = True,
183-
cfg = "exec",
184-
),
185178
})
186179

187180
_attrs.update(**_py_library.attrs)
@@ -191,6 +184,16 @@ _test_attrs = dict({
191184
doc = "Specifies additional environment variables to inherit from the external environment when the test is executed by bazel test.",
192185
default = [],
193186
),
187+
# Magic attribute to make coverage --combined_report flag work.
188+
# There's no docs about this.
189+
# See https://github.com/bazelbuild/bazel/blob/fde4b67009d377a3543a3dc8481147307bd37d36/tools/test/collect_coverage.sh#L186-L194
190+
# NB: rules_python ALSO includes this attribute on the py_binary rule, but we think that's a mistake.
191+
# see https://github.com/aspect-build/rules_py/pull/520#pullrequestreview-2579076197
192+
"_lcov_merger": attr.label(
193+
default = configuration_field(fragment = "coverage", name = "output_generator"),
194+
executable = True,
195+
cfg = "exec",
196+
),
194197
})
195198

196199
def _python_version_transition_impl(_, attr):

py/private/pytest.py.tmpl

+17-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,23 @@ if __name__ == "__main__":
9393
elif cov:
9494
cov.stop()
9595
# https://bazel.build/configure/coverage
96-
cov.lcov_report(outfile = os.getenv("COVERAGE_OUTPUT_FILE"))
96+
coverage_output_file = os.getenv("COVERAGE_OUTPUT_FILE")
97+
98+
# Workaround https://github.com/bazelbuild/bazel/issues/25118
99+
# by removing 'end line number' from FN: records
100+
unfixed_dat = coverage_output_file + ".tmp"
101+
cov.lcov_report(outfile = unfixed_dat)
97102
cov.save()
103+
104+
with open(unfixed_dat, "r") as unfixed:
105+
with open(coverage_output_file, "w") as output_file:
106+
for line in unfixed:
107+
if line.startswith('FN:'):
108+
parts = line[3:].split(",") # Remove 'FN:' and split by commas
109+
if len(parts) == 3:
110+
output_file.write(f"FN:{parts[0]},{parts[2]}")
111+
continue
112+
output_file.write(line)
113+
os.unlink(unfixed_dat)
98114

99115
sys.exit(exit_code)

0 commit comments

Comments
 (0)