बेज़ेल के साथ कोड कवरेज

Bazel में coverage सब-कमांड होती है. इसकी मदद से, उन रिपॉज़िटरी के लिए कोड कवरेज रिपोर्ट जनरेट की जा सकती हैं जिन्हें bazel coverage की मदद से टेस्ट किया जा सकता है. अलग-अलग भाषाओं के इकोसिस्टम की खासियतों की वजह से, किसी प्रोजेक्ट के लिए इसे लागू करना हमेशा आसान नहीं होता.

इस पेज पर, कवरेज रिपोर्ट बनाने और देखने का सामान्य तरीका बताया गया है. साथ ही, उन भाषाओं के लिए कुछ खास नोट भी दिए गए हैं जिनके कॉन्फ़िगरेशन के बारे में ज़्यादा जानकारी उपलब्ध है. इसे सबसे पहले सामान्य सेक्शन पढ़कर समझें. इसके बाद, किसी भाषा से जुड़ी ज़रूरी शर्तों के बारे में पढ़ें. साथ ही, रिमोट एक्ज़ीक्यूशन सेक्शन के बारे में भी ध्यान दें. इसके लिए, कुछ अतिरिक्त बातों का ध्यान रखना ज़रूरी है.

हालांकि, रिपोर्ट को अपनी ज़रूरत के हिसाब से बनाने के कई तरीके हैं, लेकिन इस दस्तावेज़ में lcov रिपोर्ट बनाने और उनका इस्तेमाल करने पर फ़ोकस किया गया है. फ़िलहाल, यह सबसे ज़्यादा इस्तेमाल किया जाने वाला तरीका है.

कवरेज रिपोर्ट बनाना

वीडियो की रणनीति

कवरेज रिपोर्ट बनाने के लिए, यह बुनियादी वर्कफ़्लो ज़रूरी है:

  • टेस्ट टारगेट वाली सामान्य रिपॉज़िटरी
  • ऐसी टूलचेन जिसमें भाषा के हिसाब से कोड कवरेज टूल इंस्टॉल किए गए हों
  • "इंस्ट्रूमेंटेशन" का सही कॉन्फ़िगरेशन

पहले दो तरीके, भाषा के हिसाब से होते हैं और ज़्यादातर सीधे होते हैं. हालांकि, जटिल प्रोजेक्ट के लिए बाद वाला तरीका ज़्यादा मुश्किल हो सकता है.

इस मामले में "इंस्ट्रूमेंटेशन" का मतलब, कवरेज टूल से है. इनका इस्तेमाल किसी खास टारगेट के लिए किया जाता है. Bazel, --instrumentation_filter फ़्लैग का इस्तेमाल करके, फ़ाइलों के किसी खास सबसेट के लिए इसे चालू करने की अनुमति देता है. यह उन टारगेट के लिए फ़िल्टर तय करता है जिनकी जांच, इंस्ट्रुमेंटेशन चालू करके की जाती है. टेस्ट के लिए इंस्ट्रुमेंटेशन चालू करने के लिए, --instrument_test_targets फ़्लैग ज़रूरी है.

डिफ़ॉल्ट रूप से, Bazel टारगेट पैकेज से मेल खाने की कोशिश करता है. साथ ही, काम के फ़िल्टर को INFO मैसेज के तौर पर प्रिंट करता है.

चल रही कवरेज

कवरेज रिपोर्ट जनरेट करने के लिए, bazel coverage --combined_report=lcov [target] का इस्तेमाल करें. इससे टारगेट के लिए टेस्ट किए जाते हैं. साथ ही, हर फ़ाइल के लिए lcov फ़ॉर्मैट में कवरेज रिपोर्ट जनरेट की जाती हैं.

प्रोसेस पूरी होने के बाद, Bazel एक ऐसी कार्रवाई करता है जिससे जनरेट की गई सभी कवरेज फ़ाइलें इकट्ठा हो जाती हैं. इसके बाद, उन्हें एक फ़ाइल में मर्ज कर दिया जाता है. यह फ़ाइल आखिर में $(bazel info output_path)/_coverage/_coverage_report.dat में बनाई जाती है.

जांच में गड़बड़ी होने पर भी कवरेज रिपोर्ट जनरेट की जाती हैं. हालांकि, ध्यान दें कि यह सुविधा, जांच में गड़बड़ी होने पर काम नहीं करती. सिर्फ़ पास की गई जांचों की रिपोर्ट जनरेट की जाती है.

कवरेज देखना

कवरेज रिपोर्ट सिर्फ़ ऐसे lcov फ़ॉर्मैट में आउटपुट होती है जिसे इंसान नहीं पढ़ सकता. इसके बाद, हम genhtml यूटिलिटी (lcov प्रोजेक्ट का हिस्सा) का इस्तेमाल करके, एक रिपोर्ट जनरेट कर सकते हैं. इसे वेब ब्राउज़र में देखा जा सकता है:

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

