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

nordisk/dwarves

dwarves-gui/src/benches.rs

//! **The Testmatrix + Benches tab** (task #66) — the testmatrix + bench-trend of the
//! **SELECTED workspace**, where a human watches the 100-bench mimir epic (#62) go green.
//!
//! Thin host wrapper over the reuse pane [`dwarves_embed::BenchesView`]: it holds that
//! pane (built from the selected workspace's testmatrix + bench-history files) and, when
//! the warehouse selector broadcasts `{"set_workspace":…}`, RELOADS both halves for the
//! newly-selected workspace — the SAME binding gesture the Release tab uses. The pane
//! itself owns the two REUSE consumptions:
//!   * `facett_matrix::MatrixView` for the testmatrix grid (surface × coverage cells);
//!   * the `nornir_bench` read API for the `nornir.mimir.<populate|search>` trend.
//!
//! Nothing here re-implements a grid or a bench harness. The workspace/source oracle
//! lives in the pane's own `state_json` (`selected_workspace`, `source_matrix`,
//! `source_benches`), so this wrapper only re-binds + forwards.

use facett_core::{Facet, Panel};

/// The Testmatrix + Benches tab: the reused pane, re-bound to the selected workspace.
pub struct BenchesTab {
    /// The reused `dwarves_embed::BenchesView` pane for the current workspace.
    inner: Box<dyn Panel>,
    /// The active workspace the pane is bound to (`""` = none → each half named empty).
    workspace: String,
}

impl BenchesTab {
    /// Bind to `workspace` (may be `""`) and load its testmatrix + benches now.
    pub fn new(workspace: impl Into<String>) -> Self {
        let workspace = workspace.into();
        Self {
            inner: dwarves_embed::benches_panel(&workspace),
            workspace,
        }
    }

    /// The tab with nothing selected — the honest UNBOUND tab (tests / pre-selection
    /// shell). It was called `fixture()` and the reused pane behind it drew one; the
    /// embed side deleted both fixtures on 2026-09-10, so with nothing selected each
    /// half now carries its own named empty state instead.
    #[allow(dead_code)]
    pub fn unbound() -> Self {
        Self::new("")
    }

    /// **Re-bind the pane to `workspace`** — rebuild the reused pane from that
    /// workspace's testmatrix + bench-history files (a NAMED empty on absence, never a
    /// fixture — both were deleted 2026-09-10). Never panics.
    pub fn bind(&mut self, workspace: &str) {
        self.workspace = workspace.to_string();
        self.inner = dwarves_embed::benches_panel(workspace);
    }

    /// The workspace the pane is bound to (`""` = none → each half named empty).
    #[allow(dead_code)]
    pub fn workspace(&self) -> &str {
        &self.workspace
    }
}

impl Facet for BenchesTab {
    fn title(&self) -> &str {
        "Testmatrix + Benches"
    }

    fn ui(&mut self, ui: &mut egui::Ui) {
        self.inner.ui(ui);
    }

    fn state_json(&self) -> serde_json::Value {
        // The reused pane's state is already self-complete (component, matrix, benches,
        // selected_workspace, source_matrix/source_benches) — pass it through.
        self.inner.state_json()
    }

