Couverture de code avec Bazel

Bazel dispose d'une sous-commande coverage pour générer des rapports de couverture de code sur les dépôts qui peuvent être testés avec bazel coverage. En raison des particularités des différents écosystèmes de langage, il n'est pas toujours simple de faire ce travail pour un projet donné.

Cette page décrit le processus général de création et d'affichage des rapports de couverture et contient également des notes spécifiques aux langages dont la configuration est connue. Nous vous recommandons de commencer par lire la section générale, puis de lire les exigences applicables à un langage spécifique. Notez également la section sur l'exécution à distance, qui nécessite des considérations supplémentaires.

Bien qu'une grande partie de la personnalisation soit possible, ce document se concentre sur la création et la consommation de rapports lcov, qui est actuellement la route la plus compatible.

Créer un rapport de couverture

Préparation

Le workflow de base pour créer des rapports de couverture nécessite les éléments suivants:

  • Dépôt de base avec cibles de test
  • Chaîne d'outils avec les outils de couverture de code spécifiques au langage installés
  • Configuration d'instrumentation correcte

Les deux sont propres à un langage et sont pour la plupart simples à utiliser, mais le second peut s'avérer plus difficile pour les projets complexes.

Dans ce cas, le terme "instrumentation" fait référence aux outils de couverture utilisés pour une cible spécifique. Bazel permet d'activer cette fonctionnalité pour un sous-ensemble spécifique de fichiers à l'aide de l'option --instrumentation_filter, qui spécifie un filtre pour les cibles testées avec l'instrumentation. activées. Pour activer l'instrumentation pour les tests, vous devez spécifier l'option --instrument_test_targets.

Par défaut, bazel tente de faire correspondre le ou les packages cibles et affiche le filtre correspondant sous la forme d'un message INFO.

Diffusion en cours

Pour générer un rapport de couverture, utilisez bazel coverage --combined_report=lcov [target]. Cette opération exécute les tests pour la cible et génère des rapports de couverture au format lcov pour chaque fichier.

Une fois l'opération terminée, Bazel exécute une action qui collecte tous les fichiers de couverture produits et les fusionne en un, qui est ensuite créé sous $(bazel info output_path)/_coverage/_coverage_report.dat.

Des rapports de couverture sont également générés si les tests échouent. Notez toutefois que cela ne s'applique pas aux tests qui ont échoué. Seuls les tests réussis sont signalés.

Afficher la couverture

Le rapport de couverture n'est généré qu'au format lcov, dans un format non lisible. À partir de là, nous pouvons utiliser l'utilitaire genhtml (partie du projet lcov) pour générer un rapport consultable dans un navigateur Web:

genhtml --output genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat"

Notez que genhtml lit également le code source pour annoter la couverture manquante dans ces fichiers. Pour que cela fonctionne, il est prévu que genhtml soit exécuté à la racine du projet Bazel.

Pour afficher le résultat, il vous suffit d'ouvrir le fichier index.html produit dans le répertoire genhtml de n'importe quel navigateur Web.

Pour obtenir de l'aide et des informations supplémentaires sur l'outil genhtml ou le format de couverture lcov, consultez la section Le projet Lcov.

Exécution à distance

L'exécution de tests à distance présente quelques restrictions:

  • Impossible de lancer l'action de combinaison du rapport à distance. En effet, Bazel ne prend pas en compte les fichiers de sortie de couverture dans son graphe (voir ce problème) et ne peut donc pas les traiter correctement comme des entrées dans la combinaison. action. Pour contourner ce problème, utilisez --strategy=CoverageReport=local.
    • Remarque: Vous devrez peut-être spécifier quelque chose comme --strategy=CoverageReport=local,remote, si Bazel est configuré pour essayer local,remote, en raison de la manière dont Bazel résout les stratégies.
  • --remote_download_minimal et des options similaires ne peuvent pas non plus être utilisés comme conséquence du premier.
  • Bazel ne parvient actuellement pas à créer les informations de couverture si les tests ont déjà été mis en cache. Pour contourner ce problème, --nocache_test_results peut être défini spécifiquement pour les exécutions de couverture, même si cela entraîne bien sûr des coûts importants en termes de temps de test.
  • --experimental_split_coverage_postprocessing et --experimental_fetch_all_coverage_outputs
    • Généralement, une couverture est exécutée dans le cadre de l'action de test. Par défaut, nous ne récupérerons donc pas toute la couverture en tant que sorties de l'exécution distante. Ces options remplacent la valeur par défaut et obtiennent les données de couverture. Pour en savoir plus, consultez ce problème.

Configuration spécifique au langage

Java

Java devrait être prêt à l'emploi avec la configuration par défaut. Les chaînes d'outils Bazel contiennent tous les éléments nécessaires à l'exécution à distance, y compris JUnit.

Python

Prérequis

L'exécution de la couverture avec Python est soumise à certaines conditions préalables:

Consommer la couverture couverte.py

Pour cela, vous pouvez utiliserrègles_python , il est ainsi possible d'utiliser unrequirements.txt , les exigences répertoriées dans le fichier sont ensuite créées en tant que cibles Bazel à l'aide de la commandepip_install Règle de dépôt.

requirements.txt doit comporter l'entrée suivante:

git+https://github.com/ulfjack/coveragepy.git@lcov-support

Les fichiers rules_python, pip_install et requirements.txt doivent ensuite être utilisés dans le fichier WORKSPACE comme:

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",
)

L'exigence de couverture.py peut ensuite être utilisée par les cibles de test en définissant les éléments suivants dans les fichiers 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",
    ],
)