Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

crates/gunnar-recipe/tests/rows_equal_cargo_install.rs

//! **Every cargo row of the Build Thing builds what its `cargo install` line builds**
//! (owner order 2026-10-26, G-BOTH-2; Holger's H5 is the model).
//!
//! Each row that compiles a binary has exactly one line in [`gunnar_recipe::twins::TWINS`];
//! the line is a local `--locked` install of the row's package with the same effective
//! features, profile and target, and it leaves exactly the row's binary behind. The
//! README shows every line. The measured half (both resolutions through `cargo tree`,
//! the same crates, the binary per row) is `cargo xtask twins`, a step of `cargo xtask ci`.

use std::path::{Path, PathBuf};

use gunnar_recipe::twins::{
    cargo_rows, point_of_install, point_of_row, twin_differences, twin_of, Manifest, TWINS,
};
use gunnar_recipe::variants::by_name;

fn repo_root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .ancestors()
        .nth(2)
        .expect("crates/gunnar-recipe has a repository root")
        .to_path_buf()
}

/// The bin and feature facts of a package's own manifest.
fn manifest(dir: &str) -> Manifest {
    let path = repo_root().join(dir).join("Cargo.toml");
    let m: toml::Table = std::fs::read_to_string(&path)
        .unwrap_or_else(|e| panic!("{}: {e}", path.display()))
        .parse()
        .unwrap_or_else(|e| panic!("{}: {e}", path.display()));
    let strings = |v: &toml::Value| -> Vec<String> {
        v.as_array()
            .map(|a| {
                a.iter()
                    .filter_map(|x| x.as_str().map(str::to_string))
                    .collect()
            })
            .unwrap_or_default()
    };
    let features = m
        .get("features")
        .and_then(|f| f.as_table())
        .cloned()
        .unwrap_or_default();
    let default_features = features
        .get("default")
        .map(strings)
        .unwrap_or_default()
        .into_iter()
        .collect();
    let local: Vec<(String, Vec<String>)> = features
        .iter()
        .filter(|(k, _)| k.as_str() != "default")
        .map(|(k, v)| {
            (
                k.clone(),
                strings(v)
                    .into_iter()
                    .filter(|f| !f.contains(':') && !f.contains('/'))
                    .collect(),
            )
        })
        .collect();
    let bins = m
        .get("bin")
        .and_then(|b| b.as_array())
        .map(|a| {
            a.iter()
                .map(|b| {
                    (
                        b["name"].as_str().unwrap_or_default().to_string(),
                        b.get("required-features").map(strings).unwrap_or_default(),
                    )
                })
                .collect()
        })
        .unwrap_or_default();
    Manifest {
        default_features,
        features: local,
        bins,
    }
}

#[test]
fn every_cargo_row_has_exactly_one_cargo_install_line() {
    let rows: Vec<&str> = cargo_rows().iter().map(|v| v.name).collect();
    assert!(rows.len() >= 15, "the cargo rows: {rows:?}");
    let missing: Vec<&str> = rows
        .iter()
        .copied()
        .filter(|r| twin_of(r).is_none())
        .collect();
    assert!(
        missing.is_empty(),
        "no `cargo install` line for the rows {missing:?}"
    );
    let stray: Vec<&str> = TWINS
        .iter()
        .map(|t| t.row)
        .filter(|r| !rows.contains(r))
        .collect();
    assert!(
        stray.is_empty(),
        "lines for rows that are not cargo rows: {stray:?}"
    );
    let mut seen = std::collections::BTreeSet::new();
    for t in TWINS {
        assert!(seen.insert(t.row), "row {} has two lines", t.row);
    }
}

#[test]
fn every_row_builds_what_its_cargo_install_line_builds() {
    let mut bad = Vec::new();
    for t in TWINS {
        let Some(v) = by_name(t.row) else {
            bad.push(format!("{}: no such row", t.row));
            continue;
        };
        let row = point_of_row(v).unwrap_or_else(|e| panic!("{e}"));
        let line = match point_of_install(t.install) {
            Ok(p) => p,
            Err(e) => {
                bad.push(format!("{}: {e}", t.row));
                continue;
            }
        };
        // The console's crate is a sibling checkout's (`../gunnar-ui`); a lone clone has
        // none, and the gunnar-ui repository holds that row to its line itself
        // (gunnar-ui-recipe `tests/the_console_product.rs`).
        if row.path.starts_with("../") && !repo_root().join(&row.path).join("Cargo.toml").is_file()
        {
            eprintln!(
                "NOT RUN for {}: no {} checkout beside this one",
                t.row, row.path
            );
            continue;
        }
        for d in twin_differences(&row, &line, &manifest(&row.path)) {
            bad.push(format!("{} (`{}`): {d}", t.row, t.install));
        }
    }
    assert!(
        bad.is_empty(),
        "a row is not its `cargo install` line:\n  {}",
        bad.join("\n  ")
    );
}

