From f317aefa442a8f17c13909b4c6204089dbfab3b7 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 05:32:35 -0800 Subject: [PATCH] Tests: the file-dialog menu test reruns while fonts or plug-ins change `the_menu_bar_is_inert_while_a_file_dialog_is_open` excused a missing menu row when the composite font or plug-in it named was gone at the time of the check. A test running alongside can remove such a font and define it again between the menu read and the check, and the test then failed. It now runs inside `undisturbed`, as the file's other tests that compare menu reads do. When the shortcut, plug-in or font generation changes during a run, the test runs again on a new app. Every row from before the dialog must then be back as it was. --- crates/ui-egui/src/tests/nativemenu.rs | 108 ++++++++++--------------- 1 file changed, 44 insertions(+), 64 deletions(-) diff --git a/crates/ui-egui/src/tests/nativemenu.rs b/crates/ui-egui/src/tests/nativemenu.rs index a5a8ea0d7a..689ae8a60c 100644 --- a/crates/ui-egui/src/tests/nativemenu.rs +++ b/crates/ui-egui/src/tests/nativemenu.rs @@ -69,25 +69,6 @@ fn find<'a>(bar: &'a MenuBar, command: &str) -> &'a native_menu::Item { bar.items().into_iter().find(|it| it.command == Some(command)).unwrap_or_else(|| panic!("no {command} item")) } -/// Is the font or plug-in that an item runs `command` with `params` for gone from the process-wide -/// font database or plug-in registry? A test running alongside can remove a composite font or a -/// plug-in, and the menus then drop its item. -fn source_gone(command: Option<&str>, params: &Value) -> bool { - use vectorcraft_plugins::{PREFIX, effect, export, registry}; - let field = |key| params.get(key).and_then(Value::as_str); - let plugin_gone = |id: Option<&str>| id.is_some_and(|id| registry::get(id).is_none()); - match command { - Some("text.setStyle") => field("font").is_some_and(|f| !vectorcraft_text::FontDb::global().menu_family_list_now().iter().any(|x| x == f)), - Some("plugin.dialog") => plugin_gone(field("id")), - Some("file.openWith") => plugin_gone(field("importer").and_then(export::plugin_id)), - Some("effect.apply" | "effect.dialog") => plugin_gone(field("effect").and_then(effect::plugin_id)), - Some(c) if vectorcraft_engine::cmd::plugin::is_command_id(c) => { - plugin_gone(c.strip_prefix(PREFIX).and_then(|rest| rest.rsplit_once('.')).map(|(id, _)| id)) - } - _ => false, - } -} - #[test] fn vectorcraft_gets_a_mac_app_menu() { let bar = bar(&app(true)); @@ -702,52 +683,51 @@ fn the_app_bar_hides_its_menus_with_the_mac_menu_bar() { /// answers, the menus are as before and work again. #[test] fn the_menu_bar_is_inert_while_a_file_dialog_is_open() { - let mut m = Mac::new(true); - m.app.services.dialogs_are_sheets = true; - let (shown, _) = crate::tests_picks::off_the_ui_thread(&mut m.app.services); - m.app.run("shape.rectangle", json!({"x": 200, "y": 150, "width": 20, "height": 20})).unwrap(); - m.app.run("select.none", json!({})).unwrap(); - m.frame(vec![]); let rows = |bar: &MenuBar| bar.items().iter().map(|it| (it.command, it.params.clone(), it.enabled)).collect::>(); - let before = m.fake.last(); - let system = [native_menu::HIDE, native_menu::HIDE_OTHERS, native_menu::MINIMIZE]; - assert!(system.iter().all(|c| find(&before, c).enabled)); - assert!(m.app.run("file.open", json!({})).is_err()); - assert!(m.app.file_dialog_open()); - m.frame(vec![]); - let inert = m.fake.last(); - for it in inert.items() { - let kept = it.command.is_some_and(|c| system.contains(&c)); - assert_eq!(it.enabled, kept, "{:?} {} while the dialog is open", it.command, it.label); - } - // A click that raced the menu going inert isn't run; Minimize is. - let rulers = (m.app.ui.view.rulers, find(&before, "view.rulers").clone()); - m.fake.report(native_menu::Event::Click(rulers.1.clone())); - m.frame(vec![]); - assert_eq!(m.app.ui.view.rulers, rulers.0); - m.fake.report(native_menu::Event::Click(find(&inert, native_menu::MINIMIZE).clone())); - assert!(m.frame(vec![]).viewport_output[&egui::ViewportId::ROOT].commands.contains(&egui::ViewportCommand::Minimized(true))); - // A key equivalent that raced it doesn't reach egui. - let mut k = crate::shortcuts::parse("Cmd+A").unwrap(); - k.modifiers.mac_cmd = k.modifiers.command; - m.fake.report(native_menu::Event::Key(k)); - let mut raw = egui::RawInput::default(); - m.app.raw_input_hook(&m.ctx, &mut raw); - assert!(!raw.events.iter().any(|e| matches!(e, Event::Key { .. })), "{:?}", raw.events); - m.key_equivalent("Cmd+A"); - assert_eq!(m.selected(), 0, "⌘A selected nothing"); - // Cancelled: the menus are as before, and work again. - shown.answer(&[]); - m.frame(vec![]); - m.frame(vec![]); - assert!(!m.app.file_dialog_open()); - // Every row from before is back as it was. Tests running alongside may add rows, and may - // remove the composite font or plug-in a row is for. - let after = rows(&m.fake.last()); - for r in rows(&before) { - let listed = after.iter().any(|a| (a.0, &a.1) == (r.0, &r.1)); - assert!(after.contains(&r) || (!listed && source_gone(r.0, &r.1)), "{r:?} is not as before"); - } + // The menus read before the dialog and after it are compared, so the whole run is undisturbed: + // a test running alongside can add or remove a composite font or a plug-in in between. + let (mut m, (before, after, rulers)) = undisturbed(|m| { + m.app.services.dialogs_are_sheets = true; + let (shown, _) = crate::tests_picks::off_the_ui_thread(&mut m.app.services); + m.app.run("shape.rectangle", json!({"x": 200, "y": 150, "width": 20, "height": 20})).unwrap(); + m.app.run("select.none", json!({})).unwrap(); + m.frame(vec![]); + let before = m.fake.last(); + let system = [native_menu::HIDE, native_menu::HIDE_OTHERS, native_menu::MINIMIZE]; + assert!(system.iter().all(|c| find(&before, c).enabled)); + assert!(m.app.run("file.open", json!({})).is_err()); + assert!(m.app.file_dialog_open()); + m.frame(vec![]); + let inert = m.fake.last(); + for it in inert.items() { + let kept = it.command.is_some_and(|c| system.contains(&c)); + assert_eq!(it.enabled, kept, "{:?} {} while the dialog is open", it.command, it.label); + } + // A click that raced the menu going inert isn't run; Minimize is. + let rulers = (m.app.ui.view.rulers, find(&before, "view.rulers").clone()); + m.fake.report(native_menu::Event::Click(rulers.1.clone())); + m.frame(vec![]); + assert_eq!(m.app.ui.view.rulers, rulers.0); + m.fake.report(native_menu::Event::Click(find(&inert, native_menu::MINIMIZE).clone())); + assert!(m.frame(vec![]).viewport_output[&egui::ViewportId::ROOT].commands.contains(&egui::ViewportCommand::Minimized(true))); + // A key equivalent that raced it doesn't reach egui. + let mut k = crate::shortcuts::parse("Cmd+A").unwrap(); + k.modifiers.mac_cmd = k.modifiers.command; + m.fake.report(native_menu::Event::Key(k)); + let mut raw = egui::RawInput::default(); + m.app.raw_input_hook(&m.ctx, &mut raw); + assert!(!raw.events.iter().any(|e| matches!(e, Event::Key { .. })), "{:?}", raw.events); + m.key_equivalent("Cmd+A"); + assert_eq!(m.selected(), 0, "⌘A selected nothing"); + // Cancelled: the menus are as before, and work again. + shown.answer(&[]); + m.frame(vec![]); + m.frame(vec![]); + assert!(!m.app.file_dialog_open()); + (rows(&before), rows(&m.fake.last()), rulers) + }); + // Every row from before is back as it was. + assert!(before.iter().all(|r| after.contains(r)), "the menus are as before"); m.fake.report(native_menu::Event::Click(rulers.1)); m.frame(vec![]); assert_eq!(m.app.ui.view.rulers, !rulers.0);