Skip to main content

tauri/test/
mock_runtime.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5#![allow(dead_code)]
6#![allow(missing_docs)]
7
8use tauri_runtime::{
9  dpi::{PhysicalPosition, PhysicalSize, Position, Size},
10  monitor::Monitor,
11  webview::{DetachedWebview, PendingWebview},
12  window::{
13    CursorIcon, DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WindowBuilder,
14    WindowBuilderBase, WindowEvent, WindowId,
15  },
16  DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
17  Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent,
18  WebviewDispatch, WindowDispatch, WindowEventId,
19};
20
21#[cfg(target_os = "macos")]
22use tauri_utils::TitleBarStyle;
23use tauri_utils::{config::WindowConfig, Theme};
24use url::Url;
25
26#[cfg(windows)]
27use windows::Win32::Foundation::HWND;
28
29use std::{
30  cell::RefCell,
31  collections::HashMap,
32  fmt,
33  sync::{
34    atomic::{AtomicBool, AtomicU32, Ordering},
35    mpsc::{channel, sync_channel, Receiver, SyncSender},
36    Arc, Mutex,
37  },
38};
39
40type ShortcutMap = HashMap<String, Box<dyn Fn() + Send + 'static>>;
41
42enum Message {
43  Task(Box<dyn FnOnce() + Send>),
44  CloseWindow(WindowId),
45  DestroyWindow(WindowId),
46}
47
48struct Webview;
49
50struct Window {
51  label: String,
52  webviews: Vec<Webview>,
53}
54
55#[derive(Clone)]
56pub struct RuntimeContext {
57  is_running: Arc<AtomicBool>,
58  windows: Arc<RefCell<HashMap<WindowId, Window>>>,
59  shortcuts: Arc<Mutex<ShortcutMap>>,
60  run_tx: SyncSender<Message>,
61  next_window_id: Arc<AtomicU32>,
62  next_webview_id: Arc<AtomicU32>,
63  next_window_event_id: Arc<AtomicU32>,
64  next_webview_event_id: Arc<AtomicU32>,
65}
66
67// SAFETY: we ensure this type is only used on the main thread.
68#[allow(clippy::non_send_fields_in_send_ty)]
69unsafe impl Send for RuntimeContext {}
70
71// SAFETY: we ensure this type is only used on the main thread.
72#[allow(clippy::non_send_fields_in_send_ty)]
73unsafe impl Sync for RuntimeContext {}
74
75impl RuntimeContext {
76  fn send_message(&self, message: Message) -> Result<()> {
77    if self.is_running.load(Ordering::Relaxed) {
78      self
79        .run_tx
80        .send(message)
81        .map_err(|_| Error::FailedToSendMessage)
82    } else {
83      match message {
84        Message::Task(task) => task(),
85        Message::CloseWindow(id) | Message::DestroyWindow(id) => {
86          self.windows.borrow_mut().remove(&id);
87        }
88      }
89      Ok(())
90    }
91  }
92
93  fn next_window_id(&self) -> WindowId {
94    self.next_window_id.fetch_add(1, Ordering::Relaxed).into()
95  }
96
97  fn next_webview_id(&self) -> u32 {
98    self.next_webview_id.fetch_add(1, Ordering::Relaxed)
99  }
100
101  fn next_window_event_id(&self) -> WindowEventId {
102    self.next_window_event_id.fetch_add(1, Ordering::Relaxed)
103  }
104
105  fn next_webview_event_id(&self) -> WindowEventId {
106    self.next_webview_event_id.fetch_add(1, Ordering::Relaxed)
107  }
108}
109
110impl fmt::Debug for RuntimeContext {
111  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112    f.debug_struct("RuntimeContext").finish()
113  }
114}
115
116#[derive(Debug, Clone)]
117pub struct MockRuntimeHandle {
118  context: RuntimeContext,
119}
120
121impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
122  type Runtime = MockRuntime;
123
124  fn create_proxy(&self) -> EventProxy {
125    EventProxy {}
126  }
127
128  #[cfg(target_os = "macos")]
129  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
130  fn set_activation_policy(
131    &self,
132    activation_policy: tauri_runtime::ActivationPolicy,
133  ) -> Result<()> {
134    Ok(())
135  }
136
137  #[cfg(target_os = "macos")]
138  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
139  fn set_dock_visibility(&self, visible: bool) -> Result<()> {
140    Ok(())
141  }
142
143  fn request_exit(&self, code: i32) -> Result<()> {
144    unimplemented!()
145  }
146
147  /// Create a new webview window.
148  fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
149    &self,
150    pending: PendingWindow<T, Self::Runtime>,
151    _after_window_creation: Option<F>,
152  ) -> Result<DetachedWindow<T, Self::Runtime>> {
153    let id = self.context.next_window_id();
154
155    let (webview_id, webviews) = if let Some(w) = &pending.webview {
156      (Some(self.context.next_webview_id()), vec![Webview])
157    } else {
158      (None, Vec::new())
159    };
160
161    self.context.windows.borrow_mut().insert(
162      id,
163      Window {
164        label: pending.label.clone(),
165        webviews,
166      },
167    );
168
169    let webview = webview_id.map(|id| DetachedWindowWebview {
170      webview: DetachedWebview {
171        label: pending.label.clone(),
172        dispatcher: MockWebviewDispatcher {
173          id,
174          context: self.context.clone(),
175          url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
176          last_evaluated_script: Default::default(),
177        },
178      },
179      use_https_scheme: false,
180    });
181
182    Ok(DetachedWindow {
183      id,
184      label: pending.label,
185      dispatcher: MockWindowDispatcher {
186        id,
187        context: self.context.clone(),
188      },
189      webview,
190    })
191  }
192
193  fn create_webview(
194    &self,
195    window_id: WindowId,
196    pending: PendingWebview<T, Self::Runtime>,
197  ) -> Result<DetachedWebview<T, Self::Runtime>> {
198    let id = self.context.next_webview_id();
199    let webview = Webview;
200    if let Some(w) = self.context.windows.borrow_mut().get_mut(&window_id) {
201      w.webviews.push(webview);
202    }
203
204    Ok(DetachedWebview {
205      label: pending.label,
206      dispatcher: MockWebviewDispatcher {
207        id,
208        context: self.context.clone(),
209        last_evaluated_script: Default::default(),
210        url: Arc::new(Mutex::new(pending.url)),
211      },
212    })
213  }
214
215  /// Run a task on the main thread.
216  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
217    self.context.send_message(Message::Task(Box::new(f)))
218  }
219
220  fn display_handle(
221    &self,
222  ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
223    #[cfg(target_os = "linux")]
224    return Ok(unsafe {
225      raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Xlib(
226        raw_window_handle::XlibDisplayHandle::new(None, 0),
227      ))
228    });
229    #[cfg(target_os = "macos")]
230    return Ok(unsafe {
231      raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::AppKit(
232        raw_window_handle::AppKitDisplayHandle::new(),
233      ))
234    });
235    #[cfg(windows)]
236    return Ok(unsafe {
237      raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Windows(
238        raw_window_handle::WindowsDisplayHandle::new(),
239      ))
240    });
241    #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
242    unimplemented!();
243  }
244
245  fn primary_monitor(&self) -> Option<Monitor> {
246    unimplemented!()
247  }
248
249  fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
250    unimplemented!()
251  }
252
253  fn available_monitors(&self) -> Vec<Monitor> {
254    unimplemented!()
255  }
256
257  fn set_theme(&self, theme: Option<Theme>) {
258    unimplemented!()
259  }
260
261  /// Shows the application, but does not automatically focus it.
262  #[cfg(target_os = "macos")]
263  fn show(&self) -> Result<()> {
264    Ok(())
265  }
266
267  /// Hides the application.
268  #[cfg(target_os = "macos")]
269  fn hide(&self) -> Result<()> {
270    Ok(())
271  }
272
273  fn set_device_event_filter(&self, _: DeviceEventFilter) {
274    // no-op
275  }
276
277  #[cfg(target_os = "android")]
278  fn find_class<'a>(
279    &self,
280    env: &mut jni::JNIEnv<'a>,
281    activity: &jni::objects::JObject<'_>,
282    name: impl Into<String>,
283  ) -> std::result::Result<jni::objects::JClass<'a>, jni::errors::Error> {
284    todo!()
285  }
286
287  #[cfg(target_os = "android")]
288  fn run_on_android_context<F>(&self, f: F)
289  where
290    F: FnOnce(&mut jni::JNIEnv, &jni::objects::JObject, &jni::objects::JObject) + Send + 'static,
291  {
292    todo!()
293  }
294
295  #[cfg(any(target_os = "macos", target_os = "ios"))]
296  fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
297    &self,
298    cb: F,
299  ) -> Result<()> {
300    todo!()
301  }
302
303  #[cfg(any(target_os = "macos", target_os = "ios"))]
304  fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
305    &self,
306    uuid: [u8; 16],
307    cb: F,
308  ) -> Result<()> {
309    todo!()
310  }
311
312  fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
313    Ok(PhysicalPosition::new(0.0, 0.0))
314  }
315}
316
317#[derive(Debug, Clone)]
318pub struct MockWebviewDispatcher {
319  id: u32,
320  context: RuntimeContext,
321  url: Arc<Mutex<String>>,
322  last_evaluated_script: Arc<Mutex<Option<String>>>,
323}
324
325impl MockWebviewDispatcher {
326  pub fn last_evaluated_script(&self) -> Option<String> {
327    self.last_evaluated_script.lock().unwrap().clone()
328  }
329}
330
331#[derive(Debug, Clone)]
332pub struct MockWindowDispatcher {
333  id: WindowId,
334  context: RuntimeContext,
335}
336
337#[derive(Debug, Clone)]
338pub struct MockWindowBuilder {}
339
340impl WindowBuilderBase for MockWindowBuilder {}
341
342impl WindowBuilder for MockWindowBuilder {
343  fn new() -> Self {
344    Self {}
345  }
346
347  fn with_config(config: &WindowConfig) -> Self {
348    Self {}
349  }
350
351  fn center(self) -> Self {
352    self
353  }
354
355  fn position(self, x: f64, y: f64) -> Self {
356    self
357  }
358
359  fn inner_size(self, min_width: f64, min_height: f64) -> Self {
360    self
361  }
362
363  fn min_inner_size(self, min_width: f64, min_height: f64) -> Self {
364    self
365  }
366
367  fn max_inner_size(self, max_width: f64, max_height: f64) -> Self {
368    self
369  }
370
371  fn inner_size_constraints(
372    self,
373    constraints: tauri_runtime::window::WindowSizeConstraints,
374  ) -> Self {
375    self
376  }
377
378  fn prevent_overflow(self) -> Self {
379    self
380  }
381
382  fn prevent_overflow_with_margin(self, margin: tauri_runtime::dpi::Size) -> Self {
383    self
384  }
385
386  fn resizable(self, resizable: bool) -> Self {
387    self
388  }
389
390  fn maximizable(self, resizable: bool) -> Self {
391    self
392  }
393
394  fn minimizable(self, resizable: bool) -> Self {
395    self
396  }
397
398  fn closable(self, resizable: bool) -> Self {
399    self
400  }
401
402  fn title<S: Into<String>>(self, title: S) -> Self {
403    self
404  }
405
406  fn fullscreen(self, fullscreen: bool) -> Self {
407    self
408  }
409
410  fn focused(self, focused: bool) -> Self {
411    self
412  }
413
414  fn focusable(self, focusable: bool) -> Self {
415    self
416  }
417
418  fn maximized(self, maximized: bool) -> Self {
419    self
420  }
421
422  fn visible(self, visible: bool) -> Self {
423    self
424  }
425
426  #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
427  #[cfg_attr(
428    docsrs,
429    doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
430  )]
431  fn transparent(self, transparent: bool) -> Self {
432    self
433  }
434
435  fn decorations(self, decorations: bool) -> Self {
436    self
437  }
438
439  fn always_on_bottom(self, always_on_bottom: bool) -> Self {
440    self
441  }
442
443  fn always_on_top(self, always_on_top: bool) -> Self {
444    self
445  }
446
447  fn visible_on_all_workspaces(self, visible_on_all_workspaces: bool) -> Self {
448    self
449  }
450
451  fn content_protected(self, protected: bool) -> Self {
452    self
453  }
454
455  fn icon(self, icon: Icon<'_>) -> Result<Self> {
456    Ok(self)
457  }
458
459  fn skip_taskbar(self, skip: bool) -> Self {
460    self
461  }
462
463  fn window_classname<S: Into<String>>(self, classname: S) -> Self {
464    self
465  }
466
467  fn shadow(self, enable: bool) -> Self {
468    self
469  }
470
471  #[cfg(windows)]
472  fn owner(self, owner: HWND) -> Self {
473    self
474  }
475
476  #[cfg(windows)]
477  fn parent(self, parent: HWND) -> Self {
478    self
479  }
480
481  #[cfg(target_os = "macos")]
482  fn parent(self, parent: *mut std::ffi::c_void) -> Self {
483    self
484  }
485
486  #[cfg(any(
487    target_os = "linux",
488    target_os = "dragonfly",
489    target_os = "freebsd",
490    target_os = "netbsd",
491    target_os = "openbsd"
492  ))]
493  fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
494    self
495  }
496
497  #[cfg(windows)]
498  fn drag_and_drop(self, enabled: bool) -> Self {
499    self
500  }
501
502  #[cfg(target_os = "macos")]
503  fn title_bar_style(self, style: TitleBarStyle) -> Self {
504    self
505  }
506
507  #[cfg(target_os = "macos")]
508  fn traffic_light_position<P: Into<Position>>(self, position: P) -> Self {
509    self
510  }
511
512  #[cfg(target_os = "macos")]
513  fn hidden_title(self, transparent: bool) -> Self {
514    self
515  }
516
517  #[cfg(target_os = "macos")]
518  fn tabbing_identifier(self, identifier: &str) -> Self {
519    self
520  }
521
522  fn theme(self, theme: Option<Theme>) -> Self {
523    self
524  }
525
526  fn has_icon(&self) -> bool {
527    false
528  }
529
530  fn get_theme(&self) -> Option<Theme> {
531    None
532  }
533
534  fn background_color(self, _color: tauri_utils::config::Color) -> Self {
535    self
536  }
537
538  #[cfg(target_os = "android")]
539  fn activity_name<S: Into<String>>(self, _class_name: S) -> Self {
540    self
541  }
542
543  #[cfg(target_os = "android")]
544  fn created_by_activity_name<S: Into<String>>(self, _class_name: S) -> Self {
545    self
546  }
547
548  #[cfg(target_os = "ios")]
549  fn requested_by_scene_identifier<S: Into<String>>(self, _identifier: S) -> Self {
550    self
551  }
552}
553
554impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
555  type Runtime = MockRuntime;
556
557  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
558    self.context.send_message(Message::Task(Box::new(f)))
559  }
560
561  fn on_webview_event<F: Fn(&tauri_runtime::window::WebviewEvent) + Send + 'static>(
562    &self,
563    f: F,
564  ) -> tauri_runtime::WebviewEventId {
565    self.context.next_window_event_id()
566  }
567
568  fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
569    Ok(())
570  }
571
572  #[cfg(any(debug_assertions, feature = "devtools"))]
573  fn open_devtools(&self) {}
574
575  #[cfg(any(debug_assertions, feature = "devtools"))]
576  fn close_devtools(&self) {}
577
578  #[cfg(any(debug_assertions, feature = "devtools"))]
579  fn is_devtools_open(&self) -> Result<bool> {
580    Ok(false)
581  }
582
583  fn set_zoom(&self, scale_factor: f64) -> Result<()> {
584    Ok(())
585  }
586
587  fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
588    self
589      .last_evaluated_script
590      .lock()
591      .unwrap()
592      .replace(script.into());
593    Ok(())
594  }
595
596  fn eval_script_with_callback<S: Into<String>>(
597    &self,
598    script: S,
599    callback: impl Fn(String) + Send + 'static,
600  ) -> Result<()> {
601    self
602      .last_evaluated_script
603      .lock()
604      .unwrap()
605      .replace(script.into());
606    Ok(())
607  }
608
609  fn url(&self) -> Result<String> {
610    Ok(self.url.lock().unwrap().clone())
611  }
612
613  fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
614    Ok(tauri_runtime::dpi::Rect::default())
615  }
616
617  fn position(&self) -> Result<PhysicalPosition<i32>> {
618    Ok(PhysicalPosition { x: 0, y: 0 })
619  }
620
621  fn size(&self) -> Result<PhysicalSize<u32>> {
622    Ok(PhysicalSize {
623      width: 0,
624      height: 0,
625    })
626  }
627
628  fn navigate(&self, url: Url) -> Result<()> {
629    *self.url.lock().unwrap() = url.to_string();
630    Ok(())
631  }
632
633  fn reload(&self) -> Result<()> {
634    Ok(())
635  }
636
637  fn print(&self) -> Result<()> {
638    Ok(())
639  }
640
641  fn close(&self) -> Result<()> {
642    Ok(())
643  }
644
645  fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
646    Ok(())
647  }
648
649  fn set_size(&self, _size: Size) -> Result<()> {
650    Ok(())
651  }
652
653  fn set_position(&self, _position: Position) -> Result<()> {
654    Ok(())
655  }
656
657  fn set_focus(&self) -> Result<()> {
658    Ok(())
659  }
660
661  fn reparent(&self, window_id: WindowId) -> Result<()> {
662    Ok(())
663  }
664
665  fn cookies(&self) -> Result<Vec<tauri_runtime::Cookie<'static>>> {
666    Ok(Vec::new())
667  }
668
669  fn cookies_for_url(&self, url: Url) -> Result<Vec<tauri_runtime::Cookie<'static>>> {
670    Ok(Vec::new())
671  }
672
673  fn set_cookie(&self, cookie: tauri_runtime::Cookie<'_>) -> Result<()> {
674    Ok(())
675  }
676
677  fn delete_cookie(&self, cookie: tauri_runtime::Cookie<'_>) -> Result<()> {
678    Ok(())
679  }
680
681  fn set_auto_resize(&self, auto_resize: bool) -> Result<()> {
682    Ok(())
683  }
684
685  fn clear_all_browsing_data(&self) -> Result<()> {
686    Ok(())
687  }
688
689  fn hide(&self) -> Result<()> {
690    Ok(())
691  }
692
693  fn show(&self) -> Result<()> {
694    Ok(())
695  }
696
697  fn set_background_color(&self, color: Option<tauri_utils::config::Color>) -> Result<()> {
698    Ok(())
699  }
700}
701
702impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {
703  type Runtime = MockRuntime;
704
705  type WindowBuilder = MockWindowBuilder;
706
707  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
708    self.context.send_message(Message::Task(Box::new(f)))
709  }
710
711  fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
712    self.context.next_window_event_id()
713  }
714
715  fn scale_factor(&self) -> Result<f64> {
716    Ok(1.0)
717  }
718
719  fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
720    Ok(PhysicalPosition { x: 0, y: 0 })
721  }
722
723  fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
724    Ok(PhysicalPosition { x: 0, y: 0 })
725  }
726
727  fn inner_size(&self) -> Result<PhysicalSize<u32>> {
728    Ok(PhysicalSize {
729      width: 0,
730      height: 0,
731    })
732  }
733
734  fn outer_size(&self) -> Result<PhysicalSize<u32>> {
735    Ok(PhysicalSize {
736      width: 0,
737      height: 0,
738    })
739  }
740
741  fn is_fullscreen(&self) -> Result<bool> {
742    Ok(false)
743  }
744
745  fn is_minimized(&self) -> Result<bool> {
746    Ok(false)
747  }
748
749  fn is_maximized(&self) -> Result<bool> {
750    Ok(false)
751  }
752
753  fn is_focused(&self) -> Result<bool> {
754    Ok(false)
755  }
756
757  fn is_decorated(&self) -> Result<bool> {
758    Ok(false)
759  }
760
761  fn is_resizable(&self) -> Result<bool> {
762    Ok(false)
763  }
764
765  fn is_maximizable(&self) -> Result<bool> {
766    Ok(true)
767  }
768
769  fn is_minimizable(&self) -> Result<bool> {
770    Ok(true)
771  }
772
773  fn is_closable(&self) -> Result<bool> {
774    Ok(true)
775  }
776
777  fn is_visible(&self) -> Result<bool> {
778    Ok(true)
779  }
780
781  fn title(&self) -> Result<String> {
782    Ok(String::new())
783  }
784
785  fn current_monitor(&self) -> Result<Option<Monitor>> {
786    Ok(None)
787  }
788
789  fn primary_monitor(&self) -> Result<Option<Monitor>> {
790    Ok(None)
791  }
792
793  fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
794    Ok(None)
795  }
796
797  fn available_monitors(&self) -> Result<Vec<Monitor>> {
798    Ok(Vec::new())
799  }
800
801  fn theme(&self) -> Result<Theme> {
802    Ok(Theme::Light)
803  }
804
805  #[cfg(any(
806    target_os = "linux",
807    target_os = "dragonfly",
808    target_os = "freebsd",
809    target_os = "netbsd",
810    target_os = "openbsd"
811  ))]
812  fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
813    unimplemented!()
814  }
815
816  #[cfg(any(
817    target_os = "linux",
818    target_os = "dragonfly",
819    target_os = "freebsd",
820    target_os = "netbsd",
821    target_os = "openbsd"
822  ))]
823  fn default_vbox(&self) -> Result<gtk::Box> {
824    unimplemented!()
825  }
826
827  #[cfg(target_os = "android")]
828  fn activity_name(&self) -> Result<String> {
829    unimplemented!()
830  }
831
832  #[cfg(target_os = "ios")]
833  fn scene_identifier(&self) -> Result<String> {
834    unimplemented!()
835  }
836
837  fn window_handle(
838    &self,
839  ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
840    #[cfg(target_os = "linux")]
841    return unsafe {
842      Ok(raw_window_handle::WindowHandle::borrow_raw(
843        raw_window_handle::RawWindowHandle::Xlib(raw_window_handle::XlibWindowHandle::new(0)),
844      ))
845    };
846    #[cfg(target_os = "macos")]
847    return unsafe {
848      Ok(raw_window_handle::WindowHandle::borrow_raw(
849        raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle::new(
850          std::ptr::NonNull::from(&()).cast(),
851        )),
852      ))
853    };
854    #[cfg(windows)]
855    return unsafe {
856      Ok(raw_window_handle::WindowHandle::borrow_raw(
857        raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::new(
858          std::num::NonZeroIsize::MIN,
859        )),
860      ))
861    };
862    #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
863    unimplemented!();
864  }
865
866  fn center(&self) -> Result<()> {
867    Ok(())
868  }
869
870  fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
871    Ok(())
872  }
873
874  fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
875    &mut self,
876    pending: PendingWindow<T, Self::Runtime>,
877    _after_window_creation: Option<F>,
878  ) -> Result<DetachedWindow<T, Self::Runtime>> {
879    let id = self.context.next_window_id();
880
881    let (webview_id, webviews) = if let Some(w) = &pending.webview {
882      (Some(self.context.next_webview_id()), vec![Webview])
883    } else {
884      (None, Vec::new())
885    };
886
887    self.context.windows.borrow_mut().insert(
888      id,
889      Window {
890        label: pending.label.clone(),
891        webviews,
892      },
893    );
894
895    let webview = webview_id.map(|id| DetachedWindowWebview {
896      webview: DetachedWebview {
897        label: pending.label.clone(),
898        dispatcher: MockWebviewDispatcher {
899          id,
900          context: self.context.clone(),
901          url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
902          last_evaluated_script: Default::default(),
903        },
904      },
905      use_https_scheme: false,
906    });
907
908    Ok(DetachedWindow {
909      id,
910      label: pending.label,
911      dispatcher: MockWindowDispatcher {
912        id,
913        context: self.context.clone(),
914      },
915      webview,
916    })
917  }
918
919  fn create_webview(
920    &mut self,
921    pending: PendingWebview<T, Self::Runtime>,
922  ) -> Result<DetachedWebview<T, Self::Runtime>> {
923    let id = self.context.next_webview_id();
924    let webview = Webview;
925    if let Some(w) = self.context.windows.borrow_mut().get_mut(&self.id) {
926      w.webviews.push(webview);
927    }
928
929    Ok(DetachedWebview {
930      label: pending.label,
931      dispatcher: MockWebviewDispatcher {
932        id,
933        context: self.context.clone(),
934        last_evaluated_script: Default::default(),
935        url: Arc::new(Mutex::new(pending.url)),
936      },
937    })
938  }
939
940  fn set_resizable(&self, resizable: bool) -> Result<()> {
941    Ok(())
942  }
943
944  fn set_maximizable(&self, maximizable: bool) -> Result<()> {
945    Ok(())
946  }
947
948  fn set_minimizable(&self, minimizable: bool) -> Result<()> {
949    Ok(())
950  }
951
952  fn set_closable(&self, closable: bool) -> Result<()> {
953    Ok(())
954  }
955
956  fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
957    Ok(())
958  }
959
960  fn maximize(&self) -> Result<()> {
961    Ok(())
962  }
963
964  fn unmaximize(&self) -> Result<()> {
965    Ok(())
966  }
967
968  fn minimize(&self) -> Result<()> {
969    Ok(())
970  }
971
972  fn unminimize(&self) -> Result<()> {
973    Ok(())
974  }
975
976  fn show(&self) -> Result<()> {
977    Ok(())
978  }
979
980  fn hide(&self) -> Result<()> {
981    Ok(())
982  }
983
984  fn close(&self) -> Result<()> {
985    self.context.send_message(Message::CloseWindow(self.id))?;
986    Ok(())
987  }
988
989  fn destroy(&self) -> Result<()> {
990    self.context.send_message(Message::DestroyWindow(self.id))?;
991    Ok(())
992  }
993
994  fn set_decorations(&self, decorations: bool) -> Result<()> {
995    Ok(())
996  }
997
998  fn set_shadow(&self, shadow: bool) -> Result<()> {
999    Ok(())
1000  }
1001
1002  fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> {
1003    Ok(())
1004  }
1005
1006  fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
1007    Ok(())
1008  }
1009
1010  fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> {
1011    Ok(())
1012  }
1013
1014  fn set_content_protected(&self, protected: bool) -> Result<()> {
1015    Ok(())
1016  }
1017
1018  fn set_size(&self, size: Size) -> Result<()> {
1019    Ok(())
1020  }
1021
1022  fn set_min_size(&self, size: Option<Size>) -> Result<()> {
1023    Ok(())
1024  }
1025
1026  fn set_max_size(&self, size: Option<Size>) -> Result<()> {
1027    Ok(())
1028  }
1029
1030  fn set_position(&self, position: Position) -> Result<()> {
1031    Ok(())
1032  }
1033
1034  fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
1035    Ok(())
1036  }
1037
1038  #[cfg(target_os = "macos")]
1039  fn set_simple_fullscreen(&self, enable: bool) -> Result<()> {
1040    Ok(())
1041  }
1042
1043  fn set_focus(&self) -> Result<()> {
1044    Ok(())
1045  }
1046
1047  fn set_focusable(&self, focusable: bool) -> Result<()> {
1048    Ok(())
1049  }
1050
1051  fn set_icon(&self, icon: Icon<'_>) -> Result<()> {
1052    Ok(())
1053  }
1054
1055  fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
1056    Ok(())
1057  }
1058
1059  fn set_cursor_grab(&self, grab: bool) -> Result<()> {
1060    Ok(())
1061  }
1062
1063  fn set_cursor_visible(&self, visible: bool) -> Result<()> {
1064    Ok(())
1065  }
1066
1067  fn set_cursor_icon(&self, icon: CursorIcon) -> Result<()> {
1068    Ok(())
1069  }
1070
1071  fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> Result<()> {
1072    Ok(())
1073  }
1074
1075  fn set_ignore_cursor_events(&self, ignore: bool) -> Result<()> {
1076    Ok(())
1077  }
1078
1079  fn start_dragging(&self) -> Result<()> {
1080    Ok(())
1081  }
1082
1083  fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
1084    Ok(())
1085  }
1086
1087  fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
1088    Ok(())
1089  }
1090
1091  fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
1092    Ok(())
1093  }
1094
1095  fn set_badge_label(&self, label: Option<String>) -> Result<()> {
1096    Ok(())
1097  }
1098
1099  fn set_overlay_icon(&self, icon: Option<Icon<'_>>) -> Result<()> {
1100    Ok(())
1101  }
1102
1103  fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
1104    Ok(())
1105  }
1106
1107  fn set_traffic_light_position(&self, position: Position) -> Result<()> {
1108    Ok(())
1109  }
1110
1111  fn set_size_constraints(
1112    &self,
1113    constraints: tauri_runtime::window::WindowSizeConstraints,
1114  ) -> Result<()> {
1115    Ok(())
1116  }
1117
1118  fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
1119    Ok(())
1120  }
1121
1122  fn set_enabled(&self, enabled: bool) -> Result<()> {
1123    Ok(())
1124  }
1125
1126  fn is_enabled(&self) -> Result<bool> {
1127    Ok(true)
1128  }
1129
1130  fn is_always_on_top(&self) -> Result<bool> {
1131    Ok(false)
1132  }
1133
1134  fn set_background_color(&self, color: Option<tauri_utils::config::Color>) -> Result<()> {
1135    Ok(())
1136  }
1137}
1138
1139#[derive(Debug, Clone)]
1140pub struct EventProxy {}
1141
1142impl<T: UserEvent> EventLoopProxy<T> for EventProxy {
1143  fn send_event(&self, event: T) -> Result<()> {
1144    Ok(())
1145  }
1146}
1147
1148#[derive(Debug)]
1149pub struct MockRuntime {
1150  is_running: Arc<AtomicBool>,
1151  pub context: RuntimeContext,
1152  run_rx: Receiver<Message>,
1153}
1154
1155impl MockRuntime {
1156  fn init() -> Self {
1157    let is_running = Arc::new(AtomicBool::new(false));
1158    let (tx, rx) = sync_channel(256);
1159    let context = RuntimeContext {
1160      is_running: is_running.clone(),
1161      windows: Default::default(),
1162      shortcuts: Default::default(),
1163      run_tx: tx,
1164      next_window_id: Default::default(),
1165      next_webview_id: Default::default(),
1166      next_window_event_id: Default::default(),
1167      next_webview_event_id: Default::default(),
1168    };
1169    Self {
1170      is_running,
1171      context,
1172      run_rx: rx,
1173    }
1174  }
1175}
1176
1177impl<T: UserEvent> Runtime<T> for MockRuntime {
1178  type WindowDispatcher = MockWindowDispatcher;
1179  type WebviewDispatcher = MockWebviewDispatcher;
1180  type Handle = MockRuntimeHandle;
1181  type EventLoopProxy = EventProxy;
1182
1183  fn new(_args: RuntimeInitArgs) -> Result<Self> {
1184    Ok(Self::init())
1185  }
1186
1187  #[cfg(any(
1188    windows,
1189    target_os = "linux",
1190    target_os = "dragonfly",
1191    target_os = "freebsd",
1192    target_os = "netbsd",
1193    target_os = "openbsd"
1194  ))]
1195  fn new_any_thread(_args: RuntimeInitArgs) -> Result<Self> {
1196    Ok(Self::init())
1197  }
1198
1199  fn create_proxy(&self) -> EventProxy {
1200    EventProxy {}
1201  }
1202
1203  fn handle(&self) -> Self::Handle {
1204    MockRuntimeHandle {
1205      context: self.context.clone(),
1206    }
1207  }
1208
1209  fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
1210    &self,
1211    pending: PendingWindow<T, Self>,
1212    _after_window_creation: Option<F>,
1213  ) -> Result<DetachedWindow<T, Self>> {
1214    let id = self.context.next_window_id();
1215
1216    let (webview_id, webviews) = if let Some(w) = &pending.webview {
1217      (Some(self.context.next_webview_id()), vec![Webview])
1218    } else {
1219      (None, Vec::new())
1220    };
1221
1222    self.context.windows.borrow_mut().insert(
1223      id,
1224      Window {
1225        label: pending.label.clone(),
1226        webviews,
1227      },
1228    );
1229
1230    let webview = webview_id.map(|id| DetachedWindowWebview {
1231      webview: DetachedWebview {
1232        label: pending.label.clone(),
1233        dispatcher: MockWebviewDispatcher {
1234          id,
1235          context: self.context.clone(),
1236          url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
1237          last_evaluated_script: Default::default(),
1238        },
1239      },
1240      use_https_scheme: false,
1241    });
1242
1243    Ok(DetachedWindow {
1244      id,
1245      label: pending.label,
1246      dispatcher: MockWindowDispatcher {
1247        id,
1248        context: self.context.clone(),
1249      },
1250      webview,
1251    })
1252  }
1253
1254  fn create_webview(
1255    &self,
1256    window_id: WindowId,
1257    pending: PendingWebview<T, Self>,
1258  ) -> Result<DetachedWebview<T, Self>> {
1259    let id = self.context.next_webview_id();
1260    let webview = Webview;
1261    if let Some(w) = self.context.windows.borrow_mut().get_mut(&window_id) {
1262      w.webviews.push(webview);
1263    }
1264
1265    Ok(DetachedWebview {
1266      label: pending.label,
1267      dispatcher: MockWebviewDispatcher {
1268        id,
1269        context: self.context.clone(),
1270        last_evaluated_script: Default::default(),
1271        url: Arc::new(Mutex::new(pending.url)),
1272      },
1273    })
1274  }
1275
1276  fn primary_monitor(&self) -> Option<Monitor> {
1277    unimplemented!()
1278  }
1279
1280  fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
1281    unimplemented!()
1282  }
1283
1284  fn available_monitors(&self) -> Vec<Monitor> {
1285    unimplemented!()
1286  }
1287
1288  fn set_theme(&self, theme: Option<Theme>) {
1289    unimplemented!()
1290  }
1291
1292  #[cfg(target_os = "macos")]
1293  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1294  fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {}
1295
1296  #[cfg(target_os = "macos")]
1297  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1298  fn set_dock_visibility(&mut self, visible: bool) {}
1299
1300  #[cfg(target_os = "macos")]
1301  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1302  fn show(&self) {}
1303
1304  #[cfg(target_os = "macos")]
1305  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1306  fn hide(&self) {}
1307
1308  fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {}
1309
1310  #[cfg(any(
1311    target_os = "macos",
1312    windows,
1313    target_os = "linux",
1314    target_os = "dragonfly",
1315    target_os = "freebsd",
1316    target_os = "netbsd",
1317    target_os = "openbsd"
1318  ))]
1319  fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F) {}
1320
1321  fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
1322    self.run(callback);
1323
1324    0
1325  }
1326
1327  fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
1328    self.is_running.store(true, Ordering::Relaxed);
1329    callback(RunEvent::Ready);
1330
1331    loop {
1332      if let Ok(m) = self.run_rx.try_recv() {
1333        match m {
1334          Message::Task(p) => p(),
1335          Message::CloseWindow(id) => {
1336            let label = self
1337              .context
1338              .windows
1339              .borrow()
1340              .get(&id)
1341              .map(|w| w.label.clone());
1342            if let Some(label) = label {
1343              let (tx, rx) = channel();
1344              callback(RunEvent::WindowEvent {
1345                label,
1346                event: WindowEvent::CloseRequested { signal_tx: tx },
1347              });
1348
1349              let should_prevent = matches!(rx.try_recv(), Ok(true));
1350              if !should_prevent {
1351                self.context.windows.borrow_mut().remove(&id);
1352
1353                let is_empty = self.context.windows.borrow().is_empty();
1354                if is_empty {
1355                  let (tx, rx) = channel();
1356                  callback(RunEvent::ExitRequested { code: None, tx });
1357
1358                  let recv = rx.try_recv();
1359                  let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
1360
1361                  if !should_prevent {
1362                    break;
1363                  }
1364                }
1365              }
1366            }
1367          }
1368          Message::DestroyWindow(id) => {
1369            let removed = self.context.windows.borrow_mut().remove(&id).is_some();
1370            if removed {
1371              let is_empty = self.context.windows.borrow().is_empty();
1372              if is_empty {
1373                let (tx, rx) = channel();
1374                callback(RunEvent::ExitRequested { code: None, tx });
1375
1376                let recv = rx.try_recv();
1377                let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
1378
1379                if !should_prevent {
1380                  break;
1381                }
1382              }
1383            }
1384          }
1385        }
1386      }
1387
1388      callback(RunEvent::MainEventsCleared);
1389
1390      std::thread::sleep(std::time::Duration::from_secs(1));
1391    }
1392
1393    callback(RunEvent::Exit);
1394  }
1395
1396  fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
1397    Ok(PhysicalPosition::new(0.0, 0.0))
1398  }
1399}