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

nordisk/nornir

crates/mimir-room/tests/room/shop.rs

//! A second hand-built log with everything a READER needs to see: real
//! monikers, names and parents, spans and edge sites, site counts, tests of
//! a harness unit, a `cfg` gate, linked symbols with sizes, and two kinds of
//! repeated key. Written through mimir-ingest's own consumer.
//!
//! repo `shop`, crate shop@0.1.0 — units `lib` and `lib+test`, one artifact:
//!
//! ```text
//! shop                      src/lib.rs
//! ├─ checkout()               10–20    CALLS total ×1 · charge ×3 (lines 14 15 18)   USES_TYPE Cart
//! ├─ cart/                  src/cart.rs
//! │  ├─ Cart { items }        3–6
//! │  ├─ impl Cart             8–30     FOR_TYPE Cart
//! │  │  ├─ add(item)          9–14
//! │  │  └─ total()           16–22     CALLS cart::open
//! │  ├─ open()               40–44
//! │  ├─ helper()             46–47     the dump repeats this key (two payloads)
//! │  └─ tests/ adds_up()     50–58     #[test]  CALLS add ×2 · total   TESTS add · total
//! └─ pay/                   src/pay.rs
//!    ├─ charge()              5–20     cfg(feature = "stripe")  CALLS stripe send · audit ×2
//!    ├─ audit()              22–30     CALLS libc_write   (lib and lib+test describe it differently)
//!    ├─ open()               32–36
//!    ├─ charges_once()       40–46     #[test]  CALLS charge   TESTS charge
//!    └─ libc_write()         48        ffi
//! stripe@2.0.0 Client#send()           reference stub (its module is not in the room)
//! linked: checkout 120 B · charge 300 B + its closure 40 B · import `write`
//! seq 2: charge moves two lines down (spans only)
//! ```
//!
//! repo `web`, crate web@0.2.0, seq 1: `render()` CALLS shop checkout (stub).

use std::collections::BTreeSet;
use std::path::Path;

use mimir_ingest::{BinaryDump, CommitDump, Consumer, CrateDump, Dumper, EventLog};
use mimir_schema::{EdgeKind, EdgeRow, EffectiveVisibility, Level, NodeRow, Origin, Provenance, SPAN_EDGE, SPAN_NODE, SpanRow, SymbolRow, edge_key, moniker, role, visibility};
use skade::arrow_array::RecordBatch;

use super::common::{open_log, repo_spec, scratch};

pub const SHOP: &str = "shop";
pub const WEB: &str = "web";
pub const LIB: &str = "shop@0.1.0:lib";
pub const TEST: &str = "shop@0.1.0:lib+test";
pub const ARTIFACT: &str = "shop@x86_64-fixture:dev";

/// A shop moniker from its descriptors.
pub fn m(d: &str) -> String {
    moniker::moniker("shop", "0.1.0", d)
}

pub fn send() -> String {
    moniker::moniker("stripe", "2.0.0", "Client#send().")
}
pub fn stripe() -> String {
    moniker::crate_moniker("stripe", "2.0.0")
}
pub fn render() -> String {
    moniker::moniker("web", "0.2.0", "render().")
}
pub fn web() -> String {
    moniker::crate_moniker("web", "0.2.0")
}

pub const CRATE: &str = "crate/";
pub const CHECKOUT: &str = "checkout().";
pub const CART: &str = "cart/";
pub const CART_T: &str = "cart/Cart#";
pub const ITEMS: &str = "cart/Cart#items.";
pub const IMPL: &str = "cart/impl#[Cart]";
pub const ADD: &str = "cart/impl#[Cart]add().";
pub const ADD_ITEM: &str = "cart/impl#[Cart]add().(item)";
pub const TOTAL: &str = "cart/impl#[Cart]total().";
pub const CART_OPEN: &str = "cart/open().";
pub const HELPER: &str = "cart/helper().";
pub const TESTS_MOD: &str = "cart/tests/";
pub const ADDS_UP: &str = "cart/tests/adds_up().";
pub const PAY: &str = "pay/";
pub const CHARGE: &str = "pay/charge().";
pub const AUDIT: &str = "pay/audit().";
pub const PAY_OPEN: &str = "pay/open().";
pub const CHARGES: &str = "pay/charges_once().";
pub const WRITE: &str = "pay/libc_write().";

