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