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

nordisk/nornir

tests/viz_callgraph_render.rs

//! 🕸 **Call Graph lens — honest empty-workspace proof** (regression guard).
//!
//! The lens draws the code room's call rows. Over a workspace nothing was
//! consumed into it must SAY so: drawn through the real `draw_ui` path
//! (kittest) over a real warehouse, its `state_json` slice carries
//! `state == "unavailable"` and an `error` with the reader's `not_consumed` —
//! never a silent blank board, never a fake graph. (The drawn graph over a
//! consumed workspace: `viz::callgraph::tests`.) Both entries are covered: the
//! Architecture container's CallGraph sub-view and the back-compat top-level tab
//! (which must route to the call graph, not the Metro map).
//!
//! Run: `cargo test --features viz --test viz_callgraph_render -- --nocapture`
#![cfg(feature = "viz")]

use egui_kittest::Harness;
use nornir::viz::{Timeline, UrdrThreadsApp};
use nornir::warehouse::iceberg::IcebergWarehouse;

/// An empty timeline — the CallGraph lens does not read the release timeline.
fn empty_timeline() -> Timeline {
    Timeline {
        workspace_name: REPO.to_string(),
        lanes: Vec::new(),
        release_order: Vec::new(),
        release_snapshot: Default::default(),
        snapshots: Default::default(),
        latest_snapshot: None,
        bench_history: Default::default(),
    }
}

const REPO: &str = "nornir";

/// A real (empty) warehouse under a fresh temp dir; returns `(dir, warehouse root)`.
fn fresh_warehouse(tag: &str) -> (std::path::PathBuf, std::path::PathBuf) {
    let pid = std::process::id();
    let dir = std::env::temp_dir().join(format!("nornir-cg-{tag}-{pid}"));
    let _ = std::fs::remove_dir_all(&dir);
    let wh_root = dir.join("warehouse");
    std::fs::create_dir_all(&wh_root).unwrap();
    IcebergWarehouse::open(&wh_root).expect("open warehouse");
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("NORNIR_VIZ_NO_DURABLE_ACTIONLOG", "1") };
    (dir, wh_root)
}

/// The nothing-consumed contract on the lens's `state_json` slice.
fn assert_not_consumed(cg: &serde_json::Value) {
    assert!(!cg.is_null(), "the call-graph lens emits its own state slice");
    assert_eq!(cg["state"], "unavailable", "the lens states it could not read: {cg}");
    let err = cg["error"].as_str().unwrap_or_default();
    assert!(
        err.contains("[not_consumed]"),
        "the lens names the reader's reason instead of drawing a blank board: {cg}"
    );
    assert_eq!(cg["node_count"].as_u64(), Some(0), "no fake graph: {cg}");
}

/// The lens, driven the way the user does: switched to via the Architecture
/// container's CallGraph sub-view, drawn through the real frame loop.
#[test]
fn callgraph_lens_says_not_consumed() {
    let (dir, wh_root) = fresh_warehouse("subview");
    let mut app = UrdrThreadsApp::with_repos(
        wh_root,
        REPO.to_string(),
        dir.clone(),
        vec![REPO.to_string()],
    );
    assert!(
        app.inject_timeline_for_test("Architecture", empty_timeline(), vec![REPO.to_string()]),
        "the Architecture tab is selectable"
    );
    assert!(
        app.arch_set_sub_view_for_test("CallGraph"),
        "the CallGraph lens is a switchable Architecture sub-view"
    );
    let mut h = Harness::builder()
        .with_size(egui::Vec2::new(1400.0, 900.0))
        .build_ui_state(|ui, app: &mut UrdrThreadsApp| app.draw_ui(ui), app);
    for _ in 0..6 {
        h.run_ok();
    }
    let cg = h.state().state_json()["callgraph"].clone();
    assert_not_consumed(&cg);

    nornir_testmatrix::functional_status(
        "viz-callgraph",
        "callgraph_lens_says_not_consumed",
        cg["state"] == "unavailable",
        &format!("state={} error={}", cg["state"], cg["error"]),
    );
    let _ = std::fs::remove_dir_all(&dir);
}

/// The OTHER entry: the back-compat top-level 🕸 Call Graph tab (`Tab::CallGraph`)
/// must route to the call-graph lens (NOT the Metro map it was once hijacked
/// to), which states the same nothing-consumed contract.
#[test]
fn top_level_callgraph_tab_routes_to_the_call_graph_lens() {
    let (dir, wh_root) = fresh_warehouse("tab");
    let mut app = UrdrThreadsApp::with_repos(
        wh_root,
        REPO.to_string(),
        dir.clone(),
        vec![REPO.to_string()],
    );
    assert!(
        app.inject_timeline_for_test("CallGraph", empty_timeline(), vec![REPO.to_string()]),
        "the top-level CallGraph tab is selectable"
    );
    let mut h = Harness::builder()
        .with_size(egui::Vec2::new(1400.0, 900.0))
        .build_ui_state(|ui, app: &mut UrdrThreadsApp| app.draw_ui(ui), app);
    for _ in 0..6 {
        h.run_ok();
    }
    let cg = h.state().state_json()["callgraph"].clone();
    assert!(cg.get("line_count").is_none(), "must NOT be the metro slice: {cg}");
    assert_not_consumed(&cg);
    let _ = std::fs::remove_dir_all(&dir);
}