//! What the graph computes, versus where it is drawn. //! //! `Graph::semantic_key` is a cache key: the editor memoizes T0 evaluation on it so that //! dragging a node does not re-run the dataflow at pointer rate. A key that misses a //! semantic field serves a STALE value, which is a worse failure than the slowness it //! exists to fix — so every field that changes what the graph computes gets a test that //! fails if the key ignores it. use flowrs_core::graph::ir::WireMode; use flowrs_core::{Graph, NodeId}; fn two_nodes() -> (Graph, NodeId, NodeId) { let mut g = Graph::new(); let a = g.add_node("IntLiteral", 0.0, 0.0); let b = g.add_node("PrintLn", 100.0, 0.0); (g, a, b) } #[test] fn moving_a_node_does_not_change_what_it_computes() { let (mut g, a, _) = two_nodes(); let before = g.semantic_key(); g.get_node_mut(a).unwrap().position = (900.0, -450.0); assert_eq!( g.semantic_key(), before, "position is not an input to any computation; if it moves the key, every drag \ re-runs the graph", ); } #[test] fn changing_a_property_changes_the_key() { let (mut g, a, _) = two_nodes(); let before = g.semantic_key(); g.get_node_mut(a) .unwrap() .properties .insert("value".into(), "7".into()); assert_ne!( g.semantic_key(), before, "a literal's value is what it computes" ); } #[test] fn a_property_changing_value_changes_the_key() { let (mut g, a, _) = two_nodes(); g.get_node_mut(a) .unwrap() .properties .insert("value".into(), "7".into()); let before = g.semantic_key(); g.get_node_mut(a) .unwrap() .properties .insert("value".into(), "8".into()); assert_ne!( g.semantic_key(), before, "7 and 8 must not hash alike — the classic stale-cache bug is a key that only \ notices a property appearing, not changing", ); } #[test] fn connecting_a_wire_changes_the_key() { let (mut g, a, b) = two_nodes(); let before = g.semantic_key(); g.add_wire(a, "value", b, "value", WireMode::Move) .expect("the two default ports should connect"); assert_ne!(g.semantic_key(), before, "a wire changes what flows where"); } #[test] fn a_wires_mode_changes_the_key() { let (mut g, a, b) = two_nodes(); let w = g .add_wire(a, "value", b, "value", WireMode::Move) .expect("connect"); let before = g.semantic_key(); g.wires.get_mut(&w).unwrap().mode = WireMode::Borrow; assert_ne!( g.semantic_key(), before, "move vs borrow is a real difference in the generated program", ); } #[test] fn adding_and_removing_a_node_changes_the_key() { let (mut g, _, _) = two_nodes(); let before = g.semantic_key(); let c = g.add_node("FloatLiteral", 0.0, 0.0); let with_node = g.semantic_key(); assert_ne!(with_node, before, "a new node changes the graph"); g.remove_node(c); assert_eq!( g.semantic_key(), before, "removing it should return the key to where it was", ); } #[test] fn the_key_is_stable_across_repeated_calls() { let (g, _, _) = two_nodes(); assert_eq!( g.semantic_key(), g.semantic_key(), "an unstable key would make the cache never hit — HashMap iteration order is the \ usual culprit, which is why nodes, wires and properties are all sorted first", ); } /// The cache holds one entry, so a graph that returns to a previous shape must return to /// its previous key or the memo silently stops hitting. #[test] fn an_unstable_key_would_show_up_as_order_dependence() { let mut a = Graph::new(); let n1 = a.add_node("IntLiteral", 0.0, 0.0); let n2 = a.add_node("FloatLiteral", 0.0, 0.0); a.get_node_mut(n1) .unwrap() .properties .insert("x".into(), "1".into()); a.get_node_mut(n1) .unwrap() .properties .insert("y".into(), "2".into()); a.get_node_mut(n2) .unwrap() .properties .insert("z".into(), "3".into()); let mut b = Graph::new(); let m1 = b.add_node("IntLiteral", 0.0, 0.0); let m2 = b.add_node("FloatLiteral", 0.0, 0.0); // Same content, inserted in a different order. b.get_node_mut(m2) .unwrap() .properties .insert("z".into(), "3".into()); b.get_node_mut(m1) .unwrap() .properties .insert("y".into(), "2".into()); b.get_node_mut(m1) .unwrap() .properties .insert("x".into(), "1".into()); assert_eq!( a.semantic_key(), b.semantic_key(), "two graphs with identical content must key alike regardless of insertion order", ); }