111 lines
3.5 KiB
Rust
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}");
|
|
}
|