Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
148 changes: 148 additions & 0 deletions .github/benchmark-site/coverage/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta
name="description"
content="LLVM line coverage for the harness integration and E2E suites."
>
<meta name="color-scheme" content="dark light">
<title>Harness stack coverage</title>
<link rel="stylesheet" href="../styles.css">
<style>
.coverage-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
gap: 1.25rem;
}
.coverage-card {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 1.5rem;
}
.coverage-card h2 {
margin: 0;
}
.coverage-percent {
font-size: 2.5rem;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.coverage-meta {
opacity: 0.75;
font-size: 0.875rem;
}
.coverage-links {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
</style>
</head>
<body>
<a class="skip-link" href="#main">Skip to coverage reports</a>
<div class="ambient ambient-one" aria-hidden="true"></div>
<div class="ambient ambient-two" aria-hidden="true"></div>

<header class="topbar">
<a class="brand" href="https://github.com/iii-hq/workers" aria-label="iii workers">
<span class="brand-copy">
<strong>iii</strong>
<span>Harness benchmarks</span>
</span>
</a>
<nav class="topbar-actions" aria-label="Dashboard actions">
<a class="button button-secondary" href="../index.html">Executions</a>
<a class="button button-secondary" href="../coverage-trend/">Trend</a>
</nav>
</header>

<main id="main" class="page-shell overview-shell">
<section class="page-heading" aria-labelledby="page-title">
<div>
<div class="eyebrow">
<span class="live-dot" aria-hidden="true"></span>
Harness stack
</div>
<h1 id="page-title">Code coverage</h1>
<p>LLVM line coverage of the instrumented stack, refreshed by the daily E2E cycle.</p>
</div>
<div class="sync-block">
<span id="sync-label">Last published</span>
<time id="last-update" datetime="">Waiting for data</time>
</div>
</section>

<section id="empty-state" class="empty-state" hidden>
<div class="empty-icon" aria-hidden="true">⌁</div>
<h2>No coverage published yet</h2>
<p>The next scheduled Harness E2E Daily workflow will publish the first reports here.</p>
</section>

<section id="coverage-content" class="coverage-grid" hidden>
<article class="panel coverage-card" id="card-integration">
<h2>Integration suite</h2>
<div class="coverage-percent" data-percent>–</div>
<div class="coverage-meta" data-meta>Not published yet.</div>
<div class="coverage-links">
<a class="button button-secondary" href="./integration/index.html" data-report hidden>
Browse report
</a>
</div>
</article>
<article class="panel coverage-card" id="card-e2e">
<h2>E2E suite</h2>
<div class="coverage-percent" data-percent>–</div>
<div class="coverage-meta" data-meta>Not published yet.</div>
<div class="coverage-links">
<a class="button button-secondary" href="./e2e/index.html" data-report hidden>
Browse report
</a>
</div>
</article>
</section>
</main>

<script>
window.HARNESS_COVERAGE = null;
</script>
<script src="./summary.js"></script>
<script>
(function () {
var data = window.HARNESS_COVERAGE;
var empty = document.getElementById("empty-state");
var content = document.getElementById("coverage-content");
var suites = data && data.suites ? data.suites : {};
var hasAny = Boolean(suites.e2e || suites.integration);
if (!hasAny) {
empty.hidden = false;
return;
}
content.hidden = false;

if (data.updated_at) {
var time = document.getElementById("last-update");
time.dateTime = data.updated_at;
time.textContent = new Date(data.updated_at).toLocaleString();
}

function render(cardId, summary) {
var card = document.getElementById(cardId);
if (!summary || !summary.totals || !summary.totals.lines) return;
var lines = summary.totals.lines;
card.querySelector("[data-percent]").textContent =
lines.percent.toFixed(1) + "%";
card.querySelector("[data-meta]").textContent =
(lines.covered != null ? lines.covered.toLocaleString() : "?") +
" of " + (lines.count != null ? lines.count.toLocaleString() : "?") +
" lines covered · " + (summary.generated_at || "");
card.querySelector("[data-report]").hidden = false;
}
render("card-integration", suites.integration);
render("card-e2e", suites.e2e);
})();
</script>
</body>
</html>
1 change: 1 addition & 0 deletions .github/benchmark-site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
</a>
<nav class="topbar-actions" aria-label="Dashboard actions">
<span id="preview-badge" class="badge badge-preview" hidden>Preview data</span>
<a class="button button-secondary" href="./coverage/">Coverage</a>
<a id="actions-link" class="button button-secondary" href="https://github.com/iii-hq/workers/actions">
View Actions <span aria-hidden="true">↗</span>
</a>
Expand Down
90 changes: 90 additions & 0 deletions .github/scripts/coverage_report.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# Merge LLVM coverage profiles from a dedicated profraw directory and emit a
# combined multi-binary report: html/, summary.json, and report.txt.
#
# Usage:
# coverage_report.sh --profraw-dir DIR --output-dir DIR --title NAME \
# --object BIN [--object BIN ...]
#
# Binaries must be built with:
# RUSTFLAGS="-Cinstrument-coverage -Cllvm-args=-runtime-counter-relocation"
# and run with LLVM_PROFILE_FILE containing %c (continuous mode) so profiles
# survive SIGTERM/SIGKILL teardown. Requires the rustup llvm-tools component.
set -euo pipefail

IGNORE_REGEX='\.cargo/(registry|git)|/rustc/|harness/tests/'

profraw_dir=""
output_dir=""
title="coverage"
objects=()

while [[ $# -gt 0 ]]; do
case "$1" in
--profraw-dir) profraw_dir="$2"; shift 2 ;;
--output-dir) output_dir="$2"; shift 2 ;;
--title) title="$2"; shift 2 ;;
--object) objects+=("$2"); shift 2 ;;
*) echo "unknown argument: $1" >&2; exit 2 ;;
esac
done

