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"]
),
);
}