use std::collections::HashMap;
use std::path::{Path, PathBuf};
use anyhow::{anyhow, bail, Context, Result};
use crate::bench::BenchRun;
use crate::introspect::{artifact, callgraph_dwarf, depgraph};
#[derive(Debug, Default)]
pub struct FileReport {
pub path: PathBuf,
pub sections: Vec<String>,
pub changed: bool,
}
pub struct Ctx<'a> {
pub repo_root: &'a Path,
pub workspace_root: &'a Path,
pub run: Option<&'a BenchRun>,
pub history: &'a [BenchRun],
pub doc_dir: Option<&'a Path>,
pub coverage: &'a [crate::warehouse::coverage::CoverageRow],
pub no_assets: bool,
pub manual: bool,
}
impl<'a> Ctx<'a> {
pub fn new(
repo_root: &'a Path,
workspace_root: &'a Path,
run: Option<&'a BenchRun>,
) -> Self {
Ctx { repo_root, workspace_root, run, history: &[], doc_dir: None, coverage: &[], no_assets: false, manual: false }
}
pub fn manual(mut self) -> Self {
self.manual = true;
self
}
pub fn markdown_only(mut self) -> Self {
self.no_assets = true;
self
}
pub fn with_history(mut self, history: &'a [BenchRun]) -> Self {
self.history = history;
self
}
pub fn with_coverage(mut self, coverage: &'a [crate::warehouse::coverage::CoverageRow]) -> Self {
self.coverage = coverage;
self
}
pub fn with_doc_dir(mut self, doc_dir: &'a Path) -> Self {
self.doc_dir = Some(doc_dir);
self
}
}
#[derive(Debug)]
struct Marker {
line_start: usize,
line_end: usize,
name: String,
args: HashMap<String, String>,
}
const START: &str = "<!-- nornir:gen:start:";
const END: &str = "<!-- nornir:gen:end:";
fn tokenize_marker(s: &str) -> Vec<String> {
let mut out = Vec::new();
let mut cur = String::new();
let mut in_q = false;
let mut started = false;
for c in s.chars() {
match c {
'"' => {
in_q = !in_q;
started = true;
}
c if c.is_whitespace() && !in_q => {
if started {
out.push(std::mem::take(&mut cur));
started = false;
}
}
c => {
cur.push(c);
started = true;
}
}
}
if started {
out.push(cur);
}
out
}
fn parse_markers(text: &str) -> Result<Vec<(Marker, usize)>> {
let lines: Vec<&str> = text.split_inclusive('\n').collect();
let mut open: Vec<(usize, String, HashMap<String, String>)> = Vec::new();
let mut out: Vec<(Marker, usize)> = Vec::new();
let mut in_fence = false;
for (i, line) in lines.iter().enumerate() {
let t = line.trim();
if t.starts_with("```") || t.starts_with("~~~") {
in_fence = !in_fence;
continue;
}
if in_fence {
continue;
}
if let Some(rest) = t.strip_prefix(START) {
let body = rest
.strip_suffix("-->")
.ok_or_else(|| anyhow!("malformed start marker at line {}: {t}", i + 1))?
.trim();
let mut parts = tokenize_marker(body).into_iter();
let name = parts
.next()
.ok_or_else(|| anyhow!("missing section name at line {}", i + 1))?;
let mut args = HashMap::new();
for kv in parts {
if let Some((k, v)) = kv.split_once('=') {
args.insert(k.to_string(), v.to_string());
}
}
open.push((i, name, args));
} else if let Some(rest) = t.strip_prefix(END) {
let name = rest
.strip_suffix("-->")
.ok_or_else(|| anyhow!("malformed end marker at line {}: {t}", i + 1))?
.trim()
.to_string();
let (start_i, start_name, args) = open
.pop()
.ok_or_else(|| anyhow!("unmatched end marker `{name}` at line {}", i + 1))?;
if start_name != name {
bail!(
"marker mismatch: start `{start_name}` at line {} vs end `{name}` at line {}",
start_i + 1,
i + 1
);
}
out.push((
Marker {
line_start: start_i,
line_end: i,
name,
args,
},
0,
));
}
}
if let Some((i, name, _)) = open.pop() {
bail!("unclosed marker `{name}` opened at line {}", i + 1);
}
Ok(out)
}
fn render(ctx: &Ctx, m: &Marker) -> Result<String> {
let body = match m.name.as_str() {
"bench" => render_bench(ctx)?,
"bench_history" => render_bench_history(ctx, &m.args)?,
"benches" => render_benches(ctx, &m.args)?,
"bench_chart" => render_bench_chart(ctx, &m.args)?,
"bench_hero" => render_bench_hero(ctx, &m.args)?,
"mashup" => render_mashup(ctx, &m.args)?,
"tests" => render_tests(ctx, &m.args)?,
"coverage" => render_coverage(ctx, &m.args)?,
"capabilities" => render_capabilities(ctx, &m.args)?,
"depgraph" => render_depgraph(ctx)?,
"symbols" => render_symbols(ctx, &m.args)?,
"callgraph" => render_callgraph(ctx, &m.args)?,
other => bail!("unknown section `{other}`"),
};
let mut out = String::new();
out.push_str("<!-- nornir:gen:start:");
out.push_str(&m.name);
for (k, v) in args_sorted(&m.args) {
out.push(' ');
out.push_str(&k);
out.push('=');
if v.chars().any(char::is_whitespace) {
out.push('"');
out.push_str(&v);
out.push('"');
} else {
out.push_str(&v);
}
}
out.push_str(" -->\n");
out.push_str(body.trim_end());
out.push('\n');
out.push_str("<!-- nornir:gen:end:");
out.push_str(&m.name);
out.push_str(" -->\n");
Ok(out)
}
fn args_sorted(a: &HashMap<String, String>) -> Vec<(String, String)> {
let mut v: Vec<(String, String)> = a.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
v.sort_by(|a, b| a.0.cmp(&b.0));
v
}
pub fn assemble_file(path: &Path, ctx: &Ctx) -> Result<FileReport> {
let text = std::fs::read_to_string(path)
.with_context(|| format!("read {}", path.display()))?;
let parent = path.parent().map(|p| p.to_path_buf());
let scoped = match parent.as_deref() {
Some(dir) => Ctx { doc_dir: Some(dir), ..*ctx },
None => Ctx { ..*ctx },
};
let (new_text, sections) = rewrite(&text, &scoped)?;
let changed = new_text != text;
if changed {
std::fs::write(path, &new_text)
.with_context(|| format!("write {}", path.display()))?;
}
Ok(FileReport {
path: path.to_path_buf(),
sections,
changed,
})
}
pub fn check_file(path: &Path, ctx: &Ctx) -> Result<FileReport> {
let text = std::fs::read_to_string(path)
.with_context(|| format!("read {}", path.display()))?;
let (new_text, sections) = rewrite(&text, ctx)?;
if new_text != text {
bail!(
"{} is out of date — run `nornir docs assemble` to regenerate",
path.display()
);
}
Ok(FileReport {
path: path.to_path_buf(),
sections,
changed: false,
})
}
pub fn rewrite_str(text: &str, ctx: &Ctx) -> Result<(String, Vec<String>)> {
rewrite(text, ctx)
}
fn rewrite(text: &str, ctx: &Ctx) -> Result<(String, Vec<String>)> {
let markers = parse_markers(text)?;
if markers.is_empty() {
return Ok((text.to_string(), Vec::new()));
}
let lines: Vec<&str> = text.split_inclusive('\n').collect();
let mut out = String::new();
let mut cursor = 0usize;
let mut sections = Vec::new();
for (m, _) in &markers {
for l in &lines[cursor..m.line_start] {
out.push_str(l);
}
out.push_str(&render(ctx, m)?);
sections.push(m.name.clone());
cursor = m.line_end + 1;
}
for l in &lines[cursor..] {
out.push_str(l);
}
Ok((out, sections))
}
fn bench_header(run: &BenchRun) -> String {
let mut parts = vec![format!("v{}", run.version)];
if !run.machine.is_empty() {
parts.push(run.machine.clone());
}
parts.push(format!("{} cores", run.cores));
if !run.date.is_empty() {
parts.push(run.date.clone());
}
format!("**{}**\n\n", parts.join(" · "))
}
fn render_bench(ctx: &Ctx) -> Result<String> {
let Some(run) = ctx.run else {
return Ok("_(no bench history yet — run a benchmark to populate this section)_\n".to_string());
};
let mut out = String::new();
out.push_str(&bench_header(run));
if run.results.is_empty() {
out.push_str("_(no bench results)_\n");
return Ok(out);
}
out.push_str("| name | metric | value |\n|------|--------|-------|\n");
let mut rows: Vec<(String, String, f64)> = Vec::new();
for r in &run.results {
for (k, v) in &r.metrics {
if let Some(f) = v.as_f64() {
rows.push((r.name.clone(), k.clone(), f));
}
}
}
rows.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
for (n, k, v) in rows {
out.push_str(&format!("| {n} | {k} | {v:.2} |\n"));
}
Ok(out)
}
fn render_bench_history(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
if ctx.history.is_empty() {
return Ok(
"_(no bench history yet — run benchmarks to populate this section)_\n".to_string()
);
}
let limit: usize = args.get("limit").and_then(|s| s.parse().ok()).unwrap_or(20);
let metric = args.get("metric").map(|s| s.as_str());
let mut runs: Vec<&BenchRun> = ctx.history.iter().collect();
runs.sort_by(|a, b| {
let ka = a.timestamp.as_deref().unwrap_or(&a.date);
let kb = b.timestamp.as_deref().unwrap_or(&b.date);
kb.cmp(ka)
});
let metric_label = metric.unwrap_or("best metric");
let mut out = String::new();
out.push_str(&format!(
"| Version | Date | Machine | {metric_label} |\n|---|---|---|---|\n"
));
for run in runs.into_iter().take(limit) {
let date = if run.date.is_empty() { "-" } else { run.date.as_str() };
let machine = if run.machine.is_empty() { "-" } else { run.machine.as_str() };
let val = best_metric(run, metric)
.map(|v| format!("{v:.2}"))
.unwrap_or_else(|| "-".to_string());
out.push_str(&format!("| v{} | {} | {} | {} |\n", run.version, date, machine, val));
}
Ok(out)
}
fn best_metric(run: &BenchRun, metric: Option<&str>) -> Option<f64> {
let mut best: Option<f64> = None;
for r in &run.results {
for (k, v) in &r.metrics {
let Some(f) = v.as_f64() else { continue };
let take = match metric {
Some(name) => k == name || r.name == name,
None => true,
};
if take {
best = Some(best.map_or(f, |b| b.max(f)));
}
}
}
best
}
fn col_rank(col: &str) -> u8 {
if col == "ops_sec" || col.ends_with("_per_sec") {
0
} else if col.starts_with("p50") {
10
} else if col.starts_with("p90") {
11
} else if col.starts_with("p999") {
13
} else if col.starts_with("p99") {
12
} else if col.starts_with("mean") {
20
} else if col.starts_with("min") {
21
} else if col.starts_with("max") {
22
} else {
15
}
}
fn token_matches(name: &str, token: &str) -> bool {
if token.is_empty() {
return false;
}
if name == token {
return true;
}
let nb = name.as_bytes();
let tb = token.as_bytes();
if tb.len() > nb.len() {
return false;
}
let boundary = |b: Option<&u8>| b.is_none_or(|c| !c.is_ascii_alphanumeric());
let mut i = 0;
while i + tb.len() <= nb.len() {
if &nb[i..i + tb.len()] == tb
&& boundary(i.checked_sub(1).map(|p| &nb[p]))
&& boundary(nb.get(i + tb.len()))
{
return true;
}
i += 1;
}
false
}
fn render_benches(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
use crate::bench::{direction_of, unit_of, MetricDirection};
let Some(run) = ctx.run else {
return Ok("_(no bench history yet — run a benchmark to populate this section)_\n".to_string());
};
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
let bold_enabled = args.get("bold").map(|v| v != "false").unwrap_or(true);
let winner_by_row = matches!(args.get("winner").map(|s| s.as_str()), Some("row" | "rows"));
let ours_tokens: Vec<String> = args
.get("self")
.map(|s| s.split(',').map(str::trim).filter(|t| !t.is_empty()).map(str::to_string).collect())
.unwrap_or_default();
let filter = args.get("name").map(|s| s.as_str());
let exclude = args.get("exclude").map(|s| s.as_str());
let mut results: Vec<&crate::bench::BenchResult> = run
.results
.iter()
.filter(|r| filter.is_none_or(|p| r.name.contains(p)))
.filter(|r| exclude.is_none_or(|p| !r.name.contains(p)))
.collect();
if results.is_empty() {
return Ok("_(no bench results)_\n".to_string());
}
if let Some(spec) = args.get("compare") {
let mut used = vec![false; results.len()];
let mut picked: Vec<&crate::bench::BenchResult> = Vec::new();
for t in spec.split(',').map(str::trim).filter(|t| !t.is_empty()) {
let hit = (0..results.len())
.find(|&i| !used[i] && results[i].name == t)
.or_else(|| (0..results.len()).find(|&i| !used[i] && token_matches(&results[i].name, t)));
if let Some(i) = hit {
used[i] = true;
picked.push(results[i]);
}
}
if !picked.is_empty() {
results = picked;
}
}
if let Some(n) = args.get("top").and_then(|v| v.parse::<usize>().ok()).filter(|n| *n > 0) {
let rank_col = args
.get("rank")
.cloned()
.or_else(|| {
results.iter().find_map(|r| {
r.metrics
.keys()
.find(|k| {
r.metrics[*k].as_f64().is_some()
&& (k.as_str() == "ops_sec" || k.ends_with("_per_sec"))
})
.cloned()
})
})
.or_else(|| {
results
.iter()
.find_map(|r| r.metrics.keys().find(|k| r.metrics[*k].as_f64().is_some()).cloned())
});
if let Some(col) = rank_col {
let dir = direction_of(&overrides, &col);
let key = |r: &&crate::bench::BenchResult| r.metrics.get(&col).and_then(|v| v.as_f64());
results.sort_by(|a, b| {
let (av, bv) = (key(a), key(b));
match (av, bv) {
(Some(x), Some(y)) => match dir {
MetricDirection::High => y.total_cmp(&x),
_ => x.total_cmp(&y),
},
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => std::cmp::Ordering::Equal,
}
});
results.truncate(n);
}
}
let compact_holder: Vec<crate::bench::BenchResult>;
if args.get("mode").map(|s| s.as_str()) == Some("compact") && !results.is_empty() {
let mut cols: Vec<String> = Vec::new();
for r in &results {
for k in r.metrics.keys() {
if r.metrics[k].as_f64().is_some() && !cols.contains(k) {
cols.push(k.clone());
}
}
}
let mut best = serde_json::Map::new();
for c in &cols {
let dir = direction_of(&overrides, c);
if dir == MetricDirection::Neutral {
continue; }
let vals: Vec<f64> =
results.iter().filter_map(|r| r.metrics.get(c).and_then(|v| v.as_f64())).collect();
let pick = match dir {
MetricDirection::High => vals.iter().copied().reduce(f64::max),
MetricDirection::Low => vals.iter().copied().reduce(f64::min),
MetricDirection::Neutral => None,
};
if let Some(v) = pick {
best.insert(c.clone(), serde_json::Value::from(v));
}
}
let label = args.get("label").cloned().unwrap_or_else(|| "best-of".to_string());
compact_holder = vec![crate::bench::BenchResult { name: label, metrics: best , ..Default::default() }];
results = compact_holder.iter().collect();
}
let mut columns: Vec<String> = Vec::new();
for r in &results {
for k in r.metrics.keys() {
if r.metrics[k].as_f64().is_some() && !columns.contains(k) {
columns.push(k.clone());
}
}
}
if let Some(spec) = args.get("cols") {
let want: Vec<&str> = spec.split(',').map(str::trim).filter(|t| !t.is_empty()).collect();
columns.retain(|c| want.iter().any(|w| *w == c));
columns.sort_by_key(|c| want.iter().position(|w| *w == c).unwrap_or(usize::MAX));
} else if args.contains_key("compare") && results.len() >= 2 {
columns.retain(|c| {
let vals: Vec<f64> = results
.iter()
.filter_map(|r| r.metrics.get(c).and_then(|v| v.as_f64()))
.collect();
!(vals.len() == results.len() && vals.windows(2).all(|w| w[0] == w[1]))
});
columns.sort_by(|a, b| col_rank(a).cmp(&col_rank(b)).then_with(|| a.cmp(b)));
} else {
columns.sort();
}
if let Some(spec) = args.get("drop") {
let unwanted: Vec<&str> =
spec.split(',').map(str::trim).filter(|t| !t.is_empty()).collect();
columns.retain(|c| !unwanted.iter().any(|w| *w == c));
}
let mut out = String::new();
out.push_str(&bench_header(run));
out.push_str("| workload |");
for c in &columns {
out.push_str(&format!(" {c} |"));
}
out.push('\n');
out.push_str("|---|");
for _ in &columns {
out.push_str("---|");
}
out.push('\n');
let pick_best = |cells: &[(usize, f64)], dir: MetricDirection| -> Option<usize> {
match dir {
MetricDirection::High => cells.iter().max_by(|a, b| a.1.total_cmp(&b.1)),
MetricDirection::Low => cells.iter().min_by(|a, b| a.1.total_cmp(&b.1)),
MetricDirection::Neutral => None,
}
.map(|(i, _)| *i)
};
let mut bold: std::collections::HashSet<(usize, &String)> = std::collections::HashSet::new();
if bold_enabled && winner_by_row {
for c in &columns {
let dir = direction_of(&overrides, c);
if dir == MetricDirection::Neutral {
continue;
}
let cells: Vec<(usize, f64)> = results
.iter()
.enumerate()
.filter_map(|(i, r)| r.metrics.get(c).and_then(|v| v.as_f64()).map(|f| (i, f)))
.collect();
if cells.len() < 2 {
continue;
}
if let Some(default_i) = pick_best(&cells, dir) {
let best_val = cells
.iter()
.find(|(i, _)| *i == default_i)
.map(|(_, f)| *f)
.unwrap_or(f64::NAN);
let chosen = cells
.iter()
.filter(|(_, f)| f.total_cmp(&best_val) == std::cmp::Ordering::Equal)
.map(|(i, _)| *i)
.find(|i| row_is_ours(&results[*i].name, &ours_tokens))
.unwrap_or(default_i);
bold.insert((chosen, c));
}
}
} else if bold_enabled {
for (i, r) in results.iter().enumerate() {
let mut groups: HashMap<&'static str, Vec<(usize, f64)>> = HashMap::new();
let mut col_of: HashMap<usize, &String> = HashMap::new();
for (ci, c) in columns.iter().enumerate() {
if let Some(f) = r.metrics.get(c).and_then(|v| v.as_f64()) {
if let Some(u) = unit_of(c) {
groups.entry(u.name).or_default().push((ci, f));
col_of.insert(ci, c);
}
}
}
for cells in groups.values() {
if cells.len() < 2 {
continue;
}
let dir = direction_of(&overrides, col_of[&cells[0].0]);
if let Some(ci) = pick_best(cells, dir) {
bold.insert((i, col_of[&ci]));
}
}
}
}
let col_scale: Vec<Option<(f64, &'static str, usize)>> = columns
.iter()
.map(|c| {
let vals: Vec<f64> = results
.iter()
.filter_map(|r| r.metrics.get(c).and_then(|v| v.as_f64()))
.collect();
column_format(&vals)
})
.collect();
let trophies = hard_broken_trophies(ctx, &results, &ours_tokens);
for (i, r) in results.iter().enumerate() {
let name_cell = if trophies.contains(r.name.as_str()) {
format!("{} 🏆", r.name)
} else {
r.name.clone()
};
out.push_str(&format!("| {} |", name_cell));
for (ci, c) in columns.iter().enumerate() {
match r.metrics.get(c).and_then(|v| v.as_f64()) {
Some(f) => {
let cell = fmt_metric_scaled(f, col_scale[ci]);
if bold.contains(&(i, c)) {
out.push_str(&format!(" **{cell}** |"));
} else {
out.push_str(&format!(" {cell} |"));
}
}
None => out.push_str(" |"),
}
}
out.push('\n');
}
Ok(out)
}
fn render_bench_hero(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let Some(run) = ctx.run else {
return Ok("_(no bench history yet — run a benchmark to populate this section)_\n".to_string());
};
let name = args.get("name").map(|s| s.as_str()).unwrap_or("");
let (Some(fast_tok), Some(slow_tok)) = (args.get("fast"), args.get("slow")) else {
return Ok("_(bench_hero needs `fast=` and `slow=` backend tokens)_\n".to_string());
};
let unit = args.get("unit").map(|s| s.as_str()).unwrap_or("_s");
let find = |tok: &str| {
run.results
.iter()
.find(|r| r.name.contains(name) && r.name.contains(tok))
};
let (Some(fast), Some(slow)) = (find(fast_tok), find(slow_tok)) else {
return Ok("_(bench_hero: backend rows not found for this run)_\n".to_string());
};
let mut best: Option<(String, f64, f64, f64)> = None;
for (k, v) in fast.metrics.iter() {
let Some(stem) = k.strip_suffix(unit) else { continue };
let is_item =
stem.len() > 1 && stem.starts_with('q') && stem[1..].bytes().all(|b| b.is_ascii_digit());
if !is_item {
continue;
}
let (Some(fv), Some(sv)) = (v.as_f64(), slow.metrics.get(k).and_then(|x| x.as_f64())) else {
continue;
};
if fv <= 0.0 {
continue;
}
let ratio = sv / fv;
if best.as_ref().is_none_or(|b| ratio > b.1) {
best = Some((stem.to_string(), ratio, fv, sv));
}
}
let Some((item, ratio, fv, sv)) = best else {
return Ok("_(bench_hero: the two backends share no per-item metrics)_\n".to_string());
};
let label = args.get("label").map(|s| s.as_str()).unwrap_or("query");
let mut out = String::new();
out.push_str(&bench_header(run));
out.push_str(&format!(
"> **Most dramatic {label} — `{item}`: nornir is {ratio:.1}× faster** \
({:.1} ms vs {:.1} ms).\n\n",
fv * 1000.0,
sv * 1000.0
));
out.push_str(&format!(
"| {label} | {} | {} | speedup |\n|---|---|---|---|\n| `{item}` | **{:.1} ms** | {:.1} ms | **{:.1}×** |\n",
fast.name,
slow.name,
fv * 1000.0,
sv * 1000.0,
ratio
));
Ok(out)
}
fn system_token(name: &str) -> &str {
name.split('_').next().unwrap_or(name)
}
fn system_token_variant(name: &str, keep_st: bool) -> String {
if keep_st && crate::bench::is_single_thread(name) {
let base = system_token(crate::bench::strip_single_thread(name));
format!("{base}{}", crate::bench::SINGLE_THREAD_SUFFIX)
} else {
system_token(name).to_string()
}
}
fn row_is_ours(name: &str, ours: &[String]) -> bool {
if ours.is_empty() {
return false;
}
let tok = system_token(name).to_ascii_lowercase();
let fam = tok.split('-').next().unwrap_or(tok.as_str());
ours.iter().any(|o| {
let ol = o.to_ascii_lowercase();
let of = ol.split('-').next().unwrap_or(ol.as_str());
tok == ol || (!of.is_empty() && of == fam)
})
}
fn discover_systems_variant(run: &BenchRun, keep_st: bool) -> Vec<String> {
let mut seen: Vec<String> = Vec::new();
for r in &run.results {
let tok = system_token_variant(&r.name, keep_st);
if !tok.is_empty() && !seen.iter().any(|s| *s == tok) {
seen.push(tok);
}
}
seen
}
fn discover_systems(run: &BenchRun) -> Vec<String> {
discover_systems_variant(run, false)
}
fn most_frequent_system(run: &BenchRun) -> Option<String> {
let mut counts: Vec<(String, usize)> = Vec::new();
for r in &run.results {
let tok = system_token(&r.name);
if tok.is_empty() {
continue;
}
if let Some(e) = counts.iter_mut().find(|(t, _)| t == tok) {
e.1 += 1;
} else {
counts.push((tok.to_string(), 1));
}
}
let mut best: Option<(String, usize)> = None;
for (t, c) in counts {
if best.as_ref().is_none_or(|b| c > b.1) {
best = Some((t, c));
}
}
best.map(|(t, _)| t)
}
fn repo_self_name(ctx: &Ctx) -> String {
let cargo = ctx.repo_root.join("Cargo.toml");
if let Ok(txt) = std::fs::read_to_string(&cargo) {
if let Ok(val) = txt.parse::<toml::Value>() {
if let Some(n) =
val.get("package").and_then(|p| p.get("name")).and_then(|n| n.as_str())
{
return n.to_string();
}
}
}
ctx.repo_root
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_string()
}
fn resolve_token(want: &str, systems: &[String]) -> String {
let wl = want.to_ascii_lowercase();
systems
.iter()
.find(|t| {
let tl = t.to_ascii_lowercase();
tl == wl || tl.contains(&wl) || wl.contains(&tl)
})
.cloned()
.unwrap_or_else(|| want.to_string())
}
fn detect_ours(
ctx: &Ctx,
run: &BenchRun,
systems: &[String],
args: &HashMap<String, String>,
) -> String {
if let Some(s) = args.get("self") {
return resolve_token(s, systems);
}
let repo = repo_self_name(ctx).to_ascii_lowercase();
if !repo.is_empty() {
if let Some(t) = systems.iter().find(|t| {
let tl = t.to_ascii_lowercase();
tl == repo || tl.contains(&repo) || repo.contains(&tl)
}) {
return t.clone();
}
}
most_frequent_system(run).unwrap_or_else(|| systems[0].clone())
}
struct MashupStory {
token: String,
metric: String,
ratio: f64,
ours_val: f64,
rival_val: f64,
}
fn biggest_story(
ours: &crate::bench::BenchResult,
rivals: &[(String, &crate::bench::BenchResult)],
overrides: &HashMap<String, crate::bench::MetricDirection>,
only_metric: Option<&str>,
) -> Option<MashupStory> {
use crate::bench::{direction_of, MetricDirection};
let mut best: Option<MashupStory> = None;
for (k, v) in ours.metrics.iter() {
if let Some(m) = only_metric {
if k != m {
continue;
}
}
let Some(ov) = v.as_f64() else { continue };
if ov <= 0.0 {
continue;
}
let higher_better = match direction_of(overrides, k) {
MetricDirection::High => true,
MetricDirection::Low => false,
MetricDirection::Neutral => continue,
};
for (tok, rr) in rivals {
let Some(rv) = rr.metrics.get(k).and_then(|x| x.as_f64()) else { continue };
if rv <= 0.0 {
continue;
}
let ratio = if higher_better { ov / rv } else { rv / ov };
if ratio <= 1.0 {
continue; }
if best.as_ref().is_none_or(|b| ratio > b.ratio) {
best = Some(MashupStory {
token: tok.clone(),
metric: k.clone(),
ratio,
ours_val: ov,
rival_val: rv,
});
}
}
}
best
}
fn is_excluded(token: &str, exclude: Option<&str>) -> bool {
let tl = token.to_ascii_lowercase();
exclude.is_some_and(|pats| {
pats.split(',')
.map(str::trim)
.filter(|p| !p.is_empty())
.any(|p| tl.contains(&p.to_ascii_lowercase()))
})
}
fn render_mashup(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let Some(run) = ctx.run else {
return Ok("_(no bench history yet — run a benchmark to populate this section)_\n".to_string());
};
if run.results.is_empty() {
return Ok("_(no bench results)_\n".to_string());
}
let exclude = args.get("exclude").map(|s| s.as_str());
let keep_st = args
.get("variant")
.map(|v| {
let v = v.trim().to_ascii_lowercase();
!matches!(v.as_str(), "" | "false" | "none" | "off" | "0" | "no")
})
.unwrap_or(false);
let systems: Vec<String> = discover_systems_variant(run, keep_st)
.into_iter()
.filter(|t| !is_excluded(t, exclude))
.collect();
let repo = repo_self_name(ctx).to_ascii_lowercase();
let ours_is_row_system = args.contains_key("self")
|| systems.iter().any(|t| {
let tl = t.to_ascii_lowercase();
!repo.is_empty() && (tl == repo || tl.contains(&repo) || repo.contains(&tl))
});
if ours_is_row_system && systems.len() >= 2 {
let grouped = args.get("grouped").map(|v| v != "false").unwrap_or(false)
|| matches!(
args.get("pairing").map(|s| s.as_str()),
Some("grouped" | "group" | "pairs" | "format")
);
if grouped {
if let Some(s) = render_mashup_grouped(ctx, args, run, &systems)? {
return Ok(s);
}
}
return render_mashup_rowwise(ctx, args, run, &systems);
}
if let Some(s) = render_mashup_columnwise(ctx, args, run, exclude)? {
return Ok(s);
}
Ok(render_mashup_solo(run))
}
fn render_mashup_rowwise(
ctx: &Ctx,
args: &HashMap<String, String>,
run: &crate::bench::BenchRun,
systems: &[String],
) -> Result<String> {
let ours = detect_ours(ctx, run, systems, args);
let mut order: Vec<String> = Vec::new();
if systems.iter().any(|s| *s == ours) {
order.push(ours.clone());
}
for s in systems {
if !order.iter().any(|o| o == s) {
order.push(s.clone());
}
}
let mut bargs: HashMap<String, String> = HashMap::new();
bargs.insert("compare".into(), order.join(","));
bargs.insert("winner".into(), "row".into());
bargs.insert("self".into(), ours.clone());
if let Some(c) = args.get("cols") {
bargs.insert("cols".into(), c.clone());
} else if let Some(m) = args.get("metric") {
bargs.insert("cols".into(), m.clone()); }
if let Some(t) = args.get("top") {
bargs.insert("top".into(), t.clone());
}
if let Some(b) = args.get("best") {
bargs.insert("best".into(), b.clone());
}
let table = render_benches(ctx, &bargs)?;
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
let pick = |tok: &str| run.results.iter().find(|r| r.name.contains(tok));
let only_metric = args.get("metric").map(|s| s.as_str());
let mut headline = String::new();
if let Some(ours_row) = pick(&ours) {
let rivals: Vec<(String, &crate::bench::BenchResult)> = order
.iter()
.filter(|t| **t != ours)
.filter_map(|t| pick(t).map(|r| (t.clone(), r)))
.collect();
if let Some(s) = biggest_story(ours_row, &rivals, &overrides, only_metric) {
headline = format!(
"> **Biggest gap — `{}`: {} leads {} by {:.1}×** ({} vs {}).\n\n",
s.metric,
ours,
s.token,
s.ratio,
fmt_metric(s.ours_val),
fmt_metric(s.rival_val),
);
}
}
Ok(format!("{headline}{table}"))
}
fn common_run(a: &str, b: &str) -> usize {
let norm = |s: &str| -> Vec<u8> {
s.bytes().filter(|c| c.is_ascii_alphanumeric()).map(|c| c.to_ascii_lowercase()).collect()
};
let (a, b) = (norm(a), norm(b));
if a.is_empty() || b.is_empty() {
return 0;
}
let mut best = 0usize;
let mut prev = vec![0usize; b.len() + 1];
for i in 1..=a.len() {
let mut cur = vec![0usize; b.len() + 1];
for j in 1..=b.len() {
if a[i - 1] == b[j - 1] {
cur[j] = prev[j - 1] + 1;
best = best.max(cur[j]);
}
}
prev = cur;
}
best
}
fn render_mashup_grouped(
ctx: &Ctx,
args: &HashMap<String, String>,
run: &crate::bench::BenchRun,
systems: &[String],
) -> Result<Option<String>> {
let ours = detect_ours(ctx, run, systems, args);
let ours_family: Vec<String> = vec![ours.clone()];
let (mine, rivals): (Vec<&String>, Vec<&String>) =
systems.iter().partition(|s| row_is_ours(s, &ours_family));
if mine.is_empty() || rivals.is_empty() {
return Ok(None);
}
let mut groups: Vec<(String, Vec<String>)> = Vec::new(); for m in &mine {
let mut best: Option<(usize, &String)> = None;
for rv in &rivals {
let run_len = common_run(m, rv);
if best.as_ref().is_none_or(|(l, _)| run_len > *l) {
best = Some((run_len, rv));
}
}
if let Some((len, rv)) = best {
if len >= 2 {
match groups.iter_mut().find(|(h, _)| h.as_str() == rv.as_str()) {
Some((_, ours_here)) => ours_here.push((*m).clone()),
None => groups.push(((*rv).clone(), vec![(*m).clone()])),
}
}
}
}
if groups.is_empty() {
return Ok(None);
}
let mut out = String::new();
for (rival_tok, mine_toks) in &groups {
let header = rival_tok;
let mut ordered = mine_toks.clone();
ordered.sort_by(|a, b| b.len().cmp(&a.len()).then_with(|| a.cmp(b)));
let self_tok = ordered.first().cloned().unwrap_or_default();
let mut compare = ordered;
compare.push(rival_tok.clone());
let mut bargs: HashMap<String, String> = HashMap::new();
bargs.insert("compare".into(), compare.join(","));
bargs.insert("winner".into(), "row".into());
bargs.insert("self".into(), self_tok);
for k in ["cols", "metric", "drop", "best", "top"] {
if let Some(v) = args.get(k) {
let key = if k == "metric" && !args.contains_key("cols") { "cols" } else { k };
bargs.insert(key.into(), v.clone());
}
}
let table = render_benches(ctx, &bargs)?;
let table = if out.is_empty() { table } else { strip_bench_header(&table) };
out.push_str(&format!("**{header}**\n\n{table}\n"));
}
Ok(Some(out))
}
fn strip_bench_header(table: &str) -> String {
match table.find("\n| ") {
Some(idx) => table[idx + 1..].to_string(),
None => table.to_string(),
}
}
fn classify_column<'a>(
col: &'a str,
systems: &std::collections::BTreeSet<String>,
ours: &str,
) -> (String, &'a str) {
if let Some((head, tail)) = col.split_once('_') {
if systems.iter().any(|s| s == head) {
return (head.to_string(), tail);
}
}
(ours.to_string(), col)
}
fn render_mashup_columnwise(
ctx: &Ctx,
args: &HashMap<String, String>,
run: &crate::bench::BenchRun,
exclude: Option<&str>,
) -> Result<Option<String>> {
use crate::bench::{direction_of, MetricDirection};
use std::collections::{BTreeMap, BTreeSet};
let ours = repo_self_name(ctx).to_ascii_lowercase();
if ours.is_empty() {
return Ok(None);
}
let mut cols: Vec<String> = Vec::new();
for r in &run.results {
for (k, v) in &r.metrics {
if v.as_f64().is_some() && !cols.contains(k) {
cols.push(k.clone());
}
}
}
let mut head_tails: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
for c in &cols {
if let Some((head, tail)) = c.split_once('_') {
head_tails.entry(head).or_default().insert(tail);
}
}
let candidates: BTreeSet<&str> =
head_tails.iter().filter(|(_, t)| t.len() >= 2).map(|(h, _)| *h).collect();
let mut tail_cands: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
for h in &candidates {
for t in &head_tails[h] {
tail_cands.entry(*t).or_default().insert(*h);
}
}
let mut systems: BTreeSet<String> = BTreeSet::new();
for hs in tail_cands.values() {
if hs.len() >= 2 {
for h in hs {
systems.insert((*h).to_string());
}
}
}
systems.retain(|s| s.eq_ignore_ascii_case(&ours) || !is_excluded(s, exclude));
let has_rival = systems.iter().any(|s| !s.eq_ignore_ascii_case(&ours));
if !has_rival {
return Ok(None);
}
let mut suffix_systems: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
for c in &cols {
let (sys, suf) = classify_column(c, &systems, &ours);
suffix_systems.entry(suf.to_string()).or_default().insert(sys);
}
let rival_suffixes: BTreeSet<String> = suffix_systems
.iter()
.filter(|(_, sys)| sys.len() >= 2 && sys.iter().any(|s| s.eq_ignore_ascii_case(&ours)))
.map(|(s, _)| s.clone())
.collect();
if rival_suffixes.is_empty() {
return Ok(None);
}
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
let mut ordered: Vec<String> = Vec::new();
if systems.iter().any(|s| s.eq_ignore_ascii_case(&ours)) {
ordered.push(ours.clone());
}
for s in &systems {
if !s.eq_ignore_ascii_case(&ours) {
ordered.push(s.clone());
}
}
let mut sufs: Vec<String> = rival_suffixes.iter().cloned().collect();
sufs.sort_by(|a, b| col_rank(a).cmp(&col_rank(b)).then_with(|| a.cmp(b)));
let mut sys_rows: Vec<crate::bench::BenchResult> = Vec::new();
for sys in &ordered {
let mut m = serde_json::Map::new();
for suf in &sufs {
let dir = direction_of(&overrides, suf);
let mut best: Option<f64> = None;
for r in &run.results {
let contested_here = r.metrics.iter().any(|(k, v)| {
v.as_f64().is_some() && {
let (cs, cf) = classify_column(k, &systems, &ours);
cf == suf.as_str() && !cs.eq_ignore_ascii_case(&ours)
}
});
if !contested_here {
continue;
}
for (k, v) in &r.metrics {
if let Some(f) = v.as_f64() {
let (cs, cf) = classify_column(k, &systems, &ours);
if cf == suf.as_str() && cs.eq_ignore_ascii_case(sys) {
best = Some(match dir {
MetricDirection::Low => best.map_or(f, |b| b.min(f)),
_ => best.map_or(f, |b| b.max(f)),
});
}
}
}
}
if let Some(b) = best {
m.insert(suf.clone(), serde_json::Value::from(b));
}
}
if !m.is_empty() {
sys_rows.push(crate::bench::BenchResult { name: sys.clone(), metrics: m , ..Default::default() });
}
}
if sys_rows.len() < 2 || !sys_rows.iter().any(|r| r.name.eq_ignore_ascii_case(&ours)) {
return Ok(None);
}
let synth = crate::bench::BenchRun {
date: run.date.clone(),
timestamp: run.timestamp.clone(),
version: run.version.clone(),
machine: run.machine.clone(),
cores: run.cores,
results: sys_rows.clone(),
tests: vec![],
};
let sctx = Ctx { run: Some(&synth), ..*ctx };
let mut bargs: HashMap<String, String> = HashMap::new();
bargs.insert("winner".into(), "row".into());
bargs.insert("cols".into(), sufs.join(","));
let table = render_benches(&sctx, &bargs)?;
let ours_row = sys_rows.iter().find(|r| r.name.eq_ignore_ascii_case(&ours));
let mut best: Option<(String, String, f64, f64, f64)> = None; if let Some(orow) = ours_row {
for suf in &sufs {
let Some(ov) = orow.metrics.get(suf).and_then(|v| v.as_f64()) else { continue };
if ov <= 0.0 {
continue;
}
let higher = match direction_of(&overrides, suf) {
MetricDirection::High => true,
MetricDirection::Low => false,
MetricDirection::Neutral => continue,
};
for rr in &sys_rows {
if rr.name.eq_ignore_ascii_case(&ours) {
continue;
}
let Some(rv) = rr.metrics.get(suf).and_then(|v| v.as_f64()) else { continue };
if rv <= 0.0 {
continue;
}
let ratio = if higher { ov / rv } else { rv / ov };
if ratio <= 1.0 {
continue;
}
if best.as_ref().is_none_or(|b| ratio > b.2) {
best = Some((suf.clone(), rr.name.clone(), ratio, ov, rv));
}
}
}
}
let headline = match best {
Some((suf, rsys, ratio, ov, rv)) => format!(
"> **Biggest gap — `{}`: {} leads {} by {:.1}×** ({} vs {}).\n\n",
suf,
ours,
rsys,
ratio,
fmt_metric(ov),
fmt_metric(rv),
),
None => String::new(),
};
Ok(Some(format!("{headline}{table}")))
}
fn render_mashup_solo(run: &crate::bench::BenchRun) -> String {
let Some(row) = run
.results
.iter()
.max_by_key(|r| r.metrics.values().filter(|v| v.as_f64().is_some()).count())
else {
return "_(no bench results)_\n".to_string();
};
let mut keys: Vec<&String> =
row.metrics.keys().filter(|k| row.metrics[*k].as_f64().is_some()).collect();
keys.sort_by(|a, b| col_rank(a).cmp(&col_rank(b)).then_with(|| a.cmp(b)));
let bits: Vec<String> = keys
.iter()
.take(3)
.filter_map(|k| row.metrics[*k].as_f64().map(|f| format!("**{}** {}", fmt_metric(f), k)))
.collect();
let header = bench_header(run);
if bits.is_empty() {
return format!("{header}_(single-system bench — no rival persisted yet.)_\n");
}
format!(
"{header}`{}` — {} _(single-system bench; competitive matrix in [`README-full`](.nornir/README-full.md))_\n",
row.name,
bits.join(" · "),
)
}
fn render_bench_chart(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
use crate::bench::{direction_of, MetricDirection};
let Some(run) = ctx.run else {
return Ok("_(no bench history yet — run a benchmark to populate this section)_\n".to_string());
};
let metric = args
.get("metric")
.map(|s| s.as_str())
.ok_or_else(|| anyhow!("bench_chart requires a `metric=` arg"))?;
let filter = args.get("name").map(|s| s.as_str());
let exclude = args.get("exclude").map(|s| s.as_str());
let mut points: Vec<(String, f64)> = run
.results
.iter()
.filter(|r| filter.is_none_or(|p| r.name.contains(p)))
.filter(|r| exclude.is_none_or(|p| !r.name.contains(p)))
.filter_map(|r| r.metrics.get(metric).and_then(|v| v.as_f64()).map(|f| (r.name.clone(), f)))
.collect();
if points.is_empty() {
return Ok(format!("_(no bench results carry metric `{metric}`)_\n"));
}
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
let dir = match args.get("dir").map(|s| s.to_ascii_lowercase()) {
Some(d) if d == "high" || d == "max" => MetricDirection::High,
Some(d) if d == "low" || d == "min" => MetricDirection::Low,
_ => direction_of(&overrides, metric),
};
match dir {
MetricDirection::High => points.sort_by(|a, b| b.1.total_cmp(&a.1)),
MetricDirection::Low => points.sort_by(|a, b| a.1.total_cmp(&b.1)),
MetricDirection::Neutral => {}
}
let best_val = match dir {
MetricDirection::High => points.iter().map(|p| p.1).fold(f64::MIN, f64::max),
MetricDirection::Low => points.iter().map(|p| p.1).fold(f64::MAX, f64::min),
MetricDirection::Neutral => f64::NAN,
};
let title = args
.get("title")
.cloned()
.unwrap_or_else(|| format!("{metric} by workload"));
let unit = friendly_unit(metric);
let max_v = points.iter().map(|p| p.1).fold(f64::MIN, f64::max);
let min_pos = points.iter().map(|p| p.1).filter(|v| *v > 0.0).fold(f64::MAX, f64::min);
let log = match args.get("log").map(|s| s.as_str()) {
Some("true") => true,
Some("false") => false,
_ => min_pos.is_finite() && max_v > 0.0 && max_v / min_pos > 100.0,
};
#[cfg(feature = "docs-export")]
{
let bars: Vec<crate::docs::svg::Bar> = points
.iter()
.map(|(label, v)| crate::docs::svg::Bar {
label: label.clone(),
value: *v,
best: dir != MetricDirection::Neutral && *v == best_val,
})
.collect();
let chart = crate::docs::svg::BarChart { title: title.clone(), unit: unit.clone(), bars, log };
let base = args
.get("file")
.cloned()
.unwrap_or_else(|| format!("bench_{}", sanitize_asset(metric)));
let rel = std::path::PathBuf::from(format!(".nornir/assets/{base}.svg"));
let abs = ctx.repo_root.join(&rel);
if !ctx.no_assets {
crate::docs::svg::render_bars_to_file(&chart, &abs)?;
}
let alt = title.replace(['[', ']'], "");
return Ok(format!("\n", alt, rel.display()));
}
#[cfg(not(feature = "docs-export"))]
{
let _ = (log, best_val);
let mut out = format!("**{title}**\n\n");
let max = points.iter().map(|p| p.1).fold(f64::MIN, f64::max).max(f64::MIN_POSITIVE);
let scale = column_format(&points.iter().map(|p| p.1).collect::<Vec<_>>());
for (label, v) in &points {
let filled = ((v / max).clamp(0.0, 1.0) * 24.0).round() as usize;
let bar: String = "█".repeat(filled);
let star = if dir != MetricDirection::Neutral && *v == best_val { " ★" } else { "" };
let unit_sfx = if unit.is_empty() { String::new() } else { format!(" {unit}") };
out.push_str(&format!("- `{label}` {bar} {}{}{star}\n", fmt_metric_scaled(*v, scale), unit_sfx));
}
out.push('\n');
Ok(out)
}
}
fn friendly_unit(metric: &str) -> String {
let m = metric.to_ascii_lowercase();
if m.contains("ops_sec") || m.contains("ops_per_sec") || m.contains("commits_per_sec") {
"ops/sec".into()
} else if m.contains("rows_per_sec") || m.ends_with("_per_sec") {
"/sec".into()
} else if m.ends_with("_us") {
"µs".into()
} else if m.ends_with("_ms") {
"ms".into()
} else if m.ends_with("_ns") {
"ns".into()
} else if m.ends_with("_mbs") || m.contains("mbps") || m.contains("mb_per_sec") {
"MB/s".into()
} else if m.ends_with("_secs") || m == "seconds" {
"s".into()
} else {
String::new()
}
}
#[cfg_attr(not(feature = "docs-export"), allow(dead_code))]
fn sanitize_asset(s: &str) -> String {
s.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect()
}
fn render_tests(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let include_benches = args.get("include_benches").map(|v| v == "true").unwrap_or(false);
let inv = crate::docs::test_inventory::scan_opts(
ctx.repo_root,
args.get("crate").map(|s| s.as_str()),
include_benches,
);
let only = args.get("kind").map(|s| s.as_str());
let mut out = String::new();
let want = |k: &str| only.is_none_or(|o| o == k);
if want("unit") {
out.push_str(&format!("**Unit tests** ({})\n\n", inv.unit_total()));
out.push_str(&file_tests_table(&inv.unit));
out.push('\n');
}
if want("integration") {
out.push_str(&format!("**Integration tests** ({})\n\n", inv.integration_total()));
out.push_str(&file_tests_table(&inv.integration));
out.push('\n');
}
if want("doc") {
out.push_str(&format!("**Doc tests** ({})\n\n", inv.doc_total()));
if inv.doc.is_empty() {
out.push_str("_(none)_\n");
} else {
out.push_str("| file | doc-tests |\n|---|--:|\n");
for f in &inv.doc {
out.push_str(&format!("| `{}` | {} |\n", md_escape(&f.file), f.count));
}
}
}
Ok(out.trim_end().to_string() + "\n")
}
fn render_coverage(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let rows = ctx.coverage;
if rows.is_empty() {
return Ok("_(no coverage recorded — run `nornir coverage <repo>`)_\n".to_string());
}
let mut out = String::new();
if let Some(o) = rows.iter().find(|r| r.scope == "overall") {
out.push_str(&format!(
"**Overall coverage:** {:.1}% lines · {:.1}% regions\n\n",
o.line_pct(),
o.region_pct()
));
}
out.push_str("| crate | line % | region % |\n|---|--:|--:|\n");
for c in rows.iter().filter(|r| r.scope.starts_with("crate:")) {
out.push_str(&format!(
"| `{}` | {:.1}% | {:.1}% |\n",
md_escape(&c.krate),
c.line_pct(),
c.region_pct()
));
}
if let Some(n) = args.get("worst").and_then(|v| v.parse::<usize>().ok()) {
let mut files: Vec<_> =
rows.iter().filter(|r| r.scope.starts_with("file:") && r.lines > 0).collect();
files.sort_by(|a, b| a.line_pct().total_cmp(&b.line_pct()));
out.push_str("\n**Worst-covered files**\n\n| file | line % |\n|---|--:|\n");
for f in files.iter().take(n) {
out.push_str(&format!("| `{}` | {:.1}% |\n", md_escape(&f.file), f.line_pct()));
}
}
Ok(out.trim_end().to_string() + "\n")
}
fn capability_glyph(code: f64) -> &'static str {
const EPS: f64 = 1e-9;
if (code - 1.0).abs() < EPS {
"✅"
} else if (code - 0.5).abs() < EPS {
"◐"
} else if code.abs() < EPS {
"✗"
} else if (code + 1.0).abs() < EPS {
"NA"
} else {
"—"
}
}
fn render_capabilities(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let source = args
.get("source")
.ok_or_else(|| anyhow!("`capabilities` needs a `source=<bench-name>` arg"))?;
let Some(run) = ctx.run else {
return Ok(format!(
"_(no capability matrix yet — run the `{source}` static-capabilities bench)_\n"
));
};
let Some(res) = run.results.iter().find(|r| r.name == *source && r.is_static()) else {
return Ok(format!(
"_(no `{source}` static-capabilities row in the latest run)_\n"
));
};
let ours = args
.get("ours")
.cloned()
.unwrap_or_else(|| source.strip_suffix(".capabilities").unwrap_or(source).to_string());
let mut cells: HashMap<(String, String), f64> = HashMap::new();
let mut slugs: Vec<String> = Vec::new();
let mut rivals: Vec<String> = Vec::new();
for (k, v) in &res.metrics {
let Some(rest) = k.strip_prefix("cap.") else { continue };
let Some((slug, rival)) = rest.rsplit_once('.') else { continue };
let Some(code) = v.as_f64() else { continue };
if !slugs.iter().any(|s| s == slug) {
slugs.push(slug.to_string());
}
if !rivals.iter().any(|r| r == rival) {
rivals.push(rival.to_string());
}
cells.insert((slug.to_string(), rival.to_string()), code);
}
if cells.is_empty() {
return Ok(format!(
"_(the `{source}` row carries no `cap.<slug>.<rival>` capability cells)_\n"
));
}
slugs.sort();
rivals.sort();
let mut cols: Vec<String> = Vec::new();
if rivals.iter().any(|r| *r == ours) {
cols.push(ours.clone());
}
for r in &rivals {
if *r != ours {
cols.push(r.clone());
}
}
let is_ours = |c: &str| c == ours;
let mut out = String::from("| capability |");
for c in &cols {
if is_ours(c) {
out.push_str(&format!(" **{}** |", md_escape(c)));
} else {
out.push_str(&format!(" {} |", md_escape(c)));
}
}
out.push_str("\n|---|");
for _ in &cols {
out.push_str(":--:|");
}
out.push('\n');
for slug in &slugs {
out.push_str(&format!("| {} |", md_escape(slug)));
for c in &cols {
let glyph = cells
.get(&(slug.clone(), c.clone()))
.map(|code| capability_glyph(*code))
.unwrap_or("—");
if is_ours(c) {
out.push_str(&format!(" **{glyph}** |"));
} else {
out.push_str(&format!(" {glyph} |"));
}
}
out.push('\n');
}
Ok(out)
}
fn file_tests_table(files: &[crate::docs::test_inventory::FileTests]) -> String {
if files.is_empty() {
return "_(none)_\n".to_string();
}
let mut out = String::from("| file | tests |\n|---|---|\n");
for f in files {
let names = f
.tests
.iter()
.map(|t| format!("`{}`", md_escape(t)))
.collect::<Vec<_>>()
.join(", ");
out.push_str(&format!("| `{}` | {} |\n", md_escape(&f.file), names));
}
out
}
fn parse_best_overrides(arg: Option<&str>) -> HashMap<String, crate::bench::MetricDirection> {
use crate::bench::MetricDirection;
let mut out = HashMap::new();
let Some(s) = arg else { return out };
for pair in s.split(',') {
if let Some((metric, dir)) = pair.split_once(':') {
let d = match dir.trim().to_ascii_lowercase().as_str() {
"high" | "max" | "up" => MetricDirection::High,
"low" | "min" | "down" => MetricDirection::Low,
"neutral" | "none" => MetricDirection::Neutral,
_ => continue,
};
out.insert(metric.trim().to_string(), d);
}
}
out
}
fn fmt_metric(f: f64) -> String {
let a = f.abs();
if a >= 1e6 {
let (scaled, suffix) = if a >= 1e12 {
(f / 1e12, "T")
} else if a >= 1e9 {
(f / 1e9, "B")
} else {
(f / 1e6, "M")
};
let s = format!("{scaled:.2}");
let s = s.trim_end_matches('0').trim_end_matches('.');
return format!("{s}{suffix}");
}
if a >= 1e3 {
return group_thousands(&format!("{}", f.round() as i64));
}
let plain = if f.fract() == 0.0 {
format!("{}", f as i64)
} else {
format!("{f:.2}")
};
group_thousands(&plain)
}
pub(crate) fn metric_scale(max_abs: f64) -> Option<(f64, &'static str)> {
if max_abs >= 1e12 {
Some((1e12, "T"))
} else if max_abs >= 1e9 {
Some((1e9, "B"))
} else if max_abs >= 1e6 {
Some((1e6, "M"))
} else {
None
}
}
pub(crate) fn column_format(vals: &[f64]) -> Option<(f64, &'static str, usize)> {
let max_abs = vals.iter().copied().map(f64::abs).fold(0.0_f64, f64::max);
let (div, suffix) = metric_scale(max_abs)?;
let min_scaled = vals
.iter()
.copied()
.map(f64::abs)
.filter(|v| *v > 0.0)
.map(|v| v / div)
.fold(f64::INFINITY, f64::min);
let decimals = if min_scaled.is_finite() && min_scaled > 0.0 {
((2 - min_scaled.log10().floor() as i32).max(2) as usize).min(6)
} else {
2
};
Some((div, suffix, decimals))
}
pub(crate) fn fmt_metric_scaled(f: f64, fmt: Option<(f64, &'static str, usize)>) -> String {
match fmt {
Some((div, suffix, decimals)) => format!("{:.*}{suffix}", decimals, f / div),
None => fmt_metric(f),
}
}
fn group_thousands(s: &str) -> String {
s.to_string()
}
fn hard_broken_trophies(
ctx: &Ctx,
results: &[&crate::bench::BenchResult],
ours_tokens: &[String],
) -> std::collections::HashSet<String> {
let mut trophies = std::collections::HashSet::new();
let Ok(text) = std::fs::read_to_string(ctx.repo_root.join(".nornir/records.toml")) else {
return trophies;
};
let Ok(tbl) = text.parse::<toml::Table>() else { return trophies };
let is_ours = |name: &str| ours_tokens.iter().any(|t| token_matches(name, t));
for (rival, spec) in &tbl {
let Some(rv) = results.iter().find(|r| r.name.as_str() == rival.as_str()) else { continue };
let metric = spec.get("metric").and_then(|v| v.as_str()).unwrap_or("decompress_mbs");
let Some(rival_val) = rv.metrics.get(metric).and_then(|v| v.as_f64()) else { continue };
let best = results
.iter()
.filter(|r| is_ours(&r.name))
.filter_map(|r| r.metrics.get(metric).and_then(|v| v.as_f64()).map(|f| (f, r.name.clone())))
.fold(None::<(f64, String)>, |acc, (f, n)| match acc {
Some((bf, _)) if bf >= f => acc,
_ => Some((f, n)),
});
if let Some((of, on)) = best {
if of > rival_val {
trophies.insert(on);
}
}
}
trophies
}
fn render_depgraph(ctx: &Ctx) -> Result<String> {
let g = depgraph::extract_with_path_deps(ctx.repo_root, ctx.workspace_root, &|repo, root| {
crate::security::clone_missing_path_dep_siblings(repo, root, &|_| None).unwrap_or(0)
})?;
if g.nodes.is_empty() {
return Ok(format!(
"_no dependency-graph data for `{}` — `cargo metadata` returned no \
packages. Ensure this is a Cargo workspace and re-run \
`nornir docs render`._\n",
ctx.repo_root.display()
));
}
#[cfg(feature = "docs-export")]
{
let rel = std::path::PathBuf::from(".nornir/assets/depgraph.svg");
let abs = ctx.repo_root.join(&rel);
if !ctx.no_assets {
crate::docs::svg::render_to_file(&g, &abs)?;
}
let link = doc_relative_asset(ctx, &abs);
return Ok(format!("\n"));
}
#[cfg(not(feature = "docs-export"))]
{
let mut out = g.to_svg();
if !out.ends_with('\n') {
out.push('\n');
}
Ok(out)
}
}
fn doc_relative_asset(ctx: &Ctx, asset_abs: &Path) -> String {
use std::path::Component;
let repo_rel = asset_abs
.strip_prefix(ctx.repo_root)
.map(|p| p.to_path_buf())
.unwrap_or_else(|_| asset_abs.to_path_buf());
let Some(doc_dir) = ctx.doc_dir else {
return repo_rel.display().to_string();
};
let Ok(doc_rel) = doc_dir.strip_prefix(ctx.repo_root) else {
return repo_rel.display().to_string();
};
let from: Vec<&std::ffi::OsStr> = doc_rel
.components()
.filter_map(|c| match c {
Component::Normal(s) => Some(s),
_ => None,
})
.collect();
let to: Vec<&std::ffi::OsStr> = repo_rel
.components()
.filter_map(|c| match c {
Component::Normal(s) => Some(s),
_ => None,
})
.collect();
let common = from.iter().zip(to.iter()).take_while(|(a, b)| a == b).count();
let mut out = PathBuf::new();
for _ in common..from.len() {
out.push("..");
}
for s in &to[common..] {
out.push(s);
}
if out.as_os_str().is_empty() {
".".to_string()
} else {
out.display().to_string()
}
}
fn resolve_binary(ctx: &Ctx, raw: &str) -> PathBuf {
let p = PathBuf::from(raw);
if p.is_absolute() {
p
} else if ctx.repo_root.join(&p).exists() {
ctx.repo_root.join(&p)
} else {
ctx.workspace_root.join(&p)
}
}
fn render_symbols(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let bin_arg = args
.get("binary")
.ok_or_else(|| anyhow!("section `symbols` needs binary=PATH"))?;
let limit: usize = args
.get("limit")
.and_then(|v| v.parse().ok())
.unwrap_or(20);
let binary = resolve_binary(ctx, bin_arg);
let syms = artifact::extract_symbols(&binary, ctx.workspace_root)?;
let mut visible: Vec<&artifact::Symbol> = syms
.iter()
.filter(|s| !s.name_demangled.contains("::{{closure}}"))
.collect();
visible.sort_by(|a, b| b.size_bytes.unwrap_or(0).cmp(&a.size_bytes.unwrap_or(0)));
visible.truncate(limit);
let mut out = String::new();
out.push_str(&format!(
"_top {} symbols by size from `{}`_\n\n",
visible.len(),
bin_arg
));
out.push_str("| symbol | size | file |\n|--------|------|------|\n");
for s in &visible {
let file = if s.file.is_empty() { "?" } else { s.file.as_str() };
let size = s
.size_bytes
.map(|n| n.to_string())
.unwrap_or_else(|| "-".into());
out.push_str(&format!(
"| `{}` | {} | {} |\n",
md_escape(&s.name_demangled),
size,
file
));
}
Ok(out)
}
fn render_callgraph(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let bin_arg = args
.get("binary")
.ok_or_else(|| anyhow!("section `callgraph` needs binary=PATH"))?;
let limit: usize = args
.get("limit")
.and_then(|v| v.parse().ok())
.unwrap_or(15);
let binary = resolve_binary(ctx, bin_arg);
let edges = callgraph_dwarf::extract_callgraph(&binary, ctx.workspace_root)?;
let mut count: HashMap<String, usize> = HashMap::new();
for e in &edges {
*count.entry(e.callee.clone()).or_default() += 1;
}
let mut ranked: Vec<(String, usize)> = count.into_iter().collect();
ranked.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
ranked.truncate(limit);
let mut out = String::new();
out.push_str(&format!(
"_{} inline edges in `{}`; top {} callees:_\n\n",
edges.len(),
bin_arg,
ranked.len()
));
out.push_str("| callee | inline sites |\n|--------|-------------:|\n");
for (n, c) in ranked {
out.push_str(&format!("| `{}` | {} |\n", md_escape(&n), c));
}
Ok(out)
}
fn md_escape(s: &str) -> String {
s.replace('|', "\\|").replace('`', "\\`")
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{Map, Value};
fn ctx<'a>(root: &'a Path, run: Option<&'a BenchRun>) -> Ctx<'a> {
Ctx::new(root, root, run)
}
#[test]
fn doc_relative_asset_is_relative_to_doc_dir() {
let root = Path::new("/repo");
let asset = Path::new("/repo/.nornir/assets/depgraph.svg");
let c_root = Ctx::new(root, root, None).with_doc_dir(root);
assert_eq!(
doc_relative_asset(&c_root, asset),
".nornir/assets/depgraph.svg"
);
let nornir_dir = root.join(".nornir");
let c_nornir = Ctx::new(root, root, None).with_doc_dir(&nornir_dir);
assert_eq!(
doc_relative_asset(&c_nornir, asset),
"assets/depgraph.svg"
);
let c_none = Ctx::new(root, root, None);
assert_eq!(
doc_relative_asset(&c_none, asset),
".nornir/assets/depgraph.svg"
);
}
#[cfg(feature = "docs-export")]
#[test]
fn markdown_only_suppresses_svg_asset_write() {
let t = tempfile::TempDir::new().unwrap();
let root = t.path();
std::fs::write(
root.join("Cargo.toml"),
"[package]\nname = \"widget_lib\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n\
[dependencies]\nserde = \"1\"\n",
)
.unwrap();
std::fs::create_dir_all(root.join("src")).unwrap();
std::fs::write(root.join("src/lib.rs"), "pub fn noop() {}\n").unwrap();
let svg = root.join(".nornir/assets/depgraph.svg");
let c = Ctx::new(root, root, None).markdown_only();
let body = render_depgraph(&c).expect("render depgraph");
assert!(
body.contains("![workspace dependency graph]"),
"markdown-only depgraph must still emit the image link; got {body}"
);
assert!(
!svg.exists(),
"markdown-only render must NOT (re)write the depgraph SVG asset"
);
let c_full = Ctx::new(root, root, None);
render_depgraph(&c_full).expect("render depgraph full");
assert!(svg.exists(), "full render must write the depgraph SVG asset");
}
fn run_fixture() -> BenchRun {
let mut m: Map<String, Value> = Map::new();
m.insert("ops".to_string(), Value::from(123.45f64));
BenchRun {
date: "2026-05-30".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 8,
results: vec![crate::bench::BenchResult {
name: "decode".into(),
metrics: m,
..Default::default()
}],
tests: vec![],
}
}
fn arm(name: &str, val: f64) -> crate::bench::BenchResult {
let mut m: Map<String, Value> = Map::new();
m.insert("decompress_mbs".into(), Value::from(val));
crate::bench::BenchResult { name: name.into(), metrics: m, ..Default::default() }
}
fn multi_arm_run(arms: &[(&str, f64)]) -> BenchRun {
BenchRun {
date: "2026-07-13".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 12,
results: arms.iter().map(|(n, v)| arm(n, *v)).collect(),
tests: vec![],
}
}
#[test]
fn token_matches_is_word_bounded_not_substring() {
assert!(!token_matches("lbzip2", "bzip2"), "bzip2 ⊄ lbzip2 (l-adjacent)");
assert!(!token_matches("zoomies-lbzip2", "bzip2"), "bzip2 ⊄ zoomies-lbzip2");
assert!(token_matches("bzip2", "bzip2"));
assert!(token_matches("zoomies-jar", "zoomies-jar"));
assert!(token_matches("zoomies-lbzip2", "lbzip2"), "`-`-delimited segment matches");
assert!(token_matches("data_pipe_skade_s3", "skade_s3"));
assert!(token_matches("oxr_skade", "skade"));
assert!(!token_matches("data_pipe_skade_nvme", "skade_s3"));
assert!(!token_matches("lbzip2", "zoomies-lbzip2"));
assert!(!token_matches("bzip2", "zoomies-lbzip2"));
}
#[test]
fn compare_selects_exact_arm_no_rival_contamination() {
let run = multi_arm_run(&[
("bzip2", 100.0),
("lbzip2", 700.0),
("zoomies-lbzip2", 755.0),
("jar", 200.0),
("zoomies-jar", 900.0),
("zip", 300.0),
("zoomies-zip", 800.0),
]);
let root = Path::new("/repo");
let c = ctx(root, Some(&run));
let mut args = HashMap::new();
args.insert("compare".to_string(), "bzip2".to_string());
args.insert("cols".to_string(), "decompress_mbs".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("bzip2"), "bzip2 row present:\n{out}");
assert!(out.contains("100"), "bzip2's OWN number shown:\n{out}");
assert!(!out.contains("755"), "must NOT show zoomies-lbzip2's number:\n{out}");
assert!(!out.contains("700"), "must NOT show lbzip2's number:\n{out}");
assert!(!out.contains("zoomies-lbzip2"), "rival row excluded:\n{out}");
let mut a2 = HashMap::new();
a2.insert("compare".to_string(), "zoomies-lbzip2".to_string());
a2.insert("self".to_string(), "zoomies-lbzip2".to_string());
a2.insert("cols".to_string(), "decompress_mbs".to_string());
let out2 = render_benches(&c, &a2).unwrap();
assert!(out2.contains("755"), "ours row shows its own number:\n{out2}");
assert!(!out2.contains("100"), "bzip2's number must not leak in:\n{out2}");
}
#[test]
fn parses_and_rewrites_bench_section() {
let r = run_fixture();
let d = tempfile::tempdir().unwrap();
let p = d.path().join("README.md");
std::fs::write(
&p,
"# x\n\n<!-- nornir:gen:start:bench -->\nstale\n<!-- nornir:gen:end:bench -->\n\ntail\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let rep = assemble_file(&p, &c).unwrap();
assert!(rep.changed);
assert_eq!(rep.sections, vec!["bench".to_string()]);
let txt = std::fs::read_to_string(&p).unwrap();
assert!(txt.contains("v0.1.0"));
assert!(txt.contains("decode"));
assert!(txt.ends_with("tail\n"));
}
#[test]
fn bench_history_renders_rows_newest_first() {
let mk = |date: &str, ver: &str, ops: f64| -> BenchRun {
let mut m: Map<String, Value> = Map::new();
m.insert("ops".into(), Value::from(ops));
BenchRun {
date: date.into(),
timestamp: None,
version: ver.into(),
machine: "tr".into(),
cores: 8,
results: vec![crate::bench::BenchResult { name: "x".into(), metrics: m , ..Default::default() }],
tests: vec![],
}
};
let history = vec![mk("2026-05-01", "0.1.0", 100.0), mk("2026-06-01", "0.2.0", 200.0)];
let root = Path::new(".");
let c = Ctx::new(root, root, None).with_history(&history);
let text =
"<!-- nornir:gen:start:bench_history -->\n<!-- nornir:gen:end:bench_history -->\n";
let (out, sections) = rewrite_str(text, &c).unwrap();
assert_eq!(sections, vec!["bench_history".to_string()]);
assert!(out.contains("| Version | Date | Machine |"), "header: {out}");
let i2 = out.find("v0.2.0").expect("v0.2.0 present");
let i1 = out.find("v0.1.0").expect("v0.1.0 present");
assert!(i2 < i1, "newest (0.2.0) must come first:\n{out}");
assert!(out.contains("200.00") && out.contains("100.00"), "best-metric values: {out}");
}
#[test]
fn bench_history_empty_is_graceful() {
let root = Path::new(".");
let c = Ctx::new(root, root, None); let text =
"<!-- nornir:gen:start:bench_history -->\n<!-- nornir:gen:end:bench_history -->\n";
let (out, _) = rewrite_str(text, &c).unwrap();
assert!(out.contains("no bench history"), "graceful empty: {out}");
}
#[test]
fn coverage_marker_fills_overall_and_per_crate() {
use crate::warehouse::coverage::CoverageRow;
let r = |scope: &str, krate: &str, file: &str, l: i64, lc: i64| CoverageRow {
run_id: "run".into(), ts_micros: 1, repo: "nornir".into(),
scope: scope.into(), krate: krate.into(), file: file.into(),
lines: l, lines_covered: lc, regions: l, regions_covered: lc,
};
let rows = vec![
r("overall", "", "", 100, 80),
r("crate:nornir", "nornir", "", 60, 42),
r("crate:nornir-testmatrix", "nornir-testmatrix", "", 40, 38),
r("file:src/viz/test_tab.rs", "nornir", "src/viz/test_tab.rs", 50, 20),
];
let root = Path::new(".");
let c = Ctx::new(root, root, None).with_coverage(&rows);
let text = "<!-- nornir:gen:start:coverage worst=1 -->\n<!-- nornir:gen:end:coverage -->\n";
let (out, sections) = rewrite_str(text, &c).unwrap();
assert_eq!(sections, vec!["coverage".to_string()]);
assert!(out.contains("80.0% lines"), "overall line %: {out}");
assert!(out.contains("`nornir`") && out.contains("70.0%"), "nornir crate row: {out}");
assert!(out.contains("`nornir-testmatrix`") && out.contains("95.0%"), "testmatrix row: {out}");
assert!(out.contains("Worst-covered files"), "worst table: {out}");
assert!(out.contains("src/viz/test_tab.rs") && out.contains("40.0%"), "worst file: {out}");
}
#[test]
fn coverage_marker_empty_is_graceful() {
let root = Path::new(".");
let c = Ctx::new(root, root, None); let text = "<!-- nornir:gen:start:coverage -->\n<!-- nornir:gen:end:coverage -->\n";
let (out, _) = rewrite_str(text, &c).unwrap();
assert!(out.contains("no coverage recorded"), "graceful empty: {out}");
}
#[test]
fn capabilities_marker_renders_glyph_matrix() {
let mut m: Map<String, Value> = Map::new();
m.insert("cap.speed.mytool".into(), Value::from(1.0)); m.insert("cap.speed.rival".into(), Value::from(0.5)); m.insert("cap.safety.mytool".into(), Value::from(1.0)); m.insert("cap.safety.rival".into(), Value::from(0.0)); m.insert("cap.portable.mytool".into(), Value::from(-1.0)); m.insert("cap.portable.rival".into(), Value::from(-2.0)); m.insert("capability_rows".into(), Value::from(3.0)); let run = BenchRun {
date: "2026-07-08".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "t".into(),
cores: 4,
results: vec![crate::bench::BenchResult::static_capabilities(
"mytool.capabilities",
m,
)],
tests: vec![],
};
let root = Path::new(".");
let c = ctx(root, Some(&run));
let text = "<!-- nornir:gen:start:capabilities source=mytool.capabilities -->\n<!-- nornir:gen:end:capabilities -->\n";
let (out, sections) = rewrite_str(text, &c).unwrap();
assert_eq!(sections, vec!["capabilities".to_string()]);
assert!(out.contains("| capability | **mytool** | rival |"), "header: {out}");
assert!(out.contains("|---|:--:|:--:|"), "separator: {out}");
assert!(out.contains("| speed | **✅** | ◐ |"), "speed row: {out}");
assert!(out.contains("| safety | **✅** | ✗ |"), "safety row: {out}");
assert!(out.contains("| portable | **NA** | — |"), "portable row: {out}");
assert!(!out.contains("capability_rows"), "count metric leaked: {out}");
}
#[test]
fn capabilities_ours_override_and_graceful() {
let mut m: Map<String, Value> = Map::new();
m.insert("cap.speed.foo".into(), Value::from(1.0));
m.insert("cap.speed.bar".into(), Value::from(0.0));
let run = BenchRun {
date: "2026-07-08".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "t".into(),
cores: 4,
results: vec![crate::bench::BenchResult::static_capabilities("cap.row", m)],
tests: vec![],
};
let root = Path::new(".");
let c = ctx(root, Some(&run));
let text = "<!-- nornir:gen:start:capabilities source=cap.row ours=bar -->\n<!-- nornir:gen:end:capabilities -->\n";
let (out, _) = rewrite_str(text, &c).unwrap();
assert!(out.contains("| capability | **bar** | foo |"), "ours=bar header: {out}");
assert!(out.contains("| speed | **✗** | ✅ |"), "ours=bar row: {out}");
let text2 = "<!-- nornir:gen:start:capabilities source=absent.capabilities -->\n<!-- nornir:gen:end:capabilities -->\n";
let (out2, _) = rewrite_str(text2, &c).unwrap();
assert!(out2.contains("no `absent.capabilities` static-capabilities row"), "graceful: {out2}");
}
#[test]
fn capability_glyph_maps_every_code_and_unknown_falls_back() {
assert_eq!(capability_glyph(1.0), "✅");
assert_eq!(capability_glyph(0.5), "◐");
assert_eq!(capability_glyph(0.0), "✗");
assert_eq!(capability_glyph(-1.0), "NA");
assert_eq!(capability_glyph(-2.0), "—");
assert_eq!(capability_glyph(0.7), "—");
assert_eq!(capability_glyph(42.0), "—");
assert_eq!(capability_glyph(-5.0), "—");
}
fn vs_run() -> BenchRun {
let mut m: Map<String, Value> = Map::new();
m.insert("ljar_mbs".into(), Value::from(2527.0f64));
m.insert("unzip_mbs".into(), Value::from(1404.0f64));
m.insert("speedup_x".into(), Value::from(1.8f64));
BenchRun {
date: "2026-06-05".into(),
timestamp: None,
version: "0.9.0".into(),
machine: "test".into(),
cores: 32,
results: vec![crate::bench::BenchResult { name: "jar_2000".into(), metrics: m , ..Default::default() }],
tests: vec![],
}
}
#[test]
fn benches_pivots_and_bolds_winner() {
let r = vs_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let out = render_benches(&c, &HashMap::new()).unwrap();
assert!(out.contains("| workload |"), "got: {out}");
assert!(out.contains("ljar_mbs"));
assert!(out.contains("jar_2000"));
assert!(out.contains("**2527**"), "winner not bolded: {out}");
assert!(!out.contains("**1404**"), "loser bolded: {out}");
assert!(!out.contains("**1.8"), "neutral bolded: {out}");
}
#[test]
fn benches_exclude_and_bold_false() {
let mut a: Map<String, Value> = Map::new();
a.insert("ljar_mbs".into(), Value::from(1000.0f64));
a.insert("unzip_mbs".into(), Value::from(60.0f64));
let mut b: Map<String, Value> = Map::new();
b.insert("ljar_mbs".into(), Value::from(120.0f64));
b.insert("unzip_mbs".into(), Value::from(60.0f64));
let run = BenchRun {
date: "2026-06-06".into(), timestamp: None, version: "0.9.0".into(),
machine: "tr".into(), cores: 32,
results: vec![
crate::bench::BenchResult { name: "ljar_guava".into(), metrics: a , ..Default::default() },
crate::bench::BenchResult { name: "ljar_guava_st".into(), metrics: b , ..Default::default() },
],
tests: vec![],
};
let c = ctx(Path::new("/tmp"), Some(&run));
let mut args = HashMap::new();
args.insert("exclude".to_string(), "_st".to_string());
args.insert("bold".to_string(), "false".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("ljar_guava"), "got: {out}");
assert!(!out.contains("ljar_guava_st"), "exclude failed: {out}");
assert!(!out.contains("**1000**"), "bold=false should suppress cell bolding: {out}");
assert!(out.contains("tr · 32 cores"), "header missing machine: {out}");
}
#[test]
fn benches_best_override_flips_direction() {
let r = vs_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("best".to_string(), "ljar_mbs:low,unzip_mbs:low".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("**1404**"), "override not applied: {out}");
assert!(!out.contains("**2527**"), "override ignored: {out}");
}
#[test]
fn common_run_snaps_codec_to_its_format_rival() {
assert_eq!(common_run("zoomie-lgz", "gzip"), 2); assert_eq!(common_run("zoomie-lbzip2", "bzip2"), 5); assert!(common_run("zoomie-lgz", "gzip") > common_run("zoomie-lgz", "bzip2"));
assert!(common_run("zoomie-lbzip2", "bzip2") > common_run("zoomie-lbzip2", "gzip"));
}
fn codec_run() -> BenchRun {
let mk = |name: &str, decode: f64, compress: f64, ratio: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("decompress_mbs".into(), Value::from(decode));
m.insert("compress_mbs".into(), Value::from(compress));
m.insert("ratio".into(), Value::from(ratio));
crate::bench::BenchResult { name: name.into(), metrics: m , ..Default::default() }
};
BenchRun {
date: "2026-07-07".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 32,
results: vec![
mk("zoomie-lgz", 900.0, 400.0, 2.8),
mk("gzip", 500.0, 90.0, 2.8),
mk("zoomie-lbzip2", 700.0, 120.0, 3.5),
mk("bzip2", 300.0, 20.0, 3.5),
],
tests: vec![],
}
}
#[test]
fn mashup_grouped_pairs_each_codec_with_its_rival() {
let r = codec_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("self".to_string(), "zoomie".to_string());
args.insert("grouped".to_string(), "true".to_string());
args.insert("drop".to_string(), "compress_mbs,ratio".to_string());
let out = render_mashup(&c, &args).unwrap();
assert!(out.contains("**gzip**"), "missing gzip group header: {out}");
assert!(out.contains("**bzip2**"), "missing bzip2 group header: {out}");
for row in ["zoomie-lgz", "gzip", "zoomie-lbzip2", "bzip2"] {
assert!(out.contains(row), "missing row {row}: {out}");
}
let gz = out.find("**gzip**").unwrap();
let bz = out.find("**bzip2**").unwrap();
assert!(gz < bz, "gzip group must precede bzip2 group: {out}");
assert!(out.contains("decompress_mbs"), "decode column dropped: {out}");
assert!(!out.contains("| compress_mbs |"), "compress_mbs column not dropped: {out}");
assert!(!out.contains("| ratio |"), "ratio column not dropped: {out}");
assert!(out.contains("**900**"), "gzip-group winner not bolded: {out}");
assert!(out.contains("**700**"), "bzip2-group winner not bolded: {out}");
assert_eq!(out.matches("32 cores").count(), 1, "header repeated per group: {out}");
}
fn variant_run() -> BenchRun {
let mk = |name: &str, decode: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("decompress_mbs".into(), Value::from(decode));
crate::bench::BenchResult { name: name.into(), metrics: m, ..Default::default() }
};
BenchRun {
date: "2026-07-08".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 32,
results: vec![
mk("zoomies-lgz", 900.0), mk("zoomies-lgz_st", 250.0), mk("gzip", 500.0), ],
tests: vec![],
}
}
#[test]
fn mashup_variant_keeps_st_as_distinct_grouped_row() {
let r = variant_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("self".to_string(), "zoomies".to_string());
args.insert("pairing".to_string(), "grouped".to_string());
args.insert("variant".to_string(), "st".to_string());
let out = render_mashup(&c, &args).unwrap();
assert!(out.contains("**gzip**"), "missing gzip group header: {out}");
assert_eq!(out.matches("**gzip**").count(), 1, "variants must share ONE group: {out}");
for row in ["zoomies-lgz_st", "zoomies-lgz", "gzip"] {
assert!(out.contains(row), "missing row {row}: {out}");
}
assert!(out.contains("| zoomies-lgz |"), "base row not rendered distinctly: {out}");
assert!(out.contains("| zoomies-lgz_st |"), "`_st` variant row not rendered distinctly: {out}");
let mut base_args = HashMap::new();
base_args.insert("self".to_string(), "zoomies".to_string());
base_args.insert("pairing".to_string(), "grouped".to_string());
let base_out = render_mashup(&c, &base_args).unwrap();
assert!(
!base_out.contains("| zoomies-lgz_st |"),
"without variant= the `_st` row must collapse, not render distinctly: {base_out}"
);
}
#[test]
fn st_suffix_notion_is_shared_with_the_gate() {
assert!(crate::bench::is_single_thread("zoomies-lgz_st"));
assert!(!crate::bench::is_single_thread("zoomies-lgz"));
assert_eq!(crate::bench::strip_single_thread("zoomies-lgz_st"), "zoomies-lgz");
assert_eq!(system_token_variant("zoomies-lgz_st", true), "zoomies-lgz_st");
assert_eq!(system_token_variant("zoomies-lgz_st", false), "zoomies-lgz");
assert_eq!(system_token_variant("zoomies-lgz", true), "zoomies-lgz");
}
#[test]
fn mashup_without_grouped_stays_flat() {
let r = codec_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("self".to_string(), "zoomie".to_string());
let out = render_mashup(&c, &args).unwrap();
assert_eq!(out.matches("| workload |").count(), 1, "expected one flat table: {out}");
}
fn systems_run() -> BenchRun {
let mk = |name: &str, ops: f64, p50: f64, p99: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("ops_sec".into(), Value::from(ops));
m.insert("p50_us".into(), Value::from(p50));
m.insert("p99_us".into(), Value::from(p99));
crate::bench::BenchResult { name: name.into(), metrics: m , ..Default::default() }
};
BenchRun {
date: "2026-06-08".into(),
timestamp: None,
version: "0.4.0".into(),
machine: "oden".into(),
cores: 32,
results: vec![
mk("nessie_table_exists", 5270.0, 156.51, 882.0),
mk("nornir_embedded_table_exists", 2345144.0, 0.30, 0.40),
mk("polaris_table_exists", 3282.0, 291.91, 457.0),
],
tests: vec![],
}
}
#[test]
fn benches_winner_by_row_bolds_best_system_per_column() {
let r = systems_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("winner".to_string(), "row".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("**2.34514M**"), "ops/sec winner not bolded: {out}");
assert!(out.contains("**0.3**") || out.contains("**0.30**"), "p50 winner not bolded: {out}");
assert!(out.contains("**0.4**") || out.contains("**0.40**"), "p99 winner not bolded: {out}");
assert!(!out.contains("**156.51**"), "nessie p50 wrongly bolded: {out}");
assert!(!out.contains("**291.91**"), "polaris p50 wrongly bolded: {out}");
}
#[test]
fn benches_winner_tie_favours_ours() {
let mk = |name: &str, compress: f64, ratio: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("compress_mbs".into(), Value::from(compress));
m.insert("ratio".into(), Value::from(ratio));
crate::bench::BenchResult { name: name.into(), metrics: m , ..Default::default() }
};
let run = BenchRun {
date: "2026-07-04".into(),
timestamp: None,
version: "0.1.15".into(),
machine: "ryzen".into(),
cores: 12,
results: vec![
mk("zoomies-lbz2", 22.84, 7.50),
mk("zoomies-lgz", 256.39, 4.64),
mk("pigz", 256.39, 4.64),
mk("gzip", 256.39, 4.64),
mk("bzip2", 22.84, 7.50),
],
tests: vec![],
};
let c = ctx(Path::new("/tmp"), Some(&run));
let mut args = HashMap::new();
args.insert("winner".to_string(), "row".to_string());
args.insert("self".to_string(), "zoomies-lbz2".to_string());
args.insert("best".to_string(), "ratio:high,compress_mbs:high".to_string());
let out = render_benches(&c, &args).unwrap();
assert_eq!(out.matches("**256.39**").count(), 1, "compress_mbs bold count: {out}");
assert_eq!(out.matches("**7.5**").count() + out.matches("**7.50**").count(), 1,
"ratio bold count: {out}");
let lgz = out.lines().find(|l| l.starts_with("| zoomies-lgz |")).unwrap();
assert!(lgz.contains("**256.39**"), "zoomies-lgz should win compress_mbs tie: {out}");
let lbz2 = out.lines().find(|l| l.starts_with("| zoomies-lbz2 |")).unwrap();
assert!(lbz2.contains("**7.5**") || lbz2.contains("**7.50**"),
"zoomies-lbz2 should win ratio tie: {out}");
let gzip = out.lines().find(|l| l.starts_with("| gzip |")).unwrap();
assert!(!gzip.contains("**256.39**"), "gzip wrongly bolded (old bug): {out}");
let bzip2 = out.lines().find(|l| l.starts_with("| bzip2 |")).unwrap();
assert!(!bzip2.contains("**7.5**") && !bzip2.contains("**7.50**"),
"bzip2 wrongly bolded: {out}");
}
#[test]
fn benches_compact_mode_takes_best_per_column() {
let r = systems_run(); let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("mode".to_string(), "compact".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("| best-of |"), "no best-of row: {out}");
assert!(!out.contains("nessie"), "rival row leaked: {out}");
assert!(!out.contains("polaris"), "rival row leaked: {out}");
let data_rows = out.lines().filter(|l| l.starts_with("| best-of |")).count();
assert_eq!(data_rows, 1, "compact must emit one row: {out}");
assert!(out.contains("2.345144M") || out.contains("2.35M") || out.contains("2345144"),
"ops_sec best (nornir 2.345M) missing: {out}");
assert!(out.contains("| 0.3 |") || out.contains("0.30"), "p50 best (0.30) missing: {out}");
assert!(!out.contains("156.51"), "p50 took a non-best (nessie) value: {out}");
assert!(!out.contains("291.91"), "p50 took a non-best (polaris) value: {out}");
assert!(out.contains("0.4"), "p99 best (0.40) missing: {out}");
assert!(!out.contains("882"), "p99 took a non-best value: {out}");
assert!(!out.contains("457"), "p99 took a non-best value: {out}");
}
#[test]
fn benches_compact_mixes_best_source_per_column() {
let mk = |name: &str, ops: f64, p50: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("ops_sec".into(), Value::from(ops));
m.insert("p50_us".into(), Value::from(p50));
crate::bench::BenchResult { name: name.into(), metrics: m , ..Default::default() }
};
let run = BenchRun {
date: "2026-06-12".into(), timestamp: None, version: "0.4.4".into(),
machine: "oden".into(), cores: 32,
results: vec![
mk("alpha_backend", 9000.0, 50.0), mk("beta_backend", 1000.0, 5.0), ],
tests: vec![],
};
let c = ctx(Path::new("/tmp"), Some(&run));
let mut args = HashMap::new();
args.insert("mode".to_string(), "compact".to_string());
args.insert("compare".to_string(), "alpha,beta".to_string());
let out = render_benches(&c, &args).unwrap();
let row = out.lines().find(|l| l.starts_with("| best-of |")).expect("best-of row");
let cells: Vec<&str> = row.trim_matches('|').split('|').map(str::trim).collect();
assert_eq!(cells, vec!["best-of", "9000", "5"], "best-of did not mix per-column champions: {out}");
}
#[test]
fn benches_top_n_keeps_strongest_rows() {
let r = systems_run(); let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("top".to_string(), "2".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("nornir_embedded_table_exists"), "top row missing: {out}");
assert!(out.contains("nessie_table_exists"), "2nd row missing: {out}");
assert!(!out.contains("polaris"), "top=2 should drop the weakest (polaris): {out}");
}
fn mashup_run() -> BenchRun {
let mk = |name: &str, ops: f64, p50: f64, p99: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("ops_sec".into(), Value::from(ops));
m.insert("p50_us".into(), Value::from(p50));
m.insert("p99_us".into(), Value::from(p99));
crate::bench::BenchResult { name: name.into(), metrics: m , ..Default::default() }
};
BenchRun {
date: "2026-07-01".into(),
timestamp: None,
version: "1.2.0".into(),
machine: "oden".into(),
cores: 32,
results: vec![
mk("holger_get_blob", 812_000.0, 3.1, 9.4),
mk("nexus_get_blob", 41_000.0, 61.0, 220.0),
mk("artifactory_get_blob", 12_500.0, 190.0, 640.0),
],
tests: vec![],
}
}
#[test]
fn mashup_zero_arg_auto_discovers_all_rivals() {
let r = mashup_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"holger\"\nversion = \"1.2.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let out = render_mashup(&c, &HashMap::new()).unwrap();
assert!(out.contains("| workload |"), "pivot header missing: {out}");
for sys in ["holger_get_blob", "nexus_get_blob", "artifactory_get_blob"] {
assert!(out.contains(sys), "rival {sys} not auto-discovered: {out}");
}
}
#[test]
fn mashup_bolds_ours_winning_cells() {
let r = mashup_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"holger\"\nversion = \"1.2.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let out = render_mashup(&c, &HashMap::new()).unwrap();
assert!(out.contains("**812000**"), "ours ops/sec winner not bolded: {out}");
assert!(out.contains("**3.10**"), "ours p50 winner not bolded: {out}");
assert!(!out.contains("**61**"), "rival p50 wrongly bolded: {out}");
assert!(!out.contains("**190**"), "rival p50 wrongly bolded: {out}");
}
#[test]
fn mashup_meaty_columns_lead() {
let r = mashup_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"holger\"\nversion = \"1.2.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let out = render_mashup(&c, &HashMap::new()).unwrap();
let table = &out[out.find("| workload |").expect("table header")..];
let i_ops = table.find("ops_sec").expect("ops_sec column");
let i_p50 = table.find("p50_us").expect("p50_us column");
let i_p99 = table.find("p99_us").expect("p99_us column");
assert!(i_ops < i_p50, "throughput must lead latency: {out}");
assert!(i_p50 < i_p99, "p50 must precede p99: {out}");
}
fn columnwise_run() -> BenchRun {
let mk = |name: &str, kv: &[(&str, f64)]| {
let mut m: Map<String, Value> = Map::new();
for (k, v) in kv {
m.insert((*k).into(), Value::from(*v));
}
crate::bench::BenchResult { name: name.into(), metrics: m , ..Default::default() }
};
BenchRun {
date: "2026-07-02".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "loki".into(),
cores: 12,
results: vec![
mk("load_x", &[
("acme_served_mbs", 100.0), ("rivx_served_mbs", 10.0),
("ops_per_sec", 5000.0), ("rivx_ops_per_sec", 400.0),
("rivx_upload_mbs", 2.0), ("acme_files", 999.0),
]),
mk("load_y", &[
("acme_served_mbs", 90.0), ("rivy_served_mbs", 12.0), ("rivy_upload_mbs", 3.0),
]),
mk("selfmicro", &[("mb_per_sec", 6000.0), ("bytes", 123.0)]),
],
tests: vec![],
}
}
#[test]
fn mashup_columnwise_shows_only_contested_crush_columns() {
let r = columnwise_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"acme\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let out = render_mashup(&c, &HashMap::new()).unwrap();
for sysrow in ["| acme |", "| rivx |", "| rivy |"] {
assert!(out.contains(sysrow), "system row {sysrow} missing: {out}");
}
for col in ["served_mbs", "ops_per_sec"] {
assert!(out.contains(col), "contested metric column {col} missing: {out}");
}
for junk in [
"selfmicro", "mb_per_sec", "acme_files", "upload_mbs", "bytes",
"load_x", "load_y", "acme_served_mbs", "rivx_served_mbs",
] {
assert!(!out.contains(junk), "noise `{junk}` leaked into the crush table: {out}");
}
assert!(out.contains("**100**"), "ours served_mbs winner not bolded: {out}");
assert!(out.contains("**5000**"), "ours ops/sec winner not bolded: {out}");
assert!(out.contains("acme leads rivx"), "headline missing ours-vs-rival: {out}");
assert!(out.contains("12.5×"), "headline ratio wrong: {out}");
}
#[test]
fn mashup_single_system_degrades_to_a_headline_line() {
let mut m: Map<String, Value> = Map::new();
m.insert("queries_per_sec".into(), Value::from(478_996.0));
m.insert("build_ms".into(), Value::from(406.0));
let run = BenchRun {
date: "2026-07-02".into(), timestamp: None, version: "0.1.0".into(),
machine: "loki".into(), cores: 12,
results: vec![crate::bench::BenchResult { name: "dep_graph_build".into(), metrics: m , ..Default::default() }],
tests: vec![],
};
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"solo\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&run));
let out = render_mashup(&c, &HashMap::new()).unwrap();
assert!(!out.contains("| workload |"), "single-system must NOT render a pivot table: {out}");
assert!(out.contains("dep_graph_build"), "self-bench name missing: {out}");
assert!(out.contains("single-system"), "graceful note missing: {out}");
}
#[test]
fn mashup_headline_picks_biggest_ratio() {
let r = mashup_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"holger\"\nversion = \"1.2.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let out = render_mashup(&c, &HashMap::new()).unwrap();
assert!(out.contains("Biggest gap"), "no headline: {out}");
assert!(out.contains("`p99_us`"), "headline metric wrong: {out}");
assert!(out.contains("artifactory"), "headline rival wrong: {out}");
assert!(out.contains("68.1×"), "headline ratio wrong: {out}");
assert!(out.contains("holger leads artifactory"), "headline framing wrong: {out}");
}
#[test]
fn mashup_self_override_wins() {
let r = mashup_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"unrelated_pkg\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let mut args = HashMap::new();
args.insert("self".to_string(), "nexus".to_string());
let out = render_mashup(&c, &args).unwrap();
assert!(out.contains("nexus leads artifactory"), "self= override not honoured: {out}");
}
#[test]
fn mashup_empty_is_graceful() {
let root = Path::new(".");
let c_none = Ctx::new(root, root, None);
let out = render_mashup(&c_none, &HashMap::new()).unwrap();
assert!(out.contains("no bench history"), "graceful empty (no run): {out}");
let empty = BenchRun {
date: "2026-07-02".into(), timestamp: None, version: "0.0.0".into(),
machine: "m".into(), cores: 1, results: vec![], tests: vec![],
};
let c_empty = Ctx::new(root, root, Some(&empty));
let out = render_mashup(&c_empty, &HashMap::new()).unwrap();
assert!(out.contains("no bench results"), "graceful empty (no results): {out}");
}
#[test]
fn mashup_exclude_drops_rival() {
let r = mashup_run();
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join("Cargo.toml"),
"[package]\nname = \"holger\"\nversion = \"1.2.0\"\nedition = \"2021\"\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let mut args = HashMap::new();
args.insert("exclude".to_string(), "artifactory".to_string());
let out = render_mashup(&c, &args).unwrap();
assert!(out.contains("holger_get_blob") && out.contains("nexus_get_blob"), "kept rows: {out}");
assert!(!out.contains("artifactory"), "excluded rival leaked: {out}");
let mut args2 = HashMap::new();
args2.insert("exclude".to_string(), "nexus, zzz-no-such".to_string());
let out2 = render_mashup(&c, &args2).unwrap();
assert!(!out2.contains("nexus"), "comma exclude dropped nexus: {out2}");
assert!(out2.contains("artifactory"), "comma exclude kept the non-listed rival: {out2}");
}
#[test]
fn fmt_metric_humanizes_and_rounds() {
assert_eq!(fmt_metric(3_390_444.67), "3.39M");
assert_eq!(fmt_metric(2_999_875.48), "3M");
assert_eq!(fmt_metric(1_071_379.54), "1.07M");
assert_eq!(fmt_metric(8_869.62), "8870");
assert_eq!(fmt_metric(12_809.87), "12810");
assert_eq!(fmt_metric(3_591.38), "3591");
assert_eq!(fmt_metric(842.14), "842.14");
assert_eq!(fmt_metric(0.32), "0.32");
assert_eq!(fmt_metric(9.0), "9");
}
#[test]
fn benches_default_axis_does_not_compare_across_rows() {
let r = systems_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let out = render_benches(&c, &HashMap::new()).unwrap();
assert!(!out.contains("**2.35M**"), "col-axis should not bold across rows: {out}");
}
#[test]
fn bench_chart_text_fallback_marks_winner() {
let r = systems_run();
let d = tempfile::tempdir().unwrap();
let c = ctx(d.path(), Some(&r));
let mut args = HashMap::new();
args.insert("metric".to_string(), "ops_sec".to_string());
args.insert("name".to_string(), "table_exists".to_string());
let out = render_bench_chart(&c, &args).unwrap();
#[cfg(feature = "docs-export")]
assert!(out.contains("![") && out.contains(".svg"), "expected image link: {out}");
#[cfg(not(feature = "docs-export"))]
{
assert!(out.contains('★'), "winner star missing: {out}");
assert!(out.contains("nornir_embedded_table_exists"), "label missing: {out}");
}
}
#[test]
fn check_errors_on_drift() {
let r = run_fixture();
let d = tempfile::tempdir().unwrap();
let p = d.path().join("README.md");
std::fs::write(
&p,
"<!-- nornir:gen:start:bench -->\nstale\n<!-- nornir:gen:end:bench -->\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
assert!(check_file(&p, &c).is_err());
}
#[test]
fn idempotent_after_assemble() {
let r = run_fixture();
let d = tempfile::tempdir().unwrap();
let p = d.path().join("README.md");
std::fs::write(
&p,
"<!-- nornir:gen:start:bench -->\n<!-- nornir:gen:end:bench -->\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
assemble_file(&p, &c).unwrap();
let r1 = check_file(&p, &c).unwrap();
assert!(!r1.changed);
}
#[test]
fn unknown_section_errors() {
let d = tempfile::tempdir().unwrap();
let p = d.path().join("README.md");
std::fs::write(
&p,
"<!-- nornir:gen:start:nope -->\n<!-- nornir:gen:end:nope -->\n",
)
.unwrap();
let c = ctx(d.path(), None);
assert!(assemble_file(&p, &c).is_err());
}
#[test]
fn unmatched_marker_errors() {
let d = tempfile::tempdir().unwrap();
let p = d.path().join("README.md");
std::fs::write(&p, "<!-- nornir:gen:start:bench -->\nno end\n").unwrap();
let c = ctx(d.path(), None);
assert!(assemble_file(&p, &c).is_err());
}
}