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

nordisk/facett

facett-graph3d/tests/a11y_determinism.rs

//! **FC-4 + FC-7** interaction-layer proofs for `Graph3D`.
//!
//! * FC-4: the drag-to-rotate canvas is an AccessKit Image node carrying the
//!   current yaw (radians) as its numeric value — readable, not just paintable.
//! * FC-7: with an **injected clock**, the spin yaw is a pure function of the
//!   clock; two renders of the same state at the same clock produce an identical
//!   AccessKit tree (same yaw value). Stepping the clock changes it deterministically.

use egui::Color32;
use egui_kittest::Harness;
use egui_kittest::kittest::{NodeT, Queryable};

use facett_core::Facet;
use facett_graph3d::{Graph3D, Layout3D};

fn sample() -> Graph3D {
    let nodes: Vec<(String, Color32)> = (0..6).map(|i| (format!("n{i}"), Color32::from_rgb(0, 200, 220))).collect();
    let edges: Vec<(usize, usize)> = (0..6).map(|i| (i, (i + 1) % 6)).collect();
    Graph3D::new(nodes, edges, Layout3D::Sphere)
}

/// The numeric yaw carried by the canvas's AccessKit Image node.
fn yaw_value(harness: &Harness) -> f64 {
    harness
        .get_all_by_role(egui::accesskit::Role::Image)
        .filter_map(|n| n.accesskit_node().numeric_value())
        .next()
        .expect("the 3D canvas exposes an Image node carrying the yaw value")
}

#[test]
fn canvas_exposes_yaw_as_an_accesskit_value() {
    let mut g = sample().with_clock(0.0); // clock 0 ⇒ spin offset 0 ⇒ yaw == base (0.6)
    let mut harness = Harness::builder().with_size(egui::vec2(400.0, 400.0)).build_ui(move |ui| g.ui(ui));
    harness.run();
    let yaw = yaw_value(&harness);
    assert!((yaw - 0.6).abs() < 1e-5, "at clock 0 the yaw is the base 0.6, got {yaw}");
    // Feed the matrix: ok iff the canvas exposes the yaw value as an a11y node.
    #[cfg(feature = "testmatrix")]
    facett_core::testmatrix::emit(
        "Graph3D",
        "fc4_yaw_accesskit_value",
        (yaw - 0.6).abs() < 1e-5,
        &format!("canvas Image node yaw={yaw} at clock 0"),
    );
}

#[test]
fn injected_clock_yaw_is_deterministic_and_steps() {
    // Same state, same injected clock → identical yaw (FC-7).
    let mut a = sample().with_clock(2.0);
    let mut b = sample().with_clock(2.0);
    let mut ha = Harness::builder().with_size(egui::vec2(400.0, 400.0)).build_ui(move |ui| a.ui(ui));
    let mut hb = Harness::builder().with_size(egui::vec2(400.0, 400.0)).build_ui(move |ui| b.ui(ui));
    ha.run();
    hb.run();
    let ya = yaw_value(&ha);
    let yb = yaw_value(&hb);
    assert_eq!(ya.to_bits(), yb.to_bits(), "same state + clock ⇒ identical yaw (FC-7), {ya} vs {yb}");
    // The spin yaw advances with the clock: base 0.6 + clock·spin_rate(0.4) = 1.4.
    assert!((ya - (0.6 + 2.0 * 0.4)).abs() < 1e-5, "yaw = base + clock·rate, got {ya}");

    // A later clock gives a strictly larger yaw — deterministic stepping.
    let mut c = sample().with_clock(5.0);
    let mut hc = Harness::builder().with_size(egui::vec2(400.0, 400.0)).build_ui(move |ui| c.ui(ui));
    hc.run();
    let yc = yaw_value(&hc);
    assert!(yc > ya, "advancing the injected clock advances the yaw");
    // Feed the matrix: ok iff yaw is deterministic at a fixed clock AND steps.
    #[cfg(feature = "testmatrix")]
    facett_core::testmatrix::emit(
        "Graph3D",
        "fc7_clock_determinism",
        ya.to_bits() == yb.to_bits() && yc > ya,
        &format!("clock2 yaw={ya} (==dup) clock5 yaw={yc} (>)"),
    );
}