-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python Coverage does not work #10660
Comments
In order for cross-language coverage to work in Bazel, all coverage data needs to be converted into a common format. The above workaround uses an upstream patch to coverage.py. As an alternative, we could also change LcovMerger to support coverage.py's output format. Reference to upstream's issue: nedbat/coveragepy#587 |
The issue you were looking for may have been under python_rules bazelbuild/rules_python#43 Thanks for the suggested workaround! |
@ulfjack thank you for the suggestion. Unfortunately I am seeing empty coverage.dat file with some warnings in in the test.log
Also is there a documentation about using Bazel Coverage on python that might be helpful to read? |
The most likely reason for coverage not to work is an incorrect |
@ulfjack I get empty coverage files regardless what I try, and these are repositories with only Do you have a basic example that you used during development? Or a test case that works? It would be really helpful to see any sort of functioning test to fast-track some experimentation. For reference, I've tried various combinations of this script, changing the parameters of the last line to get a run that doesn't fail, but gives non-zero coverage.dat files. So far, never ever a nonzero coverage.dat. #!bash
COVERAGEPY=${HOME}/src/coveragepy
test -d "${COVERAGEPY}" || git clone -b lcov-support https://github.com/ulfjack/coveragepy.git "${COVERAGEPY}"
# According to https://github.com/bazelbuild/bazel/issues/10660, and some assumption:
bazel coverage --test_env=PYTHON_COVERAGE=${COVERAGEPY}/coverage/__main__.py //:test_ns
# Now, in other documentation, there is much discussion about local_jdk and various coverage flags.
# The following completes without error, but still empty coverage.dat files
#
# NOTE: //:test_ns is the only test target for this example.
bazel coverage -s --test_env=PYTHON_COVERAGE=${COVERAGEPY}/coverage/__main__.py --sandbox_debug \
--instrument_test_targets --instrumentation_filter=//...:all --combined_report=lcov \
--javabase=@bazel_tools//tools/jdk:remote_jdk11 //:test_ns Sometimes I also try with |
@chickenandpork I've been generating reports for a while now. Not directly with bazel, but by manually calling - name: Python coverage
run: |
cd "${GITHUB_WORKSPACE}/src"
curl -L https://github.com/ulfjack/coveragepy/archive/lcov-support.tar.gz | tar xvz
~/bin/bazel coverage -t- --instrument_test_targets --experimental_cc_coverage \
--test_output=errors --linkopt=--coverage --linkopt=-lc \
--test_env=PYTHON_COVERAGE=${GITHUB_WORKSPACE}/src/coveragepy-lcov-support/__main__.py \
--define=config_file=test //python/:tests
# Unfortunately, coverage does not produce the hit information. Running
# the compiled tests explicitly seems to work though.
find bazel-bin/python/ -maxdepth 1 -regex ".*-test$" -exec {} \;
cp bazel-out/k8-fastbuild/testlogs/pd3/python/smoke-test/coverage.dat coverage.py.info
env:
CC: clang-9
- name: codecov
if: always()
run: |
set -x
cd "${GITHUB_WORKSPACE}/src"
~/bin/grcov coverage.py.info bazel-bin/python/_objs/ -t lcov \
--ignore "external/*" --ignore "/usr/*" \
--ignore "*deps_*" --ignore "*_pb2.py" \
--llvm > lcov.py.info
sed -i 's/SF:.*test\.runfiles\/python\/python/SF:python/g' coverage.py.info
cd ..
bash -x <(curl -s https://codecov.io/bash) -Z -v -f src/lcov.py.info -F python_tests
env:
CC: clang-9
CODECOV_TOKEN: ${{ secrets.CODE_COV }} |
/cc @oquenchil Maybe this can fit into your coverage sprint? |
@chickenandpork In case this is still relevant for you, or maybe this will help someone: bazel's Leaving the option unset, bazel will generally guess a good value, or if you want to instrument everything all the time, then |
@chickenandpork @ulfjack If you use a pytest_runner to run your pytest tests, it's a convenient place to put the lconv conversion code ;) That way you can still use standard tools (and not need to use the fork or special shell scripts) The following requires import contextlib
import os
import pathlib
import sys
import coverage
import coverage_lcov.converter
import pytest
@contextlib.contextmanager
def coverage_decorator():
if os.getenv("COVERAGE", None) == "1":
coverage_dir = pathlib.Path(os.getenv("COVERAGE_DIR"))
coverage_file = coverage_dir / ".coverage"
coverage_manifest = pathlib.Path(os.getenv("COVERAGE_MANIFEST"))
coverage_sources = coverage_manifest.read_text().splitlines()
# @TODO: Handle config stuff
cov = coverage.Coverage(data_file=str(coverage_file), include=coverage_sources)
cov.start()
try:
yield
finally:
if os.getenv("COVERAGE", None) == "1":
cov.stop()
cov.save()
# @TODO: Handle config stuff
coverage_lcov.converter.Converter(
relative_path=True,
config_file=False,
data_file_path=str(coverage_file),
).create_lcov(os.getenv("COVERAGE_OUTPUT_FILE"))
if __name__ == "__main__":
with coverage_decorator():
sys.exit(pytest.main(sys.argv[1:])) |
The dropbox Python rules also seem to do something similar for coverage support: https://github.com/dropbox/dbx_build_tools/blob/fe5c9e668a9e6951970c0595089742d8a0247b8c/build_tools/py/pytest_plugins/codecoverage.py |
Interestingly running the |
FYI, I created bazelbuild/rules_python#977 to make
I think that all of these issues may be related to Python entrypoint template that is stored in this repository and not an issue with |
I'll admit to not having actually checked any test cases for a And yes, you won't get coverage in subprocesses, because |
@adam-azarchs there is a pytest plugin called pytest-cov that is able to get around this issue and will profile all subprocesses. this means that it will also work with pytest-xdist which is not compatible with |
Last I checked, though, |
(also |
Ah sorry I was a bit confusing - I meant that it would allow using pytest-xdist which is quite common. Pytest-cov is now able to output in lcov format just as coverage can. And you're absolutely right about pytest bring heavy. What I really meant to suggest is that between pytest-cov and coverage-enable-subprocess there's probably enough info to make bazel compatible with these subprocesses during coverage automatically and natively if the desire is there. But adding a call to |
Hi All, |
rules_python now supports coverage natively: https://rules-python.readthedocs.io/en/stable/coverage.html |
Hi @phst |
I thought that there was an existing issue for this, but I can't find it.
I was able to successfully collect coverage for python code with a modified version of coverage.py:
Test coverage should be written to
bazel-testlogs/python/lib_test/coverage.dat
in lcov format.The text was updated successfully, but these errors were encountered: