fix(coverage): missing files in the coverage report if they have no tests (#2607)
This ensures that un-executed files _(i.e. files that aren't tested)_
are included in the coverage report. The current behavior is that
coverage.py excludes them by default.
This PR configures source files via the auto-generated `.coveragerc`
file.
See https://coverage.readthedocs.io/en/7.6.10/source.html#execution:
> If the source option is specified, only code in those locations will
be measured. Specifying the source option also enables coverage.py to
report on un-executed files, since it can search the source tree for
files that haven’t been measured at all.
Closes #2599
Closes #2597
Fixes #2575
---------
Co-authored-by: Ignas Anikevicius <240938+aignas@users.noreply.github.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 413442e..403dbaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -70,6 +70,7 @@
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).
* (gazelle) Don't collapse depsets to a list or into args when generating the modules mapping file.
Support spilling modules mapping args into a params file.
+* (coverage) Fix missing files in the coverage report if they have no tests.
* (pypi) From now on `python` invocations in repository and module extension
evaluation contexts will invoke Python interpreter with `-B` to avoid
creating `.pyc` files.
diff --git a/examples/bzlmod/.python_version b/examples/bzlmod/.python_version
new file mode 100644
index 0000000..bd28b9c
--- /dev/null
+++ b/examples/bzlmod/.python_version
@@ -0,0 +1 @@
+3.9
diff --git a/python/private/python_bootstrap_template.txt b/python/private/python_bootstrap_template.txt
index e3b39e3..9f671dd 100644
--- a/python/private/python_bootstrap_template.txt
+++ b/python/private/python_bootstrap_template.txt
@@ -425,12 +425,21 @@
directory under the runfiles tree, and will recursively delete the
runfiles directory if set.
"""
+ instrumented_files = [abs_path for abs_path, _ in InstrumentedFilePaths()]
+ unique_dirs = {os.path.dirname(file) for file in instrumented_files}
+ source = "\n\t".join(unique_dirs)
+
+ PrintVerboseCoverage("[coveragepy] Instrumented Files:\n" + "\n".join(instrumented_files))
+ PrintVerboseCoverage("[coveragepy] Sources:\n" + "\n".join(unique_dirs))
+
# We need for coveragepy to use relative paths. This can only be configured
unique_id = uuid.uuid4()
rcfile_name = os.path.join(os.environ['COVERAGE_DIR'], ".coveragerc_{}".format(unique_id))
with open(rcfile_name, "w") as rcfile:
- rcfile.write('''[run]
+ rcfile.write(f'''[run]
relative_files = True
+source =
+\t{source}
''')
PrintVerboseCoverage('Coverage entrypoint:', coverage_entrypoint)
# First run the target Python file via coveragepy to create a .coverage
diff --git a/python/private/stage2_bootstrap_template.py b/python/private/stage2_bootstrap_template.py
index b1f6b03..4687bc0 100644
--- a/python/private/stage2_bootstrap_template.py
+++ b/python/private/stage2_bootstrap_template.py
@@ -276,6 +276,13 @@
yield
return
+ instrumented_files = [abs_path for abs_path, _ in instrumented_file_paths()]
+ unique_dirs = {os.path.dirname(file) for file in instrumented_files}
+ source = "\n\t".join(unique_dirs)
+
+ print_verbose_coverage("Instrumented Files:\n" + "\n".join(instrumented_files))
+ print_verbose_coverage("Sources:\n" + "\n".join(unique_dirs))
+
import uuid
import coverage
@@ -289,8 +296,10 @@
print_verbose_coverage("coveragerc file:", rcfile_name)
with open(rcfile_name, "w") as rcfile:
rcfile.write(
- """[run]
+ f"""[run]
relative_files = True
+source =
+\t{source}
"""
)
try: