Files
ARCADIA-Studio/crates/oa-desktop/src/app/scenario_spacing/tests.rs
T

394 lines
15 KiB
Rust

use super::*;
use behavior::scenario::{
CanvasEvent,
spacing::{
Target,
controller::tests::{fixture, packet},
},
};
use iced::{Point, Size, Vector};
fn setup() -> (App, Id, Id) {
let (document, owner, diagram, lane, message) = fixture();
let mut app = App::with_project(document.project().clone());
let _ = app.update(Message::Select(owner));
let _ = app.update(Message::Diagram(Choice {
id: diagram,
label: "Saved".into(),
}));
let c = app.scenario_context().unwrap();
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
c,
Size::new(900., 600.),
)));
assert!(app.spacing_enabled());
(app, lane, message)
}
#[test]
fn stale_text_and_scenario_messages_do_not_retire_a_new_saved_spacing_gesture() {
let (mut app, lane, _) = setup();
let previous = app.scenario_context().unwrap();
let diagram = match previous.representation {
behavior::scenario_layout::Representation::Saved { diagram } => diagram,
_ => unreachable!(),
};
let _ = app.update(Message::Page(Page::Text));
let old_indent = Message::SourceIndent(app.source_symbol_context(), false);
let old_drawer = Message::TextWorkspace(text_workspace::Event::ShowDrawer(
app.text_drawer_context(),
text_workspace::DrawerTab::Problems,
));
let old_command = app.text_command_message(text_workspace::Command::ShowStatus);
let _ = app.update(Message::Diagram(Choice {
id: diagram,
label: "Saved".into(),
}));
let context = app.scenario_context().unwrap();
assert_ne!(context.visit, previous.visit);
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
context,
Size::new(900., 600.),
)));
let context = app.scenario_context().unwrap();
let (event, start, _) = begin(&mut app, Target::Lane(lane));
move_to(&mut app, event, start + Vector::new(24., 0.));
let accepted = app.document.snapshot();
let source = app.source_workspace.bundle();
let source_version = app.source_version;
let camera = app.camera_boundary();
let selected = app.selected;
let events = [
old_indent,
old_drawer,
old_command,
Message::ScenarioCanvas(CanvasEvent::Shelf(
previous,
behavior::scenario::Shelf::Selection,
)),
Message::ScenarioCanvas(CanvasEvent::Properties(previous, lane)),
];
for rejected in events {
let _ = app.update(rejected);
assert_eq!(app.scenario_context(), Some(context));
assert_eq!(app.behavior.scenario.spacing_token(), Some(event.token()));
assert_eq!(app.behavior.scenario.spacing_stamp(), Some(event.stamp()));
assert!(app.camera_boundary() == camera);
assert_eq!(app.selected, selected);
assert_eq!(app.source_version, source_version);
assert_eq!(app.source_workspace.bundle(), source);
assert_eq!(app.document.bundle(), &accepted.bundle);
assert_eq!(app.project(), &accepted.decoded.project);
assert!(app.text_workspace.drawer().is_none());
assert!(!app.document.can_undo() && !app.document.can_redo());
}
finish(&mut app, event, start + Vector::new(24., 0.));
assert!(
app.document.can_undo(),
"the current gesture remains usable"
);
}
fn begin(app: &mut App, target: Target) -> (Event, Point, f32) {
let event = packet(
app.project(),
&app.behavior.scenario,
app.scenario_context().unwrap(),
app.camera_epoch,
target,
);
let Event::Begin {
press_screen,
scale,
..
} = event
else {
unreachable!()
};
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Spacing(event)));
assert_eq!(app.behavior.scenario.spacing_token(), Some(event.token()));
(event, press_screen, scale)
}
fn send(app: &mut App, event: Event) {
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Spacing(event)));
}
fn move_to(app: &mut App, begin: Event, screen: Point) {
send(
app,
Event::Move {
stamp: begin.stamp(),
token: begin.token(),
screen,
},
);
}
fn finish(app: &mut App, begin: Event, screen: Point) {
send(
app,
Event::Finish {
stamp: begin.stamp(),
token: begin.token(),
screen,
},
);
}
#[test]
fn same_batch_preview_is_immutable_and_one_commit_undo_redo_has_exact_source_parity() {
let (mut app, lane, _) = setup();
let original = app.document.snapshot();
let source = app.source_workspace.bundle();
let selected = app.selected;
let epoch = app.canvas_epoch;
let (event, start, scale) = begin(&mut app, Target::Lane(lane));
move_to(&mut app, event, start + Vector::new(40., 300.));
assert_eq!(app.selected, selected);
assert_eq!(app.canvas_epoch, epoch);
assert_eq!(app.project(), &original.decoded.project);
assert_eq!(app.source_workspace.bundle(), source);
assert!(!app.document.can_undo() && !app.document.can_redo());
finish(&mut app, event, start + Vector::new(40., 300.));
assert_eq!(app.selected, lane);
assert!(app.document.can_undo() && !app.document.can_redo());
let accepted = app.document.snapshot();
assert_ne!(accepted.bundle, original.bundle);
let expected = behavior::scenario::spacing::controller::tests::expected(
&original.decoded.project,
match event.stamp().context.representation {
behavior::scenario_layout::Representation::Saved { diagram } => diagram,
_ => unreachable!(),
},
Target::Lane(lane),
40. / scale,
);
assert_eq!(app.project(), &expected);
let _ = app.update(Message::ShortcutUndo(false));
assert_eq!(app.project(), &original.decoded.project);
assert_eq!(app.document.bundle(), &original.bundle);
assert!(!app.document.can_undo() && app.document.can_redo());
let _ = app.update(Message::ShortcutUndo(true));
assert_eq!(app.project(), &accepted.decoded.project);
assert_eq!(app.document.bundle(), &accepted.bundle);
}
#[test]
fn click_selects_but_crossed_return_and_cancel_preserve_selection_camera_and_history() {
let (mut app, lane, message) = setup();
let original = app.document.snapshot();
let camera = app.behavior.scenario.camera_boundary();
let (event, start, _) = begin(&mut app, Target::Message(message));
finish(&mut app, event, start + Vector::new(30., 2.));
assert_eq!(app.selected, message);
assert!(camera == app.behavior.scenario.camera_boundary());
for cancel in [false, true] {
let (event, start, _) = begin(&mut app, Target::Lane(lane));
move_to(&mut app, event, start + Vector::new(30., 0.));
if cancel {
send(
&mut app,
Event::Cancel {
stamp: event.stamp(),
token: event.token(),
},
);
} else {
finish(&mut app, event, start);
}
assert_eq!(app.selected, message);
assert!(camera == app.behavior.scenario.camera_boundary());
assert_eq!(app.document.bundle(), &original.bundle);
assert_eq!(app.project(), &original.decoded.project);
assert!(!app.document.can_undo() && !app.document.can_redo());
}
}
#[test]
fn stale_finish_after_new_view_and_forged_token_are_inert_before_reveal_reconciliation() {
let (mut app, lane, _) = setup();
let (event, start, _) = begin(&mut app, Target::Lane(lane));
let held_epoch = app.canvas_epoch;
send(
&mut app,
Event::Finish {
stamp: event.stamp(),
token: Id::new_v4(),
screen: start + Vector::new(30., 0.),
},
);
assert_eq!(app.canvas_epoch, held_epoch);
assert_eq!(app.behavior.scenario.spacing_token(), Some(event.token()));
let owner = event.stamp().context.scenario;
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Representation(
event.stamp().context,
behavior::scenario_layout::Representation::Derived { owner },
)));
let current = app.scenario_context();
let epoch = app.canvas_epoch;
let original = app.document.snapshot();
finish(&mut app, event, start + Vector::new(50., 0.));
assert_eq!(app.scenario_context(), current);
assert_eq!(app.canvas_epoch, epoch);
assert!(app.behavior.scenario.spacing_token().is_none());
assert_eq!(app.document.bundle(), &original.bundle);
}
#[test]
fn source_write_failure_cannot_commit_candidate_or_materialize_camera() {
let (mut app, lane, _) = setup();
let bundle = app.document.bundle().clone();
let mut limits = oa_core::source_project::ProjectLimits::default();
limits.model.max_source_bytes = bundle.files.values().map(String::len).sum();
app.document = Document::from_bundle(bundle, limits).unwrap();
app.sync();
let original = app.document.snapshot();
let selected = app.selected;
let camera = app.behavior.scenario.camera_boundary();
let (event, start, _) = begin(&mut app, Target::Lane(lane));
move_to(&mut app, event, start + Vector::new(40., 0.));
finish(&mut app, event, start + Vector::new(40., 0.));
assert!(
app.status.starts_with("Spacing was not applied:"),
"{}",
app.status
);
assert_eq!(app.selected, selected);
assert!(camera == app.behavior.scenario.camera_boundary());
assert_eq!(app.project(), &original.decoded.project);
assert_eq!(app.document.bundle(), &original.bundle);
assert!(!app.document.can_undo() && !app.document.can_redo());
assert!(app.behavior.scenario.spacing_token().is_none());
}
#[test]
fn pending_semantic_draft_and_source_edit_cancel_and_reject_held_finish() {
for source in [false, true] {
let (mut app, lane, _) = setup();
let original = app.document.snapshot();
let selected = app.selected;
let (event, start, _) = begin(&mut app, Target::Lane(lane));
move_to(&mut app, event, start + Vector::new(30., 0.));
if source {
let _ = app.update(Message::SourceEdit(text_editor::Action::Edit(
text_editor::Edit::Insert(' '),
)));
assert!(app.source_dirty());
} else {
let _ = app.update(Message::Behavior(behavior::Event::Scenario(
behavior::scenario::Event::Start(
event.stamp().context.scenario,
behavior::scenario::Tool::Lifeline,
),
)));
assert!(app.behavior.has_pending());
}
let draft = app.source_workspace.bundle();
assert!(app.behavior.scenario.spacing_token().is_none());
finish(&mut app, event, start + Vector::new(30., 0.));
assert_eq!(app.project(), &original.decoded.project);
assert_eq!(app.document.bundle(), &original.bundle);
assert_eq!(app.source_workspace.bundle(), draft);
assert_eq!(app.selected, selected);
assert!(!app.document.can_undo());
}
}
#[test]
fn stale_finish_cannot_remove_new_same_view_deferred_reveal() {
let (mut app, lane, message) = setup();
let (event, start, _) = begin(&mut app, Target::Lane(lane));
let behavior::scenario_layout::Representation::Saved { diagram } =
event.stamp().context.representation
else {
unreachable!()
};
let _ = app.update(Message::RevealInDiagram(diagram, message));
assert!(app.pending_diagram_reveal.is_some());
let epoch = app.canvas_epoch;
let selected = app.selected;
let visit = app.pending_scenario_reveal_visit;
let original = app.document.snapshot();
finish(&mut app, event, start + Vector::new(50., 0.));
assert_eq!(app.canvas_epoch, epoch);
assert_eq!(app.pending_scenario_reveal_visit, visit);
assert!(app.pending_diagram_reveal.is_some());
assert_eq!(app.selected, selected);
assert_eq!(app.document.bundle(), &original.bundle);
assert!(!app.document.can_undo());
}
#[test]
fn stale_active_actions_cannot_remove_new_same_visit_deferred_reveal() {
let (mut app, lane, message) = setup();
let old = app.scenario_context().unwrap();
let behavior::scenario_layout::Representation::Saved { diagram } = old.representation else {
unreachable!()
};
let accepted = app.document.snapshot();
let source = app.source_workspace.bundle();
let _ = app.update(Message::RevealInDiagram(diagram, message));
let current = app.scenario_context().unwrap();
assert_eq!(current.visit, old.visit);
assert_eq!(current.representation, old.representation);
assert_eq!(current.revision, old.revision);
assert_ne!(current.epoch, old.epoch);
let pending = app.pending_diagram_reveal.clone();
assert!(pending.is_some());
let focus = app.diagram_navigation_focus.as_ref().unwrap();
let focus = (focus.context, focus.usage.clone(), focus.selected);
let camera = app.camera_boundary();
for event in [
CanvasEvent::Representation(old, old.representation),
CanvasEvent::Shelf(old, behavior::scenario::Shelf::Selection),
CanvasEvent::Select(old, lane),
CanvasEvent::Zoom(old, Some(1.)),
] {
let _ = app.update(Message::ScenarioCanvas(event));
assert_eq!(app.scenario_context(), Some(current));
assert_eq!(app.pending_diagram_reveal, pending);
assert_eq!(app.pending_scenario_reveal_visit, Some(current.visit));
let actual = app.diagram_navigation_focus.as_ref().unwrap();
assert_eq!(
(actual.context, actual.usage.clone(), actual.selected),
focus
);
assert!(app.camera_boundary() == camera);
assert_eq!(app.selected, message);
assert_eq!(app.document.bundle(), &accepted.bundle);
assert_eq!(app.project(), &accepted.decoded.project);
assert_eq!(app.source_workspace.bundle(), source);
assert!(!app.document.can_undo() && !app.document.can_redo());
}
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
current,
Size::new(800., 500.),
)));
assert!(app.pending_diagram_reveal.is_none());
assert!(app.pending_scenario_reveal_visit.is_none());
assert!(
app.diagram_focus(crate::diagram_usage::Surface::Saved(diagram))
.is_some()
);
assert_eq!(app.selected, message);
assert_eq!(app.document.bundle(), &accepted.bundle);
assert!(!app.document.can_undo() && !app.document.can_redo());
}
#[test]
fn modal_open_cancels_preview_and_captured_begin_stays_retired_after_close() {
let (mut app, lane, _) = setup();
let original = app.document.snapshot();
let camera = app.behavior.scenario.camera_boundary();
let (event, start, _) = begin(&mut app, Target::Lane(lane));
move_to(&mut app, event, start + Vector::new(30., 0.));
let _ = app.update(Message::Delete);
assert!(app.delete_pending);
assert!(app.behavior.scenario.spacing_token().is_none());
finish(&mut app, event, start + Vector::new(30., 0.));
let _ = app.update(Message::CancelDelete);
send(&mut app, event);
assert!(app.behavior.scenario.spacing_token().is_none());
assert_eq!(app.project(), &original.decoded.project);
assert_eq!(app.document.bundle(), &original.bundle);
assert!(camera == app.behavior.scenario.camera_boundary());
assert!(!app.document.can_undo());
}