flowrs-engine/crates/flowrs-core/tests/installed_seam.rs
flowRS fd958c0547 flowRS engine, cut from flowRS 3451b065
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 19:04:39 -05:00

111 lines
3.5 KiB
Rust

//! A seam in a widget from outside flowrs-core warns, as a builtin's did.
//!
//! The builtins have no seams left (`seam_visibility.rs` locks that census), so an
//! [`install`]ed widget is the one place a program that compiles and then stops at
//! `unimplemented!` can still come from. `install` puts a widget everywhere a builtin is;
//! the diagnostics bar has to be one of those places, or its seam warning could never
//! fire again and an extension's panic would arrive unannounced.
//!
//! Its own test binary, and one test: installing is process-wide and refused once the
//! node catalog is built, so nothing else may run first.
use flowrs_core::Graph;
use flowrs_core::graph::diagnostics::{Severity, check, is_buildable};
use flowrs_core::graph::widget::{
CodegenContext, ParameterDef, PortSpec, Widget, WidgetCodegenResult, WidgetVisual,
generates_seam, install, seam_widget_ids,
};
use std::collections::HashMap;
struct Stub {
id: &'static str,
process: &'static str,
}
impl Widget for Stub {
fn id(&self) -> String {
self.id.into()
}
fn name(&self) -> String {
self.id.into()
}
fn category(&self) -> String {
"Extension".into()
}
fn inputs(&self) -> Vec<PortSpec> {
vec![]
}
fn outputs(&self) -> Vec<PortSpec> {
vec![]
}
fn parameters(&self) -> Vec<ParameterDef> {
vec![]
}
fn visual(&self) -> WidgetVisual {
WidgetVisual {
icon: String::new(),
color: "#000".into(),
min_size: (1, 1),
resizable: false,
}
}
fn codegen(&self, _: &CodegenContext, _: HashMap<String, String>) -> WidgetCodegenResult {
WidgetCodegenResult {
outputs: HashMap::new(),
imports: vec![],
init: None,
process: self.process.into(),
cleanup: None,
state: vec![],
}
}
}
#[test]
fn an_installed_seam_warns_and_an_installed_implementation_does_not() {
install(Box::new(Stub {
id: "ext.seam",
process: "unimplemented!(\"ext.seam: call the vendor SDK here\");",
}))
.expect("installs before the catalog is built");
install(Box::new(Stub {
id: "ext.done",
process: "let done = 1;",
}))
.expect("installs before the catalog is built");
assert!(generates_seam("ext.seam"));
assert!(!generates_seam("ext.done"));
assert!(!generates_seam("image.grayscale"), "a builtin that ships");
assert!(
!seam_widget_ids().contains("ext.seam"),
"an installed widget stays out of the builtin census"
);
let mut g = Graph::new();
let seam = g.add_node("ext.seam", 0.0, 0.0);
g.add_node("ext.done", 40.0, 0.0);
let warned: Vec<_> = check(&g)
.into_iter()
.filter(|d| d.message.contains("generates a seam"))
.collect();
assert_eq!(warned.len(), 1, "only the seam is flagged: {warned:?}");
let d = &warned[0];
assert_eq!(
d.node, seam,
"the warning points at the node that has the seam"
);
assert_eq!(
d.severity,
Severity::Warning,
"a seam is a warning, not an error"
);
assert!(d.message.contains("`ext.seam`"), "{}", d.message);
assert!(d.message.contains("unimplemented!"), "{}", d.message);
assert!(is_buildable(&g), "a seam must not block the build");
// The warning describes the program that is really generated.
let src = g.to_rust().expect("the graph generates");
assert!(src.contains("unimplemented!(\"ext.seam"), "{src}");
}