128 lines
4.6 KiB
Rust
128 lines
4.6 KiB
Rust
//! Dropping a wire onto an input that already has one.
|
|
//!
|
|
//! The editor added a second wire. An input takes one, so the graph became ambiguous:
|
|
//! `diagnostics` reported an error and the LOWER wire id won — the old wire kept feeding
|
|
//! the node and the one just drawn did nothing. `Graph::connect_replacing` is what the drop
|
|
//! means: the new connection takes the input over.
|
|
|
|
use flowrs_core::graph::diagnostics::{Severity, check};
|
|
use flowrs_core::{Graph, NodeId, WireId, WireMode};
|
|
|
|
/// 7 and 6 feed a Multiply, which feeds a PrintLn: the editor's starting graph.
|
|
fn seed() -> (Graph, [NodeId; 4]) {
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("IntLiteral", 60.0, 60.0);
|
|
let b = g.add_node("IntLiteral", 60.0, 180.0);
|
|
let mul = g.add_node("Multiply", 320.0, 120.0);
|
|
let print = g.add_node("PrintLn", 580.0, 120.0);
|
|
g.add_wire(a, "value", mul, "a", WireMode::Copy).unwrap();
|
|
g.add_wire(b, "value", mul, "b", WireMode::Copy).unwrap();
|
|
g.add_wire(mul, "result", print, "input", WireMode::Move)
|
|
.unwrap();
|
|
(g, [a, b, mul, print])
|
|
}
|
|
|
|
fn feeding(g: &Graph, node: NodeId, port: &str) -> Vec<(NodeId, String)> {
|
|
let mut v: Vec<_> = g
|
|
.wires
|
|
.values()
|
|
.filter(|w| w.to_node == node && w.to_port == port)
|
|
.map(|w| (w.from_node, w.from_port.clone()))
|
|
.collect();
|
|
v.sort_by_key(|(n, p)| (n.0, p.clone()));
|
|
v
|
|
}
|
|
|
|
fn ambiguous(g: &Graph) -> bool {
|
|
check(g)
|
|
.iter()
|
|
.any(|d| d.severity == Severity::Error && d.message.contains("is fed by"))
|
|
}
|
|
|
|
#[test]
|
|
fn a_drop_onto_an_occupied_input_replaces_its_wire() {
|
|
let (mut g, [a, b, mul, _]) = seed();
|
|
let (id, displaced) = g
|
|
.connect_replacing(b, "value", mul, "a", WireMode::Borrow)
|
|
.expect("both endpoints exist");
|
|
|
|
assert_eq!(feeding(&g, mul, "a"), vec![(b, "value".to_string())]);
|
|
assert_eq!(g.wires.len(), 3, "a replacement, not an addition");
|
|
assert!(!ambiguous(&g), "the input must not be left doubly fed");
|
|
assert_eq!(g.wires[&id].mode, WireMode::Borrow);
|
|
|
|
// The displaced wire is the undo record: it is the one that was there, intact.
|
|
assert_eq!(displaced.len(), 1);
|
|
assert_eq!(displaced[0].from_node, a);
|
|
assert_eq!(displaced[0].mode, WireMode::Copy);
|
|
}
|
|
|
|
#[test]
|
|
fn a_drop_onto_a_free_input_just_connects() {
|
|
let (mut g, [a, _, _, _]) = seed();
|
|
let sink = g.add_node("PrintLn", 580.0, 300.0);
|
|
let (id, displaced) = g
|
|
.connect_replacing(a, "value", sink, "input", WireMode::Borrow)
|
|
.unwrap();
|
|
assert!(displaced.is_empty());
|
|
assert_eq!(g.wires.len(), 4);
|
|
assert_eq!(g.wires[&id].to_node, sink);
|
|
// The output still feeds what it fed before: an output fans out, only inputs are
|
|
// exclusive.
|
|
assert_eq!(g.wires.values().filter(|w| w.from_node == a).count(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn redrawing_the_same_connection_keeps_its_wire_and_mode() {
|
|
let (mut g, [a, _, mul, _]) = seed();
|
|
let before: WireId = g
|
|
.wires
|
|
.values()
|
|
.find(|w| w.to_node == mul && w.to_port == "a")
|
|
.unwrap()
|
|
.id;
|
|
let (id, displaced) = g
|
|
.connect_replacing(a, "value", mul, "a", WireMode::Borrow)
|
|
.unwrap();
|
|
assert_eq!(id, before, "the existing wire is kept, not re-made");
|
|
assert!(displaced.is_empty());
|
|
assert_eq!(
|
|
g.wires[&id].mode,
|
|
WireMode::Copy,
|
|
"and its mode is not reset"
|
|
);
|
|
assert_eq!(g.wires.len(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn an_input_already_doubly_fed_is_left_with_exactly_the_new_wire() {
|
|
// Graphs saved before this exist with ambiguous inputs. Drawing onto one should
|
|
// resolve it, not displace one of the two and leave the other.
|
|
let (mut g, [_, b, mul, _]) = seed();
|
|
g.add_wire(b, "value", mul, "a", WireMode::Copy).unwrap();
|
|
assert!(ambiguous(&g));
|
|
let c = g.add_node("IntLiteral", 60.0, 300.0);
|
|
let (_, displaced) = g
|
|
.connect_replacing(c, "value", mul, "a", WireMode::Borrow)
|
|
.unwrap();
|
|
assert_eq!(displaced.len(), 2);
|
|
assert_eq!(feeding(&g, mul, "a"), vec![(c, "value".to_string())]);
|
|
assert!(!ambiguous(&g));
|
|
}
|
|
|
|
#[test]
|
|
fn a_missing_endpoint_changes_nothing() {
|
|
let (mut g, [a, _, mul, _]) = seed();
|
|
let ghost = NodeId(999);
|
|
assert!(
|
|
g.connect_replacing(ghost, "value", mul, "a", WireMode::Borrow)
|
|
.is_err()
|
|
);
|
|
assert!(
|
|
g.connect_replacing(a, "value", ghost, "a", WireMode::Borrow)
|
|
.is_err()
|
|
);
|
|
// In particular the existing wire was not disconnected on the way to the error.
|
|
assert_eq!(feeding(&g, mul, "a"), vec![(a, "value".to_string())]);
|
|
assert_eq!(g.wires.len(), 3);
|
|
}
|