Skip to content

Commit 5e07460

Browse files
authored
feat: add coverage output to py_pytest_main (#492)
If the user runs with coverage enabled, Bazel sets an environment variable pointing to the files to be instrumented. When that's present, attempt to import the coverage.py package, and use it to instrument and gather coverage, writing lcov directly. Note the user must provide the coverage.py package and it must be version 6.3 or greater, so that lcov reporting is built-in. Fixes #353 Tested: `bazel test --collect_code_coverage --combined_report=lcov -s examples/pytest:pytest_test`
1 parent 82f445b commit 5e07460

File tree

9 files changed

+154
-6
lines changed

9 files changed

+154
-6
lines changed

.github/workflows/ci.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ jobs:
6060
steps:
6161
- uses: actions/checkout@v4
6262
- run: ./minimal_download_test.sh
63+
- run: |
64+
bazel coverage //...
65+
ls -R $(bazel info bazel-testlogs)/_coverage/test
6366
6467
# For branch protection settings, this job provides a "stable" name that can be used to gate PR merges
6568
# on "all matrix jobs were successful".

examples/pytest/BUILD.bazel

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
load("@aspect_rules_py//py:defs.bzl", "py_pytest_main", "py_test")
1+
load("@aspect_rules_py//py:defs.bzl", "py_library", "py_pytest_main", "py_test")
2+
3+
py_library(
4+
name = "lib",
5+
srcs = ["foo.py"],
6+
imports = ["../.."],
7+
)
28

39
py_pytest_main(
410
name = "__test__",
5-
deps = ["@pypi_pytest//:pkg"],
11+
deps = [
12+
"@pypi_coverage//:pkg",
13+
"@pypi_pytest//:pkg",
14+
],
615
)
716

817
py_test(
@@ -17,6 +26,7 @@ py_test(
1726
package_collisions = "warning",
1827
deps = [
1928
":__test__",
29+
":lib",
2030
"@pypi_ftfy//:pkg",
2131
"@pypi_neptune//:pkg",
2232
"@pypi_pytest//:pkg",
@@ -35,6 +45,7 @@ py_test(
3545
package_collisions = "warning",
3646
deps = [
3747
":__test__",
48+
":lib",
3849
"@pypi_ftfy//:pkg",
3950
"@pypi_neptune//:pkg",
4051
"@pypi_pytest//:pkg",

examples/pytest/foo.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def add(a, b):
2+
return a + b
3+
4+
def subtract(a, b):
5+
return a - b
6+
7+

examples/pytest/foo_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
from examples.pytest.foo import add
34

45
def test_add():
5-
assert 1 + 1 == 2, "Expected 1 + 1 to equal 2"
6+
assert add(1, 1) == 2, "Expected 1 + 1 to equal 2"

gazelle_python.yaml

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,48 @@ manifest:
312312
colorama.tests.winterm_test: colorama
313313
colorama.win32: colorama
314314
colorama.winterm: colorama
315+
coverage: coverage
316+
coverage.annotate: coverage
317+
coverage.bytecode: coverage
318+
coverage.cmdline: coverage
319+
coverage.collector: coverage
320+
coverage.config: coverage
321+
coverage.context: coverage
322+
coverage.control: coverage
323+
coverage.core: coverage
324+
coverage.data: coverage
325+
coverage.debug: coverage
326+
coverage.disposition: coverage
327+
coverage.env: coverage
328+
coverage.exceptions: coverage
329+
coverage.execfile: coverage
330+
coverage.files: coverage
331+
coverage.html: coverage
332+
coverage.inorout: coverage
333+
coverage.jsonreport: coverage
334+
coverage.lcovreport: coverage
335+
coverage.misc: coverage
336+
coverage.multiproc: coverage
337+
coverage.numbits: coverage
338+
coverage.parser: coverage
339+
coverage.phystokens: coverage
340+
coverage.plugin: coverage
341+
coverage.plugin_support: coverage
342+
coverage.python: coverage
343+
coverage.pytracer: coverage
344+
coverage.regions: coverage
345+
coverage.report: coverage
346+
coverage.report_core: coverage
347+
coverage.results: coverage
348+
coverage.sqldata: coverage
349+
coverage.sqlitedb: coverage
350+
coverage.sysmon: coverage
351+
coverage.templite: coverage
352+
coverage.tomlconfig: coverage
353+
coverage.tracer: coverage
354+
coverage.types: coverage
355+
coverage.version: coverage
356+
coverage.xmlreport: coverage
315357
cowsay: cowsay
316358
cowsay.characters: cowsay
317359
cowsay.main: cowsay
@@ -2161,7 +2203,6 @@ manifest:
21612203
numpy.lib.ufunclike: numpy
21622204
numpy.lib.user_array: numpy
21632205
numpy.lib.utils: numpy
2164-
numpy.libs.libopenblas64_p-r0-0cf96a72: numpy
21652206
numpy.linalg: numpy
21662207
numpy.linalg.lapack_lite: numpy
21672208
numpy.linalg.linalg: numpy
@@ -3717,7 +3758,6 @@ manifest:
37173758
past.types.olddict: future
37183759
past.types.oldstr: future
37193760
past.utils: future
3720-
pillow.libs.libopenjp2-05423b53: pillow
37213761
pluggy: pluggy
37223762
proxytypes: jsonref
37233763
psutil: psutil
@@ -3990,4 +4030,4 @@ manifest:
39904030
yaml.tokens: PyYAML
39914031
pip_repository:
39924032
name: pypi
3993-
integrity: de76d829a37e0a5afc12859854a0f8fa096534eb4c32e6a346b6378b55634f44
4033+
integrity: 6359bc010056b6e19ee6cbb2fc9a8ac841449521e3b4997fec58163e5c635496

py/private/pytest.py.tmpl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ import os
1717
from typing import List
1818

1919
import pytest
20+
# None means coverage wasn't enabled
21+
cov = None
22+
23+
# Since our py_test had InstrumentedFilesInfo, we know Bazel will hand us this environment variable.
24+
# https://bazel.build/rules/lib/providers/InstrumentedFilesInfo
25+
if "COVERAGE_MANIFEST" in os.environ:
26+
try:
27+
import coverage
28+
# The lines are files that matched the --instrumentation_filter flag
29+
with open(os.getenv("COVERAGE_MANIFEST"), "r") as mf:
30+
cov = coverage.Coverage(include = mf.read().splitlines())
31+
cov.start()
32+
except Exception as e:
33+
print("WARNING: python coverage setup failed. Do you need to include the 'coverage' library as a dependency of py_pytest_main?", e)
34+
pass
2035

2136
if __name__ == "__main__":
2237
# Change to the directory where we need to run the test or execute a no-op
@@ -57,5 +72,10 @@ if __name__ == "__main__":
5772
if exit_code != 0:
5873
print("Pytest exit code: " + str(exit_code), file=sys.stderr)
5974
print("Ran pytest.main with " + str(args), file=sys.stderr)
75+
elif cov:
76+
cov.stop()
77+
# https://bazel.build/configure/coverage
78+
cov.lcov_report(outfile = os.getenv("COVERAGE_OUTPUT_FILE"))
79+
cov.save()
6080

6181
sys.exit(exit_code)

py/tools/venv_bin/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
load("//tools/release:defs.bzl", "rust_binary")
22

3+
# TODO(#497): transition to --nocollect_code_coverage to avoid rules_rust trying to instrument this binary
34
rust_binary(
45
name = "venv",
56
srcs = [

requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ django~=4.2.7
22
colorama~=0.4.0
33
click
44
pytest
5+
coverage
56
cowsay
67
snakesay
78
ftfy==6.2.0

requirements.txt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,70 @@ colorama==0.4.6 \
141141
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
142142
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
143143
# via -r requirements.in
144+
coverage==7.6.10 \
145+
--hash=sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9 \
146+
--hash=sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f \
147+
--hash=sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273 \
148+
--hash=sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994 \
149+
--hash=sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e \
150+
--hash=sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50 \
151+
--hash=sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e \
152+
--hash=sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e \
153+
--hash=sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c \
154+
--hash=sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853 \
155+
--hash=sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8 \
156+
--hash=sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8 \
157+
--hash=sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe \
158+
--hash=sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165 \
159+
--hash=sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb \
160+
--hash=sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59 \
161+
--hash=sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609 \
162+
--hash=sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18 \
163+
--hash=sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098 \
164+
--hash=sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd \
165+
--hash=sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3 \
166+
--hash=sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43 \
167+
--hash=sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d \
168+
--hash=sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359 \
169+
--hash=sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90 \
170+
--hash=sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78 \
171+
--hash=sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a \
172+
--hash=sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99 \
173+
--hash=sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988 \
174+
--hash=sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2 \
175+
--hash=sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0 \
176+
--hash=sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694 \
177+
--hash=sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377 \
178+
--hash=sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d \
179+
--hash=sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23 \
180+
--hash=sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312 \
181+
--hash=sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf \
182+
--hash=sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6 \
183+
--hash=sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b \
184+
--hash=sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c \
185+
--hash=sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690 \
186+
--hash=sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a \
187+
--hash=sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f \
188+
--hash=sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4 \
189+
--hash=sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25 \
190+
--hash=sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd \
191+
--hash=sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852 \
192+
--hash=sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0 \
193+
--hash=sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244 \
194+
--hash=sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315 \
195+
--hash=sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078 \
196+
--hash=sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0 \
197+
--hash=sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27 \
198+
--hash=sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132 \
199+
--hash=sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5 \
200+
--hash=sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247 \
201+
--hash=sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022 \
202+
--hash=sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b \
203+
--hash=sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3 \
204+
--hash=sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18 \
205+
--hash=sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5 \
206+
--hash=sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f
207+
# via -r requirements.in
144208
cowsay==6.1 \
145209
--hash=sha256:274b1e6fc1b966d53976333eb90ac94cb07a450a700b455af9fbdf882244b30a
146210
# via -r requirements.in

0 commit comments

Comments
 (0)