|
6 | 6 | use accesskit::{
|
7 | 7 | Action, ActionHandler, ActionRequest, ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate,
|
8 | 8 | };
|
9 |
| -use windows::Win32::{Foundation::*, UI::Accessibility::*}; |
| 9 | +use once_cell::sync::Lazy; |
| 10 | +use windows::{ |
| 11 | + core::*, |
| 12 | + Win32::{ |
| 13 | + Foundation::*, |
| 14 | + System::LibraryLoader::GetModuleHandleW, |
| 15 | + UI::{Accessibility::*, WindowsAndMessaging::*}, |
| 16 | + }, |
| 17 | +}; |
10 | 18 | use winit::{
|
11 | 19 | application::ApplicationHandler,
|
12 | 20 | event::WindowEvent,
|
@@ -63,7 +71,14 @@ impl ActivationHandler for SimpleActivationHandler {
|
63 | 71 | }
|
64 | 72 |
|
65 | 73 | // This module uses winit for the purpose of testing with a real third-party
|
66 |
| -// window implementation that we don't control. |
| 74 | +// window implementation that we don't control. However, only one test |
| 75 | +// can use winit, because winit only allows an event loop to be created |
| 76 | +// once per process. So we end up creating our own window anyway for the |
| 77 | +// double-instantiation test. |
| 78 | +// |
| 79 | +// Also, while these tests don't use the main test harness or show the window, |
| 80 | +// they still need to run with the main harness's mutex, to avoid disturbing |
| 81 | +// other tests, particularly the focus test. |
67 | 82 |
|
68 | 83 | struct TestApplication;
|
69 | 84 |
|
@@ -92,12 +107,69 @@ impl ApplicationHandler<()> for TestApplication {
|
92 | 107 |
|
93 | 108 | #[test]
|
94 | 109 | fn has_native_uia() {
|
95 |
| - // This test is simple enough that we know it's fine to run entirely |
96 |
| - // on one thread, so we don't need a full multithreaded test harness. |
97 |
| - // Still, we must prevent this test from running concurrently with other |
98 |
| - // tests, especially the focus test. |
99 |
| - let _lock_guard = MUTEX.lock().unwrap(); |
| 110 | + let _lock_guard = MUTEX.lock(); |
100 | 111 | let event_loop = EventLoop::builder().with_any_thread(true).build().unwrap();
|
101 | 112 | let mut state = TestApplication {};
|
102 | 113 | event_loop.run_app(&mut state).unwrap();
|
103 | 114 | }
|
| 115 | + |
| 116 | +extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { |
| 117 | + unsafe { DefWindowProcW(window, message, wparam, lparam) } |
| 118 | +} |
| 119 | + |
| 120 | +static WINDOW_CLASS_ATOM: Lazy<u16> = Lazy::new(|| { |
| 121 | + let class_name = w!("AccessKitSubclassTest"); |
| 122 | + |
| 123 | + let wc = WNDCLASSW { |
| 124 | + hCursor: unsafe { LoadCursorW(None, IDC_ARROW) }.unwrap(), |
| 125 | + hInstance: unsafe { GetModuleHandleW(None) }.unwrap().into(), |
| 126 | + lpszClassName: class_name, |
| 127 | + style: CS_HREDRAW | CS_VREDRAW, |
| 128 | + lpfnWndProc: Some(wndproc), |
| 129 | + ..Default::default() |
| 130 | + }; |
| 131 | + |
| 132 | + let atom = unsafe { RegisterClassW(&wc) }; |
| 133 | + if atom == 0 { |
| 134 | + panic!("{}", Error::from_win32()); |
| 135 | + } |
| 136 | + atom |
| 137 | +}); |
| 138 | + |
| 139 | +fn create_window(title: &str) -> HWND { |
| 140 | + let module = HINSTANCE::from(unsafe { GetModuleHandleW(None).unwrap() }); |
| 141 | + |
| 142 | + let window = unsafe { |
| 143 | + CreateWindowExW( |
| 144 | + Default::default(), |
| 145 | + PCWSTR(*WINDOW_CLASS_ATOM as usize as _), |
| 146 | + &HSTRING::from(title), |
| 147 | + WS_OVERLAPPEDWINDOW, |
| 148 | + CW_USEDEFAULT, |
| 149 | + CW_USEDEFAULT, |
| 150 | + CW_USEDEFAULT, |
| 151 | + CW_USEDEFAULT, |
| 152 | + None, |
| 153 | + None, |
| 154 | + Some(module), |
| 155 | + None, |
| 156 | + ) |
| 157 | + } |
| 158 | + .unwrap(); |
| 159 | + if window.is_invalid() { |
| 160 | + panic!("{}", Error::from_win32()); |
| 161 | + } |
| 162 | + |
| 163 | + window |
| 164 | +} |
| 165 | + |
| 166 | +#[test] |
| 167 | +#[should_panic(expected = "already instantiated")] |
| 168 | +fn double_instantiate() { |
| 169 | + let _lock_guard = MUTEX.lock(); |
| 170 | + let window = create_window(WINDOW_TITLE); |
| 171 | + let _adapter1 = |
| 172 | + SubclassingAdapter::new(window, SimpleActivationHandler {}, NullActionHandler {}); |
| 173 | + let _adapter2 = |
| 174 | + SubclassingAdapter::new(window, SimpleActivationHandler {}, NullActionHandler {}); |
| 175 | +} |
0 commit comments