Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/pytest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,12 @@ sh_test(
":coverage_setup_test",
],
)

sh_test(
name = "coverage_dir_test",
srcs = ["run_coverage_dir_and_check.sh"],
data = [
":coverage_manifest",
":coverage_setup_test",
],
)
50 changes: 50 additions & 0 deletions examples/pytest/run_coverage_dir_and_check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Verifies that when COVERAGE_DIR is set, pytest_main.py writes coverage
# data to $COVERAGE_DIR/python_coverage.dat instead of COVERAGE_OUTPUT_FILE.
# This exercises the --experimental_split_coverage_postprocessing code path.

set -euo pipefail

LAUNCHER="$TEST_SRCDIR/_main/examples/pytest/coverage_setup_test"
MANIFEST="$TEST_SRCDIR/_main/examples/pytest/coverage_manifest.txt"

[[ -x "$LAUNCHER" ]] || { echo "launcher not found or not executable: $LAUNCHER" >&2; exit 1; }
[[ -f "$MANIFEST" ]] || { echo "manifest not found: $MANIFEST" >&2; exit 1; }

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

COVERAGE_DIR="$WORK/cov_dir"
mkdir -p "$COVERAGE_DIR"

# COVERAGE_OUTPUT_FILE is set to a different path to prove the output does NOT
# go there when COVERAGE_DIR is present.
DECOY_OUTPUT="$WORK/decoy.lcov"

COVERAGE_MANIFEST="$MANIFEST" \
COVERAGE_DIR="$COVERAGE_DIR" \
COVERAGE_OUTPUT_FILE="$DECOY_OUTPUT" \
"$LAUNCHER"

EXPECTED="$COVERAGE_DIR/python_coverage.dat"
[[ -s "$EXPECTED" ]] || {
echo "Expected $EXPECTED to exist and be non-empty." >&2
echo "Contents of COVERAGE_DIR:" >&2
ls -la "$COVERAGE_DIR" >&2
exit 1
}

# The decoy file must NOT have been written.
[[ ! -f "$DECOY_OUTPUT" ]] || {
echo "Coverage was incorrectly written to COVERAGE_OUTPUT_FILE ($DECOY_OUTPUT) instead of COVERAGE_DIR." >&2
exit 1
}

# Basic shape check: SF: record must point to foo.py.
grep -qE '^SF:.*examples/pytest/foo\.py$' "$EXPECTED" || {
echo "Expected SF: record for examples/pytest/foo.py not found." >&2
cat "$EXPECTED" >&2
exit 1
}

echo "OK: coverage data written to \$COVERAGE_DIR/python_coverage.dat as expected."
27 changes: 17 additions & 10 deletions py/private/pytest.py.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,18 @@ if "COVERAGE_MANIFEST" in os.environ:
try:
import coverage
# The lines are files that matched the --instrumentation_filter flag
with open(os.getenv("COVERAGE_MANIFEST"), "r") as mf:
manifest_entries = mf.read().splitlines()
cov = coverage.Coverage(include = manifest_entries)
# coveragepy incorrectly converts our entries by following symlinks
# record a mapping of their conversion so we can undo it later in reporting the coverage
coveragepy_absfile_mapping = {coverage.files.abs_file(mfe): mfe for mfe in manifest_entries}
cov.start()

existing_cov = coverage.Coverage.current()
if existing_cov is not None:
cov = existing_cov
else:
with open(os.getenv("COVERAGE_MANIFEST"), "r") as mf:
manifest_entries = mf.read().splitlines()
cov = coverage.Coverage(include = manifest_entries)
# coveragepy incorrectly converts our entries by following symlinks
# record a mapping of their conversion so we can undo it later in reporting the coverage
coveragepy_absfile_mapping = {coverage.files.abs_file(mfe): mfe for mfe in manifest_entries}
cov.start()
except ModuleNotFoundError as e:
print("WARNING: python coverage setup failed. Do you need to include the 'coverage' package as a dependency of py_pytest_main?", e)
pass
Expand Down Expand Up @@ -108,12 +113,14 @@ if __name__ == "__main__":
# https://bazel.build/configure/coverage
coverage_output_file = os.getenv("COVERAGE_OUTPUT_FILE")

unfixed_dat = coverage_output_file + ".tmp"
coverage_dir = os.environ.get("COVERAGE_DIR")
target_dat = os.path.join(coverage_dir, "python_coverage.dat") if coverage_dir else coverage_output_file
unfixed_dat = target_dat + ".tmp"
cov.lcov_report(outfile = unfixed_dat)
cov.save()

with open(unfixed_dat, "r") as unfixed:
with open(coverage_output_file, "w") as output_file:
with open(target_dat, "w") as output_file:
for line in unfixed:
# Workaround https://github.com/nedbat/coveragepy/issues/963
# by mapping SF: records to un-do the symlink-following
Expand Down
Loading