SUMMARY=<log> keeps the full log and prints a bounded verdict summary; wait <log> blocks at most 270 s and returns it once. mutation-table.sh runs hand mutants in one verify job and returns a kill table. testparse.sh is the one parser for both. Fixtures: real cargo and mutation logs from Yggdrasil (tiny-crate), plus hand-written dotnet, vitest, pytest, exit-0-but-failed and no-verdict logs. Every commit here builds; verified live on Yggdrasil. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
90 lines
3.8 KiB
Shell
90 lines
3.8 KiB
Shell
#!/usr/bin/env bash
|
|
# Sourced, not run. Reads a test-run log and says what it contains.
|
|
#
|
|
# tp_scan <log> one line per fact:
|
|
# FRAMEWORK <name> passed=<n> failed=<n> skipped=<n> (summed over the log)
|
|
# FAILED <test name> (one per failing test)
|
|
# tp_sections <log> [max-lines] cargo failure sections ("---- name stdout
|
|
# ----"), each cut to its last <max-lines> lines (default 30)
|
|
#
|
|
# Frameworks: cargo test, dotnet test, vitest/jest, pytest. Anything else is
|
|
# simply not recognised; the callers treat "recognised nothing" as a fact to
|
|
# print, never as success.
|
|
#
|
|
# Owner: tools/stopgap/ygg-verify.sh (summary mode) and tools/mutation-table.sh
|
|
# (kill attribution). One parser, so a summary and a kill table cannot disagree
|
|
# about what failed. Written for mawk: no match() groups, no gensub.
|
|
|
|
tp_scan() {
|
|
awk '
|
|
function num(s, a) { split(s, a, " "); return a[1] + 0 }
|
|
/^test result: / { # cargo, one line per test binary
|
|
line = $0; sub(/^test result: [A-Za-z]+\. /, "", line)
|
|
n = split(line, parts, "; ")
|
|
for (i = 1; i <= n; i++) {
|
|
v = num(parts[i])
|
|
if (parts[i] ~ /passed/) cp += v
|
|
if (parts[i] ~ /failed/) cf += v
|
|
if (parts[i] ~ /ignored/) cs += v
|
|
}
|
|
cargo = 1; next
|
|
}
|
|
/^test .* \.\.\. FAILED$/ { name = $0; sub(/^test /, "", name); sub(/ \.\.\. FAILED$/, "", name); print "FAILED " name; next }
|
|
/^(Passed|Failed)! - Failed: *[0-9]+, Passed: *[0-9]+, Skipped: *[0-9]+, Total:/ { # dotnet
|
|
line = $0; sub(/^[A-Za-z]+! - /, "", line)
|
|
n = split(line, parts, ", ")
|
|
for (i = 1; i <= n; i++) {
|
|
v = parts[i]; sub(/^[A-Za-z]+: */, "", v); v += 0
|
|
if (parts[i] ~ /^Failed:/) df += v
|
|
if (parts[i] ~ /^Passed:/) dp += v
|
|
if (parts[i] ~ /^Skipped:/) ds += v
|
|
}
|
|
dotnet = 1; next
|
|
}
|
|
/^[ \t]+Failed [^ ]+ \[/ { name = $0; sub(/^[ \t]+Failed /, "", name); sub(/ \[.*$/, "", name); print "FAILED " name; next }
|
|
/^ *Tests +[0-9]+ (failed|passed|skipped)/ { # vitest / jest
|
|
line = $0; sub(/^ *Tests +/, "", line); sub(/ *\(.*$/, "", line)
|
|
n = split(line, parts, " \\| ")
|
|
for (i = 1; i <= n; i++) {
|
|
v = num(parts[i])
|
|
if (parts[i] ~ /failed/) vf += v
|
|
if (parts[i] ~ /passed/) vp += v
|
|
if (parts[i] ~ /skipped/) vs += v
|
|
}
|
|
vitest = 1; next
|
|
}
|
|
/^ *FAIL / { name = $0; sub(/^ *FAIL +/, "", name); print "FAILED " name; next }
|
|
/^=+ .*(passed|failed|no tests ran).* in [0-9.]+s/ { # pytest summary line
|
|
line = $0; gsub(/=/, "", line); sub(/ in [0-9.]+s.*$/, "", line)
|
|
n = split(line, parts, ", ")
|
|
for (i = 1; i <= n; i++) {
|
|
v = num(parts[i])
|
|
if (parts[i] ~ /failed/) pf += v
|
|
if (parts[i] ~ /passed/) pp += v
|
|
if (parts[i] ~ /(skipped|deselected)/) ps += v
|
|
if (parts[i] ~ /errors?/) pf += v
|
|
}
|
|
pytest = 1; next
|
|
}
|
|
/^FAILED [^ ]+::/ { name = $2; print "FAILED " name; next }
|
|
END {
|
|
if (cargo) printf "FRAMEWORK cargo passed=%d failed=%d skipped=%d\n", cp, cf, cs
|
|
if (dotnet) printf "FRAMEWORK dotnet passed=%d failed=%d skipped=%d\n", dp, df, ds
|
|
if (vitest) printf "FRAMEWORK vitest passed=%d failed=%d skipped=%d\n", vp, vf, vs
|
|
if (pytest) printf "FRAMEWORK pytest passed=%d failed=%d skipped=%d\n", pp, pf, ps
|
|
}' "$1"
|
|
}
|
|
|
|
tp_sections() {
|
|
awk -v max="${2:-30}" '
|
|
function flush( i, start) {
|
|
if (!n) return
|
|
start = n > max ? n - max + 1 : 1
|
|
for (i = start; i <= n; i++) print buf[i]
|
|
n = 0
|
|
}
|
|
/^---- .* stdout ----$/ { flush(); n = 0; buf[++n] = $0; inside = 1; next }
|
|
inside && (/^failures:$/ || /^test result: /) { flush(); inside = 0; next }
|
|
inside { buf[++n] = $0 }
|
|
END { flush() }' "$1"
|
|
}
|