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/// Unlike `application`, this function returns a single-threaded web application.
 22#[cfg(target_family = "wasm")]
 23pub fn single_threaded_web() -> gpui::Application {
 24    gpui::Application::with_platform(Rc::new(gpui_web::WebPlatform::new(false)))
 25}
 26
 27/// Initializes panic hooks and logging for the web platform.
 28/// Call this before running the application in a wasm_bindgen entrypoint.
 29#[cfg(target_family = "wasm")]
 30pub fn web_init() {
 31    console_error_panic_hook::set_once();
 32    gpui_web::init_logging();
 33}
 34
 35/// Returns the default [`Platform`] for the current OS.
 36pub fn current_platform(headless: bool) -> Rc<dyn Platform> {
 37    #[cfg(target_os = "macos")]
 38    {
 39        Rc::new(gpui_macos::MacPlatform::new(headless))
 40    }
 41
 42    #[cfg(target_os = "windows")]
 43    {
 44        Rc::new(
 45            gpui_windows::WindowsPlatform::new(headless)
 46                .expect("failed to initialize Windows platform"),
 47        )
 48    }
 49
 50    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 51    {
 52        gpui_linux::current_platform(headless)
 53    }
 54
 55    #[cfg(target_family = "wasm")]
 56    {
 57        let _ = headless;
 58        Rc::new(gpui_web::WebPlatform::new(true))
 59    }
 60}
 61
 62#[cfg(all(test, target_os = "macos"))]
 63mod tests {
 64    use super::*;
 65    use gpui::{AppContext, Empty, VisualTestAppContext};
 66    use std::cell::RefCell;
 67    use std::time::Duration;
 68
 69    // Note: All VisualTestAppContext tests are ignored by default because they require
 70    // the macOS main thread. Standard Rust tests run on worker threads, which causes
 71    // SIGABRT when interacting with macOS AppKit/Cocoa APIs.
 72    //
 73    // To run these tests, use:
 74    // cargo test -p gpui visual_test_context -- --ignored --test-threads=1
 75
 76    #[test]
 77    #[ignore] // Requires macOS main thread
 78    fn test_foreground_tasks_run_with_run_until_parked() {
 79        let mut cx = VisualTestAppContext::new(current_platform(false));
 80
 81        let task_ran = Rc::new(RefCell::new(false));
 82
 83        // Spawn a foreground task via the App's spawn method
 84        // This should use our TestDispatcher, not the MacDispatcher
 85        {
 86            let task_ran = task_ran.clone();
 87            cx.update(|cx| {
 88                cx.spawn(async move |_| {
 89                    *task_ran.borrow_mut() = true;
 90                })
 91                .detach();
 92            });
 93        }
 94
 95        // The task should not have run yet
 96        assert!(!*task_ran.borrow());
 97
 98        // Run until parked should execute the foreground task
 99        cx.run_until_parked();
100
101        // Now the task should have run
102        assert!(*task_ran.borrow());
103    }
104
105    #[test]
106    #[ignore] // Requires macOS main thread
107    fn test_advance_clock_triggers_delayed_tasks() {
108        let mut cx = VisualTestAppContext::new(current_platform(false));
109
110        let task_ran = Rc::new(RefCell::new(false));
111
112        // Spawn a task that waits for a timer
113        {
114            let task_ran = task_ran.clone();
115            let executor = cx.background_executor.clone();
116            cx.update(|cx| {
117                cx.spawn(async move |_| {
118                    executor.timer(Duration::from_millis(500)).await;
119                    *task_ran.borrow_mut() = true;
120                })
121                .detach();
122            });
123        }
124
125        // Run until parked - the task should be waiting on the timer
126        cx.run_until_parked();
127        assert!(!*task_ran.borrow());
128
129        // Advance clock past the timer duration
130        cx.advance_clock(Duration::from_millis(600));
131
132        // Now the task should have completed
133        assert!(*task_ran.borrow());
134    }
135
136    #[test]
137    #[ignore] // Requires macOS main thread - window creation fails on test threads
138    fn test_window_spawn_uses_test_dispatcher() {
139        let mut cx = VisualTestAppContext::new(current_platform(false));
140
141        let task_ran = Rc::new(RefCell::new(false));
142
143        let window = cx
144            .open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
145            .expect("Failed to open window");
146
147        // Spawn a task via window.spawn - this is the critical test case
148        // for tooltip behavior, as tooltips use window.spawn for delayed show
149        {
150            let task_ran = task_ran.clone();
151            cx.update_window(window.into(), |_, window, cx| {
152                window
153                    .spawn(cx, async move |_| {
154                        *task_ran.borrow_mut() = true;
155                    })
156                    .detach();
157            })
158            .ok();
159        }
160
161        // The task should not have run yet
162        assert!(!*task_ran.borrow());
163
164        // Run until parked should execute the foreground task spawned via window
165        cx.run_until_parked();
166
167        // Now the task should have run
168        assert!(*task_ran.borrow());
169    }
170}