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

nordisk/facett

facett-pipeline/tests/flow_lane.rs

use egui::{Rect, pos2, vec2};
use facett_core::Facet;
use facett_pipeline::{PipelineNode, PipelineView, Status};

#[test]
fn six_column_layout_keeps_public_pitch_and_state_shape() {
    let view = PipelineView::new(
        "chain",
        (0..6).map(|i| (format!("n{i}"), Status::Idle)).collect(),
        (1..6).map(|i| (i, i - 1)).collect(),
    );
    assert_eq!(
        view.layout_scene_centers(),
        (0..6)
            .map(|i| pos2(110.0 + i as f32 * 220.0, 68.0))
            .collect::<Vec<_>>()
    );
    assert_eq!(
        view.state_json()["dag"],
        serde_json::json!({"nodes":6,"edges":5,"columns":6,"selected":null})
    );
    assert_eq!(view.flow_scene().graph().edges[0].from, "p0");
    assert_eq!(view.flow_scene().graph().edges[0].to, "p1");
}

#[test]
fn reconstruction_and_camera_do_not_change_geometry_revision() {
    let make = || {
        PipelineView::from_nodes(
            "p",
            vec![
                PipelineNode::new("source", Status::Done),
                PipelineNode::new("sink", Status::Running).with_throughput_rows_s(0.0),
            ],
            vec![(1, 0)],
        )
    };
    let mut a = make();
    let b = make();
    let revision = a.flow_scene().generation();
    assert_eq!(revision, b.flow_scene().generation());
    a.fit_to(Rect::from_min_size(pos2(0.0, 0.0), vec2(800.0, 400.0)));
    a.pan_pixels(10.0, 20.0);
    a.set_clock(27.0);
    assert_eq!(revision, a.flow_scene().generation());
}

#[test]
fn invalid_public_edges_remain_in_state_but_do_not_enter_flow() {
    let view = PipelineView::new("p", vec![("n".into(), Status::Idle)], vec![(8, 0)]);
    assert_eq!(view.state_json()["dag"]["edges"], 1);
    assert!(view.flow_scene().graph().edges.is_empty());
}

#[test]
fn statuses_and_consumer_rates_keep_the_public_node_order() {
    use facett_flow::attr::Status as FlowStatus;
    let view = PipelineView::from_nodes(
        "p",
        vec![
            PipelineNode::new("source", Status::Done),
            PipelineNode::new("sink", Status::Failed).with_throughput_rows_s(123.0),
            PipelineNode::new(
                "unobserved",
                Status::Unknown(facett_pipeline::UnknownReason::Stale),
            ),
            PipelineNode::new("running", Status::Running),
        ],
        vec![(1, 0), (2, 1), (3, 2)],
    );
    let scene = view.flow_scene();
    assert_eq!(
        scene.status().unwrap().nodes,
        vec![
            Some(FlowStatus::Ok),
            Some(FlowStatus::Error),
            Some(FlowStatus::Slow),
            None,
        ]
    );
    assert_eq!(scene.rates().unwrap().edges, vec![Some(123.0), None, None]);
    assert_eq!(scene.edge_point(0, 0.0).unwrap(), pos2(190.0, 68.0));
    assert_eq!(scene.edge_point(0, 1.0).unwrap(), pos2(250.0, 68.0));
}

fn cpu_frame(clock: f64, rate: Option<f32>) -> String {
    let source = PipelineNode::new("source", Status::Idle);
    let mut sink = PipelineNode::new("sink", Status::Idle);
    sink.throughput_rows_s = rate;
    let mut view =
        PipelineView::from_nodes("p", vec![source, sink], vec![(1, 0)]).with_clock(clock);
    let ctx = egui::Context::default();
    let mut shapes = String::new();
    for _ in 0..3 {
        let mut output = ctx.run_ui(
            egui::RawInput {
                screen_rect: Some(Rect::from_min_size(pos2(0.0, 0.0), vec2(900.0, 500.0))),
                ..Default::default()
            },
            |ui| {
                egui::CentralPanel::default().show(ui, |ui| view.ui(ui));
            },
        );
        shapes = format!("{:?}", output.shapes);
        output.textures_delta.clear();
    }
    shapes
}

#[test]
fn cpu_packets_obey_injected_clock_and_zero_rate() {
    let first = cpu_frame(0.0, None);
    assert_eq!(first, cpu_frame(0.0, None));
    assert_ne!(
        first,
        cpu_frame(1.0, None),
        "unspecified throughput preserves flow packets"
    );
    assert_eq!(
        cpu_frame(0.0, Some(0.0)),
        cpu_frame(1.0, Some(0.0)),
        "dead edge has no moving packets"
    );
}

#[test]
fn accessibility_budget_does_not_remove_visible_geometry() {
    use egui_kittest::kittest::{NodeT, Queryable};
    let mut view = PipelineView::new(
        "many stages",
        (0..2_050)
            .map(|i| (format!("stage{i}"), Status::Idle))
            .collect(),
        Vec::new(),
    )
    .with_animation(false);
    // Stack all 2,050 rows inside the viewport at a deliberately tiny overview scale.
    view.set_camera(
        facett_core::nav::Navigable {
            scale: 0.002,
            offset: [100.0, 100.0],
            min_scale: 0.001,
            ..Default::default()
        },
        true,
    );
    assert_eq!(
        view.visible_node_count(Rect::from_min_size(pos2(0.0, 0.0), vec2(900.0, 600.0))),
        2_050
    );
    let mut harness = egui_kittest::Harness::builder()
        .with_size(vec2(900.0, 600.0))
        .build_ui(move |ui| view.ui(ui));
    harness.run();
    let stages = harness
        .get_all_by_role(egui::accesskit::Role::Button)
        .filter(|n| {
            n.accesskit_node()
                .label()
                .is_some_and(|l| l.starts_with("stage"))
        })
        .count();
    assert_eq!(stages, 2_000);
}