1use crate::{
2 ActivityGuard, AnyWindowHandle, AppLifecyclePhase, BackgroundExecutor, ClipboardItem,
3 CursorStyle, DevicePixels, DisplayId, DummyKeyboardMapper, ForegroundExecutor, Keymap,
4 OwnedMenu, Platform, PlatformDisplay, PlatformHeadlessRenderer, PlatformKeyboardLayout,
5 PlatformKeyboardMapper, PlatformTextSystem, PromptButton, ScreenCaptureFrame,
6 ScreenCaptureSource, ScreenCaptureStream, SharedString, SourceMetadata, SystemNotification,
7 SystemNotificationResponse, Task, TestDisplay, TestWindow, ThermalState, WindowAppearance,
8 WindowParams, size,
9};
10#[cfg(any(test, feature = "test-support"))]
11use crate::{PathPromptOptions, TestTextSystem};
12use anyhow::Result;
13#[cfg(any(test, feature = "test-support"))]
14use collections::VecDeque;
15use futures::channel::oneshot;
16use parking_lot::Mutex;
17use std::{
18 cell::{Cell, RefCell},
19 path::{Path, PathBuf},
20 rc::{Rc, Weak},
21 sync::{
22 Arc,
23 atomic::{AtomicUsize, Ordering},
24 },
25 time::Duration,
26};
27
28pub(crate) struct TestPlatform {
30 background_executor: BackgroundExecutor,
31 foreground_executor: ForegroundExecutor,
32
33 pub(crate) active_window: RefCell<Option<TestWindow>>,
34 displays: RefCell<Vec<TestDisplay>>,
35 displays_changed_callback: RefCell<Option<Box<dyn FnMut()>>>,
36 active_cursor: Mutex<CursorStyle>,
37 current_clipboard_item: Mutex<Option<ClipboardItem>>,
38 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
39 current_primary_item: Mutex<Option<ClipboardItem>>,
40 #[cfg(target_os = "macos")]
41 current_find_pasteboard_item: Mutex<Option<ClipboardItem>>,
42 #[cfg(any(test, feature = "test-support"))]
43 pub(crate) prompts: RefCell<TestPrompts>,
44 screen_capture_sources: RefCell<Vec<TestScreenCaptureSource>>,
45 pub opened_url: RefCell<Option<String>>,
46 pub(crate) system_notifications: RefCell<TestSystemNotifications>,
47 pub text_system: Arc<dyn PlatformTextSystem>,
48 pub expect_restart:
49 RefCell<Option<oneshot::Sender<(Option<PathBuf>, Vec<std::ffi::OsString>)>>>,
50 idle_sleep_prevention_count: Arc<AtomicUsize>,
51 idle_sleep_prevention_delay: Cell<Duration>,
52 idle_sleep_prevention_fails: Cell<bool>,
53 headless_renderer_factory:
54 Option<Box<dyn Fn() -> anyhow::Result<Option<Box<dyn PlatformHeadlessRenderer>>>>>,
55 weak: Weak<Self>,
56 menus: RefCell<Vec<OwnedMenu>>,
57 pub(crate) app_lifecycle_callback: RefCell<Option<Box<dyn FnMut(AppLifecyclePhase)>>>,
58 pub(crate) memory_warning_callback: RefCell<Option<Box<dyn FnMut()>>>,
59}
60
61#[derive(Clone)]
63pub struct TestScreenCaptureSource {}
64
65pub struct TestScreenCaptureStream {}
67
68impl ScreenCaptureSource for TestScreenCaptureSource {
69 fn metadata(&self) -> Result<SourceMetadata> {
70 Ok(SourceMetadata {
71 id: 0,
72 is_main: None,
73 label: None,
74 resolution: size(DevicePixels(1), DevicePixels(1)),
75 })
76 }
77
78 fn stream(
79 &self,
80 _foreground_executor: &ForegroundExecutor,
81 _frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
82 ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
83 let (mut tx, rx) = oneshot::channel();
84 let stream = TestScreenCaptureStream {};
85 tx.send(Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>))
86 .ok();
87 rx
88 }
89}
90
91impl ScreenCaptureStream for TestScreenCaptureStream {
92 fn metadata(&self) -> Result<SourceMetadata> {
93 TestScreenCaptureSource {}.metadata()
94 }
95}
96
97#[cfg(any(test, feature = "test-support"))]
98struct TestPrompt {
99 msg: String,
100 detail: Option<String>,
101 answers: Vec<String>,
102 tx: oneshot::Sender<usize>,
103}
104
105#[derive(Default)]
106pub(crate) struct TestSystemNotifications {
107 pub(crate) app_identity: Option<(SharedString, SharedString)>,
108 pub(crate) shown: Vec<SystemNotification>,
109 pub(crate) delivered: Vec<SystemNotification>,
110 pub(crate) dismissed: Vec<SharedString>,
111 response_callback: Option<Box<dyn FnMut(SystemNotificationResponse)>>,
112}
113
114#[cfg(any(test, feature = "test-support"))]
115#[derive(Default)]
116pub(crate) struct TestPrompts {
117 multiple_choice: VecDeque<TestPrompt>,
118 new_path: VecDeque<(PathBuf, oneshot::Sender<Result<Option<PathBuf>>>)>,
119 paths: VecDeque<(
120 PathPromptOptions,
121 oneshot::Sender<Result<Option<Vec<PathBuf>>>>,
122 )>,
123}
124
125impl TestPlatform {
126 pub(crate) fn cursor_style(&self) -> CursorStyle {
127 *self.active_cursor.lock()
128 }
129
130 #[cfg(any(test, feature = "test-support"))]
131 pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc<Self> {
132 Self::with_platform(
133 executor,
134 foreground_executor,
135 Arc::new(TestTextSystem),
136 None,
137 )
138 }
139
140 #[cfg(any(test, feature = "test-support"))]
141 pub fn with_text_system(
142 executor: BackgroundExecutor,
143 foreground_executor: ForegroundExecutor,
144 text_system: Arc<dyn PlatformTextSystem>,
145 ) -> Rc<Self> {
146 Self::with_platform(executor, foreground_executor, text_system, None)
147 }
148
149 pub(crate) fn simulate_display_added(
150 &self,
151 display_id: DisplayId,
152 refresh_interval: Option<Duration>,
153 ) {
154 self.connect_display(display_id, refresh_interval);
155 self.report_displays_changed();
156 }
157
158 pub(crate) fn connect_display(
161 &self,
162 display_id: DisplayId,
163 refresh_interval: Option<Duration>,
164 ) {
165 let mut display = TestDisplay::with_id(display_id);
166 display.refresh_interval = refresh_interval;
167 self.displays.borrow_mut().push(display);
168 }
169
170 pub(crate) fn simulate_display_removed(&self, display_id: DisplayId) {
171 self.displays
172 .borrow_mut()
173 .retain(|display| display.id() != display_id);
174 self.report_displays_changed();
175 }
176
177 pub(crate) fn simulate_display_refresh_interval_change(
178 &self,
179 display_id: DisplayId,
180 refresh_interval: Option<Duration>,
181 ) {
182 for display in self.displays.borrow_mut().iter_mut() {
183 if display.id() == display_id {
184 display.refresh_interval = refresh_interval;
185 }
186 }
187 self.report_displays_changed();
188 }
189
190 fn report_displays_changed(&self) {
191 let callback = self.displays_changed_callback.borrow_mut().take();
192 if let Some(mut callback) = callback {
193 callback();
194 self.displays_changed_callback
195 .borrow_mut()
196 .get_or_insert(callback);
197 }
198 }
199
200 pub fn with_platform(
201 executor: BackgroundExecutor,
202 foreground_executor: ForegroundExecutor,
203 text_system: Arc<dyn PlatformTextSystem>,
204 headless_renderer_factory: Option<
205 Box<dyn Fn() -> anyhow::Result<Option<Box<dyn PlatformHeadlessRenderer>>>>,
206 >,
207 ) -> Rc<Self> {
208 Rc::new_cyclic(|weak| TestPlatform {
209 background_executor: executor,
210 foreground_executor,
211 #[cfg(any(test, feature = "test-support"))]
212 prompts: Default::default(),
213 screen_capture_sources: Default::default(),
214 active_cursor: Default::default(),
215 displays: RefCell::new(vec![TestDisplay::new()]),
216 displays_changed_callback: Default::default(),
217 active_window: Default::default(),
218 expect_restart: Default::default(),
219 current_clipboard_item: Mutex::new(None),
220 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
221 current_primary_item: Mutex::new(None),
222 #[cfg(target_os = "macos")]
223 current_find_pasteboard_item: Mutex::new(None),
224 idle_sleep_prevention_count: Arc::new(AtomicUsize::new(0)),
225 idle_sleep_prevention_delay: Cell::new(Duration::ZERO),
226 idle_sleep_prevention_fails: Cell::new(false),
227 weak: weak.clone(),
228 opened_url: Default::default(),
229 system_notifications: Default::default(),
230 text_system,
231 headless_renderer_factory,
232 menus: Default::default(),
233 app_lifecycle_callback: Default::default(),
234 memory_warning_callback: Default::default(),
235 })
236 }
237
238 #[cfg(any(test, feature = "test-support"))]
239 pub(crate) fn simulate_new_path_selection(
240 &self,
241 select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
242 ) {
243 let (path, tx) = self
244 .prompts
245 .borrow_mut()
246 .new_path
247 .pop_front()
248 .expect("no pending new path prompt");
249 tx.send(Ok(select_path(&path))).ok();
250 }
251
252 #[cfg(any(test, feature = "test-support"))]
253 pub(crate) fn simulate_path_prompt_response(
254 &self,
255 select_paths: impl FnOnce(&PathPromptOptions) -> Option<Vec<std::path::PathBuf>>,
256 ) {
257 let (options, tx) = self
258 .prompts
259 .borrow_mut()
260 .paths
261 .pop_front()
262 .expect("no pending paths prompt");
263 let selection = select_paths(&options);
264 if let Some(paths) = &selection
265 && !options.multiple
266 && paths.len() > 1
267 {
268 panic!(
269 "selected {} paths for a prompt that does not allow multiple selection",
270 paths.len()
271 );
272 }
273 tx.send(Ok(selection)).ok();
274 }
275
276 #[cfg(any(test, feature = "test-support"))]
277 pub(crate) fn did_prompt_for_paths(&self) -> bool {
278 !self.prompts.borrow().paths.is_empty()
279 }
280
281 #[cfg(any(test, feature = "test-support"))]
282 #[track_caller]
283 pub(crate) fn simulate_prompt_answer(&self, response: &str) {
284 let prompt = self
285 .prompts
286 .borrow_mut()
287 .multiple_choice
288 .pop_front()
289 .expect("no pending multiple choice prompt");
290 let Some(ix) = prompt.answers.iter().position(|a| a == response) else {
291 panic!(
292 "PROMPT: {}\n{:?}\n{:?}\nCannot respond with {}",
293 prompt.msg, prompt.detail, prompt.answers, response
294 )
295 };
296 prompt.tx.send(ix).ok();
297 }
298
299 #[cfg(any(test, feature = "test-support"))]
300 pub(crate) fn has_pending_prompt(&self) -> bool {
301 !self.prompts.borrow().multiple_choice.is_empty()
302 }
303
304 #[cfg(any(test, feature = "test-support"))]
305 pub(crate) fn pending_prompt(&self) -> Option<(String, String)> {
306 let prompts = self.prompts.borrow();
307 let prompt = prompts.multiple_choice.front()?;
308 Some((
309 prompt.msg.clone(),
310 prompt.detail.clone().unwrap_or_default(),
311 ))
312 }
313
314 #[cfg(any(test, feature = "test-support"))]
315 pub(crate) fn set_screen_capture_sources(&self, sources: Vec<TestScreenCaptureSource>) {
316 *self.screen_capture_sources.borrow_mut() = sources;
317 }
318
319 #[cfg(any(test, feature = "test-support"))]
322 pub(crate) fn prompt(
323 &self,
324 msg: &str,
325 detail: Option<&str>,
326 answers: &[PromptButton],
327 ) -> oneshot::Receiver<usize> {
328 let (tx, rx) = oneshot::channel();
329 let answers: Vec<String> = answers.iter().map(|s| s.label().to_string()).collect();
330 self.prompts
331 .borrow_mut()
332 .multiple_choice
333 .push_back(TestPrompt {
334 msg: msg.to_string(),
335 detail: detail.map(|s| s.to_string()),
336 answers,
337 tx,
338 });
339 rx
340 }
341
342 #[cfg(not(any(test, feature = "test-support")))]
346 pub(crate) fn prompt(
347 &self,
348 _msg: &str,
349 _detail: Option<&str>,
350 _answers: &[PromptButton],
351 ) -> oneshot::Receiver<usize> {
352 oneshot::channel().1
353 }
354
355 pub(crate) fn set_active_window(&self, window: Option<TestWindow>) {
356 let executor = self.foreground_executor();
357 let previous_window = self.active_window.borrow_mut().take();
358 self.active_window.borrow_mut().clone_from(&window);
359
360 executor
361 .spawn(async move {
362 if let Some(previous_window) = previous_window {
363 if let Some(window) = window.as_ref()
364 && Rc::ptr_eq(&previous_window.0, &window.0)
365 {
366 return;
367 }
368 previous_window.simulate_active_status_change(false);
369 }
370 if let Some(window) = window {
371 window.simulate_active_status_change(true);
372 }
373 })
374 .detach();
375 }
376
377 #[cfg(any(test, feature = "test-support"))]
378 pub(crate) fn did_prompt_for_new_path(&self) -> bool {
379 !self.prompts.borrow().new_path.is_empty()
380 }
381
382 #[cfg(any(test, feature = "test-support"))]
383 pub(crate) fn active_idle_sleep_preventions(&self) -> usize {
384 self.idle_sleep_prevention_count.load(Ordering::SeqCst)
385 }
386
387 #[cfg(any(test, feature = "test-support"))]
388 pub(crate) fn set_idle_sleep_prevention_delay(&self, delay: Duration) {
389 self.idle_sleep_prevention_delay.set(delay);
390 }
391
392 #[cfg(any(test, feature = "test-support"))]
393 pub(crate) fn set_idle_sleep_prevention_fails(&self, fails: bool) {
394 self.idle_sleep_prevention_fails.set(fails);
395 }
396
397 #[cfg(any(test, feature = "test-support"))]
398 pub(crate) fn app_identity(&self) -> Option<(SharedString, SharedString)> {
399 self.system_notifications.borrow().app_identity.clone()
400 }
401
402 #[cfg(any(test, feature = "test-support"))]
403 pub(crate) fn shown_system_notifications(&self) -> Vec<SystemNotification> {
404 self.system_notifications.borrow().shown.clone()
405 }
406
407 #[cfg(any(test, feature = "test-support"))]
408 pub(crate) fn delivered_system_notifications(&self) -> Vec<SystemNotification> {
409 self.system_notifications.borrow().delivered.clone()
410 }
411
412 #[cfg(any(test, feature = "test-support"))]
413 pub(crate) fn dismissed_system_notifications(&self) -> Vec<SharedString> {
414 self.system_notifications.borrow().dismissed.clone()
415 }
416
417 #[cfg(any(test, feature = "test-support"))]
418 pub(crate) fn simulate_system_notification_response(
419 &self,
420 response: SystemNotificationResponse,
421 ) {
422 let callback = self
423 .system_notifications
424 .borrow_mut()
425 .response_callback
426 .take();
427 if let Some(mut callback) = callback {
428 callback(response);
429 self.system_notifications
430 .borrow_mut()
431 .response_callback
432 .get_or_insert(callback);
433 }
434 }
435}
436
437impl Platform for TestPlatform {
438 fn background_executor(&self) -> BackgroundExecutor {
439 self.background_executor.clone()
440 }
441
442 fn foreground_executor(&self) -> ForegroundExecutor {
443 self.foreground_executor.clone()
444 }
445
446 fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
447 self.text_system.clone()
448 }
449
450 fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
451 Box::new(TestKeyboardLayout)
452 }
453
454 fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
455 Rc::new(DummyKeyboardMapper)
456 }
457
458 fn on_keyboard_layout_change(&self, _: Box<dyn FnMut()>) {}
459
460 fn on_thermal_state_change(&self, _: Box<dyn FnMut()>) {}
461
462 fn thermal_state(&self) -> ThermalState {
463 ThermalState::Nominal
464 }
465
466 fn prevent_idle_sleep(&self, reason: &str) -> Task<Result<ActivityGuard>> {
467 let count = self.idle_sleep_prevention_count.clone();
468 let fails = self.idle_sleep_prevention_fails.get();
469 let reason = reason.to_owned();
470 let acquire = move || {
471 if fails {
472 anyhow::bail!("Idle sleep prevention for {reason:?} is set to fail in this test");
473 }
474 count.fetch_add(1, Ordering::SeqCst);
475 Ok(ActivityGuard::new(move || {
476 count.fetch_sub(1, Ordering::SeqCst);
477 }))
478 };
479
480 let delay = self.idle_sleep_prevention_delay.get();
481 if delay.is_zero() {
482 Task::ready(acquire())
483 } else {
484 let delay = self.background_executor.timer(delay);
485 self.foreground_executor.spawn(async move {
486 delay.await;
487 acquire()
488 })
489 }
490 }
491
492 fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
493 unimplemented!()
494 }
495
496 fn quit(&self) {}
497
498 fn restart(&self, path: Option<PathBuf>, arguments: Vec<std::ffi::OsString>) {
499 if let Some(tx) = self.expect_restart.take() {
500 tx.send((path, arguments)).unwrap();
501 }
502 }
503
504 fn activate(&self, _ignoring_other_apps: bool) {
505 }
507
508 fn hide(&self) {
509 unimplemented!()
510 }
511
512 fn hide_other_apps(&self) {
513 unimplemented!()
514 }
515
516 fn unhide_other_apps(&self) {
517 unimplemented!()
518 }
519
520 fn displays(&self) -> Vec<std::rc::Rc<dyn crate::PlatformDisplay>> {
521 self.displays
522 .borrow()
523 .iter()
524 .map(|display| Rc::new(display.clone()) as Rc<dyn PlatformDisplay>)
525 .collect()
526 }
527
528 fn primary_display(&self) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
529 self.displays
530 .borrow()
531 .first()
532 .map(|display| Rc::new(display.clone()) as Rc<dyn PlatformDisplay>)
533 }
534
535 fn on_displays_changed(&self, callback: Box<dyn FnMut()>) {
536 *self.displays_changed_callback.borrow_mut() = Some(callback);
537 }
538
539 fn is_screen_capture_supported(&self) -> bool {
540 true
541 }
542
543 fn screen_capture_sources(
544 &self,
545 ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
546 let (mut tx, rx) = oneshot::channel();
547 tx.send(Ok(self
548 .screen_capture_sources
549 .borrow()
550 .iter()
551 .map(|source| Rc::new(source.clone()) as Rc<dyn ScreenCaptureSource>)
552 .collect()))
553 .ok();
554 rx
555 }
556
557 fn active_window(&self) -> Option<crate::AnyWindowHandle> {
558 self.active_window
559 .borrow()
560 .as_ref()
561 .map(|window| window.0.lock().handle)
562 }
563
564 fn open_window(
565 &self,
566 handle: AnyWindowHandle,
567 params: WindowParams,
568 ) -> anyhow::Result<Box<dyn crate::PlatformWindow>> {
569 let renderer = match self.headless_renderer_factory.as_ref() {
570 Some(factory) => factory()?,
571 None => None,
572 };
573 let window = TestWindow::new(
574 handle,
575 params,
576 self.weak.clone(),
577 self.primary_display()
578 .unwrap_or_else(|| Rc::new(TestDisplay::new())),
579 renderer,
580 );
581 Ok(Box::new(window))
582 }
583
584 fn window_appearance(&self) -> WindowAppearance {
585 WindowAppearance::Light
586 }
587
588 fn open_url(&self, url: &str) {
589 *self.opened_url.borrow_mut() = Some(url.to_string())
590 }
591
592 fn on_open_urls(&self, _callback: Box<dyn FnMut(Vec<String>)>) {
593 unimplemented!()
594 }
595
596 #[cfg(any(test, feature = "test-support"))]
599 fn prompt_for_paths(
600 &self,
601 options: crate::PathPromptOptions,
602 ) -> oneshot::Receiver<Result<Option<Vec<std::path::PathBuf>>>> {
603 let (tx, rx) = oneshot::channel();
604 self.prompts.borrow_mut().paths.push_back((options, tx));
605 rx
606 }
607
608 #[cfg(not(any(test, feature = "test-support")))]
613 fn prompt_for_paths(
614 &self,
615 _options: crate::PathPromptOptions,
616 ) -> oneshot::Receiver<Result<Option<Vec<std::path::PathBuf>>>> {
617 oneshot::channel().1
618 }
619
620 #[cfg(any(test, feature = "test-support"))]
623 fn prompt_for_new_path(
624 &self,
625 directory: &std::path::Path,
626 _suggested_name: Option<&str>,
627 ) -> oneshot::Receiver<Result<Option<std::path::PathBuf>>> {
628 let (tx, rx) = oneshot::channel();
629 self.prompts
630 .borrow_mut()
631 .new_path
632 .push_back((directory.to_path_buf(), tx));
633 rx
634 }
635
636 #[cfg(not(any(test, feature = "test-support")))]
641 fn prompt_for_new_path(
642 &self,
643 _directory: &std::path::Path,
644 _suggested_name: Option<&str>,
645 ) -> oneshot::Receiver<Result<Option<std::path::PathBuf>>> {
646 oneshot::channel().1
647 }
648
649 fn can_select_mixed_files_and_dirs(&self) -> bool {
650 true
651 }
652
653 fn reveal_path(&self, _path: &std::path::Path) {
654 unimplemented!()
655 }
656
657 fn on_quit(&self, _callback: Box<dyn FnMut() -> bool>) {}
658
659 fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
660 unimplemented!()
661 }
662
663 fn on_system_sleep(&self, _callback: Box<dyn FnMut()>) {}
664
665 fn on_system_wake(&self, _callback: Box<dyn FnMut()>) {}
666
667 fn on_app_lifecycle(&self, callback: Box<dyn FnMut(AppLifecyclePhase)>) {
668 *self.app_lifecycle_callback.borrow_mut() = Some(callback);
669 }
670
671 fn on_memory_warning(&self, callback: Box<dyn FnMut()>) {
672 *self.memory_warning_callback.borrow_mut() = Some(callback);
673 }
674
675 fn set_app_identity(&self, identifier: &str, name: &str) {
676 self.system_notifications.borrow_mut().app_identity =
677 Some((identifier.to_string().into(), name.to_string().into()));
678 }
679
680 fn show_system_notification(&self, notification: SystemNotification) {
681 let mut system_notifications = self.system_notifications.borrow_mut();
682 if system_notifications.app_identity.is_none() {
683 return;
684 }
685
686 let delivered = system_notifications
687 .delivered
688 .iter_mut()
689 .find(|delivered| delivered.tag == notification.tag);
690 if let Some(delivered) = delivered {
691 *delivered = notification.clone();
692 } else {
693 system_notifications.delivered.push(notification.clone());
694 }
695 system_notifications.shown.push(notification);
696 }
697
698 fn dismiss_system_notification(&self, tag: &str) {
699 let mut system_notifications = self.system_notifications.borrow_mut();
700 system_notifications
701 .delivered
702 .retain(|notification| notification.tag != tag);
703 system_notifications
704 .dismissed
705 .push(SharedString::from(tag.to_string()));
706 }
707
708 fn on_system_notification_response(
709 &self,
710 callback: Box<dyn FnMut(SystemNotificationResponse)>,
711 ) {
712 self.system_notifications.borrow_mut().response_callback = Some(callback);
713 }
714
715 fn set_menus(&self, menus: Vec<crate::Menu>, _keymap: &Keymap) {
716 *self.menus.borrow_mut() = menus.into_iter().map(|menu| menu.owned()).collect()
717 }
718
719 fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
720 Some(self.menus.borrow().clone())
721 }
722
723 fn set_dock_menu(&self, _menu: Vec<crate::MenuItem>, _keymap: &Keymap) {}
724
725 fn add_recent_document(&self, _paths: &Path) {}
726
727 fn on_app_menu_action(&self, _callback: Box<dyn FnMut(&dyn crate::Action)>) {}
728
729 fn on_will_open_app_menu(&self, _callback: Box<dyn FnMut()>) {}
730
731 fn on_validate_app_menu_command(&self, _callback: Box<dyn FnMut(&dyn crate::Action) -> bool>) {}
732
733 fn app_path(&self) -> Result<std::path::PathBuf> {
734 unimplemented!()
735 }
736
737 fn path_for_auxiliary_executable(&self, _name: &str) -> Result<std::path::PathBuf> {
738 unimplemented!()
739 }
740
741 fn set_cursor_style(&self, style: crate::CursorStyle) {
742 *self.active_cursor.lock() = style;
743 }
744
745 fn hide_cursor_until_mouse_moves(&self) {}
746
747 fn is_cursor_visible(&self) -> bool {
748 true
749 }
750
751 fn should_auto_hide_scrollbars(&self) -> bool {
752 false
753 }
754
755 fn read_from_clipboard(&self) -> Option<ClipboardItem> {
756 self.current_clipboard_item.lock().clone()
757 }
758
759 fn write_to_clipboard(&self, item: ClipboardItem) {
760 *self.current_clipboard_item.lock() = Some(item);
761 }
762
763 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
764 fn read_from_primary(&self) -> Option<ClipboardItem> {
765 self.current_primary_item.lock().clone()
766 }
767
768 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
769 fn write_to_primary(&self, item: ClipboardItem) {
770 *self.current_primary_item.lock() = Some(item);
771 }
772
773 #[cfg(target_os = "macos")]
774 fn read_from_find_pasteboard(&self) -> Option<ClipboardItem> {
775 self.current_find_pasteboard_item.lock().clone()
776 }
777
778 #[cfg(target_os = "macos")]
779 fn write_to_find_pasteboard(&self, item: ClipboardItem) {
780 *self.current_find_pasteboard_item.lock() = Some(item);
781 }
782
783 fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task<Result<()>> {
784 Task::ready(Ok(()))
785 }
786
787 fn read_credentials(&self, _url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
788 Task::ready(Ok(None))
789 }
790
791 fn delete_credentials(&self, _url: &str) -> Task<Result<()>> {
792 Task::ready(Ok(()))
793 }
794
795 fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
796 unimplemented!()
797 }
798
799 fn open_with_system(&self, _path: &Path) {
800 unimplemented!()
801 }
802}
803
804impl TestScreenCaptureSource {
805 #[cfg(any(test, feature = "test-support"))]
807 pub fn new() -> Self {
808 Self {}
809 }
810}
811
812struct TestKeyboardLayout;
813
814impl PlatformKeyboardLayout for TestKeyboardLayout {
815 fn id(&self) -> &str {
816 "zed.keyboard.example"
817 }
818
819 fn name(&self) -> &str {
820 "zed.keyboard.example"
821 }
822}