struct N(NodeRow);

fn n(id: String, kind: &str, level: Level, name: &str, parent: Option<String>) -> N {
    let (krate, version) = moniker::parse(&id).map(|(p, v, _)| (p.to_string(), v.to_string())).unwrap_or(("extern".into(), "0".into()));
    let mut row = NodeRow::new(id, kind, level, name, krate, version, Origin::Local);
    row.parent = parent;
    N(row)
}

impl N {
    fn ty(mut self, t: &str) -> Self {
        self.0.ty = Some(t.into());
        self
    }
    fn cfg(mut self, c: &str) -> Self {
        self.0.cfg = Some(c.into());
        self
    }
    /// A `#[test]` fn (not `#[ignore]`d).
    fn test(mut self) -> Self {
        self.0.is_test = true;
        self.0.ignored = Some(false);
        self
    }
    /// `#[ignore]` on a test.
    fn ignored(mut self) -> Self {
        self.0.ignored = Some(true);
        self
    }
    /// As a lake evolved from mimir-schema 2 holds a row it has not consumed
    /// again: the schema-3 columns have no value.
    fn before_schema_3(mut self) -> Self {
        self.0.ignored = None;
        self.0.doc_hidden = None;
        self
    }
    fn stub(mut self) -> Self {
        self.0.defined_here = false;
        self.0.origin = Origin::Library;
        self
    }
    /// Nominal + effective visibility, and whether it is documented.
    fn vis(mut self, nominal: &str, effective: EffectiveVisibility, doc: Option<&str>) -> Self {
        self.0.visibility = Some(nominal.into());
        self.0.effective_visibility = Some(effective);
        self.0.has_doc = Some(doc.is_some());
        self.0.doc = doc.map(str::to_string);
        self.0.doc_hidden = Some(false);
        self
    }
}

fn item(d: &str, kind: &str, name: &str, parent: &str) -> N {
    n(m(d), kind, Level::Item, name, Some(m(parent)))
}

fn e(from: &str, to: &str, kind: EdgeKind, count: u32) -> EdgeRow {
    let mut row = EdgeRow::new(from, to, kind);
    row.count = count;
    row
}

fn span(id: &str, file: &str, start: u32, end: u32) -> SpanRow {
    SpanRow { target: SPAN_NODE, key: id.into(), file: file.into(), start_line: start, end_line: end }
}

fn site(from: &str, to: &str, kind: EdgeKind, file: &str, line: u32) -> SpanRow {
    SpanRow { target: SPAN_EDGE, key: edge_key(from, to, kind), file: file.into(), start_line: line, end_line: line }
}

/// The rows of one unit before they are batches (so a test can edit them).
type Rows = (Vec<N>, Vec<EdgeRow>, Vec<SpanRow>);

fn unit(owner: &str, nodes: Vec<N>, edges: Vec<EdgeRow>, spans: Vec<SpanRow>) -> CrateDump {
    let rows: Vec<NodeRow> = nodes.into_iter().map(|n| n.0).collect();
    CrateDump {
        krate: owner.into(),
        universe: false,
        nodes: vec![mimir_schema::nodes_batch(&rows).unwrap()],
        edges: vec![mimir_schema::edges_batch(&edges).unwrap()],
        spans: vec![mimir_schema::spans_batch(&spans).unwrap()],
    }
}

/// The lib unit; `moved` = charge sits two lines further down (seq 2).
fn lib(moved: bool) -> CrateDump {
    let (nodes, edges, spans) = lib_rows(moved);
    unit(LIB, nodes, edges, spans)
}

