Skip to main content

eetoee_macros/
lib.rs

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}