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}