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

nordisk/nornir

tests/viz_warehouse_deck.rs

//! 🏢 **Warehouse Deck** pane — robot proof (THE_BIG_PLAN inject-assert LAW #1).
//!
//! Builds the real viz app, injects a `facett_warehousedeck::WarehouseDeck` (the
//! shape `WarehouseDeckPane::build` assembles from the Iceberg warehouse: two 3D
//! graphs — call graph + dependency graph — plus a populated grid), drives a REAL
//! AccessKit click onto the 🏢 Wh Deck header, and asserts the app's `state_json()`
//! reflects the deck: the active tab, the pane count, **≥2 Graph3D panes built**,
//! and **a grid populated** (rows > 0). Same pattern as the other viz-pane robot
//! tests (`robot_viz.rs`, `viz_bench_pane.rs`).
#![cfg(feature = "viz")]

use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};

use arrow::array::{Int64Array, StringArray};
use arrow::datatypes::{DataType, Field, Schema};
use arrow::record_batch::RecordBatch;
use egui_kittest::Harness;
use facett_warehousedeck::{Color32, GraphSpec, PaneData, WarehouseDeck};
use nornir::viz::UrdrThreadsApp;
use nornir_robotui::{Observable, RobotSession};
use serde_json::json;
use std::sync::Arc;

struct VizApp(UrdrThreadsApp);
impl Observable for VizApp {
    fn state_json(&self) -> serde_json::Value {
        self.0.state_json()
    }
}

fn node(label: &str) -> (String, Color32) {
    (label.to_string(), Color32::from_rgb(120, 200, 255))
}

/// The deck the pane would build from a populated warehouse: call graph (3D) +
/// dependency graph (3D) + a `bench_runs` grid with rows.
fn seeded_deck() -> WarehouseDeck {
    let mut deck = WarehouseDeck::new("🏢 Warehouse Deck").with_cols(2);
    // call graph (call_edges: caller_path → callee_ident)
    deck.add_for(
        "call graph",
        "call_edges",
        PaneData::Graph(GraphSpec::new(
            vec![node("main"), node("run_ops"), node("scan"), node("append")],
            vec![(0, 1), (1, 2), (2, 3), (1, 3)],
        )),
    );
    // dependency graph (dep_graph_edges: from_repo → to_repo)
    deck.add_for(
        "dependency graph",
        "dep_graph_edges",
        PaneData::Graph(GraphSpec::new(
            vec![
                node("nornir"),
                node("facett"),
                node("skade"),
                node("znippy"),
            ],
            vec![(0, 1), (0, 2), (2, 3)],
        )),
    );
    // a populated bench_runs grid
    let batch = RecordBatch::try_new(
        Arc::new(Schema::new(vec![
            Field::new("run_id", DataType::Utf8, false),
            Field::new("passed", DataType::Int64, false),
        ])),
        vec![
            Arc::new(StringArray::from(vec!["run-1", "run-2", "run-3"])),
            Arc::new(Int64Array::from(vec![10, 9, 10])),
        ],
    )
    .unwrap();
    deck.add_for("bench_runs", "bench_runs", PaneData::Grid(vec![batch]));
    deck
}

fn robot() -> RobotSession<'static, VizApp> {
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("NORNIR_VIZ_NO_DURABLE_ACTIONLOG", "1") };
    static SEQ: AtomicU64 = AtomicU64::new(0);
    let n = SEQ.fetch_add(1, Ordering::Relaxed);
    let warehouse_root =
        std::env::temp_dir().join(format!("nornir-whdeck-{}-{n}", std::process::id()));
    let _ = std::fs::create_dir_all(&warehouse_root);

    let mut app = UrdrThreadsApp::with_repos(
        warehouse_root,
        "nornir".to_string(),
        PathBuf::new(),
        vec!["nornir".to_string()],
    );
    // Inject the deck (the warehouse-read seam) — selects the WarehouseDeck tab.
    app.inject_warehouse_deck_for_test(seeded_deck());

    let harness = Harness::builder()
        .with_size(egui::Vec2::new(1400.0, 900.0))
        .build_ui_state(|ui, app: &mut VizApp| app.0.draw_ui(ui), VizApp(app));
    RobotSession::new(harness, "nornir")
}

#[test]
fn robot_drives_warehouse_deck_and_state_reflects_multiple_graphs_and_a_grid() {
    let mut robot = robot();
    robot.run(4); // settle layout + first paint

    // The inject selected the tab; click the header to prove the real AccessKit
    // path reaches it.
    robot
        .click_label("🏢 Wh Deck")
        .expect("🏢 Wh Deck tab header is clickable");
    assert_eq!(
        robot
            .state_at("/tab")
            .and_then(|v| v.as_str().map(String::from)),
        Some("WarehouseDeck".to_string()),
        "the real click landed on the 🏢 Warehouse Deck tab"
    );

    let deck = robot.state()["warehouse_deck"].clone();

    // ── deck pane count ────────────────────────────────────────────────────────
    assert_eq!(deck["pane_count"], json!(3), "the deck holds three panes");

    // ── ≥2 Graph3D panes built ───────────────────────────────────────────────
    assert!(
        deck["graph3d_count"].as_u64().unwrap_or(0) >= 2,
        "the deck shows multiple 3D graphs simultaneously; got {}",
        deck["graph3d_count"]
    );

    // ── a grid populated (rows > 0) ──────────────────────────────────────────
    assert_eq!(deck["grid_count"], json!(1), "one grid pane");
    let panes = deck["panes"].as_array().expect("panes array");
    let grid = panes
        .iter()
        .find(|p| p["kind"] == json!("grid"))
        .expect("a grid pane is present");
    assert_eq!(grid["table"], json!("bench_runs"));
    assert!(
        grid["rows"].as_u64().unwrap_or(0) > 0,
        "the grid is populated; got {} rows",
        grid["rows"]
    );

    // The two graph panes carry their nodes/edges (built, not empty).
    let graphs: Vec<_> = panes
        .iter()
        .filter(|p| p["kind"] == json!("graph3d"))
        .collect();
    assert_eq!(graphs.len(), 2, "exactly two 3D graph panes");
    assert!(
        graphs.iter().all(|g| g["nodes"].as_u64().unwrap_or(0) > 0),
        "graphs have nodes"
    );

    // Wall layout → every pane drew this frame (the runtrace ledger lists each).
    assert_eq!(
        deck["layout"],
        json!("wall"),
        "the deck renders as a wall (all panes at once)"
    );
    let ran = deck["deck"]["trace"]["ran"]
        .as_object()
        .cloned()
        .unwrap_or_default();
    assert!(
        ran.contains_key("deck.wall:call graph"),
        "call graph pane drew: {ran:?}"
    );
    assert!(
        ran.contains_key("deck.wall:dependency graph"),
        "dependency graph pane drew"
    );
    assert!(ran.contains_key("deck.wall:bench_runs"), "grid pane drew");

    // Land the verdict as a functional_status matrix row.
    #[cfg(feature = "testmatrix")]
    nornir_testmatrix::functional_status(
        "viz/warehouse_deck",
        "multiple_graphs_and_a_grid",
        deck["graph3d_count"].as_u64() == Some(2)
            && deck["grid_count"].as_u64() == Some(1)
            && grid["rows"].as_u64().unwrap_or(0) > 0,
        &format!(
            "panes={} graphs={} grids={} grid_rows={}",
            deck["pane_count"], deck["graph3d_count"], deck["grid_count"], grid["rows"]
        ),
    );
}