fn lib_rows(moved: bool) -> Rows {
    use EdgeKind::*;
    use EffectiveVisibility::*;
    let down = if moved { 2 } else { 0 };
    let helper_again = item(HELPER, "fn", "helper", CART).ty("fn() -> u8");
    let nodes = vec![
        n(m(CRATE), "crate", Level::Crate, "shop", None),
        item(CHECKOUT, "fn", "checkout", CRATE).vis(visibility::PUB, Direct, Some("Check a cart out and charge it.")),
        n(m(CART), "mod", Level::Module, "cart", Some(m(CRATE))).vis(visibility::PUB, Direct, None),
        item(CART_T, "struct", "Cart", CART).vis(visibility::PUB, Direct, Some("A cart.")),
        n(m(ITEMS), "field", Level::Part, "items", Some(m(CART_T))).ty("Vec<Item>"),
        item(IMPL, "impl", "_", CART),
        item(ADD, "method", "add", IMPL).vis(visibility::PUB, Direct, None),
        n(m(ADD_ITEM), "param", Level::Part, "item", Some(m(ADD))).ty("Item"),
        item(TOTAL, "method", "total", IMPL).vis(visibility::PUB, Direct, Some("The sum.")),
        // `pub`, but reachable only through a re-export
        item(CART_OPEN, "fn", "open", CART).vis(visibility::PUB, Reexported, None),
        // the SAME key twice with two payloads: a producer collision
        item(HELPER, "fn", "helper", CART),
        helper_again,
        n(m(PAY), "mod", Level::Module, "pay", Some(m(CRATE))).vis(visibility::PRIVATE, Internal, None),
        // `pub` in a private module nobody re-exports: not API
        item(CHARGE, "fn", "charge", PAY).cfg("feature = \"stripe\"").vis(visibility::PUB, Internal, None),
        item(AUDIT, "fn", "audit", PAY).vis(visibility::PRIVATE, Internal, Some("Write the audit line.")),
        item(PAY_OPEN, "fn", "open", PAY).vis(visibility::PUB_CRATE, Internal, None),
        item(WRITE, "ffi", "libc_write", PAY),
        n(stripe(), "crate", Level::Crate, "stripe", None).stub(),
        n(send(), "method", Level::Item, "send", Some(moniker::moniker("stripe", "2.0.0", "Client#"))).stub(),
    ];
    let c = |f: &str, t: &str| e(&m(f), &m(t), Contains, 1);
    let edges = vec![
        c(CRATE, CART),
        c(CRATE, PAY),
        c(CRATE, CHECKOUT),
        c(CART, CART_T),
        c(CART_T, ITEMS),
        c(CART, IMPL),
        c(IMPL, ADD),
        c(IMPL, TOTAL),
        c(ADD, ADD_ITEM),
        c(CART, CART_OPEN),
        c(CART, HELPER),
        c(PAY, CHARGE),
        c(PAY, AUDIT),
        c(PAY, PAY_OPEN),
        c(PAY, WRITE),
        e(&m(CHECKOUT), &m(TOTAL), Calls, 1),
        e(&m(CHECKOUT), &m(CHARGE), Calls, 3),
        e(&m(CHARGE), &send(), Calls, 1),
        e(&m(CHARGE), &m(AUDIT), Calls, 2),
        e(&m(AUDIT), &m(WRITE), Calls, 1),
        e(&m(TOTAL), &m(CART_OPEN), Calls, 1),
        e(&m(CHECKOUT), &m(CART_T), UsesType, 1),
        e(&m(IMPL), &m(CART_T), ForType, 1),
        e(&m(CRATE), &stripe(), DependsOn, 1),
    ];
    let spans = vec![
        span(&m(CRATE), "src/lib.rs", 1, 40),
        span(&m(CHECKOUT), "src/lib.rs", 10, 20),
        span(&m(CART), "src/cart.rs", 1, 60),
        span(&m(CART_T), "src/cart.rs", 3, 6),
        span(&m(ITEMS), "src/cart.rs", 4, 4),
        span(&m(IMPL), "src/cart.rs", 8, 30),
        span(&m(ADD), "src/cart.rs", 9, 14),
        span(&m(ADD_ITEM), "src/cart.rs", 9, 9),
        span(&m(TOTAL), "src/cart.rs", 16, 22),
        span(&m(CART_OPEN), "src/cart.rs", 40, 44),
        span(&m(HELPER), "src/cart.rs", 46, 47),
        span(&m(PAY), "src/pay.rs", 1, 60),
        span(&m(CHARGE), "src/pay.rs", 5 + down, 20 + down),
        span(&m(AUDIT), "src/pay.rs", 22, 30),
        span(&m(PAY_OPEN), "src/pay.rs", 32, 36),
        span(&m(WRITE), "src/pay.rs", 48, 48),
        site(&m(CHECKOUT), &m(TOTAL), Calls, "src/lib.rs", 12),
        site(&m(CHECKOUT), &m(CHARGE), Calls, "src/lib.rs", 14),
        site(&m(CHECKOUT), &m(CHARGE), Calls, "src/lib.rs", 15),
        site(&m(CHECKOUT), &m(CHARGE), Calls, "src/lib.rs", 18),
        site(&m(CHARGE), &send(), Calls, "src/pay.rs", 9 + down),
        site(&m(CHARGE), &m(AUDIT), Calls, "src/pay.rs", 11 + down),
        site(&m(CHARGE), &m(AUDIT), Calls, "src/pay.rs", 17 + down),
        site(&m(AUDIT), &m(WRITE), Calls, "src/pay.rs", 25),
        site(&m(TOTAL), &m(CART_OPEN), Calls, "src/cart.rs", 18),
        site(&m(CHECKOUT), &m(CART_T), UsesType, "src/lib.rs", 11),
    ];
    (nodes, edges, spans)
}