    fn update_json(&mut self, msg_json: &str) {
        if let Ok(v) = serde_json::from_str::<serde_json::Value>(msg_json) {
            // The selector's broadcast (the SAME `{"set_workspace":…}` every tab gets):
            // re-bind to the newly-selected workspace.
            if let Some(ws) = v.get("set_workspace").and_then(|t| t.as_str()) {
                self.bind(ws);
                return;
            }
        }
        // Anything else forwards to the reused pane (repo drill-in select/clear, etc.).
        self.inner.update_json(msg_json);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tabbed::TabbedView;

    /// ROBOT SCENARIO (task #66) — the **Testmatrix + Benches** view mounted in the shell
    /// and driven over the remote seam (`state_json`/`update_json`), the SAME channel a
    /// `nornir-robotui` scenario / thin client use. Asserts BOTH reuse consumptions reach
    /// the tab: (1) the reused `facett_matrix::MatrixView` grid folds under `matrix` (real
    /// coloured cells); (2) the `nornir_bench`-read mimir trend carries populate + search
    /// benches, improving newest-vs-first. Then re-binds to a workspace WITH real files and
    /// asserts the tab reflects THAT run (source → `workspace-run`). Emits
    /// a `nornir-testmatrix` cell (reuse edda).
    #[test]
    fn robot_testmatrix_benches_view_over_the_seam() {
        // Hold ENV_LOCK: this test SEEDS a bench-history `.jsonl` under
        // `std::env::temp_dir()` (`workspace_bench_history_path`) then reads it BACK
        // asserting `source_benches == "workspace-run"`. A concurrent TMPDIR repoint (the
        // Release `scratch_tmp` tests, the hermetic robot_remote shell) would make the
        // read-back miss the file and miss it — serialize on the shared lock.
        let _env = crate::ENV_LOCK.lock().unwrap_or_else(|p| p.into_inner());
        let view: Box<dyn Panel> = Box::new(BenchesTab::unbound());
        let mut app = TabbedView::new(
            "dwarves — Testmatrix + Benches",
            vec![("Benches".into(), view)],
        );

        Facet::update_json(&mut app, r#"{"set_tab":"Benches"}"#);
        let st = Facet::state_json(&app);
        let pane = &st["benches"];

        // (1) + (2): ROWS OR A NAMED EMPTY, per half, in BOTH worlds.
        //
        // These two used to assert the deleted fixtures — `source_matrix == "fixture"`
        // with >= 3 repos and a green cell, and `source_benches == "fixture"` with 4
        // benches over 2 runs, all improving. Those were the numbers the shipped tab
        // drew when nothing had been measured.
        //
        // Replacing them with an assertion of emptiness would be the same fault
        // inverted. This mounts the SHIPPED tab against the real workspace paths, so on
        // a box that HAS run `nornir test` or a bench, the files exist and the halves
        // correctly have rows; a test demanding emptiness is green only where nothing
        // has ever run and red on the forge.
        //
        // The disjunction is what actually holds everywhere, and it is the property
        // worth having: each half either shows rows it can source, or names the feed it
        // could not read. Never numbers with no provenance, and never a silent blank.
        let half_ok = |rows: u64, empty: &serde_json::Value, source: &serde_json::Value| {
            let has_rows = rows > 0;
            let named_empty = rows == 0
                && empty["empty"] == true
                && !empty["feed"].as_str().unwrap_or("").is_empty()
                && !empty["detail"].as_str().unwrap_or("").is_empty()
                && source == "empty";
            // Exactly one of the two, and rows must name where they came from.
            (has_rows ^ named_empty) && (!has_rows || source != "empty")
        };
        let matrix_ok = pane["component"] == "BenchesView"
            && half_ok(
                pane["matrix"]["repo_count"].as_u64().unwrap_or(0),
                &pane["matrix_empty"],
                &pane["source_matrix"],
            )
            // The feed is named whichever way it went.
            && !pane["matrix_feed"].as_str().unwrap_or("").is_empty();

        let bench_ok = half_ok(
            pane["bench_count"].as_u64().unwrap_or(0),
            &pane["bench_empty"],
            &pane["source_benches"],
        ) && !pane["bench_feed"].as_str().unwrap_or("").is_empty();

        // (3) re-bind to a workspace WITH real files → the tab reflects THAT run.
        let ws = "dwarves-gui-benches-ws";
        let tm = dwarves_embed::workspace_testmatrix_path(ws);
        let bh = dwarves_embed::workspace_bench_history_path(ws);
        let _ = std::fs::remove_file(&tm);
        let _ = std::fs::remove_file(&bh);
        // Seed a distinct bench history (one mimir bench, one run) via the read API's writer.
        let mut m = serde_json::Map::new();
        m.insert("rows_per_sec".into(), serde_json::json!(777.0));
        let run = nornir_bench::BenchRun {
            id: nornir_bench::BenchRun::new_id(),
            date: "2026-07-18".into(),
            timestamp: None,
            version: "x".into(),
            machine: "Loki".into(),
            cores: 12,
            machine_facts: None,
            results: vec![nornir_bench::BenchResult::measured(
                "nornir.mimir.search.hybrid_rrf_5k",
                m,
            )],
            tests: Vec::new(),
        };
        nornir_bench::history::append(&bh, &run).unwrap();
        Facet::update_json(
            &mut app,
            &serde_json::json!({ "set_workspace": ws }).to_string(),
        );
        let st = Facet::state_json(&app);
        let pane = &st["benches"];
        let bound_ok = pane["selected_workspace"] == ws
            && pane["source_benches"] == "workspace-run"
            && pane["run_count"] == 1
            && pane["benches"][0]["op"] == "hybrid_rrf"
            && pane["benches"][0]["size"] == "5k"
            && pane["benches"][0]["latest"] == 777.0;
        let _ = std::fs::remove_file(&tm);
        let _ = std::fs::remove_file(&bh);

        let drivable = matrix_ok && bench_ok && bound_ok;
        assert!(
            drivable,
            "the Testmatrix+Benches view drives + re-binds over the remote seam: {st}"
        );

        #[cfg(feature = "testmatrix")]
        nornir_testmatrix::functional_status(
            "dwarves-gui",
            "testmatrix_benches_view_drivable",
            drivable,
            "Testmatrix+Benches view (task #66): reused facett_matrix::MatrixView grid (surface×coverage cells) + nornir_bench read-API-derived nornir.mimir.<populate|search> trend (improving newest-vs-first), re-binding to the selected workspace's files (a NAMED empty state per half vs workspace-run; both fixtures deleted 2026-09-10) — driven headless over state_json/update_json",
        );
        let _ = drivable;
    }
}