Tests: native-menu tests count menu reads only in runs no other test disturbed

This commit is contained in:
bflatastic
2026-10-24 06:26:02 +07:00
parent 0df73f7c36
commit 3fd190f747
+57 -44
View File
@@ -386,7 +386,8 @@ fn click(at: Pos2, pressed: bool) -> Event {
/// Run `drag` on a new app with the fake native menu, and run it again on another new app until
/// the shortcut and plug-in generations hold still through a run (at most 20 runs). Return that app
/// and what `drag` returned. The generations are process-wide and only grow. A test running
/// alongside can bump them, and the app then reads the menus again.
/// alongside can bump them, and the app then reads the menus again, so every step whose menu
/// reads a test counts runs inside `drag`.
fn undisturbed<R>(drag: impl Fn(&mut Mac) -> R) -> (Mac, R) {
let generations = || (crate::shortcut_editor::GENERATION.load(std::sync::atomic::Ordering::Relaxed), crate::menus::plugin_revision());
for _ in 0..20 {
@@ -433,15 +434,16 @@ fn key_equivalents_run_through_the_normal_shortcut_path_once() {
/// while the button is held, and are read when the button is released, with Undo naming the move.
#[test]
fn a_drag_reads_the_menus_when_it_is_released() {
let (mut m, (start, read)) = undisturbed(|m| {
let (m, (read, synced, ended, released)) = undisturbed(|m| {
let start = m.start_move();
(start, m.reads(6, |_, i| vec![Event::PointerMoved(start + vec2(8.0 * (i + 2) as f32, 0.0))]))
let read = m.reads(6, |_, i| vec![Event::PointerMoved(start + vec2(8.0 * (i + 2) as f32, 0.0))]);
let synced = m.fake.synced();
m.frame(vec![click(start + vec2(56.0, 0.0), false)]);
(read, synced, !m.app.session.in_interaction(), m.fake.synced())
});
assert_eq!(read, 0, "the menus weren't read at any step of the drag");
let synced = m.fake.synced();
m.frame(vec![click(start + vec2(56.0, 0.0), false)]);
assert!(!m.app.session.in_interaction());
assert_eq!(m.fake.synced(), synced + 1, "the menus were read on the release");
assert!(ended);
assert_eq!(released, synced + 1, "the menus were read on the release");
assert_eq!(find(&m.fake.last(), "edit.undo").label, "Undo Move");
}
@@ -449,21 +451,24 @@ fn a_drag_reads_the_menus_when_it_is_released() {
/// the preview's rectangle gone (nothing selected, so Cut is off), and not at the steps before.
#[test]
fn escape_during_a_drag_reads_the_menus() {
let (mut m, read) = undisturbed(|m| {
let (m, read) = undisturbed(|m| {
m.app.run("select.none", json!({})).unwrap();
m.app.run("tool.select", json!({"tool": "rectangle"})).unwrap();
let start = m.on_canvas(200.0, 150.0);
m.frame(vec![Event::PointerMoved(start), click(start, true)]);
m.frame(vec![Event::PointerMoved(start + vec2(10.0, 10.0))]);
assert!(m.app.session.in_interaction(), "the rectangle is being drawn");
m.reads(6, |_, i| vec![Event::PointerMoved(start + vec2(10.0, 10.0) * (i + 2) as f32)])
let read = m.reads(6, |_, i| vec![Event::PointerMoved(start + vec2(10.0, 10.0) * (i + 2) as f32)]);
let cut = find(&m.fake.last(), "edit.cut").enabled;
let synced = m.fake.synced();
m.frame(vec![Event::Key { key: Key::Escape, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE }]);
(read, cut, synced, !m.app.session.in_interaction(), m.fake.synced())
});
let (read, cut, synced, dropped, escaped) = read;
assert_eq!(read, 0, "the menus weren't read at any step of the drag");
assert!(find(&m.fake.last(), "edit.cut").enabled, "the preview's rectangle is selected");
let synced = m.fake.synced();
m.frame(vec![Event::Key { key: Key::Escape, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE }]);
assert!(!m.app.session.in_interaction(), "Escape dropped the rectangle");
assert_eq!(m.fake.synced(), synced + 1, "the menus were read");
assert!(cut, "the preview's rectangle is selected");
assert!(dropped, "Escape dropped the rectangle");
assert_eq!(escaped, synced + 1, "the menus were read");
assert!(!find(&m.fake.last(), "edit.cut").enabled, "nothing is selected");
}
@@ -472,22 +477,26 @@ fn escape_during_a_drag_reads_the_menus() {
/// names the step before the drag.
#[test]
fn a_move_dragged_onto_a_panel_reads_the_menus() {
let (mut m, (start, undo, read)) = undisturbed(|m| {
// The whole drag, the wait over the panel and the release run undisturbed: a generation bumped
// by a test running alongside would read the menus once more.
let (m, (undo, read, synced, waits, over, dropped, released)) = undisturbed(|m| {
let undo = find(&m.fake.last(), "edit.undo").label.clone();
let start = m.start_move();
(start, undo, m.reads(4, |_, i| vec![Event::PointerMoved(start + vec2(8.0 * (i + 2) as f32, 0.0))]))
let read = m.reads(4, |_, i| vec![Event::PointerMoved(start + vec2(8.0 * (i + 2) as f32, 0.0))]);
let synced = m.fake.synced();
let panel = Pos2::new(m.app.canvas_rect.unwrap().right() + 40.0, start.y);
// Over a panel the move waits: the menus keep the interaction's state.
m.frame(vec![Event::PointerMoved(panel)]);
let (waits, over) = (m.app.session.in_interaction(), m.fake.synced());
// Released over the panel, the move is dropped and the menus are read.
m.frame(vec![click(panel, false)]);
(undo, read, synced, waits, over, !m.app.session.in_interaction(), m.fake.synced())
});
assert_eq!(read, 0, "the menus weren't read at any step of the drag");
let synced = m.fake.synced();
let panel = Pos2::new(m.app.canvas_rect.unwrap().right() + 40.0, start.y);
// Over a panel the move waits: the menus keep the interaction's state.
m.frame(vec![Event::PointerMoved(panel)]);
assert!(m.app.session.in_interaction(), "the move waits over the panel");
assert_eq!(m.fake.synced(), synced, "the menus weren't read");
// Released over the panel, the move is dropped and the menus are read.
m.frame(vec![click(panel, false)]);
assert!(!m.app.session.in_interaction(), "the move was dropped");
assert_eq!(m.fake.synced(), synced + 1, "the menus were read");
assert!(waits, "the move waits over the panel");
assert_eq!(over, synced, "the menus weren't read");
assert!(dropped, "the move was dropped");
assert_eq!(released, synced + 1, "the menus were read");
assert_eq!(find(&m.fake.last(), "edit.undo").label, undo);
}
@@ -496,32 +505,34 @@ fn a_move_dragged_onto_a_panel_reads_the_menus() {
/// without a release or a key.
#[test]
fn a_control_port_cancel_of_a_dialog_preview_reads_the_menus() {
let (mut m, read) = undisturbed(|m| {
let (_m, (read, synced, closed, after)) = undisturbed(|m| {
m.rotate_preview();
m.angles(4)
let read = m.angles(4);
let synced = m.fake.synced();
crate::tests_synthetic::control(&mut m.app, &m.ctx, "ui.dialog.cancel", json!({}));
m.frame(vec![]);
(read, synced, !m.app.session.in_interaction() && m.app.ui.dialog.is_none(), m.fake.synced())
});
assert_eq!(read, 0, "the menus weren't read while the dialog previewed the angles");
let synced = m.fake.synced();
crate::tests_synthetic::control(&mut m.app, &m.ctx, "ui.dialog.cancel", json!({}));
m.frame(vec![]);
assert!(!m.app.session.in_interaction() && m.app.ui.dialog.is_none());
assert_eq!(m.fake.synced(), synced + 1, "the menus were read");
assert!(closed);
assert_eq!(after, synced + 1, "the menus were read");
}
/// OK applies the rotation a dialog previews as one undo step: the menus are read then, with Undo
/// naming it, and not for the angles previewed before.
#[test]
fn applying_a_dialog_preview_reads_the_menus() {
let (mut m, read) = undisturbed(|m| {
let (m, (read, synced, closed, after)) = undisturbed(|m| {
m.rotate_preview();
m.angles(4)
let read = m.angles(4);
let synced = m.fake.synced();
crate::tests_synthetic::control(&mut m.app, &m.ctx, "ui.dialog.confirm", json!({}));
m.frame(vec![]);
(read, synced, !m.app.session.in_interaction() && m.app.ui.dialog.is_none(), m.fake.synced())
});
assert_eq!(read, 0, "the menus weren't read while the dialog previewed the angles");
let synced = m.fake.synced();
crate::tests_synthetic::control(&mut m.app, &m.ctx, "ui.dialog.confirm", json!({}));
m.frame(vec![]);
assert!(!m.app.session.in_interaction() && m.app.ui.dialog.is_none());
assert_eq!(m.fake.synced(), synced + 1, "the menus were read");
assert!(closed);
assert_eq!(after, synced + 1, "the menus were read");
assert_eq!(find(&m.fake.last(), "edit.undo").label, "Undo Rotate");
}
@@ -538,16 +549,18 @@ fn the_font_menu_preview_reads_the_menus_when_it_ends() {
native_menu::sync(&mut m.app, &m.ctx);
m.fake.synced() - synced
};
let (mut m, read) = undisturbed(|m| {
let (_m, read) = undisturbed(|m| {
m.app.run("select.none", json!({})).unwrap();
m.app.run("text.create", json!({"x": 50, "y": 50, "text": "Hi"})).unwrap();
m.frame(vec![]);
assert_eq!(apply(m, FontPick::Preview("Source Serif 4".into(), None)), 1, "the menus were read as the preview began");
apply(m, FontPick::Preview("Source Sans 3".into(), None)) + apply(m, FontPick::Preview("Inter".into(), None))
let read = apply(m, FontPick::Preview("Source Sans 3".into(), None)) + apply(m, FontPick::Preview("Inter".into(), None));
(read, apply(m, FontPick::EndPreview), !m.app.session.in_interaction())
});
let (read, ended, done) = read;
assert_eq!(read, 0, "the menus weren't read for the fonts after the first");
assert_eq!(apply(&mut m, FontPick::EndPreview), 1, "the menus were read as the preview ended");
assert!(!m.app.session.in_interaction());
assert_eq!(ended, 1, "the menus were read as the preview ended");
assert!(done);
}
/// A click runs its command like an in-window click, and the menus follow: View › Show Rulers