/// The test harness unit: the tests, and what it compiles again.
fn harness(moved: bool) -> CrateDump {
    let (nodes, edges, spans) = harness_rows(moved);
    unit(TEST, nodes, edges, spans)
}

fn harness_rows(moved: bool) -> Rows {
    use EdgeKind::*;
    let down = if moved { 2 } else { 0 };
    let nodes = vec![
        n(m(TESTS_MOD), "mod", Level::Module, "tests", Some(m(CART))).cfg("test"),
        item(ADDS_UP, "fn", "adds_up", TESTS_MOD).test(),
        item(CHARGES, "fn", "charges_once", PAY).test().before_schema_3(),
        // compiled again by the harness — identically …
        item(ADD, "method", "add", IMPL).vis(visibility::PUB, EffectiveVisibility::Direct, None),
        item(TOTAL, "method", "total", IMPL).vis(visibility::PUB, EffectiveVisibility::Direct, Some("The sum.")),
        item(CHARGE, "fn", "charge", PAY).cfg("feature = \"stripe\"").vis(visibility::PUB, EffectiveVisibility::Internal, None),
        // … except audit, which the test build sees with another type
        item(AUDIT, "fn", "audit", PAY).ty("fn() /* cfg(test) body */").vis(visibility::PRIVATE, EffectiveVisibility::Internal, Some("Write the audit line.")),
    ];
    let edges = vec![
        e(&m(CART), &m(TESTS_MOD), Contains, 1),
        e(&m(TESTS_MOD), &m(ADDS_UP), Contains, 1),
        e(&m(PAY), &m(CHARGES), Contains, 1),
        e(&m(ADDS_UP), &m(ADD), Calls, 2),
        e(&m(ADDS_UP), &m(TOTAL), Calls, 1),
        e(&m(CHARGES), &m(CHARGE), Calls, 1),
        e(&m(ADDS_UP), &m(ADD), Tests, 1),
        e(&m(ADDS_UP), &m(TOTAL), Tests, 1),
        e(&m(CHARGES), &m(CHARGE), Tests, 1),
        // the same edge the lib unit carries, with the same two sites
        e(&m(CHARGE), &m(AUDIT), Calls, 2),
    ];
    let spans = vec![
        span(&m(TESTS_MOD), "src/cart.rs", 49, 59),
        span(&m(ADDS_UP), "src/cart.rs", 50, 58),
        span(&m(CHARGES), "src/pay.rs", 40, 46),
        site(&m(ADDS_UP), &m(ADD), Calls, "src/cart.rs", 52),
        site(&m(ADDS_UP), &m(ADD), Calls, "src/cart.rs", 53),
        site(&m(ADDS_UP), &m(TOTAL), Calls, "src/cart.rs", 55),
        site(&m(CHARGES), &m(CHARGE), Calls, "src/pay.rs", 42),
        site(&m(CHARGE), &m(AUDIT), Calls, "src/pay.rs", 11 + down),
        site(&m(CHARGE), &m(AUDIT), Calls, "src/pay.rs", 17 + down),
    ];
    (nodes, edges, spans)
}

