xtask/src/bench.rs
//! `cargo xtask bench` — measure holger and PROVE the row reached the warehouse.
//!
//! # What this door is, and what it deliberately is not
//!
//! It is a THIN door onto `nornir bench run holger`. It owns no benchmark
//! knowledge: not one arm name, not one scale, not one feature. Every arm lives
//! in `xtask/examples/nornir-bench.rs`, the harness that runs them lives in
//! `edda/crates/nornir-bench`, and the persistence lives in nornir. What this
//! file owns is the three things a person otherwise has to remember, and which
//! were wrong or absent every time holger was measured before 2026-09-20:
//!
//! 1. **the config path.** `nornir` finds `nornir.toml` by walking up from the
//! cwd, and there is none above `~/git/holger`, so a bare `nornir bench run
//! holger` from a holger checkout resolves no workspace. The config is
//! `workspace_nordisk/release/nornir.toml`, and nornir rejects a relative
//! one ("config path lacks grandparent dirs"), so this door resolves it to
//! an absolute path. The SAME resolved path is then used for the run and for
//! the read-back, which is the structural answer to the two-warehouse
//! defect: one config cannot name two warehouses, but two configs in one
//! repo silently do, and nothing errors — a reader just opens a warehouse
//! nobody wrote to.
//! 2. **the environment.** `NORNIR_BENCH_ONLY`, `NORNIR_BENCH_SKIP`,
//! `NORNIR_BENCH_FEATURES`, `NORNIR_MACHINE` and `HOLGER_BENCH_SCALE` are
//! passed through and PRINTED, so the run states its own selection instead
//! of leaving it to whatever happens to be in a shell.
//! 3. **the proof.** See below.
//!
//! # The proof, and why an exit code is not one
//!
//! gunnar's `benchland.rs` records the incident this file exists because of: on
//! 2026-08-12 ten gunnar runs landed in the receipt and none in any warehouse,
//! and every command exited 0 — the forge door writes the receipt and submits to
//! nornir only if a server endpoint is configured, and with none configured it
//! prints `NOT SUBMITTED — no nornir server configured` and reports success. The
//! coordinator hit the same class of thing an hour before this file was written:
//! `nornir bench run holger` returned exit 0 and wrote no row.
//!
//! So this door does not trust the exit code and does not only read the output
//! either. It COUNTS THE WAREHOUSE:
//!
//! * before the run, ask `nornir bench history-show holger --json` for every
//! run key it holds;
//! * run the measurement, echoing its output and scanning it for the known
//! not-submitted phrases;
//! * after the run, ask again. A measurement that persisted shows up as a NEW
//! key. No new key ⇒ RED, loudly, whatever the exit code said.
//!
//! The delta is the right check because it holds in both modes, and it cannot be
//! satisfied by a receipt: it is a read of the `bench_runs` table itself.
//!
//! # LOCAL versus CENTRAL, which is one environment variable
//!
//! `NORNIR_SERVER` unset ⇒ LOCAL: nornir is a fat client, runs the example in
//! this workspace's holger checkout and writes the iceberg row itself, plus the
//! `bench_history.jsonl` receipt beside it.
//!
//! `NORNIR_SERVER` set (or `--server <url>` here) ⇒ CENTRAL: nornir is a thin
//! client. It cannot write the warehouse — the server holds the single-writer
//! redb catalog lock — so `bench run` delegates to the server's `Ops.RunBench`,
//! which runs the example in the SERVER's checkout and appends the row there.
//! The row is therefore proven the same way, by the delta, but the receipt is
//! written on the server and not here; `bench-landed` says that plainly rather
//! than calling it a missing row.
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
/// The repo name in `nornir.toml`. `[repo.holger]` since 2026-09-19.
pub const REPO: &str = "holger";
/// The config, relative to the directory that holds the workspace checkouts.
pub const CONFIG_REL: &str = "workspace_nordisk/release/nornir.toml";
/// The environment this door passes through, and prints.
///
/// Listed rather than inherited-and-forgotten: an unprinted selector is how a
/// run that measured three arms gets read as a run that measured fifteen.
pub const PASSTHROUGH: &[&str] = &[
"NORNIR_BENCH_ONLY",
"NORNIR_BENCH_SKIP",
"NORNIR_BENCH_FEATURES",
"NORNIR_MACHINE",
"HOLGER_BENCH_SCALE",
];
/// Phrases that mean THE MEASUREMENT DID NOT PERSIST, whatever the exit code is.
///
/// Scanned for in the child's combined output. They are quoted from the doors
/// that print them: the forge's `NOT SUBMITTED — no nornir server configured`
/// (dwarves `bench.rs`) and nornir's own `skipped: neither …/examples/
/// nornir-bench.rs nor xtask/examples/nornir-bench.rs exists`, which means no
/// measurement happened at all.
pub const NOT_PERSISTED_MARKERS: &[&str] = &[
"NOT SUBMITTED",
"no nornir server configured",
"skipped: neither",
"no bench example",
];
/// Which warehouse the row is being written to, and by whom.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Mode {
/// `NORNIR_SERVER` unset: this process writes the iceberg row.
Local,
/// `NORNIR_SERVER` set: the server runs the bench and writes the row.
Central(String),
}
impl Mode {
pub fn from_env() -> Mode {
match std::env::var("NORNIR_SERVER") {
Ok(s) if !s.trim().is_empty() => Mode::Central(s),
_ => Mode::Local,
}
}
pub fn banner(&self, config: &Path) -> String {
match self {
Mode::Local => format!(
"LOCAL (NORNIR_SERVER unset) — nornir is a fat client: it runs the example here \
and writes the iceberg `bench_runs` row itself, plus the bench_history.jsonl \
receipt. Warehouse named by {}",
config.display()
),
Mode::Central(s) => format!(
"CENTRAL (NORNIR_SERVER={s}) — nornir is a thin client: it cannot open the \
warehouse (the server holds the single-writer redb catalog lock), so the SERVER \
runs the bench on its checkout and appends the row. Config here: {}",
config.display()
),
}
}
/// Where the row landed, in one phrase, for the closing line.
pub fn landed_in(&self) -> String {
match self {
Mode::Local => "the local iceberg `bench_runs` table".to_string(),
Mode::Central(s) => format!("the `bench_runs` table on {s}"),
}
}
}
/// Resolve the absolute `nornir.toml`.
///
/// `NORNIR_CONFIG` wins, then `--config`, then the conventional location beside
/// the checkouts: every ancestor of the repo root is tried for
/// `workspace_nordisk/release/nornir.toml`. Absolute, always — nornir refuses a
/// relative path with "config path lacks grandparent dirs".
pub fn resolve_config(root: &Path, explicit: Option<&str>) -> Result<PathBuf, String> {
let mut tried: Vec<String> = Vec::new();
let mut candidates: Vec<PathBuf> = Vec::new();
if let Some(p) = explicit {
candidates.push(PathBuf::from(p));
} else if let Ok(p) = std::env::var("NORNIR_CONFIG") {
if !p.trim().is_empty() {
candidates.push(PathBuf::from(p));
}
}
for anc in root.ancestors() {
candidates.push(anc.join(CONFIG_REL));
}
for c in candidates {
if c.is_file() {
return std::fs::canonicalize(&c)
.map_err(|e| format!("canonicalize {}: {e}", c.display()));
}
tried.push(c.display().to_string());
}
Err(format!(
"no nornir.toml found. Tried:\n {}\nPass --config <abs path> or set NORNIR_CONFIG. \
The config must declare [repo.{REPO}].",
tried.join("\n ")
))
}
/// The checkout `nornir.toml` names for this repo, if it names one.
///
/// Read so the door can say WHICH checkout the measurement will run in. It is
/// not the same as the cwd: `nornir bench run` resolves the repo by NAME, so a
/// run started from a worktree measures the path in the config. (`dwarves bench
/// run` takes a PATH and would not — but `dwarves` has no `bench` verb today.)
pub fn configured_path(config_text: &str, repo: &str) -> Option<String> {
let header = format!("[repo.{repo}]");
let mut in_section = false;
for line in config_text.lines() {
let t = line.trim();
if t.starts_with('[') {
in_section = t == header;
continue;
}
if in_section {
if let Some(rest) = t.strip_prefix("path") {
let rest = rest.trim_start().strip_prefix('=')?.trim();
return Some(rest.trim_matches('"').to_string());
}
}
}
None
}
/// The checkout `nornir.toml` names for holger, as a path.
///
/// This is the tree the measurement actually runs in, and therefore the tree
/// the `bench_history.jsonl` receipt is written to — NOT the cwd. Both verbs
/// resolve it the same way so `bench-landed` never looks for a receipt in a
/// worktree that nornir did not measure.
pub fn measured_root(config: &Path) -> Result<PathBuf, String> {
let text =
std::fs::read_to_string(config).map_err(|e| format!("read {}: {e}", config.display()))?;
configured_path(&text, REPO)
.map(PathBuf::from)
.ok_or_else(|| {
format!(
"{} does not declare [repo.{REPO}] with a path — `nornir bench run {REPO}` would not \
resolve the repo",
config.display()
)
})
}
/// Does the checkout the config LINKS match the checkout the door was ASKED
/// about?
///
/// MEASURED 2026-09-20: it did not, and nobody found out until the numbers were
/// read. `workspace_nordisk/release/nornir.toml` names
/// `[repo.holger] path = "/home/rickard/git/holger"` — the MAIN checkout — and
/// `nornir bench run holger` resolves the repo BY NAME, so a lane running
/// `cargo xtask bench` from its own worktree measured main. The lane's three new
/// arms did not exist in main, so three arms became one, the run exited 0, a row
/// landed, and every line printed said success. A measurement of the wrong tree
/// is worse than no measurement: it is a number with a lane's name on it.
///
/// Pure so it can be proven without a filesystem. `None` on either side means
/// the path could not be canonicalized (it does not exist); that is reported as
/// a mismatch rather than waved through, because a configured checkout that is
/// not there cannot be the tree you are standing in.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Checkout {
/// The config links the tree the door was asked about. Measure it.
Linked,
/// The config links a DIFFERENT tree. Refuse: whatever is measured will not
/// be the manifest the caller meant.
Foreign { asked: PathBuf, linked: PathBuf },
}
/// The verdict, from two already-canonicalized (or not) paths.
pub fn checkout_verdict(asked: Option<&Path>, linked: Option<&Path>) -> Checkout {
match (asked, linked) {
(Some(a), Some(l)) if a == l => Checkout::Linked,
(a, l) => Checkout::Foreign {
asked: a.map(Path::to_path_buf).unwrap_or_default(),
linked: l.map(Path::to_path_buf).unwrap_or_default(),
},
}
}
impl Checkout {
/// The refusal, naming BOTH trees and the three ways out.
pub fn refusal(&self, config: &Path) -> Option<String> {
match self {
Checkout::Linked => None,
Checkout::Foreign { asked, linked } => Some(format!(
"REFUSING TO MEASURE THE WRONG CHECKOUT.\n\
\x20 asked about : {}\n\
\x20 config links: {} ([repo.{REPO}] in {})\n\
`nornir bench run {REPO}` resolves the repo BY NAME, so the run would build and \
measure the LINKED tree and stamp the row as {REPO} — not the tree you are \
standing in. That is how a lane's three new arms became one on 2026-09-20: the \
arms did not exist in the linked checkout, the run exited 0, a row landed, and \
every printed line said success.\n\
Ways out, in order of honesty:\n\
\x20 1. point [repo.{REPO}].path at this checkout (or use --config with a \
nornir.toml that does);\n\
\x20 2. run the bench from the linked checkout, if main is what you meant to \
measure;\n\
\x20 3. --allow-foreign-checkout, which measures the linked tree anyway and \
says so on every line. Use it only when you know the row is about that tree.",
if asked.as_os_str().is_empty() {
"<does not exist>".into()
} else {
asked.display().to_string()
},
if linked.as_os_str().is_empty() {
"<does not exist>".into()
} else {
linked.display().to_string()
},
config.display(),
)),
}
}
}
/// Every not-persisted phrase present in the child's output.
pub fn not_persisted_markers(output: &str) -> Vec<&'static str> {
NOT_PERSISTED_MARKERS
.iter()
.copied()
.filter(|m| output.contains(m))
.collect()
}
/// `nornir bench history-show <repo> --json`, as text.
///
/// The READ half of the warehouse, used by the delta proof here and by
/// [`crate::benchland`]. In CENTRAL mode this routes over `Bench.History` to the
/// same server that wrote the row, so the read and the write cannot be looking
/// at two different stores.
pub fn history_json(root: &Path, config: &Path) -> Result<String, String> {
let out = Command::new(nornir_bin())
.current_dir(root)
.args(["bench", "history-show", REPO, "--json", "--config"])
.arg(config)
.output()
.map_err(|e| {
format!(
"run `{} bench history-show`: {e} (on PATH, or NORNIR_BIN set?)",
nornir_bin()
)
})?;
// A repo with no rows answers `ok:false` and exit 1 — that is a legitimate
// reading of the warehouse, not a failure to read it, and the delta proof
// needs it. Only a run that produced no JSON at all is an error.
let text = String::from_utf8(out.stdout).map_err(|e| format!("history is not UTF-8: {e}"))?;
if text.trim().is_empty() {
return Err(format!(
"`{} bench history-show {REPO}` produced no JSON ({}): {}",
nornir_bin(),
out.status,
String::from_utf8_lossy(&out.stderr),
));
}
Ok(text)
}
/// The run keys the warehouse holds right now.
fn warehouse_now(root: &Path, config: &Path) -> Result<crate::benchland::Warehouse, String> {
let doc: serde_json::Value = serde_json::from_str(&history_json(root, config)?)
.map_err(|e| format!("bench history-show did not parse: {e}"))?;
Ok(crate::benchland::warehouse_runs(&doc))
}
fn nornir_bin() -> String {
std::env::var("NORNIR_BIN").unwrap_or_else(|_| "nornir".to_string())
}
/// What `cargo xtask bench` was asked for.
#[derive(Debug, Default)]
pub struct Args {
pub heavy: bool,
pub config: Option<String>,
pub server: Option<String>,
/// Measure the checkout the config links even when it is not the one the
/// door was asked about. Off by default — see [`Checkout`].
pub allow_foreign_checkout: bool,
}
/// Parse the door's own flags. Unknown ones are refused rather than ignored:
/// a mistyped selector that is silently dropped measures the wrong thing.
pub fn parse_args(args: &[String]) -> Result<Args, String> {
let mut a = Args::default();
let mut it = args.iter();
while let Some(arg) = it.next() {
match arg.as_str() {
"--heavy" => a.heavy = true,
"--allow-foreign-checkout" => a.allow_foreign_checkout = true,
"--config" => {
a.config = Some(it.next().ok_or("--config needs a path")?.clone());
}
"--server" => {
a.server = Some(it.next().ok_or("--server needs a URL")?.clone());
}
other => {
return Err(format!(
"unknown argument `{other}`\n\
usage: cargo xtask bench [--heavy] [--config <nornir.toml>] [--server <url>] [--allow-foreign-checkout]\n\
arm selection is by environment: {}",
PASSTHROUGH.join(", ")
));
}
}
}
Ok(a)
}
/// Run the child with its output TEE'd: streamed to this terminal as it happens
/// AND captured, because the verdict is in the output and a person watching a
/// 20-minute bench should not have to wait for it in silence.
fn run_tee(mut cmd: Command) -> Result<(bool, String), String> {
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
let mut child = cmd
.spawn()
.map_err(|e| format!("spawn `{} bench run`: {e}", nornir_bin()))?;
let out = child.stdout.take().expect("piped");
let err = child.stderr.take().expect("piped");
let t_out = std::thread::spawn(move || {
let mut buf = String::new();
for line in BufReader::new(out).lines().map_while(Result::ok) {
println!("{line}");
let _ = std::io::stdout().flush();
buf.push_str(&line);
buf.push('\n');
}
buf
});
let t_err = std::thread::spawn(move || {
let mut buf = String::new();
for line in BufReader::new(err).lines().map_while(Result::ok) {
eprintln!("{line}");
buf.push_str(&line);
buf.push('\n');
}
buf
});
let status = child.wait().map_err(|e| format!("wait: {e}"))?;
let mut text = t_out.join().unwrap_or_default();
text.push_str(&t_err.join().unwrap_or_default());
Ok((status.success(), text))
}
/// `cargo xtask bench` — measure, then prove the row landed.
pub fn run(root: &Path, args: &Args) -> Result<(), String> {
if let Some(s) = &args.server {
// Set rather than only passed: `bench run` AND the two `history-show`
// reads must all be in the same mode, or the delta compares a local
// warehouse against a central one.
unsafe { std::env::set_var("NORNIR_SERVER", s) };
}
let config = resolve_config(root, args.config.as_deref())?;
let measured_checkout = measured_root(&config)?;
let mode = Mode::from_env();
println!("bench: repo {REPO}");
println!("bench: mode {}", mode.banner(&config));
println!(
"bench: runs {} ← the checkout nornir.toml names",
measured_checkout.display()
);
// THE CHECKOUT GATE. `nornir bench run` resolves the repo by NAME, so the
// tree the config links is the tree that gets measured — not the one the
// door was asked about. Measured 2026-09-20: a lane ran this from its own
// worktree, measured main, and its three new arms became one, silently.
let verdict = checkout_verdict(
root.canonicalize().ok().as_deref(),
measured_checkout.canonicalize().ok().as_deref(),
);
if let Some(refusal) = verdict.refusal(&config) {
if args.allow_foreign_checkout {
eprintln!("╔══════════════════════════════════════════════════════════════════════╗");
eprintln!("║ MEASURING A CHECKOUT THAT IS NOT THE ONE YOU ASKED ABOUT. ║");
eprintln!("╚══════════════════════════════════════════════════════════════════════╝");
eprintln!("{refusal}");
eprintln!(
"bench: --allow-foreign-checkout was given, so the run PROCEEDS against {}. \
Every row it writes is about THAT tree.",
measured_checkout.display()
);
} else {
eprintln!("╔══════════════════════════════════════════════════════════════════════╗");
eprintln!("║ THE MEASUREMENT WOULD BE OF THE WRONG TREE. ║");
eprintln!("╚══════════════════════════════════════════════════════════════════════╝");
eprintln!("{refusal}");
return Err(format!(
"[repo.{REPO}] in {} links {}, not {}",
config.display(),
measured_checkout.display(),
root.display(),
));
}
} else {
println!("bench: linked ✓ the config links the checkout you asked about");
}
for k in PASSTHROUGH {
match std::env::var(k) {
Ok(v) if !v.is_empty() => println!("bench: env {k}={v}"),
_ => {}
}
}
if args.heavy {
println!("bench: heavy --heavy (the #[ignore]'d container/corpus arms are IN)");
}
let before = warehouse_now(root, &config)?;
println!(
"bench: before {} run(s) in bench_runs for {REPO}",
before.keys.len()
);
for u in &before.unreadable {
println!("bench: before UNREADABLE {u}");
}
let mut cmd = Command::new(nornir_bin());
cmd.current_dir(root)
.args(["bench", "run", REPO, "--config"])
.arg(&config);
if args.heavy {
cmd.arg("--heavy");
}
println!("\n── nornir bench run {REPO} ──");
let (exit_ok, output) = run_tee(cmd)?;
println!("── end of run (exit ok = {exit_ok}; this is NOT the verdict) ──\n");
let markers = not_persisted_markers(&output);
let after = warehouse_now(root, &config)?;
let new: Vec<_> = after
.keys
.iter()
.filter(|k| !before.keys.contains(k))
.collect();
for u in &after.unreadable {
println!("bench: after UNREADABLE {u}");
}
if new.is_empty() {
eprintln!("╔══════════════════════════════════════════════════════════════════════╗");
eprintln!("║ THE MEASUREMENT DID NOT REACH THE WAREHOUSE. ║");
eprintln!("╚══════════════════════════════════════════════════════════════════════╝");
eprintln!(
"bench: {} run(s) in bench_runs before, {} after — NO NEW ROW.",
before.keys.len(),
after.keys.len()
);
if !markers.is_empty() {
eprintln!(
"bench: the run said so in its own output: {}",
markers.join(", ")
);
}
eprintln!("bench: mode was {}", mode.banner(&config));
eprintln!(
"bench: this is the 2026-08-12 shape — a real measurement, exit {}, and no row. \
Check the config names the warehouse you are reading, and that [repo.{REPO}] points \
at the checkout you measured.",
if exit_ok { "0" } else { "non-zero" }
);
return Err("no new row in bench_runs".to_string());
}
if !markers.is_empty() {
// A row landed AND a not-submitted phrase was printed. Never silent.
eprintln!(
"bench: ⚠ a new row landed, but the output also contained: {}. Read it.",
markers.join(", ")
);
}
for k in &new {
println!("bench: NEW ROW {}", k.label());
}
println!(
"✓ bench: the measurement reached {} ({} run(s) now, was {}).",
mode.landed_in(),
after.keys.len(),
before.keys.len()
);
println!(
" read it back: nornir bench history-show {REPO} --config {}",
config.display()
);
if !exit_ok {
println!(
"bench: at least one arm went red. The measurement IS persisted — the non-zero exit \
is the verdict on the arms, not on the row."
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_not_submitted_phrase_is_caught_however_the_exit_code_reads() {
let out = "…\nNOT SUBMITTED — no nornir server configured\n✓ receipt appended\n";
let m = not_persisted_markers(out);
assert!(m.contains(&"NOT SUBMITTED"), "{m:?}");
assert!(m.contains(&"no nornir server configured"), "{m:?}");
}
/// A run that never happened is not a run that persisted.
#[test]
fn a_missing_bench_example_is_a_marker() {
let out = "skipped: neither /home/rickard/git/holger/examples/nornir-bench.rs nor \
xtask/examples/nornir-bench.rs exists\n";
assert_eq!(not_persisted_markers(out), vec!["skipped: neither"]);
}
#[test]
fn a_clean_run_carries_no_marker() {
let out = "⚡ running nornir-bench in /home/rickard/git/holger\n\
✓ receipt appended → /home/rickard/git/holger/bench_history.jsonl\n\
✓ 1 result(s), 0 test(s) persisted into bench_runs as 01J…\n";
assert!(not_persisted_markers(out).is_empty());
}
#[test]
fn the_configured_checkout_is_read_out_of_the_right_section() {
let toml = "[repo.korp]\npath = \"/home/rickard/git/korp\"\n\
[repo.holger]\npath = \"/home/rickard/git/holger\"\nprivate = true\n\
[repo.holger-ui]\npath = \"/home/rickard/git/holger-ui\"\n";
assert_eq!(
configured_path(toml, "holger").as_deref(),
Some("/home/rickard/git/holger")
);
assert_eq!(
configured_path(toml, "korp").as_deref(),
Some("/home/rickard/git/korp")
);
assert_eq!(configured_path(toml, "nosuch"), None);
}
/// `[repo.holger-ui]` must not answer for `[repo.holger]` — a prefix match
/// here would measure the wrong repository.
#[test]
fn a_longer_repo_name_does_not_answer_for_a_shorter_one() {
let toml = "[repo.holger-ui]\npath = \"/home/rickard/git/holger-ui\"\n";
assert_eq!(configured_path(toml, "holger"), None);
}
#[test]
fn mode_is_one_environment_variable() {
// Not read from the process env in the test (that would be global
// state); the mapping itself is what matters.
assert_eq!(
Mode::Central("http://oden:7878".into()).landed_in(),
"the `bench_runs` table on http://oden:7878"
);
assert_eq!(
Mode::Local.landed_in(),
"the local iceberg `bench_runs` table"
);
}
#[test]
fn unknown_flags_are_refused_rather_than_ignored() {
let err = parse_args(&["--onlt".to_string()]).unwrap_err();
assert!(err.contains("unknown argument"), "{err}");
assert!(
parse_args(&["--allow-foreign-checkout".to_string()])
.unwrap()
.allow_foreign_checkout
);
let a = parse_args(&[
"--heavy".to_string(),
"--config".to_string(),
"/x.toml".to_string(),
])
.unwrap();
assert!(a.heavy);
assert_eq!(a.config.as_deref(), Some("/x.toml"));
}
/// THE GATE, measured 2026-09-20: `[repo.holger]` links the MAIN checkout, so
/// a lane that runs `cargo xtask bench` from its own worktree measures main.
/// One lane's three arms became one that way, with a green exit and a landed
/// row. The door now says which checkout it linked and refuses by name.
#[test]
fn a_config_that_links_another_checkout_is_refused_by_name() {
let lane = PathBuf::from("/home/rickard/git/holger-audit");
let main = PathBuf::from("/home/rickard/git/holger");
let v = checkout_verdict(Some(&lane), Some(&main));
assert_eq!(
v,
Checkout::Foreign {
asked: lane.clone(),
linked: main.clone()
}
);
let said = v
.refusal(Path::new(
"/home/rickard/git/workspace_nordisk/release/nornir.toml",
))
.expect("a foreign checkout must be refused");
assert!(
said.contains("holger-audit"),
"the refusal must name the tree asked about: {said}"
);
assert!(
said.contains("/home/rickard/git/holger\n") || said.contains("git/holger "),
"the refusal must name the tree it linked: {said}"
);
assert!(
said.contains("release/nornir.toml"),
"and the config that links it: {said}"
);
assert!(
said.contains("--allow-foreign-checkout"),
"and the way out: {said}"
);
}
/// The matching case says nothing and measures.
#[test]
fn the_linked_checkout_is_measured_without_ceremony() {
let p = PathBuf::from("/home/rickard/git/holger");
let v = checkout_verdict(Some(&p), Some(&p));
assert_eq!(v, Checkout::Linked);
assert_eq!(v.refusal(Path::new("/x.toml")), None);
}
/// A configured checkout that does not exist cannot be the tree you are
/// standing in, so it is a mismatch rather than a pass.
#[test]
fn an_unresolvable_checkout_is_a_mismatch_not_a_pass() {
let p = PathBuf::from("/home/rickard/git/holger");
assert!(matches!(
checkout_verdict(Some(&p), None),
Checkout::Foreign { .. }
));
assert!(matches!(
checkout_verdict(None, Some(&p)),
Checkout::Foreign { .. }
));
assert!(matches!(
checkout_verdict(None, None),
Checkout::Foreign { .. }
));
}
}