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: