Files
datastore/ci/jobs/scripts/print_newly_covered_code.py
Alexey BakharewandClaude Sonnet 4.6 354eeab49d Fix deleted/renamed files leaking ghost counters into stable totals
_parse_extra_diff_hunks keyed hunks by the +++ b/... path. For deleted
files (+++ /dev/null) no entry was added to file_hunks, so the
accumulator fell through to identity mapping and added the deleted
file's lines to stable totals as if they still existed in primary's
coordinate system. This inflated the denominator, making coverage
percentages appear lower than they are.

Renamed files (which appear as delete + add without --find-renames)
had the same problem: the old path got no hunk entry and was
identity-mapped under its old name, which never matches any line in
the primary baseline.

Fix:
- _parse_extra_diff_hunks now also reads --- a/... headers and
  records the old path in deleted_files when the +++ side is /dev/null
- Returns (file_hunks, deleted_files) as a tuple
- _accum_with_remap accepts deleted_files and skips any file whose
  old path appears there, rather than falling to identity mapping

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
2026-06-29 16:27:26 +02:00

699 lines
29 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Find code that is newly covered by the tests added/modified in this PR.
For a tests-only PR the global coverage delta is too small to be informative
(a single new test rarely shifts overall coverage by more than 0.01 pp).
What is actually informative is: "which previously-uncovered lines/functions
become covered when the new test(s) run?". This script computes that by diffing
the master baseline `.info` file against the current build's `.info` file and
reporting every (file, line) and (file, function) pair whose execution count
transitioned from zero in the baseline to greater-than-zero in the current run.
A handful of caveats are baked into the output of this script:
* The "baseline" is the nearest ancestor master commit that has a published
coverage report (downloaded earlier by generate_diff_coverage_report.sh).
Coverage drift since that baseline — including unrelated tests added on
master — will appear here too. For a typical tests-only PR the dominant
signal is still the PR's own contribution.
* Files that did not exist in the baseline are skipped entirely. If they
did not exist, they are unrelated to "tests covering previously-uncovered
code" — they may simply be new files committed in master since baseline.
* Lines categorised as `noise` by print_uncovered_code (LOGICAL_ERROR,
UNREACHABLE, structural braces, ...) are filtered out so the report is
not dominated by error-path bookkeeping.
"""
import os
import re
import subprocess
import sys
from collections import defaultdict
from ci.praktika.result import Result
from ci.praktika.utils import Utils
repo_root = Utils.cwd()
temp_dir = f"{repo_root}/ci/tmp/"
_NOISE_PATTERNS = [
re.compile(r"\bLOGICAL_ERROR\b"),
re.compile(r"\bUNREACHABLE\s*\("),
re.compile(r"__builtin_unreachable\s*\("),
re.compile(r"\babort\s*\(\s*\)"),
re.compile(r"\bstd::terminate\s*\("),
re.compile(r"\babortOnFailedAssertion\s*\("),
]
_STRUCTURAL_LINES = frozenset({"", "{", "}", "};", "else", "else{", "else {"})
_source_cache: dict[str, list[str]] = {}
def _load_source(relpath: str) -> list[str]:
if relpath not in _source_cache:
abs_path = os.path.join(repo_root, relpath)
try:
with open(abs_path, encoding="utf-8", errors="replace") as f:
_source_cache[relpath] = f.readlines()
except FileNotFoundError:
_source_cache[relpath] = []
return _source_cache[relpath]
def _is_noise(relpath: str, lineno: int) -> bool:
lines = _load_source(relpath)
if not (1 <= lineno <= len(lines)):
return False
text = lines[lineno - 1].strip()
if text in _STRUCTURAL_LINES:
return True
return any(p.search(text) for p in _NOISE_PATTERNS)
def _normalize_sf(sf: str) -> str:
# Strip the machine-specific path prefix so we can join paths from a
# baseline tracefile (different CI runner) with the current run.
marker = "/ClickHouse/"
idx = sf.rfind(marker)
if idx >= 0:
return sf[idx + len(marker):]
return sf
def _parse_info(path: str) -> dict:
"""Parse an LCOV `.info` file into
`{rel_path: {"lines": {ln: cnt}, "fns": {name: cnt}, "branches": {(ln,blk,br): cnt}}}`.
Counts accumulate when the same SF appears more than once (which happens
after lcov merges multiple tracefiles).
"""
data: dict = {}
cur_rel: str | None = None
with open(path, encoding="utf-8", errors="replace") as f:
for raw in f:
line = raw.strip()
if line.startswith("SF:"):
cur_rel = _normalize_sf(line[3:])
data.setdefault(cur_rel, {"lines": {}, "fns": {}, "branches": {}})
elif not cur_rel:
continue
elif line.startswith("DA:"):
parts = line[3:].split(",", 2)
# lcov may emit counts in scientific notation (e.g. 3.69e+19) when
# multiple .info files are merged and counters overflow. Use float
# first to handle that, then convert to int (coverage only cares
# whether the count is zero or non-zero).
ln, cnt = int(parts[0]), int(float(parts[1]))
data[cur_rel]["lines"][ln] = data[cur_rel]["lines"].get(ln, 0) + cnt
elif line.startswith("BRDA:"):
# BRDA:<line>,<block>,<branch>,<count|-|0>
parts = line[5:].split(",", 3)
if len(parts) == 4:
ln_b = int(parts[0])
blk, br, cnt_s = parts[1], parts[2], parts[3]
bkey = (ln_b, blk, br)
prev = data[cur_rel]["branches"].get(bkey, 0)
if cnt_s != "-":
data[cur_rel]["branches"][bkey] = prev + int(float(cnt_s))
elif bkey not in data[cur_rel]["branches"]:
data[cur_rel]["branches"][bkey] = 0
elif line.startswith("FNDA:"):
cnt_str, name = line[5:].split(",", 1)
data[cur_rel]["fns"][name] = (
data[cur_rel]["fns"].get(name, 0) + int(float(cnt_str))
)
elif line == "end_of_record":
cur_rel = None
return data
def _format_block_preview(rel: str, lines_in_block: list[int], context: int = 2) -> list[str]:
"""Render newly-covered lines for one file as merged blocks.
Adjacent newly-covered lines are grouped first; then *near*-adjacent
groups whose surrounding-context windows would touch or overlap are
merged into a single block, so the reader sees one cohesive snippet
instead of several near-identical stanzas that re-print the same
intermediate code. Inside the merged block, only the actually
newly-covered lines (the original set) carry the ">>" marker — lines
that fall in the gap between original sub-blocks are shown without
highlight as ordinary context.
"""
out: list[str] = []
src = _load_source(rel)
if not src:
out.append(f" [source file not found: {os.path.join(repo_root, rel)}]")
return out
nc_set = set(lines_in_block)
if not nc_set:
return out
file_lines = sorted(nc_set)
# Group consecutive newly-covered lines.
raw_blocks: list[tuple[int, int]] = []
start = prev = file_lines[0]
for ln in file_lines[1:]:
if ln == prev + 1:
prev = ln
else:
raw_blocks.append((start, prev))
start = prev = ln
raw_blocks.append((start, prev))
# Merge groups whose context windows would touch/overlap. With `context`
# lines on each side, two groups whose original ends are within
# `2 * context + 1` lines of each other would otherwise have their
# printed regions duplicate one another (and the gap lines themselves
# are useful context, so keeping them in a single block is strictly
# better than splitting).
merge_gap = 2 * context + 1
merged: list[tuple[int, int]] = [raw_blocks[0]]
for s, e in raw_blocks[1:]:
ps, pe = merged[-1]
if s - pe <= merge_gap:
merged[-1] = (ps, e)
else:
merged.append((s, e))
for block_start, block_end in merged:
s = max(1, block_start - context)
e = min(len(src), block_end + context)
out.append(f"\n--- newly covered block {block_start}-{block_end} ---")
for i in range(s, e + 1):
pfx = ">>" if i in nc_set else " "
out.append(f"{pfx} {i:6d} | {src[i - 1].rstrip()}")
return out
def _parse_extra_diff_hunks(diff_text: str) -> tuple[dict[str, list], set[str]]:
"""Parse a unified git diff (extra_sha -> primary_sha) into per-file hunk structures.
Returns (file_hunks, deleted_files):
file_hunks: {new_path: [hunk, ...]} for files that survived (possibly modified)
deleted_files: set of OLD paths for files deleted or renamed away between the two
commits — these must not be remapped to identity; instead skip them
entirely so their obsolete counters do not leak into stable totals.
Each hunk contains:
old_start, old_count, new_start, new_count
removed: set of old line numbers deleted going from extra to primary
context_map: {old_line: new_line} for lines that survived unchanged
"""
file_hunks: dict[str, list] = {}
deleted_files: set[str] = set()
current_old: str | None = None # --- a/... path
current_file: str | None = None # +++ b/... path (None = /dev/null)
current_hunk = None
old_pos = new_pos = 0
re_old_hdr = re.compile(r"^--- (?:a/)?(.*)$")
re_new_hdr = re.compile(r"^\+\+\+ (?:b/)?(.*)$")
re_hunk_hdr = re.compile(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@")
for line in diff_text.splitlines():
m = re_old_hdr.match(line)
if m:
p = m.group(1)
current_old = None if p == "/dev/null" else p
current_hunk = None
continue
m = re_new_hdr.match(line)
if m:
p = m.group(1)
current_file = None if p == "/dev/null" else p
if current_file:
file_hunks.setdefault(current_file, [])
elif current_old:
# +++ /dev/null means this old path was deleted or renamed away.
# Record it so the accumulator can skip it instead of identity-mapping.
deleted_files.add(current_old)
current_hunk = None
continue
m = re_hunk_hdr.match(line)
if m and current_file is not None:
old_start = int(m.group(1))
old_count = int(m.group(2)) if m.group(2) is not None else 1
new_start = int(m.group(3))
new_count = int(m.group(4)) if m.group(4) is not None else 1
current_hunk = {
"old_start": old_start,
"old_count": old_count,
"new_start": new_start,
"new_count": new_count,
"removed": set(),
"context_map": {},
}
file_hunks[current_file].append(current_hunk)
old_pos = old_start
new_pos = new_start
continue
if current_hunk is None or current_file is None:
continue
if line.startswith("-"):
current_hunk["removed"].add(old_pos)
old_pos += 1
elif line.startswith("+"):
new_pos += 1
elif line.startswith(" "):
current_hunk["context_map"][old_pos] = new_pos
old_pos += 1
new_pos += 1
return file_hunks, deleted_files
def _remap_old_to_new(old_line: int, hunks: list) -> int | None:
"""Map an old (extra baseline) line number to the primary baseline's coordinate system.
Returns None if the line was deleted between the two commits (not in primary).
Lines outside all hunks are shifted by the accumulated offset of preceding hunks.
"""
offset = 0
for h in hunks:
hunk_old_end = h["old_start"] + h["old_count"] - 1
if old_line < h["old_start"]:
break
if old_line > hunk_old_end:
offset += h["new_count"] - h["old_count"]
continue
# Line is inside this hunk
if old_line in h["context_map"]:
return h["context_map"][old_line]
if old_line in h["removed"]:
return None # deleted in primary
return old_line + offset # fallback
return old_line + offset
if __name__ == "__main__":
BASE = os.environ.get("COVERAGE_BASE_INFO", f"{temp_dir}/base_llvm_coverage.info")
# For partial runs (e.g. only stateless tests changed), llvm_coverage_job.py
# creates merged_llvm_coverage.info = union(pr.info, master.info) and passes it
# via COVERAGE_CURR_INFO so that gained lines are computed against the full
# coverage picture, not just the partial run.
CURR = os.environ.get("COVERAGE_CURR_INFO", f"{temp_dir}/llvm_coverage.info")
# Up to 4 extra baselines downloaded by generate_diff_coverage_report.sh.
# We intersect their zero-sets: a line must be uncovered in ALL of them to
# pass the filter. More baselines = fewer false positives from run-to-run
# flicker. The list is intentionally open-ended so raising TARGET_EXTRA_BASELINES
# in the shell script automatically takes effect here.
EXTRA_BASELINE_PATHS = [
f"{temp_dir}/base_llvm_coverage_{i}.info" for i in range(2, 8)
]
if not (os.path.exists(BASE) and os.path.exists(CURR)):
msg = "Baseline or current .info file missing - skipping newly-covered analysis."
print(msg)
r = Result.create_from(name="Newly Covered Code", status=Result.Status.OK, info=msg)
r.set_comment("")
r.dump()
sys.exit(0)
print(f"Parsing baseline coverage from {BASE} ...")
base_data = _parse_info(BASE)
print(f" {len(base_data)} files in baseline")
print(f"Parsing current coverage from {CURR} ...")
curr_data = _parse_info(CURR)
print(f" {len(curr_data)} files in current")
# Two stable union sets for computing the global coverage delta:
#
# _stable_base = Union(m1, m2, ..., mN) — all master runs
# _stable_pr = Union(m1, m2, ..., mN, PR) — master runs plus PR
#
# Delta = _stable_pr − _stable_base ≥ 0 always (union is monotone).
# The delta shows only lines the PR adds that no master run covered.
# LBC (lines lost vs master) is intentionally omitted — with this
# definition it is always empty by construction.
#
# We avoid `lcov -a` which corrupts FNDA records on some lcov 2.1+ versions.
_stable_base_line_cov: set[tuple[str, int]] = set()
_stable_base_line_tot: set[tuple[str, int]] = set()
_stable_base_fn_cov: set[tuple[str, str]] = set()
_stable_base_fn_tot: set[tuple[str, str]] = set()
_stable_base_br_cov: set[tuple[str, int, str, str]] = set()
_stable_base_br_tot: set[tuple[str, int, str, str]] = set()
# PR side: starts with the primary baseline (m1); ALL extras added in
# the loop below; PR (curr_data) added after the loop.
_stable_pr_line_cov: set[tuple[str, int]] = set()
_stable_pr_line_tot: set[tuple[str, int]] = set()
_stable_pr_fn_cov: set[tuple[str, str]] = set()
_stable_pr_fn_tot: set[tuple[str, str]] = set()
_stable_pr_br_cov: set[tuple[str, int, str, str]] = set()
_stable_pr_br_tot: set[tuple[str, int, str, str]] = set()
def _accum(data: dict,
lc: set, lt: set, fc: set, ft: set,
bc: set, bt: set) -> None:
"""Accumulate without remapping (for primary baseline and PR run)."""
for _rel, _v in data.items():
for _ln, _cnt in _v["lines"].items():
lt.add((_rel, _ln))
if _cnt > 0:
lc.add((_rel, _ln))
for _fn, _cnt in _v["fns"].items():
ft.add((_rel, _fn))
if _cnt > 0:
fc.add((_rel, _fn))
for (_ln, _blk, _br), _cnt in _v.get("branches", {}).items():
bkey = (_rel, _ln, _blk, _br)
bt.add(bkey)
if _cnt > 0:
bc.add(bkey)
def _accum_with_remap(data: dict, file_hunks: dict, deleted_files: set,
lc: set, lt: set, fc: set, ft: set,
bc: set, bt: set) -> None:
"""Accumulate with line-number remapping for extra baselines.
Lines from each extra baseline were produced from a different master
commit whose source may have shifted relative to the primary. The
file_hunks mapping (from git diff <extra_sha>..<primary_sha>) translates
each line to its position in the primary baseline's coordinate system so
all stable sets share a single consistent coordinate space.
Lines deleted between the extra and primary are dropped (new_ln = None).
Functions are identified by mangled name — stable across minor source
changes — and need no remapping.
Branch records carry a line number and are remapped the same way as lines.
Files in deleted_files were deleted or renamed away before primary; they
are skipped entirely rather than identity-mapped, which would leak obsolete
counters into stable totals.
"""
for _rel, _v in data.items():
if _rel in deleted_files:
continue # file was deleted/renamed — skip to avoid ghost counters
hunks = file_hunks.get(_rel, [])
for _ln, _cnt in _v["lines"].items():
if hunks:
new_ln = _remap_old_to_new(_ln, hunks)
if new_ln is None:
continue # line deleted between extra and primary
key = (_rel, new_ln)
else:
key = (_rel, _ln) # file unchanged — identity mapping
lt.add(key)
if _cnt > 0:
lc.add(key)
for _fn, _cnt in _v["fns"].items():
ft.add((_rel, _fn))
if _cnt > 0:
fc.add((_rel, _fn))
for (_ln, _blk, _br), _cnt in _v.get("branches", {}).items():
if hunks:
new_ln = _remap_old_to_new(_ln, hunks)
if new_ln is None:
continue
bkey = (_rel, new_ln, _blk, _br)
else:
bkey = (_rel, _ln, _blk, _br)
bt.add(bkey)
if _cnt > 0:
bc.add(bkey)
# Primary (m1) goes into both sides.
_accum(base_data,
_stable_base_line_cov, _stable_base_line_tot,
_stable_base_fn_cov, _stable_base_fn_tot,
_stable_base_br_cov, _stable_base_br_tot)
_accum(base_data,
_stable_pr_line_cov, _stable_pr_line_tot,
_stable_pr_fn_cov, _stable_pr_fn_tot,
_stable_pr_br_cov, _stable_pr_br_tot)
# Stream each extra baseline: parse, accumulate into both stable sets, discard.
# Since stable_pr = Union(m1..mN, PR), every extra goes into both stable_base
# and stable_pr with the same remapping. There is no need to retain parsed dicts
# across iterations — doing so would hold ~530 MB × N_extras in memory at once.
extra_baselines_used = 0
primary_sha_path = f"{temp_dir}/base_llvm_coverage.sha"
primary_sha = open(primary_sha_path).read().strip() if os.path.exists(primary_sha_path) else ""
for extra_path in EXTRA_BASELINE_PATHS:
if not (os.path.exists(extra_path) and os.path.getsize(extra_path) > 0):
continue
extra_sha_path = extra_path.replace(".info", ".sha")
extra_sha = open(extra_sha_path).read().strip() if os.path.exists(extra_sha_path) else ""
# Compute per-file line-number remapping from extra_sha -> primary_sha.
file_hunks: dict[str, list] = {}
deleted_files: set[str] = set()
changed_file_count = 0
if primary_sha and extra_sha and primary_sha != extra_sha:
try:
diff_result = subprocess.run(
["git", "-C", repo_root, "diff", "--unified=0",
f"{extra_sha}..{primary_sha}"],
capture_output=True, text=True, timeout=120,
)
if diff_result.returncode == 0 and diff_result.stdout:
file_hunks, deleted_files = _parse_extra_diff_hunks(diff_result.stdout)
changed_file_count = len(file_hunks)
except Exception as _e:
print(f" Warning: git diff failed for {os.path.basename(extra_path)}: {_e}")
print(
f"Parsing extra baseline coverage from {extra_path} "
f"(sha={extra_sha[:12] if extra_sha else 'unknown'}, "
f"{changed_file_count} files remapped, {len(deleted_files)} deleted/renamed skipped) ..."
)
_bx = _parse_info(extra_path)
print(f" {len(_bx)} files in {os.path.basename(extra_path)}")
# Accumulate into both stable sets immediately, then drop the parsed dict.
# stable_pr = Union(m1..mN, PR) so every extra feeds both sides identically.
_accum_with_remap(_bx, file_hunks, deleted_files,
_stable_base_line_cov, _stable_base_line_tot,
_stable_base_fn_cov, _stable_base_fn_tot,
_stable_base_br_cov, _stable_base_br_tot)
_accum_with_remap(_bx, file_hunks, deleted_files,
_stable_pr_line_cov, _stable_pr_line_tot,
_stable_pr_fn_cov, _stable_pr_fn_tot,
_stable_pr_br_cov, _stable_pr_br_tot)
del _bx
extra_baselines_used += 1
# PR side: no remapping needed (same source/binary epoch as m1).
_accum(curr_data,
_stable_pr_line_cov, _stable_pr_line_tot,
_stable_pr_fn_cov, _stable_pr_fn_tot,
_stable_pr_br_cov, _stable_pr_br_tot)
have_extra = extra_baselines_used > 0
if have_extra:
total_baselines = 1 + extra_baselines_used
print(
f" cross-validation enabled across {total_baselines} master baselines "
f"(line numbers remapped to primary coordinate system via git diff): "
f"{len(_stable_base_line_cov):,} lines covered in at least one master run"
)
else:
print(
"Note: no extra master baselines available - falling back to "
"single-baseline mode. The count below may include run-to-run "
"coverage variance (~1000 lines is typical noise between two "
"adjacent master runs)."
)
# Overall coverage delta: stable baseline (Python union of all baselines)
# vs current (PR run). Using the Python union avoids `lcov -a` which
# corrupts FNDA records on lcov 2.1+, producing wrong function percentages.
def _totals(data: dict) -> tuple[int, int, int, int]:
l_tot = l_hit = f_tot = f_hit = 0
for v in data.values():
for cnt in v["lines"].values():
l_tot += 1
if cnt > 0:
l_hit += 1
for cnt in v["fns"].values():
f_tot += 1
if cnt > 0:
f_hit += 1
return l_tot, l_hit, f_tot, f_hit
# Delta = stable_pr − stable_base = Union(m1..mN-1, PR) − Union(m1..mN).
# Lines in m1..mN-1 cancel from both sides; only PR vs mN contributes.
# Use the union of both total-sets as the shared denominator so percentages
# are comparable.
_all_tot = _stable_base_line_tot | _stable_pr_line_tot
_all_fn_tot = _stable_base_fn_tot | _stable_pr_fn_tot
_all_br_tot = _stable_base_br_tot | _stable_pr_br_tot
b_lt = c_lt = len(_all_tot) # shared line denominator
b_ft = c_ft = len(_all_fn_tot) # shared function denominator
b_bt = c_bt = len(_all_br_tot) # shared branch denominator
b_lh = len(_stable_base_line_cov)
b_fh = len(_stable_base_fn_cov)
b_bh = len(_stable_base_br_cov)
c_lh = len(_stable_pr_line_cov)
c_fh = len(_stable_pr_fn_cov)
c_bh = len(_stable_pr_br_cov)
# Fall back to primary-only counts if no extras (single-baseline mode).
if not _all_tot:
b_lt = c_lt = len(_stable_base_line_tot) or 1
b_ft = c_ft = len(_stable_base_fn_tot) or 1
b_bt = c_bt = len(_stable_base_br_tot) or 1
b_lp = (100.0 * b_lh / b_lt) if b_lt else 0.0
c_lp = (100.0 * c_lh / c_lt) if c_lt else 0.0
b_fp = (100.0 * b_fh / b_ft) if b_ft else 0.0
c_fp = (100.0 * c_fh / c_ft) if c_ft else 0.0
b_bp = (100.0 * b_bh / b_bt) if b_bt else 0.0
c_bp = (100.0 * c_bh / c_bt) if c_bt else 0.0
print("\nOverall coverage delta (current vs master baseline):")
print(
f" Lines : {c_lp - b_lp:+.4f} pp "
f"({b_lp:.4f}% -> {c_lp:.4f}%, {c_lh - b_lh:+,} covered)"
)
print(
f" Functions: {c_fp - b_fp:+.4f} pp "
f"({b_fp:.4f}% -> {c_fp:.4f}%, {c_fh - b_fh:+,} covered)"
)
print(
f" Branches : {c_bp - b_bp:+.4f} pp "
f"({b_bp:.4f}% -> {c_bp:.4f}%, {c_bh - b_bh:+,} covered)"
)
nc_lines: dict[str, list[int]] = defaultdict(list)
nc_fns: dict[str, list[str]] = defaultdict(list)
for rel, c in curr_data.items():
b = base_data.get(rel)
if b is None:
# File did not exist in baseline; transition cannot be attributed
# to "test newly covers code that existed before". Skip.
continue
for ln, cnt in c["lines"].items():
if cnt <= 0:
continue
# Newly covered = PR hits it AND it was cold in every master run
# (m1..mN, all in primary coordinate system after remapping).
# _stable_base_line_cov is the union of covered lines across all
# master baselines, so "not in" means cold in all of them.
if (rel, ln) in _stable_base_line_cov:
continue
if _is_noise(rel, ln):
continue
nc_lines[rel].append(ln)
for fn, cnt in c["fns"].items():
if cnt <= 0:
continue
if (rel, fn) in _stable_base_fn_cov:
continue
nc_fns[rel].append(fn)
total_lines = sum(len(v) for v in nc_lines.values())
total_fns = sum(len(v) for v in nc_fns.values())
all_files = set(nc_lines.keys()) | set(nc_fns.keys())
total_files = len(all_files)
if total_lines == 0 and total_fns == 0:
summary = "no newly covered lines or functions"
# The hook checks `if newly_covered_info:` to decide whether to render
# the "Newly covered by added/modified tests:" line. For PRs with no
# signal we want that line omitted entirely (otherwise every CI-fix /
# test-tweak PR with no real coverage delta would carry a confusing
# "Newly covered: no newly covered lines or functions" footer), so
# set the GH-comment payload to empty here while keeping the
# descriptive summary in the log + Result.info for the artifact.
comment_text = ""
else:
bits: list[str] = []
if total_lines > 0:
bits.append(f"{total_lines} line(s)")
if total_fns > 0:
bits.append(f"{total_fns} function(s)")
summary = f"{', '.join(bits)} across {total_files} file(s)"
comment_text = summary
print(f"\nNewly covered: {summary}\n")
file_stats = sorted(
[
(rel, len(nc_lines.get(rel, [])), len(nc_fns.get(rel, [])))
for rel in all_files
],
key=lambda x: (-x[1], -x[2], x[0]),
)
# Full file inventory — every file with newly-covered code, ranked by line
# count. This is the authoritative list; the snippet section below may be
# capped, but this is not.
if file_stats:
print(f"All {len(file_stats)} file(s) with newly-covered code, ranked by line count:")
for rel, lc, fc in file_stats:
parts = []
if lc > 0:
parts.append(f"{lc} line(s)")
if fc > 0:
parts.append(f"{fc} function(s)")
print(f" {rel}: {', '.join(parts)}")
print()
# Detailed per-file snippet preview. Capped to keep the CI job log readable;
# the file inventory above always lists every contributor in full, so a
# truncation here does not lose data — only the inline context.
MAX_BLOCK_LINES = 2000
printed = 0
truncated = False
if total_lines > 0:
print("Newly covered code (with context):\n")
for rel, lc, _ in file_stats:
if lc == 0:
continue
if printed >= MAX_BLOCK_LINES:
truncated = True
break
print("=" * 80)
print(rel)
print("=" * 80)
for out_line in _format_block_preview(rel, nc_lines[rel]):
print(out_line)
printed += 1
if printed >= MAX_BLOCK_LINES:
truncated = True
break
if truncated:
print(
f"\n[snippet preview capped at {MAX_BLOCK_LINES} lines — "
f"see the file inventory above for the complete list of "
f"files with newly-covered code; open those files at the "
f"listed line numbers for the full context]"
)
r = Result.create_from(
name="Newly Covered Code",
status=Result.Status.OK,
info=summary,
)
r.set_comment(comment_text)
r.ext["newly_covered_lines"] = total_lines
r.ext["newly_covered_fns"] = total_fns
r.ext["newly_covered_files"] = total_files
# Stable-union coverage delta for the GitHub comment table.
r.ext["stable_b_line_cov"] = b_lp
r.ext["stable_c_line_cov"] = c_lp
r.ext["stable_b_func_cov"] = b_fp
r.ext["stable_c_func_cov"] = c_fp
r.ext["stable_b_branch_cov"] = b_bp
r.ext["stable_c_branch_cov"] = c_bp
r.ext["stable_b_line_hit"] = b_lh
r.ext["stable_b_line_tot"] = b_lt
r.ext["stable_c_line_hit"] = c_lh
r.ext["stable_c_line_tot"] = c_lt
r.ext["stable_b_func_hit"] = b_fh
r.ext["stable_b_func_tot"] = b_ft
r.ext["stable_c_func_hit"] = c_fh
r.ext["stable_c_func_tot"] = c_ft
r.ext["stable_b_branch_hit"] = b_bh
r.ext["stable_b_branch_tot"] = b_bt
r.ext["stable_c_branch_hit"] = c_bh
r.ext["stable_c_branch_tot"] = c_bt
# Snapshot the top files into the result for inline rendering in the GH comment.
r.ext["newly_covered_top_files"] = [
{"rel": rel, "lines": lc, "fns": fc}
for rel, lc, fc in file_stats[:5]
]
r.dump()