//! Process-global lock serializing the two Playwright UI test suites (the //! web dashboard's `ui_playwright_checks` and the report viewer's //! `report_playwright_checks`). Both launch a chromium instance via deno; //! running them concurrently on the same machine flakes (browser //! contention), so `make check` serializes them through this guard. //! //! The guard is acquired inside `spawn_blocking` closures (blocking //! context, no await held) — see the callers in `web/tests.rs` and //! `report/ui_tests.rs`. #![cfg(test)] use std::sync::{Mutex, MutexGuard, PoisonError}; pub static PLAYWRIGHT_LOCK: Mutex<()> = Mutex::new(()); /// Serializes the tests that change the process cwd (`set_current_dir`) with /// the tests that read cwd-relative state (`./config.toml`, the recipe search /// order). Cargo runs test functions in parallel threads of one process, so a /// chdir in one test would otherwise re-point the others mid-flight. A tokio /// mutex (not std) because the holders await their HTTP round-trips. fn cwd_mutex() -> &'static tokio::sync::Mutex<()> { static LOCK: std::sync::OnceLock> = std::sync::OnceLock::new(); LOCK.get_or_init(|| tokio::sync::Mutex::new(())) } /// Acquire the cwd lock; held until the guard is dropped. pub async fn cwd_guard() -> tokio::sync::MutexGuard<'static, ()> { cwd_mutex().lock().await } /// Acquire the Playwright session lock; held until the guard is dropped. /// Tolerates a poisoned lock (a panicked holder) — the browser is the /// shared resource, not the lock's payload. pub fn playwright_session() -> MutexGuard<'static, ()> { PLAYWRIGHT_LOCK .lock() .unwrap_or_else(PoisonError::into_inner) }