lmflow 0.3.2

Dataflow-graph compute engine: a pure-Rust scheduler behind a stable C ABI
Documentation
mod common;

use std::io::Write;
use std::process::{Command, Stdio};

use lmflow::{DotView, PollerOptions, PollerOverflow};

fn render_svg(dot: &str) -> Option<String> {
    let available = Command::new("dot").arg("-V").output().is_ok();
    if !available {
        assert!(
            std::env::var_os("LMFLOW_REQUIRE_GRAPHVIZ").is_none(),
            "Graphviz `dot` is required by this test run"
        );
        return None;
    }

    let mut child = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .expect("start Graphviz");
    child
        .stdin
        .take()
        .expect("Graphviz stdin")
        .write_all(dot.as_bytes())
        .expect("write DOT");
    let output = child.wait_with_output().expect("wait for Graphviz");
    assert!(
        output.status.success(),
        "Graphviz rejected DOT:\n{}",
        String::from_utf8_lossy(&output.stderr)
    );
    Some(String::from_utf8(output.stdout).expect("Graphviz SVG is UTF-8"))
}

#[test]
fn graphviz_renders_plain_and_diagnostic_svg() {
    let graph = common::graph_from_yaml(
        r#"
subgraphs:
  pair:
    nodes:
      - { name: pass, kernel: PassThrough, input_ports: [left, right], output_ports: [out] }
    input_ports: [left, right]
    output_ports: [out]
nodes:
  - { name: nested, type: pair, input_ports: [video, metadata], output_ports: [result] }
input_ports: [video, metadata]
output_ports: [result]
"#,
    )
    .unwrap();
    let _poller = graph
        .add_poller_with_options("result", PollerOptions::new(2, PollerOverflow::DropNewest))
        .unwrap();

    let Some(plain_svg) = render_svg(&graph.to_dot()) else {
        return;
    };
    assert!(plain_svg.contains("<svg"));
    assert!(plain_svg.contains("cluster_"));

    let compact_svg =
        render_svg(&graph.to_dot_with_view(DotView::Compact)).expect("Graphviz remains available");
    assert!(compact_svg.contains("<svg"));
    assert!(compact_svg.contains("node state (border)"));
    assert!(compact_svg.contains("CREATED"));
    assert!(!compact_svg.contains("diagnostics"));

    let stats_svg = render_svg(&graph.to_dot_with_stats()).expect("Graphviz remains available");
    assert!(stats_svg.contains("<svg"));
    assert!(stats_svg.contains("diagnostics"));
    assert!(stats_svg.contains("producer currently stalled"));
    assert!(stats_svg.contains("likely missing aligned input"));
    assert!(stats_svg.contains("poller: result"));
    assert!(
        stats_svg.contains("xlink:title") || stats_svg.contains("<title>"),
        "Graphviz SVG should retain hover details"
    );
}