ध्यान दें कि genhtml, सोर्स कोड को भी पढ़ता है, ताकि इन फ़ाइलों में छूटे हुए कवरेज को एनोटेट किया जा सके. इसके लिए, यह ज़रूरी है कि genhtml को Bazel प्रोजेक्ट के रूट में एक्ज़ीक्यूट किया जाए.

नतीजा देखने के लिए, किसी भी वेब ब्राउज़र में index.html डायरेक्ट्री में मौजूद genhtml फ़ाइल खोलें.

genhtml टूल या lcov कवरेज फ़ॉर्मैट के बारे में ज़्यादा मदद और जानकारी पाने के लिए, lcov प्रोजेक्ट देखें.

रिमोट एक्ज़ीक्यूशन

फ़िलहाल, रिमोट टेस्ट एक्ज़ीक्यूशन के साथ काम करने में कुछ समस्याएं आ रही हैं:

  • रिपोर्ट कॉम्बिनेशन की कार्रवाई को अभी दूर से नहीं चलाया जा सकता. ऐसा इसलिए है, क्योंकि Bazel, कवरेज आउटपुट फ़ाइलों को अपने ग्राफ़ का हिस्सा नहीं मानता. इसके बारे में इस समस्या में बताया गया है. इसलिए, Bazel इन फ़ाइलों को कॉम्बिनेशन ऐक्शन के इनपुट के तौर पर सही तरीके से इस्तेमाल नहीं कर सकता. इस समस्या को हल करने के लिए, --strategy=CoverageReport=local का इस्तेमाल करें.
    • ध्यान दें: Bazel की रणनीतियों को हल करने के तरीके की वजह से, अगर Bazel को local,remote आज़माने के लिए सेट अप किया गया है, तो --strategy=CoverageReport=local,remote के बजाय किसी और चीज़ को तय करना ज़रूरी हो सकता है.
  • --remote_download_minimal और इसी तरह के अन्य फ़्लैग का इस्तेमाल भी नहीं किया जा सकता.
  • अगर टेस्ट पहले से कैश किए गए हैं, तो Bazel फ़िलहाल कवरेज की जानकारी नहीं बना पाएगा. इस समस्या को हल करने के लिए, --nocache_test_results को खास तौर पर कवरेज रन के लिए सेट किया जा सकता है. हालांकि, इससे टेस्ट के समय में काफ़ी बढ़ोतरी होती है.
  • --experimental_split_coverage_postprocessing और --experimental_fetch_all_coverage_outputs
    • आम तौर पर, कवरेज को टेस्ट ऐक्शन के हिस्से के तौर पर चलाया जाता है. इसलिए, डिफ़ॉल्ट रूप से हमें रिमोट एक्ज़ीक्यूशन के आउटपुट के तौर पर पूरा कवरेज वापस नहीं मिलता. ये फ़्लैग, डिफ़ॉल्ट सेटिंग को बदलते हैं और कवरेज का डेटा हासिल करते हैं. ज़्यादा जानकारी के लिए, यह समस्या देखें.

भाषा के हिसाब से कॉन्फ़िगरेशन

Java

Java को डिफ़ॉल्ट कॉन्फ़िगरेशन के साथ तुरंत काम करना चाहिए. Bazel टूलचेन में, रिमोट एक्ज़ीक्यूशन के लिए ज़रूरी सभी चीज़ें शामिल होती हैं. जैसे, JUnit.

Python

ज़रूरी शर्तें

Python के साथ कवरेज चलाने के लिए, कुछ ज़रूरी शर्तें पूरी करनी होंगी:

बदलाव किए गए coverage.py का इस्तेमाल करना

इसके लिए, rules_python का इस्तेमाल किया जा सकता है. इससे requirements.txt फ़ाइल का इस्तेमाल किया जा सकता है. फ़ाइल में दी गई ज़रूरी शर्तों को, pip_install रिपॉज़िटरी के नियम का इस्तेमाल करके, Bazel टारगेट के तौर पर बनाया जाता है.

requirements.txt में यह एंट्री होनी चाहिए:

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

इसके बाद, rules_python, pip_install, और requirements.txt फ़ाइल का इस्तेमाल WORKSPACE फ़ाइल में इस तरह किया जाना चाहिए:

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

इसके बाद, coverage.py की ज़रूरत को टेस्ट टारगेट पूरा कर सकते हैं. इसके लिए, 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",
    ],
)

अगर हर्मेटिक Python टूलचेन का इस्तेमाल किया जा रहा है, तो हर py_test टारगेट में कवरेज डिपेंडेंसी जोड़ने के बजाय, टूलचेन कॉन्फ़िगरेशन में कवरेज टूल जोड़ा जा सकता है.

pip_install नियम, Python टूलचेन पर निर्भर करता है. इसलिए, इसका इस्तेमाल coverage मॉड्यूल को फ़ेच करने के लिए नहीं किया जा सकता. इसके बजाय, WORKSPACE जोड़ें. उदाहरण के लिए,

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

इसके बाद, अपने Python टूलचेन को कॉन्फ़िगर करें. उदाहरण के लिए,

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