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

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 प्रोजेक्ट के रूट में एक्ज़ीक्यूट किया जाए.

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

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

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

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

  • रिपोर्ट को एक साथ दिखाने की कार्रवाई, फ़िलहाल रिमोट तरीके से नहीं की जा सकती. ऐसा इसलिए है, क्योंकि Bazel, कवरेज आउटपुट फ़ाइलों को अपने ग्राफ़ का हिस्सा नहीं मानता (यह समस्या देखें). इसलिए, वह उन्हें एक साथ दिखाने की कार्रवाई के लिए, इनपुट के तौर पर सही तरीके से इस्तेमाल नहीं कर सकता. इससे बचने के लिए, --strategy=CoverageReport=local का इस्तेमाल करें.
    • ध्यान दें: अगर Bazel को आजमाने के लिए सेट अप किया गया है, तो local,remote जैसा कुछ तय करना ज़रूरी हो सकता है. ऐसा इसलिए, क्योंकि Bazel, रणनीतियों को हल करने का तरीका अलग होता है.--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

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

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

इसके बाद, BUILD फ़ाइलों में यह सेट करके, टेस्ट टारगेट के लिए coverage.py की ज़रूरी शर्त का इस्तेमाल किया जा सकता है:

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