/// **The three products are rows, each its `cargo install` line** (GUN-PRODUCTS, owner
/// 2026-10-30): `cargo install gunnar` (the server), `cargo install gunnar-ui` (the console,
/// the gunnar-ui checkout's crate) and `cargo install gunnar-cli` (the command-line tool),
/// each with its crate's defaults and leaving the crate's one program.
#[test]
fn the_three_products_are_rows_and_their_lines() {
    for (row, line) in [
        ("gunnar", "cargo install --locked --path crates/gunnar"),
        (
            "gunnar-ui",
            "cargo install --locked --path ../gunnar-ui/crates/gunnar-ui",
        ),
        (
            "gunnar-cli",
            "cargo install --locked --path crates/gunnar-cli",
        ),
    ] {
        let v = by_name(row).unwrap_or_else(|| panic!("no Build Thing row `{row}`"));
        assert_eq!(
            (v.package, v.bin),
            (Some(row), Some(row)),
            "{row} builds the crate of its name and leaves its program"
        );
        assert!(
            !v.features.contains(&"metered"),
            "{row}: a product carries no money"
        );
        assert_eq!(v.target, None, "{row}: `cargo install` builds for the host");
        let t = twin_of(row).unwrap_or_else(|| panic!("{row} has no `cargo install` line"));
        assert_eq!(t.install, line, "{row}");
    }
}

#[test]
fn the_readme_shows_every_line() {
    let readme = std::fs::read_to_string(repo_root().join("crates/gunnar-recipe/README.md"))
        .expect("crates/gunnar-recipe/README.md");
    let missing: Vec<&str> = TWINS
        .iter()
        .filter(|t| !readme.contains(&format!("| `{}` | `{}` |", t.row, t.install)))
        .map(|t| t.row)
        .collect();
    assert!(
        missing.is_empty() && !TWINS.is_empty(),
        "crates/gunnar-recipe/README.md does not show the rows {missing:?} as `| `row` | `line` |`"
    );
}

/// **The crates.io server is the row `gunnar`** (OX-VARIANTS 2026-10-28, GUN-PRODUCTS
/// 2026-10-30): the README and `crates/gunnar/README.md` give `cargo install gunnar`.
#[test]
fn the_crates_io_install_is_a_row() {
    let v = by_name("gunnar").expect("the row `gunnar`");
    assert_eq!(v.package, Some("gunnar"));
    assert_eq!(
        twin_of("gunnar").map(|t| t.install),
        Some("cargo install --locked --path crates/gunnar")
    );
    for doc in ["README.md", "crates/gunnar/README.md"] {
        let text = std::fs::read_to_string(repo_root().join(doc)).unwrap();
        assert!(
            text.contains("cargo install gunnar\n"),
            "{doc} names `cargo install gunnar`"
        );
    }
}

/// **`crates/gunnar/README.md`'s feature table is the manifest's** (OX-VARIANTS 2026-10-28):
/// it is the page crates.io shows for `cargo install gunnar`, so every feature of
/// `crates/gunnar/Cargo.toml` is a row, and `(default)` stands on exactly the manifest's
/// `default` (mimalloc and SSO left the default on 2026-10-22).
#[test]
fn the_gunnar_readme_feature_table_is_the_manifest() {
    let m = manifest("crates/gunnar");
    let readme = std::fs::read_to_string(repo_root().join("crates/gunnar/README.md")).unwrap();
    let mut rows = std::collections::BTreeMap::new();
    let table = readme
        .split_once("| feature | what it adds |")
        .expect("crates/gunnar/README.md has the feature table")
        .1;
    for line in table.lines().skip(2).take_while(|l| l.starts_with('|')) {
        let Some(rest) = line.strip_prefix("| `") else {
            continue;
        };
        let Some((name, what)) = rest.split_once("` |") else {
            continue;
        };
        rows.insert(name.to_string(), what.contains("(default)"));
    }
    let mut bad = Vec::new();
    for (f, _) in &m.features {
        match rows.get(f) {
            None => bad.push(format!("`{f}` has no row")),
            Some(&d) if d != m.default_features.contains(f) => bad.push(format!(
                "`{f}`: the README says default = {d}, the manifest {}",
                m.default_features.contains(f)
            )),
            _ => {}
        }
    }
    for name in rows.keys() {
        if !m.features.iter().any(|(f, _)| f == name) {
            bad.push(format!("`{name}` is no feature of crates/gunnar"));
        }
    }
    assert!(
        bad.is_empty(),
        "crates/gunnar/README.md:\n  {}",
        bad.join("\n  ")
    );
}