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

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

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

Java

Java को डिफ़ॉल्ट कॉन्फ़िगरेशन के साथ-साथ अलग तरीके से काम करना चाहिए. bazel टूलचेन में वे सभी चीज़ें शामिल होती हैं जो रिमोट तौर पर काम करने के लिए ज़रूरी होती हैं. इनमें JUnit शामिल है.

Python

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

Python के साथ कवरेज चलाने की कुछ ज़रूरी शर्तें हैं:

संशोधित कवरेज.py का उपयोग करना

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

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

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

इसके बाद, 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 फ़ाइलों में इन चीज़ों को सेट करके, कवरेज.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",
    ],
)

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

pip_install नियम Pythontoolchain पर निर्भर करता है. इसलिए, इसका इस्तेमाल 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 Toolchain को उदाहरण के तौर पर कॉन्फ़िगर करें.

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