1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
# scripts/lib/coverage-report.awk
# Produce line-coverage reports for shell sources from an executed-line set.
#
# Inputs:
# -v COVERED=<file> TSV of "<base>\t<line>" executed lines (sorted -u).
# -v HTMLDIR=<dir> directory to write per-file <base>.html reports into.
# ARGV the source files to report on (absolute paths).
#
# Output (stdout): one CSV row per source file:
# file,total_executable,covered,percent
#
# "Executable" lines are determined heuristically: non-blank, non-comment
# lines that are not pure structural tokens (then/do/done/else/fi/esac/{/}).
# Heredoc/multiline string bodies are approximated and may read as missed;
# see docs/coverage.md for the documented limitations.
function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s }
function is_exec(line, t) {
t = trim(line)
if (t == "") return 0
if (t ~ /^#/) return 0
if (t ~ /^(then|do|done|else|fi|esac)([ \t].*)?$/) return 0
if (t ~ /^[{}]([ \t;].*)?$/) return 0
if (t ~ /^[()]([ \t].*)?$/) return 0
if (t ~ /^;;/) return 0
return 1
}
function esc(s) {
gsub(/&/, "\\&", s)
gsub(/</, "\\<", s)
gsub(/>/, "\\>", s)
return s
}
function basename(p) { sub(/.*\//, "", p); sub(/.*\\/, "", p); return p }
BEGIN {
FS = "\t"
# Load the executed-line set.
while ((getline ln < COVERED) > 0) {
nf = split(ln, a, "\t")
if (nf >= 2) covered[a[1] SUBSEP a[2]] = 1
}
close(COVERED)
}
# New source file: open its HTML report and reset counters.
FNR == 1 {
if (cur != "") finish_file()
cur = FILENAME
curbase = basename(FILENAME)
total = 0; hit = 0
html = HTMLDIR "/" curbase ".html"
print "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\">" > html
print "<title>coverage: " esc(curbase) "</title>" > html
print "<style>body{font:13px/1.5 monospace;margin:0;padding:1rem;background:#fff;color:#111}" > html
print "h1{font:600 16px sans-serif}" > html
print "table{border-collapse:collapse;width:100%}" > html
print "td{padding:0 .5rem;white-space:pre;vertical-align:top}" > html
print "td.n{color:#999;text-align:right;user-select:none;border-right:1px solid #ddd}" > html
print ".hit{background:#e6ffed}.miss{background:#ffeef0}.na{color:#999}" > html
print "</style></head><body>" > html
print "<h1>coverage: " esc(curbase) "</h1><table>" > html
}
{
cls = "na"
if (is_exec($0)) {
total++
if ((curbase SUBSEP FNR) in covered) { hit++; cls = "hit" } else { cls = "miss" }
}
print "<tr class=\"" cls "\"><td class=\"n\">" FNR "</td><td>" esc($0) "</td></tr>" > html
}
function finish_file( pct) {
print "</table></body></html>" > html
close(html)
pct = (total > 0 ? (hit * 100.0 / total) : 0)
printf "%s,%d,%d,%.1f\n", curbase, total, hit, pct
}
END { if (cur != "") finish_file() }
|