fn sym(id: &str, symbol: &str, demangled: Option<&str>, r: &str, size: Option<u64>) -> SymbolRow {
    SymbolRow {
        id: id.into(),
        symbol: symbol.into(),
        demangled: demangled.map(str::to_string),
        role: r.into(),
        krate: "shop".into(),
        version: "0.1.0".into(),
        binary: Some("shop".into()),
        section: size.map(|_| ".text".to_string()),
        addr: size.map(|_| 0x1000),
        size,
        provenance: Provenance::BinaryOnly,
        valid_from_sha: None,
        valid_to_sha: None,
    }
}

fn artifact() -> BinaryDump {
    let mut import = n("extern write".into(), "ffi_import", Level::Item, "write", None).0;
    import.provenance = Provenance::BinaryOnly;
    import.origin = Origin::Library;
    BinaryDump {
        artifact: ARTIFACT.into(),
        nodes: vec![mimir_schema::nodes_batch(&[import]).unwrap()],
        edges: vec![],
        symbols: vec![
            mimir_schema::symbols_batch(&[
                sym(&m(CHECKOUT), "_Rcheckout", Some("shop::checkout"), role::ITEM, Some(120)),
                sym(&m(CHARGE), "_Rcharge", Some("shop::pay::charge"), role::ITEM, Some(300)),
                sym(&m(CHARGE), "_Rcharge_closure0", None, role::CLOSURE, Some(40)),
                sym("extern write", "write", None, role::IMPORT, None),
            ])
            .unwrap(),
        ],
        ledger: None,
        ms: 0,
    }
}

fn shop_commit(moved: bool) -> CommitDump {
    CommitDump {
        toolchain: "nightly-fixture".into(),
        workspace_crates: [LIB.to_string(), TEST.to_string()].into(),
        workspace_targets: ["shop:lib".to_string(), "shop:bin:shop".to_string()].into(),
        failed_units: BTreeSet::new(),
        owner_disambiguations: 0,
        crates: vec![lib(moved), harness(moved)],
        binary: true,
        binaries: vec![artifact()],
    }
}

/// What an UNCOMMITTED tree of `shop` dumps, on top of its consumed state
/// (seq 2): every node row of the two units goes through `edit`, which may
/// change it or return `false` to drop it (the item is gone from the tree).
/// `version` other than `0.1.0` is a version bump: every id of the crate and
/// both owner ids move with it.
pub fn shop_tree(version: &str, edit: impl Fn(&mut NodeRow) -> bool) -> CommitDump {
    let (was, now) = ("shop 0.1.0 ", format!("shop {version} "));
    let at = |s: &str| s.replace(was, &now);
    let owner = |o: &str| o.replace("shop@0.1.0", &format!("shop@{version}"));
    let rebuilt = |name: &str, (nodes, edges, spans): Rows| {
        let mut rows = Vec::new();
        for mut n in nodes.into_iter().map(|n| n.0) {
            if !edit(&mut n) {
                continue;
            }
            if n.krate == "shop" {
                n.version = version.to_string();
            }
            n.id = at(&n.id);
            n.parent = n.parent.as_deref().map(at);
            rows.push(N(n));
        }
        let edges = edges.into_iter().map(|e| EdgeRow { from: at(&e.from), to: at(&e.to), ..e }).collect();
        let spans = spans.into_iter().map(|s| SpanRow { key: at(&s.key), ..s }).collect();
        unit(&owner(name), rows, edges, spans)
    };
    CommitDump {
        toolchain: "nightly-fixture".into(),
        workspace_crates: [owner(LIB), owner(TEST)].into(),
        workspace_targets: ["shop:lib".to_string(), "shop:bin:shop".to_string()].into(),
        failed_units: BTreeSet::new(),
        owner_disambiguations: 0,
        crates: vec![rebuilt(LIB, lib_rows(true)), rebuilt(TEST, harness_rows(true))],
        binary: false,
        binaries: vec![],
    }
}