[[ -n "$profraw_dir" && -n "$output_dir" ]] || {
echo "usage: $0 --profraw-dir DIR --output-dir DIR --title NAME --object BIN [--object BIN ...]" >&2
exit 2
}

tool_dir="$(dirname "$(rustc --print target-libdir)")/bin"
for tool in llvm-profdata llvm-cov; do
[[ -x "$tool_dir/$tool" ]] || {
echo "missing $tool_dir/$tool; install with: rustup component add llvm-tools" >&2
exit 2
}
done

mkdir -p "$output_dir"

mapfile -t profraws < <(find "$profraw_dir" -name '*.profraw' -type f 2>/dev/null | sort)
if [[ ${#profraws[@]} -eq 0 ]]; then
echo "no .profraw files under $profraw_dir; skipping report" >&2
exit 0
fi

object_args=()
for object in "${objects[@]}"; do
if [[ -x "$object" ]]; then
object_args+=(-object "$object")
else
echo "warning: skipping missing object $object" >&2
fi
done
[[ ${#object_args[@]} -gt 0 ]] || { echo "no usable --object binaries" >&2; exit 2; }

demangler_args=()
if command -v rustfilt >/dev/null 2>&1; then
demangler_args=(-Xdemangler=rustfilt)
fi

"$tool_dir/llvm-profdata" merge -sparse "${profraws[@]}" -o "$output_dir/merged.profdata"

"$tool_dir/llvm-cov" show --format=html --output-dir="$output_dir/html" \
-instr-profile="$output_dir/merged.profdata" \
--ignore-filename-regex="$IGNORE_REGEX" \
"${demangler_args[@]}" \
"${object_args[@]}"

"$tool_dir/llvm-cov" report \
-instr-profile="$output_dir/merged.profdata" \
--ignore-filename-regex="$IGNORE_REGEX" \
"${object_args[@]}" > "$output_dir/report.txt"

"$tool_dir/llvm-cov" export -summary-only \
-instr-profile="$output_dir/merged.profdata" \
--ignore-filename-regex="$IGNORE_REGEX" \
"${object_args[@]}" \
| jq --arg title "$title" --arg at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{title: $title, generated_at: $at, totals: .data[0].totals}' \
> "$output_dir/summary.json"

echo "coverage report written to $output_dir"
tail -1 "$output_dir/report.txt"
Loading
Loading