gpui_platform.rs

  1//! Convenience crate that re-exports GPUI's platform traits and the
  2//! `current_platform` constructor so consumers don't need `#[cfg]` gating.
  3
  4pub use gpui::Platform;
  5
  6use std::rc::Rc;
  7
  8/// Returns a background executor for the current platform.
  9pub fn background_executor() -> gpui::BackgroundExecutor {
 10    current_platform(true).background_executor()
 11}
 12
 13pub fn application() -> gpui::Application {
 14    gpui::Application::with_platform(current_platform(false))
 15}
 16
 17pub fn headless() -> gpui::Application {
 18    gpui::Application::with_platform(current_platform(true))
 19}
 20
 21/// Returns the default [`Platform`] for the current OS.
 22pub fn current_platform(headless: bool) -> Rc<dyn Platform> {
 23    #[cfg(target_os = "macos")]
 24    {
 25        Rc::new(gpui_macos::MacPlatform::new(headless))
 26    }
 27
 28    #[cfg(target_os = "windows")]
 29    {
 30        Rc::new(
 31            gpui_windows::WindowsPlatform::new(headless)
 32                .expect("failed to initialize Windows platform"),
 33        )
 34    }
 35
 36    #[cfg(not(any(target_os = "macos", target_os = "windows")))]
 37    {
 38        gpui_linux::current_platform(headless)
 39    }
 40}
 41
 42#[cfg(all(test, target_os = "macos"))]
 43mod tests {
 44    use super::*;
 45    use gpui::{AppContext, Empty, VisualTestAppContext};
 46    use std::cell::RefCell;
 47    use std::time::Duration;
 48
 49    // Note: All VisualTestAppContext tests are ignored by default because they require
 50    // the macOS main thread. Standard Rust tests run on worker threads, which causes
 51    // SIGABRT when interacting with macOS AppKit/Cocoa APIs.
 52    //
 53    // To run these tests, use:
 54    // cargo test -p gpui visual_test_context -- --ignored --test-threads=1
 55
 56    #[test]
 57    #[ignore] // Requires macOS main thread
 58    fn test_foreground_tasks_run_with_run_until_parked() {
 59        let mut cx = VisualTestAppContext::new(current_platform(false));
 60
 61        let task_ran = Rc::new(RefCell::new(false));
 62
 63        // Spawn a foreground task via the App's spawn method
 64        // This should use our TestDispatcher, not the MacDispatcher
 65        {
 66            let task_ran = task_ran.clone();
 67            cx.update(|cx| {
 68                cx.spawn(async move |_| {
 69                    *task_ran.borrow_mut() = true;
 70                })
 71                .detach();
 72            });
 73        }
 74
 75        // The task should not have run yet
 76        assert!(!*task_ran.borrow());
 77
 78        // Run until parked should execute the foreground task
 79        cx.run_until_parked();
 80
 81        // Now the task should have run
 82        assert!(*task_ran.borrow());
 83    }
 84
 85    #[test]
 86    #[ignore] // Requires macOS main thread
 87    fn test_advance_clock_triggers_delayed_tasks() {
 88        let mut cx = VisualTestAppContext::new(current_platform(false));
 89
 90        let task_ran = Rc::new(RefCell::new(false));
 91
 92        // Spawn a task that waits for a timer
 93        {
 94            let task_ran = task_ran.clone();
 95            let executor = cx.background_executor.clone();
 96            cx.update(|cx| {
 97                cx.spawn(async move |_| {
 98                    executor.timer(Duration::from_millis(500)).await;
 99                    *task_ran.borrow_mut() = true;
100                })
101                .detach();
102            });
103        }
104
105        // Run until parked - the task should be waiting on the timer
106        cx.run_until_parked();
107        assert!(!*task_ran.borrow());
108
109        // Advance clock past the timer duration
110        cx.advance_clock(Duration::from_millis(600));
111
112        // Now the task should have completed
113        assert!(*task_ran.borrow());
114    }
115
116    #[test]
117    #[ignore] // Requires macOS main thread - window creation fails on test threads
118    fn test_window_spawn_uses_test_dispatcher() {
119        let mut cx = VisualTestAppContext::new(current_platform(false));
120
121        let task_ran = Rc::new(RefCell::new(false));
122
123        let window = cx
124            .open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
125            .expect("Failed to open window");
126
127        // Spawn a task via window.spawn - this is the critical test case
128        // for tooltip behavior, as tooltips use window.spawn for delayed show
129        {
130            let task_ran = task_ran.clone();
131            cx.update_window(window.into(), |_, window, cx| {
132                window
133                    .spawn(cx, async move |_| {
134                        *task_ran.borrow_mut() = true;
135                    })
136                    .detach();
137            })
138            .ok();
139        }
140
141        // The task should not have run yet
142        assert!(!*task_ran.borrow());
143
144        // Run until parked should execute the foreground task spawned via window
145        cx.run_until_parked();
146
147        // Now the task should have run
148        assert!(*task_ran.borrow());
149    }
150}