fn web_commit() -> CommitDump {
    let nodes = vec![n(web(), "crate", Level::Crate, "web", None), n(render(), "fn", Level::Item, "render", Some(web())), item(CHECKOUT, "fn", "checkout", CRATE).stub()];
    let edges = vec![e(&web(), &render(), EdgeKind::Contains, 1), e(&render(), &m(CHECKOUT), EdgeKind::Calls, 1)];
    let spans = vec![span(&web(), "src/lib.rs", 1, 9), span(&render(), "src/lib.rs", 3, 8), site(&render(), &m(CHECKOUT), EdgeKind::Calls, "src/lib.rs", 5)];
    CommitDump {
        toolchain: "nightly-fixture".into(),
        workspace_crates: ["web@0.2.0:lib".to_string()].into(),
        workspace_targets: ["web:lib".to_string()].into(),
        failed_units: BTreeSet::new(),
        owner_disambiguations: 0,
        crates: vec![unit("web@0.2.0:lib", nodes, edges, spans)],
        binary: false,
        binaries: vec![],
    }
}

/// Hands out prepared dumps, one per commit.
pub struct Prepared(pub Vec<CommitDump>);

impl Dumper for Prepared {
    fn dump(&mut self, _: &Path, _: &str) -> anyhow::Result<CommitDump> {
        Ok(self.0.remove(0))
    }
}

pub async fn consume_dumps(log: EventLog, tmp: &Path, name: &str, dumps: Vec<CommitDump>) -> EventLog {
    scratch(&tmp.join(format!("src-{name}")), dumps.len());
    let mut c = Consumer::boot(log, Prepared(dumps)).await.unwrap();
    let rep = c.run(&repo_spec(tmp, name)).await.unwrap();
    assert!(rep.iter().all(|r| r.status == "ok"), "{rep:?}");
    c.into_parts().0
}

/// `shop` seq 1..2 (seq 2 moves `charge`), `web` seq 1.
pub async fn shop_log(tmp: &Path) -> EventLog {
    let log = open_log(tmp).await;
    let log = consume_dumps(log, tmp, SHOP, vec![shop_commit(false), shop_commit(true)]).await;
    consume_dumps(log, tmp, WEB, vec![web_commit()]).await
}

/// A node batch with a column no producer writes — what a producer might add
/// next (a fn's arity).
pub fn nodes_with_extras(rows: &[(NodeRow, Option<u32>)]) -> RecordBatch {
    use skade::arrow_array::{ArrayRef, UInt32Array};
    use skade::arrow_schema::{DataType, Field, Schema};
    use std::sync::Arc;
    let base = mimir_schema::nodes_batch(&rows.iter().map(|r| r.0.clone()).collect::<Vec<_>>()).unwrap();
    let mut fields: Vec<Field> = base.schema().fields().iter().map(|f| f.as_ref().clone()).collect();
    fields.push(Field::new("arity", DataType::UInt32, true));
    let mut cols: Vec<ArrayRef> = base.columns().to_vec();
    cols.push(Arc::new(UInt32Array::from(rows.iter().map(|r| r.1).collect::<Vec<_>>())));
    RecordBatch::try_new(Arc::new(Schema::new(fields).with_metadata(base.schema().metadata().clone())), cols).unwrap()
}

/// A moniker of the tiny crate `v`.
pub fn v(d: &str) -> String {
    moniker::moniker("v", "1.0.0", d)
}

/// `v`'s cargo feature `fast`.
pub fn v_fast() -> String {
    moniker::feature("v", "1.0.0", "fast")
}

/// The item `v` compiles OUT when `fast` is on: a token-level stub.
pub fn v_legacy() -> String {
    moniker::cfg_gated_out(&v(""), "legacy().", "cfg(not(feature = \"fast\"))")
}

