Bazel cuenta con un subcomando coverage
para producir informes de cobertura de código en repositorios que se pueden probar con bazel coverage
. Debido a las idiosincrasias de los distintos ecosistemas de lenguajes, no siempre es trivial hacer que esto funcione para un proyecto determinado.
En esta página, se documenta el proceso general para crear y ver informes de cobertura, y también se incluyen algunas notas específicas para los idiomas cuya configuración es conocida. Se recomienda leer primero la sección general y, luego, leer sobre los requisitos para un lenguaje específico. Observa también la sección de ejecución remota, que requiere algunas consideraciones adicionales.
Si bien es posible realizar muchas personalizaciones, este documento se centra en la producción y el consumo de informes lcov
, que es la ruta más admitida.
Cómo crear un informe de cobertura
Preparación
El flujo de trabajo básico para crear informes de cobertura requiere lo siguiente:
- Un repositorio básico con destinos de prueba
- Una cadena de herramientas con las herramientas de cobertura de código específicas del lenguaje instaladas
- Una configuración de “instrumentación” correcta
Los dos primeros son específicos del lenguaje y en su mayoría sencillos; sin embargo, los últimos pueden ser más difíciles para proyectos complejos.
En este caso, "instrumentación" se refiere a las herramientas de cobertura que se usan para un destino específico. Bazel permite activar esta función para un
subconjunto específico de archivos con la marca
--instrumentation_filter
, que especifica un filtro para los destinos que se prueban con la
instrumentación habilitada. Para habilitar la instrumentación para pruebas, se requiere la marca --instrument_test_targets
.
De forma predeterminada, Bazel intenta hacer coincidir los paquetes de destino y, luego, imprime el filtro relevante como un mensaje INFO
.
Cobertura activa
Para generar un informe de cobertura, usa bazel coverage
--combined_report=lcov
[target]
. De esta manera, se ejecutan las pruebas para el destino y se generan informes de cobertura en formato LLCov para cada archivo.
Cuando finaliza, Bazel ejecuta una acción que recopila todos los archivos de cobertura producidos y los combina en uno, que, luego, se crea en $(bazel info
output_path)/_coverage/_coverage_report.dat
.
Los informes de cobertura también se generan si las pruebas fallan. Sin embargo, ten en cuenta que esto no se extiende a las pruebas fallidas, sino que solo se informan las pruebas aprobadas.
Cómo ver la cobertura
El informe de cobertura solo es el resultado en formato lcov
no legible por humanos. Desde aquí, podemos usar la utilidad genhtml
(parte del proyecto LLCov) para producir un informe que se pueda ver en un navegador web:
genhtml --output genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat"
Ten en cuenta que genhtml
también lee el código fuente para anotar la cobertura que falta en estos archivos. Para que esto funcione, se espera que
genhtml
se ejecute en la raíz del proyecto de Bazel.
Para ver el resultado, simplemente abre el archivo index.html
producido en el directorio genhtml
en cualquier navegador web.
Para obtener más información o ayuda sobre la herramienta genhtml
o el formato de cobertura lcov
, consulta el proyecto LLCov.
Ejecución remota
Por el momento, la ejecución de prueba remota tiene las siguientes advertencias:
- La acción de combinación de informes todavía no se puede ejecutar de forma remota. Esto
se debe a que Bazel no considera los archivos de salida de cobertura como parte de
su gráfico (consulta este problema) y, por lo tanto, no
puede tratarlos de forma correcta como entradas para la acción de combinación. Para solucionar este problema, usa
--strategy=CoverageReport=local
.- Nota: Es posible que debas especificar algo como
--strategy=CoverageReport=local,remote
en su lugar, si Bazel está configurado para probarlocal,remote
, debido a la forma en que Bazel resuelve las estrategias.
- Nota: Es posible que debas especificar algo como
- Tampoco se pueden usar
--remote_download_minimal
ni marcas similares como consecuencia de lo primero. - Por el momento, Bazel no podrá crear información de cobertura si las pruebas se
almacenaron en caché con anterioridad. Para solucionar esto,
--nocache_test_results
se puede configurar específicamente para ejecuciones de cobertura, aunque esto genera un gran costo en términos de tiempos de prueba. --experimental_split_coverage_postprocessing
y--experimental_fetch_all_coverage_outputs
- Por lo general, la cobertura se ejecuta como parte de la acción de prueba y, por la configuración predeterminada, no recuperamos toda la cobertura como resultados de la ejecución remota de forma predeterminada. Estas marcas anulan el valor predeterminado y obtienen los datos de cobertura. Consulta este problema para obtener más detalles.
Configuración específica del lenguaje
Java
Java debería funcionar listo para usar con la configuración predeterminada. Las cadenas de herramientas de Bazel contienen todo lo necesario para la ejecución remota, incluido JUnit.
Python
Requisitos previos
Ejecutar la cobertura con Python tiene algunos requisitos previos:
- Un objeto binario de Bazel que incluye b01c859, que debería ser cualquier Bazel >3.0.
- Una versión modificada decover.py.
Consume la cobertura.py modificada
Una forma de hacerlo es a través de rules_python, esto proporciona
la capacidad de usar un archivo requirements.txt
; los requisitos enumerados
en el archivo se crean como destinos de Bazel con la
regla de repositorio pip_install.
requirements.txt
debe tener la siguiente entrada:
git+https://github.com/ulfjack/coveragepy.git@lcov-support
Los archivos rules_python
, pip_install
y requirements.txt
deben usarse en el archivo WORKSPACE de la siguiente manera:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_python",
url = "https://github.com/bazelbuild/rules_python/releases/download/0.5.0/rules_python-0.5.0.tar.gz",
sha256 = "cd6730ed53a002c56ce4e2f396ba3b3be262fd7cb68339f0377a45e8227fe332",
)
load("@rules_python//python:pip.bzl", "pip_install")
pip_install(
name = "python_deps",
requirements = "//:requirements.txt",
)
Luego, los objetivos de prueba pueden consumir el requisito decover.py si configuras lo siguiente en los archivos BUILD
:
load("@python_deps//:requirements.bzl", "entry_point")
alias(
name = "python_coverage_tools",
actual = entry_point("coverage"),
)
py_test(
name = "test",
srcs = ["test.py"],
env = {
"PYTHON_COVERAGE": "$(location :python_coverage_tools)",
},
deps = [
":main",
":python_coverage_tools",
],
)
Si estás utilizando una cadena de herramientas hermética de Python, en lugar de agregar la dependencia
de cobertura a cada destino py_test
, puedes agregar la herramienta de cobertura a
la configuración de la cadena de herramientas.
Debido a que la regla pip_install depende de la cadena de herramientas de Python, no se puede usar para recuperar el módulo coverage
.
En su lugar, agrega tu WORKSPACE
, p.ej.,
http_archive(
name = "coverage_linux_x86_64"",
build_file_content = """
py_library(
name = "coverage",
srcs = ["coverage/__main__.py"],
data = glob(["coverage/*", "coverage/**/*.py"]),
visibility = ["//visibility:public"],
)
""",
sha256 = "84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3",
type = "zip",
urls = [
"https://files.pythonhosted.org/packages/74/0d/0f3c522312fd27c32e1abe2fb5c323b583a5c108daf2c26d6e8dfdd5a105/coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
],
)
Luego, configura tu cadena de herramientas de Python como, p.ej.,
py_runtime(
name = "py3_runtime_linux_x86_64",
coverage_tool = "@coverage_linux_x86_64//:coverage",
files = ["@python3_9_x86_64-unknown-linux-gnu//:files"],
interpreter = "@python3_9_x86_64-unknown-linux-gnu//:bin/python3",
python_version = "PY3",
)
py_runtime_pair(
name = "python_runtimes_linux_x86_64",
py2_runtime = None,
py3_runtime = ":py3_runtime_linux_x86_64",
)
toolchain(
name = "python_toolchain_linux_x86_64",
exec_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
toolchain = ":python_runtimes_linux_x86_64",
toolchain_type = "@bazel_tools//tools/python:toolchain_type",
)