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}