use std::time::{Duration, Instant};
use zendriver::{Browser, stealth::StealthProfile};
const OUTER_BUDGET: Duration = Duration::from_secs(120);
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "zendriver=debug,info".into()),
)
.with_target(true)
.init();
let use_stealth = !std::env::args().any(|a| a == "--stealth=false");
let url = std::env::args()
.find_map(|a| a.strip_prefix("--url=").map(str::to_string))
.unwrap_or_else(|| "https://example.com".to_string());
println!("== cold-launch harness ==");
println!("stealth : {use_stealth}");
println!("url : {url}");
println!("headless: false (mandatory — a headed window is the deliverable)");
let t0 = Instant::now();
let mut builder = Browser::builder().headless(false);
if use_stealth {
builder = builder.stealth(StealthProfile::native());
}
builder = builder
.arg("--no-first-run")
.arg("--no-default-browser-check");
println!("PHASE build {}ms", t0.elapsed().as_millis());
let t_launch = Instant::now();
println!(">> launching (outer budget {OUTER_BUDGET:?}) ...");
let browser = match tokio::time::timeout(OUTER_BUDGET, builder.launch()).await {
Ok(Ok(b)) => {
println!("PHASE launch {}ms", t_launch.elapsed().as_millis());
b
}
Ok(Err(e)) => {
println!("FAIL launch {}ms — {e}", t_launch.elapsed().as_millis());
std::process::exit(2);
}
Err(_) => {
println!(
"HUNG launch — outer budget {OUTER_BUDGET:?} expired with no in-crate guard firing"
);
std::process::exit(3);
}
};
let t_tab = Instant::now();
let tab = browser.main_tab();
println!("PHASE main_tab {}ms", t_tab.elapsed().as_millis());
let t_goto = Instant::now();
println!(">> navigating to {url} ...");
match tokio::time::timeout(OUTER_BUDGET, tab.goto(&url)).await {
Ok(Ok(_)) => println!("PHASE goto {}ms", t_goto.elapsed().as_millis()),
Ok(Err(e)) => {
println!("FAIL goto {}ms — {e}", t_goto.elapsed().as_millis());
let _ = browser.close().await;
std::process::exit(4);
}
Err(_) => {
println!("HUNG goto — outer budget expired (this IS the reported symptom)");
std::process::exit(5);
}
}
let t_load = Instant::now();
match tokio::time::timeout(Duration::from_secs(30), tab.wait_for_load()).await {
Ok(Ok(())) => println!("PHASE wait_for_load {}ms", t_load.elapsed().as_millis()),
Ok(Err(e)) => println!("WARN wait_for_load — {e}"),
Err(_) => println!("WARN wait_for_load timed out at 30s"),
}
match tab.evaluate::<String>("document.location.href").await {
Ok(v) => println!("OK url={v:?}"),
Err(e) => println!("WARN evaluate — {e}"),
}
match tab.evaluate::<String>("document.title").await {
Ok(v) => println!("OK title={v:?}"),
Err(e) => println!("WARN evaluate title — {e}"),
}
let t_close = Instant::now();
match tokio::time::timeout(Duration::from_secs(30), browser.close()).await {
Ok(Ok(())) => println!("PHASE close {}ms", t_close.elapsed().as_millis()),
Ok(Err(e)) => println!("FAIL close {}ms — {e}", t_close.elapsed().as_millis()),
Err(_) => println!("HUNG close — 30s expired"),
}
println!("== done in {}ms ==", t0.elapsed().as_millis());
}