1#![forbid(unsafe_code)]
6
7pub use winit;
9
10#[macro_export]
12macro_rules! main {
13 ($ty:ty => $($test:expr),*) => {
14 #[cfg(target_arch = "wasm32")]
15 $crate::__private::wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
16
17 #[cfg(not(target_os = "android"))]
18 #[cfg_attr(target_arch = "wasm32", $crate::__private::wasm_bindgen_test::wasm_bindgen_test)]
19 fn main() -> Result<(), Box<dyn std::error::Error>> {
20 const TESTS: &[$crate::__private::WinitBasedTest<$ty>] = &[$(
21 $crate::__winit_test_internal_collect_test!($test)
22 ),*];
23
24 $crate::__private::run(TESTS, ());
25 Ok(())
26 }
27
28 #[cfg(target_os = "android")]
29 #[no_mangle]
30 fn android_main(app: $crate::__private::Context) {
31 const TESTS: &[$crate::__private::WinitBasedTest<$ty>] = &[$(
32 $crate::__winit_test_internal_collect_test!($test)
33 ),*];
34
35 $crate::__private::run(TESTS, app);
36 }
37 };
38 ($($tt:tt)*) => {
39 $crate::main!(() => $($tt)*);
40 }
41}
42
43#[doc(hidden)]
44#[macro_export]
45macro_rules! __winit_test_internal_collect_test {
46 ($name:expr) => {
47 $crate::__private::WinitBasedTest {
48 name: stringify!($name),
49 function: $crate::__private::TestFunction::Oneoff($name),
50 }
51 };
52}
53
54#[doc(hidden)]
55pub mod __private {
57 #[cfg(target_arch = "wasm32")]
58 pub use wasm_bindgen_test;
59
60 use winit::event_loop::EventLoopBuilder;
61 pub use winit::event_loop::EventLoopWindowTarget;
62
63 use owo_colors::OwoColorize;
64 use std::any::Any;
65 use std::panic::{catch_unwind, AssertUnwindSafe};
66 #[cfg(not(target_arch = "wasm32"))]
67 use std::time::Instant;
68 #[cfg(target_arch = "wasm32")]
69 use web_time::Instant;
70
71 #[cfg(target_os = "android")]
72 pub use winit::platform::android::{
73 activity::AndroidApp as Context, EventLoopBuilderExtAndroid,
74 };
75 #[cfg(target_arch = "wasm32")]
76 use winit::platform::web::EventLoopExtWebSys;
77 #[cfg(not(target_os = "android"))]
78 pub type Context = ();
79
80 struct State {
81 passed: i32,
82 panics: Vec<(&'static str, Box<dyn Any + Send>)>,
83 start: Instant,
84 run: bool,
85 code: i32,
86 }
87
88 pub fn run<T: 'static>(tests: &'static [WinitBasedTest<T>], _ctx: Context) {
90 if cfg!(miri) {
92 eprintln!();
93 eprintln!(
94 "{}: tests cannot be run under miri",
95 "warning(winit-test)".yellow().bold()
96 );
97 eprintln!(" = See this issue for more information: https://github.com/notgull/winit-test/issues/2");
98 eprintln!();
99 return;
100 }
101
102 let mut builder = EventLoopBuilder::<T>::with_user_event();
104
105 #[cfg(target_os = "android")]
107 {
108 builder.with_android_app(_ctx);
109 }
110
111 let event_loop = builder.build().expect("Failed to build event loop");
112
113 println!("\nRunning {} tests...", tests.len());
114 let mut state = State {
115 passed: 0,
116 panics: vec![],
117 start: Instant::now(),
118 run: false,
119 code: 0,
120 };
121
122 #[cfg(not(target_arch = "wasm32"))]
124 event_loop
125 .run(move |_, elwt| {
126 run_internal(tests, &mut state, elwt);
127 })
128 .expect("Event loop failed to run");
129 #[cfg(target_arch = "wasm32")]
130 event_loop.spawn(move |_, elwt| {
131 run_internal(tests, &mut state, elwt);
132 });
133 }
134
135 fn run_internal<T: 'static>(
137 tests: &'static [WinitBasedTest<T>],
138 state: &mut State,
139 elwt: &EventLoopWindowTarget<T>,
140 ) {
141 if state.run {
142 elwt.exit();
143 return;
144 }
145 state.run = true;
146
147 for test in tests {
148 print!("test {} ... ", test.name);
149
150 match test.function {
151 TestFunction::Oneoff(f) => match catch_unwind(AssertUnwindSafe(move || f(elwt))) {
152 Ok(()) => {
153 println!("{}", "ok".green());
154 state.passed += 1;
155 }
156
157 Err(e) => {
158 println!("{}", "FAILED".red());
159 state.panics.push((test.name, e));
160 }
161 },
162 }
163 }
164
165 let failures = state.panics.len();
166 println!();
167 if !state.panics.is_empty() {
168 println!("failures:\n");
169 for (name, e) in state.panics.drain(..) {
170 println!("---- {} panic ----", name);
171
172 if let Some(s) = e.downcast_ref::<&'static str>() {
173 println!("{}", s.red());
174 } else if let Some(s) = e.downcast_ref::<String>() {
175 println!("{}", s.red());
176 } else {
177 println!("{}", "unknown panic type".red());
178 }
179
180 println!();
181 }
182
183 print!("test result: {}", "FAILED".red());
184 } else {
185 print!("test result: {}", "ok".green());
186 }
187
188 let elapsed = state.start.elapsed();
189 println!(
190 ". {} passed; {} failed; finished in {:?}",
191 state.passed, failures, elapsed
192 );
193
194 state.code = if failures == 0 { 0 } else { 1 };
195 elwt.exit();
196 }
197
198 pub struct WinitBasedTest<T: 'static> {
199 pub name: &'static str,
200 pub function: TestFunction<T>,
201 }
202
203 pub enum TestFunction<T: 'static> {
204 Oneoff(fn(&EventLoopWindowTarget<T>)),
205 }
206}