//! Accepted semantic review keeps selection, layer and visible diagram coherent. use super::*; use crate::{ semantic_review::{RowEvent, State}, workflow::{Event, Workflow}, }; fn send(app: &mut App, message: Message) { let _ = app.update(message); } fn state(app: &App) -> &State { let Some(Workflow::Semantic(state)) = &app.workflow else { panic!("{}", app.status) }; state } fn row_event(app: &mut App, event: RowEvent) { let event = state(app).address_event(event); send(app, Message::Workflow(Event::Semantic(event))); } fn fixture(reverse: bool, destination_view: bool) -> (App, Id, Id, Id, Option) { let mut p = Project::empty(); let oa = p .add_element(Kind::Activity, Layer::Oa, "Confirm stable pressure", None) .unwrap(); let sa = p .add_element(Kind::Function, Layer::Sa, "Confirm safe pressure", None) .unwrap(); let trace = Id::new_v4(); p.traces.insert( trace, Trace { id: trace, alias: "confirm_trace".into(), name: "Confirmation refinement".into(), description: String::new(), oa, sa, kind: TraceKind::Refinement, baseline: None, }, ); let (source, target, source_layer, target_layer) = if reverse { (sa, oa, Layer::Sa, Layer::Oa) } else { (oa, sa, Layer::Oa, Layer::Sa) }; let from = p.ensure_diagram(source_layer); p.place(from, source, Position { x: 91., y: 143. }); let to = destination_view.then(|| { let view = p.ensure_diagram(target_layer); p.place(view, target, Position { x: 487., y: 213. }); view }); let mut app = App::with_project(p); app.page = Page::Diagram; app.layer = source_layer; app.active_diagram = Some(from); app.selected = source; app.refresh_draft(); (app, source, target, from, to) } fn name_take(app: &mut App, source: Id) -> Project { send(app, Message::StartSemanticReview(source)); let facet = FacetSelector::Header(HeaderFacet::Name); row_event(app, RowEvent::Select(facet.clone())); row_event( app, RowEvent::Decide(facet, Some(SemanticDecision::TakeSource)), ); assert!(state(app).can_apply(), "{:?}", state(app).error); let Some(Workflow::Semantic(state)) = &mut app.workflow else { unreachable!() }; let command = state.command(app.document.project()).unwrap(); execute_batch(app.document.project(), &[command]).unwrap() } #[test] fn semantic_name_take_selects_the_counterpart_view_in_both_directions_with_one_exact_history_entry() { for reverse in [false, true] { let (mut app, source, target, from, to) = fixture(reverse, true); app.search = format!("@{}", app.project().elements[&source].alias); let query = app.search.clone(); let before = app.document.snapshot(); let expected = name_take(&mut app, source); app.selected_node = app.project().diagrams[&from].nodes.keys().next().copied(); send(&mut app, Message::Workflow(Event::Apply)); assert_eq!(app.project(), &expected); assert_eq!(app.project().diagrams, before.decoded.project.diagrams); assert_eq!(app.selected, target); assert_eq!(app.layer, app.project().elements[&target].layer()); assert_eq!(app.active_diagram, to); assert_eq!( app.project().diagrams[&app.active_diagram.unwrap()].layer, app.layer ); // refresh_draft deliberately selects the target's visible placement. // The invariant is that the old-layer placement cannot survive. let placement = app.selected_node.expect("visible counterpart placement"); assert_eq!( app.project().diagrams[&to.unwrap()].nodes[&placement].element, target ); assert!(!app.project().diagrams[&from].nodes.contains_key(&placement)); assert_eq!(app.page, Page::Diagram); assert_eq!(app.search, query); assert!(app.workflow.is_none()); assert_eq!(app.document.revision(), 1); let accepted = app.document.snapshot(); assert_eq!( source_project::compile_bundle(&accepted.bundle, LIMITS) .unwrap() .decoded .project, expected ); send(&mut app, Message::Undo); assert_eq!(app.project(), &before.decoded.project); assert_eq!(app.document.bundle(), &before.bundle); assert!(!app.document.can_undo()); send(&mut app, Message::Redo); assert_eq!(app.project(), &accepted.decoded.project); assert_eq!(app.document.bundle(), &accepted.bundle); } } #[test] fn semantic_review_without_a_destination_view_clears_the_old_layer_canvas_without_creating_a_view() { let (mut app, source, target, _, _) = fixture(false, false); let expected = name_take(&mut app, source); send(&mut app, Message::Workflow(Event::Apply)); assert_eq!(app.project(), &expected); assert_eq!(app.selected, target); assert_eq!(app.layer, Layer::Sa); assert_eq!(app.active_diagram, None); assert_eq!(app.project().diagrams.len(), 1); } #[test] fn review_already_showing_the_target_keeps_its_camera_and_query() { let (mut app, source, target, _, to) = fixture(false, true); app.layer = Layer::Sa; app.active_diagram = to; app.selected = target; app.zoom = 1.31; app.pan = iced::Vector::new(-219., 88.); app.search = "pressure".into(); app.refresh_draft(); let expected = name_take(&mut app, source); send(&mut app, Message::Workflow(Event::Apply)); assert_eq!(app.project(), &expected); assert_eq!(app.active_diagram, to); assert_eq!(app.zoom, 1.31); assert_eq!(app.pan, iced::Vector::new(-219., 88.)); assert_eq!(app.search, "pressure"); } #[test] fn rejected_semantic_source_writeback_retains_review_camera_and_original_navigation() { let (mut app, source, _, from, _) = fixture(false, true); let bundle = app.document.bundle().clone(); let mut limits = source_project::ProjectLimits::default(); limits.model.max_source_bytes = bundle.files.values().map(String::len).sum::() + 1; app.document = Document::from_bundle(bundle, limits).unwrap(); app.zoom = 1.21; app.pan = iced::Vector::new(72., -44.); app.search = "confirmation".into(); let before = app.document.snapshot(); let _valid_model = name_take(&mut app, source); send(&mut app, Message::Workflow(Event::Apply)); assert!(Arc::ptr_eq(&before, &app.document.snapshot())); assert_eq!(app.selected, source); assert_eq!(app.layer, Layer::Oa); assert_eq!(app.active_diagram, Some(from)); assert_eq!(app.page, Page::Diagram); assert_eq!(app.zoom, 1.21); assert_eq!(app.pan, iced::Vector::new(72., -44.)); assert_eq!(app.search, "confirmation"); assert!(state(&app).error.is_some()); assert!(!app.document.can_undo()); }