use super::common::{passthrough, src, Mock};
use crate::{BuildError, GraphBuilder, PortKind, PortSpec};
#[test]
fn linear_chain_topo() {
let mut b = GraphBuilder::new();
b.add_node("a", src(PortKind::Raster))
.add_node("b", passthrough(PortKind::Raster, PortKind::Raster))
.add_node("c", passthrough(PortKind::Raster, PortKind::Raster))
.connect("a", "b", "input")
.connect("b", "c", "input")
.set_output("c");
let g = b.build().unwrap();
let order: Vec<_> = g.topo_order().iter().map(|&i| g.node_id(i)).collect();
assert_eq!(order, vec!["a", "b", "c"]);
}
#[test]
fn diamond_topo() {
let merge = Mock::new(
"merge",
vec![
PortSpec::new("left", &[PortKind::Raster]),
PortSpec::new("right", &[PortKind::Raster]),
],
PortKind::Raster,
)
.boxed();
let mut b = GraphBuilder::new();
b.add_node("a", src(PortKind::Raster))
.add_node("b", passthrough(PortKind::Raster, PortKind::Raster))
.add_node("c", passthrough(PortKind::Raster, PortKind::Raster))
.add_node("d", merge)
.connect("a", "b", "input")
.connect("a", "c", "input")
.connect("b", "d", "left")
.connect("c", "d", "right")
.set_output("d");
let g = b.build().unwrap();
let order: Vec<_> = g.topo_order().iter().map(|&i| g.node_id(i)).collect();
assert_eq!(order[0], "a");
assert_eq!(order[3], "d");
assert!(order.contains(&"b"));
assert!(order.contains(&"c"));
}
#[test]
fn type_mismatch_is_rejected() {
let mut b = GraphBuilder::new();
b.add_node("a", src(PortKind::Raster))
.add_node("b", passthrough(PortKind::Brush, PortKind::Brush))
.connect("a", "b", "input")
.set_output("b");
match b.build() {
Err(BuildError::TypeMismatch { accepts, got, .. }) => {
assert_eq!(accepts, vec![PortKind::Brush]);
assert_eq!(got, PortKind::Raster);
}
other => panic!("expected TypeMismatch, got {other:?}"),
}
}
#[test]
fn missing_required_port_is_rejected() {
let mut b = GraphBuilder::new();
b.add_node("b", passthrough(PortKind::Raster, PortKind::Raster))
.set_output("b");
match b.build() {
Err(BuildError::MissingInput { node, port }) => {
assert_eq!(node, "b");
assert_eq!(port, "input");
}
other => panic!("expected MissingInput, got {other:?}"),
}
}
#[test]
fn optional_port_may_be_unconnected() {
let opt = Mock::new(
"opt",
vec![
PortSpec::new("input", &[PortKind::Raster]),
PortSpec::new("extra", &[PortKind::Raster]).optional(),
],
PortKind::Raster,
)
.boxed();
let mut b = GraphBuilder::new();
b.add_node("a", src(PortKind::Raster))
.add_node("o", opt)
.connect("a", "o", "input")
.set_output("o");
let g = b.build().unwrap();
assert_eq!(g.len(), 2);
}
#[test]
fn cycle_is_detected() {
let mut b = GraphBuilder::new();
b.add_node("a", passthrough(PortKind::Raster, PortKind::Raster))
.add_node("b", passthrough(PortKind::Raster, PortKind::Raster))
.connect("a", "b", "input")
.connect("b", "a", "input")
.set_output("b");
match b.build() {
Err(BuildError::Cycle(_)) => {}
other => panic!("expected Cycle, got {other:?}"),
}
}
#[test]
fn unknown_port_name_is_rejected() {
let mut b = GraphBuilder::new();
b.add_node("a", src(PortKind::Raster))
.add_node("b", passthrough(PortKind::Raster, PortKind::Raster))
.connect("a", "b", "nope")
.set_output("b");
match b.build() {
Err(BuildError::UnknownPort { node, port }) => {
assert_eq!(node, "b");
assert_eq!(port, "nope");
}
other => panic!("expected UnknownPort, got {other:?}"),
}
}
#[test]
fn duplicate_edge_is_rejected() {
let mut b = GraphBuilder::new();
b.add_node("a", src(PortKind::Raster))
.add_node("c", src(PortKind::Raster))
.add_node("b", passthrough(PortKind::Raster, PortKind::Raster))
.connect("a", "b", "input")
.connect("c", "b", "input")
.set_output("b");
match b.build() {
Err(BuildError::DuplicateEdge { .. }) => {}
other => panic!("expected DuplicateEdge, got {other:?}"),
}
}
#[test]
fn pad_propagates_upstream_through_blur() {
let mut b = GraphBuilder::new();
b.add_node("src", src(PortKind::Raster))
.add_node(
"blur",
Mock::new(
"blur",
vec![PortSpec::new("input", &[PortKind::Raster])],
PortKind::Raster,
)
.with_pad_grow(24)
.boxed(),
)
.add_node("out", passthrough(PortKind::Raster, PortKind::Raster))
.connect("src", "blur", "input")
.connect("blur", "out", "input")
.set_output("out");
let g = b.build().unwrap();
let pads = g.compute_pad(8).unwrap();
let p = |id: &str| {
let ix = g
.topo_order()
.iter()
.find(|&&i| g.node_id(i) == id)
.unwrap();
pads[*ix]
};
assert_eq!(p("out"), 8);
assert_eq!(p("blur"), 8);
assert_eq!(p("src"), 32);
}
#[test]
fn pad_exceeded_errors() {
let mut b = GraphBuilder::new();
b.add_node("src", src(PortKind::Raster))
.add_node(
"blur",
Mock::new(
"blur",
vec![PortSpec::new("input", &[PortKind::Raster])],
PortKind::Raster,
)
.with_pad_grow(crate::MAX_PAD + 1)
.boxed(),
)
.connect("src", "blur", "input")
.set_output("blur");
let g = b.build().unwrap();
match g.compute_pad(0) {
Err(BuildError::PadExceeded { .. }) => {}
other => panic!("expected PadExceeded, got {other:?}"),
}
}