1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Ident, Lit, Token, parse::Parser, parse_macro_input, punctuated::Punctuated};
4
5extern crate proc_macro;
6
7#[proc_macro]
8pub fn entrypoint(_: TokenStream) -> TokenStream {
9 quote! {
10 eetoee::lazy_static::lazy_static! {
11 static ref BROWSER: std::sync::Arc<std::sync::Mutex<Option<eetoee::chrome::Browser>>> = {
12 #[cfg(debug_assertions)]
13 let debug = false;
14
15 #[cfg(not(debug_assertions))]
16 let debug = true;
17
18 #[cfg(debug_assertions)]
19 let args = vec![];
20
21 #[cfg(not(debug_assertions))]
22 let args = vec![
23 std::ffi::OsStr::new("--no-sandbox"),
24 std::ffi::OsStr::new("--disable-gpu"),
25 std::ffi::OsStr::new("--headless=new"),
26 std::ffi::OsStr::new("--disable-dev-shm-usage"),
27 ];
28
29 let options = eetoee::chrome::LaunchOptions {
30 headless: debug,
31 idle_browser_timeout: std::time::Duration::from_secs(60),
32 sandbox: !debug,
33 args,
34 ..Default::default()
35 };
36
37 std::sync::Arc::new(std::sync::Mutex::new(Some(eetoee::chrome::Browser::new(options).expect("Failed to create browser"))))
38 };
39 }
40
41 fn main() {
42 let result = std::panic::catch_unwind(|| {
43 eetoee::run();
44 });
45
46 let mut cell = BROWSER.lock().expect("Failed to lock browser");
47 *cell = None;
48
49 result.unwrap();
50 }
51
52 #[cfg(test)]
53 extern "C" fn dtor() {
54 let mut browser_cell = BROWSER.lock().expect("Failed to lock browser");
55 *browser_cell = None;
56 }
57
58 #[cfg(test)]
59 extern "C" fn ctor() {
60 unsafe {
61 eetoee::atexit(dtor);
62 }
63 }
64
65 #[cfg(test)]
66 #[used]
67 #[cfg_attr(target_os = "linux", unsafe(link_section = ".init_array"))]
68 #[cfg_attr(target_vendor = "apple", unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs"))]
69 #[cfg_attr(target_os = "windows", unsafe(link_section = ".CRT$XCU"))]
70 static CTOR: extern "C" fn() = ctor;
71 }.into()
72}
73
74#[proc_macro_attribute]
75pub fn test_case_at(attr: TokenStream, item: TokenStream) -> TokenStream {
76 let mut input = parse_macro_input!(item as syn::ItemFn);
77 let test_fn_name = &input.sig.ident.clone();
78 let new_fn_name = Ident::new(&format!("_{}", input.sig.ident), input.sig.ident.span());
79 input.sig.ident = new_fn_name.clone();
80
81 let args = Punctuated::<Lit, Token![,]>::parse_terminated
82 .parse(attr)
83 .expect("Failed to parse attribute args");
84
85 let path = if let Some(Lit::Str(lit_str)) = args.first() {
86 lit_str.value()
87 } else {
88 panic!("Expected a string literal with a path to concrete page");
89 };
90
91 quote! {
92 #input
93
94 #[test]
95 fn #test_fn_name() {
96 let browser_cell = BROWSER.lock().expect("Failed to lock browser");
97
98 let Some(browser) = browser_cell.as_ref() else {
99 panic!("Failed to lock browser");
100 };
101
102 let base_url = std::env::args().nth(2).and_then(|arg| eetoee::url::Url::parse(&arg).ok()).unwrap_or_else(|| eetoee::url::Url::parse(option_env!("TEST_BASE_URL").expect("Either TEST_BASE_URL environment variable or command line argument must be provided")).unwrap());
103
104 let mut tab = browser.new_tab().expect("Failed to create new tab");
105 let url = base_url.join(#path).expect("Failed to join base URL with path");
106
107 tab.navigate_to(&url.to_string()).expect("Failed to navigate to given URL");
108 let _ = tab.wait_until_navigated();
109 #new_fn_name(Arc::clone(&tab));
110 tab.close(false).expect("Failed to close tab");
111 }
112
113 eetoee::inventory::submit! {
114 eetoee::Test { path: #path, runnable: |base_url| {
115 let tab = {
116 let browser = BROWSER.lock().expect("Failed to lock browser");
117
118 let Some(browser) = browser.as_ref() else {
119 panic!("Failed to lock browser");
120 };
121
122 browser.new_tab().expect("Failed to create new tab")
123 };
124
125 let url = base_url.join(#path).expect("Failed to join base URL with path");
126
127 tab.navigate_to(&url.to_string()).expect("Failed to navigate to given URL");
128 let _ = tab.wait_until_navigated();
129 #new_fn_name(Arc::clone(&tab));
130 tab.close(false).expect("Failed to close tab");
131 }, name: stringify!(#test_fn_name) }
132 }
133 }
134 .into()
135}