Skip to repository content265 lines · 7.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T06:32:46.787Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
visual_test.rs
1//! Visual test platform that combines real rendering (macOs-only for now) with controllable TestDispatcher.
2//!
3//! This platform is used for visual tests that need:
4//! - Real rendering (e.g. Metal/compositor) for accurate screenshots
5//! - Deterministic task scheduling via TestDispatcher
6//! - Controllable time via `advance_clock`
7
8use crate::ScreenCaptureSource;
9use crate::{
10 AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, Keymap,
11 Menu, MenuItem, OwnedMenu, PathPromptOptions, Platform, PlatformDisplay,
12 PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, PlatformWindow, Task,
13 TestDispatcher, WindowAppearance, WindowParams,
14};
15use anyhow::Result;
16use futures::channel::oneshot;
17use parking_lot::Mutex;
18
19use std::{
20 path::{Path, PathBuf},
21 rc::Rc,
22 sync::Arc,
23};
24
25/// A platform that combines real Mac rendering with controllable TestDispatcher.
26///
27/// This allows visual tests to:
28/// - Render real UI via Metal for accurate screenshots
29/// - Control task scheduling deterministically via TestDispatcher
30/// - Advance simulated time for testing time-based behaviors (tooltips, animations, etc.)
31pub struct VisualTestPlatform {
32 dispatcher: TestDispatcher,
33 background_executor: BackgroundExecutor,
34 foreground_executor: ForegroundExecutor,
35 platform: Rc<dyn Platform>,
36 clipboard: Mutex<Option<ClipboardItem>>,
37 find_pasteboard: Mutex<Option<ClipboardItem>>,
38}
39
40impl VisualTestPlatform {
41 /// Creates a new VisualTestPlatform with the given random seed.
42 ///
43 /// The seed is used for deterministic random number generation in the TestDispatcher.
44 pub fn new(platform: Rc<dyn Platform>, seed: u64) -> Self {
45 let dispatcher = TestDispatcher::new(seed);
46 let arc_dispatcher = Arc::new(dispatcher.clone());
47
48 let background_executor = BackgroundExecutor::new(arc_dispatcher.clone());
49 let foreground_executor = ForegroundExecutor::new(arc_dispatcher);
50
51 Self {
52 dispatcher,
53 background_executor,
54 foreground_executor,
55 platform,
56 clipboard: Mutex::new(None),
57 find_pasteboard: Mutex::new(None),
58 }
59 }
60
61 /// Returns a reference to the TestDispatcher for controlling task scheduling and time.
62 pub fn dispatcher(&self) -> &TestDispatcher {
63 &self.dispatcher
64 }
65}
66
67impl Platform for VisualTestPlatform {
68 fn background_executor(&self) -> BackgroundExecutor {
69 self.background_executor.clone()
70 }
71
72 fn foreground_executor(&self) -> ForegroundExecutor {
73 self.foreground_executor.clone()
74 }
75
76 fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
77 self.platform.text_system()
78 }
79
80 fn run(&self, _on_finish_launching: Box<dyn 'static + FnOnce()>) {
81 panic!("VisualTestPlatform::run should not be called in tests")
82 }
83
84 fn quit(&self) {}
85
86 fn restart(&self, _binary_path: Option<PathBuf>) {}
87
88 fn activate(&self, _ignoring_other_apps: bool) {}
89
90 fn hide(&self) {}
91
92 fn hide_other_apps(&self) {}
93
94 fn unhide_other_apps(&self) {}
95
96 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
97 self.platform.displays()
98 }
99
100 fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
101 self.platform.primary_display()
102 }
103
104 fn active_window(&self) -> Option<AnyWindowHandle> {
105 self.platform.active_window()
106 }
107
108 fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
109 self.platform.window_stack()
110 }
111
112 fn is_screen_capture_supported(&self) -> bool {
113 self.platform.is_screen_capture_supported()
114 }
115
116 fn screen_capture_sources(
117 &self,
118 ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
119 self.platform.screen_capture_sources()
120 }
121
122 fn open_window(
123 &self,
124 handle: AnyWindowHandle,
125 options: WindowParams,
126 ) -> Result<Box<dyn PlatformWindow>> {
127 self.platform.open_window(handle, options)
128 }
129
130 fn window_appearance(&self) -> WindowAppearance {
131 self.platform.window_appearance()
132 }
133
134 fn open_url(&self, url: &str) {
135 self.platform.open_url(url)
136 }
137
138 fn on_open_urls(&self, _callback: Box<dyn FnMut(Vec<String>)>) {}
139
140 fn register_url_scheme(&self, _url: &str) -> Task<Result<()>> {
141 Task::ready(Ok(()))
142 }
143
144 fn prompt_for_paths(
145 &self,
146 _options: PathPromptOptions,
147 ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
148 let (tx, rx) = oneshot::channel();
149 tx.send(Ok(None)).ok();
150 rx
151 }
152
153 fn prompt_for_new_path(
154 &self,
155 _directory: &Path,
156 _suggested_name: Option<&str>,
157 ) -> oneshot::Receiver<Result<Option<PathBuf>>> {
158 let (tx, rx) = oneshot::channel();
159 tx.send(Ok(None)).ok();
160 rx
161 }
162
163 fn can_select_mixed_files_and_dirs(&self) -> bool {
164 true
165 }
166
167 fn reveal_path(&self, path: &Path) {
168 self.platform.reveal_path(path)
169 }
170
171 fn open_with_system(&self, path: &Path) {
172 self.platform.open_with_system(path)
173 }
174
175 fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
176
177 fn on_reopen(&self, _callback: Box<dyn FnMut()>) {}
178
179 fn on_system_wake(&self, _callback: Box<dyn FnMut()>) {}
180
181 fn set_menus(&self, _menus: Vec<Menu>, _keymap: &Keymap) {}
182
183 fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
184 None
185 }
186
187 fn set_dock_menu(&self, _menu: Vec<MenuItem>, _keymap: &Keymap) {}
188
189 fn on_app_menu_action(&self, _callback: Box<dyn FnMut(&dyn crate::Action)>) {}
190
191 fn on_will_open_app_menu(&self, _callback: Box<dyn FnMut()>) {}
192
193 fn on_validate_app_menu_command(&self, _callback: Box<dyn FnMut(&dyn crate::Action) -> bool>) {}
194
195 fn app_path(&self) -> Result<PathBuf> {
196 self.platform.app_path()
197 }
198
199 fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
200 self.platform.path_for_auxiliary_executable(name)
201 }
202
203 fn set_cursor_style(&self, style: CursorStyle) {
204 self.platform.set_cursor_style(style)
205 }
206
207 fn hide_cursor_until_mouse_moves(&self) {
208 self.platform.hide_cursor_until_mouse_moves();
209 }
210
211 fn is_cursor_visible(&self) -> bool {
212 self.platform.is_cursor_visible()
213 }
214
215 fn should_auto_hide_scrollbars(&self) -> bool {
216 self.platform.should_auto_hide_scrollbars()
217 }
218
219 fn read_from_clipboard(&self) -> Option<ClipboardItem> {
220 self.clipboard.lock().clone()
221 }
222
223 fn write_to_clipboard(&self, item: ClipboardItem) {
224 *self.clipboard.lock() = Some(item);
225 }
226
227 #[cfg(target_os = "macos")]
228 fn read_from_find_pasteboard(&self) -> Option<ClipboardItem> {
229 self.find_pasteboard.lock().clone()
230 }
231
232 #[cfg(target_os = "macos")]
233 fn write_to_find_pasteboard(&self, item: ClipboardItem) {
234 *self.find_pasteboard.lock() = Some(item);
235 }
236
237 fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task<Result<()>> {
238 Task::ready(Ok(()))
239 }
240
241 fn read_credentials(&self, _url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
242 Task::ready(Ok(None))
243 }
244
245 fn delete_credentials(&self, _url: &str) -> Task<Result<()>> {
246 Task::ready(Ok(()))
247 }
248
249 fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
250 self.platform.keyboard_layout()
251 }
252
253 fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
254 self.platform.keyboard_mapper()
255 }
256
257 fn on_keyboard_layout_change(&self, _callback: Box<dyn FnMut()>) {}
258
259 fn thermal_state(&self) -> super::ThermalState {
260 super::ThermalState::Nominal
261 }
262
263 fn on_thermal_state_change(&self, _callback: Box<dyn FnMut()>) {}
264}
265