/// The node rows of the tiny crate `v` (each with its hypothetical `arity`):
///
/// ```text
/// v                    feature `fast` (declared, and compiled with)
/// ├─ api()      pub, documented          #[cfg(feature = "fast")]  → GATED_BY fast
/// ├─ inner()    private, undocumented    a `cfg!(feature = "fast")` inside → GATED_BY fast (partial)
/// ├─ slow_test  #[test] #[ignore]
/// └─ legacy()   #[cfg(not(feature = "fast"))]: compiled OUT → a cfg_gated_out stub, GATED_BY fast (negated)
/// ```
pub fn extras_rows() -> Vec<(NodeRow, Option<u32>)> {
    use EffectiveVisibility::*;
    let root = Some(v("crate/"));
    let mut legacy = n(v_legacy(), mimir_schema::kind::CFG_GATED_OUT, Level::Item, "legacy", root.clone()).cfg("not(feature = \"fast\")").ty("fn").0;
    legacy.visibility = Some(visibility::PUB.into());
    vec![
        (n(v("crate/"), "crate", Level::Crate, "v", None).0, None),
        (n(v("api()."), "fn", Level::Item, "api", root.clone()).cfg("feature = \"fast\"").vis(visibility::PUB, Direct, Some("The API.")).0, Some(2)),
        (n(v("inner()."), "fn", Level::Item, "inner", root.clone()).vis(visibility::PRIVATE, Internal, None).0, Some(0)),
        (n(v("slow_test()."), "fn", Level::Item, "slow_test", root.clone()).test().ignored().vis(visibility::PRIVATE, Internal, None).0, Some(0)),
        (n(v_fast(), mimir_schema::kind::FEATURE, Level::Item, "fast", root).0, None),
        (legacy, None),
    ]
}

/// One commit of the tiny crate `v` from a prepared node batch.
pub fn tiny_dump(nodes: RecordBatch) -> CommitDump {
    use EdgeKind::*;
    let gate = |from: &str, negated: bool, partial: bool| {
        let mut row = EdgeRow::new(from, v_fast(), GatedBy);
        row.negated = Some(negated);
        row.partial = Some(partial);
        row
    };
    let edges = vec![
        e(&v("crate/"), &v("api()."), Contains, 1),
        e(&v("crate/"), &v("inner()."), Contains, 1),
        e(&v("crate/"), &v("slow_test()."), Contains, 1),
        e(&v("crate/"), &v_fast(), Contains, 1),
        e(&v("crate/"), &v_legacy(), Contains, 1),
        e(&v("api()."), &v("inner()."), Calls, 1),
        e(&v("slow_test()."), &v("api()."), Calls, 1),
        e(&v("slow_test()."), &v("api()."), Tests, 1),
        gate(&v("api()."), false, false),
        gate(&v("inner()."), false, true),
        gate(&v_legacy(), true, false),
        // the build's own selection: `v` was compiled with `fast`
        e(&v("crate/"), &v_fast(), Enables, 1),
    ];
    let spans = vec![
        span(&v("crate/"), "src/lib.rs", 1, 30),
        span(&v("api()."), "src/lib.rs", 4, 8),
        span(&v("inner()."), "src/lib.rs", 10, 14),
        span(&v("slow_test()."), "src/lib.rs", 20, 24),
        span(&v_legacy(), "src/lib.rs", 16, 18),
        site(&v("api()."), &v_fast(), GatedBy, "src/lib.rs", 3),
        site(&v("inner()."), &v_fast(), GatedBy, "src/lib.rs", 12),
        site(&v_legacy(), &v_fast(), GatedBy, "src/lib.rs", 15),
        site(&v("api()."), &v("inner()."), Calls, "src/lib.rs", 6),
    ];
    CommitDump {
        toolchain: "nightly-fixture".into(),
        workspace_crates: ["v@1.0.0:lib".to_string()].into(),
        workspace_targets: ["v:lib".to_string()].into(),
        failed_units: BTreeSet::new(),
        owner_disambiguations: 0,
        crates: vec![CrateDump {
            krate: "v@1.0.0:lib".into(),
            universe: false,
            nodes: vec![nodes],
            edges: vec![mimir_schema::edges_batch(&edges).unwrap()],
            spans: vec![mimir_schema::spans_batch(&spans).unwrap()],
        }],
        binary: false,
        binaries: vec![],
    }
}

/// The tiny crate `v` in the plain mimir-schema payload.
pub fn tiny_commit() -> CommitDump {
    let rows: Vec<NodeRow> = extras_rows().into_iter().map(|r| r.0).collect();
    tiny_dump(mimir_schema::nodes_batch(&rows).unwrap())
}