diff --git a/tests/smoke/server/runtime_support/server_runtime_smoke_contract.rs b/tests/smoke/server/runtime_support/server_runtime_smoke_contract.rs index 2c5977a..5dd4bd1 100644 --- a/tests/smoke/server/runtime_support/server_runtime_smoke_contract.rs +++ b/tests/smoke/server/runtime_support/server_runtime_smoke_contract.rs @@ -17,10 +17,25 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; use temp_env::with_var; -use tempfile::NamedTempFile; +use tempfile::{NamedTempFile, tempdir}; use tokio::runtime::Runtime; use tokio::sync::Mutex; +fn with_isolated_gadget_roots(f: impl FnOnce()) { + let dir = tempdir().expect("temp gadget roots"); + let sys_root = dir.path().join("sys"); + let cfg_root = dir.path().join("cfg"); + std::fs::create_dir_all(sys_root.join("class/udc")).expect("fake udc root"); + std::fs::create_dir_all(sys_root.join("bus/platform/devices")).expect("fake platform root"); + std::fs::create_dir_all(&cfg_root).expect("fake configfs root"); + + let sys_root = sys_root.to_string_lossy().to_string(); + let cfg_root = cfg_root.to_string_lossy().to_string(); + with_var("LESAVKA_GADGET_SYSFS_ROOT", Some(sys_root.as_str()), || { + with_var("LESAVKA_GADGET_CONFIGFS_ROOT", Some(cfg_root.as_str()), f) + }); +} + fn tmp_files_with_prefix(prefix: &str) -> HashSet { std::fs::read_dir("/tmp") .ok() @@ -305,44 +320,53 @@ fn runtime_recover_hid_resets_cycle_flag_after_async_recovery_path() { #[test] #[serial] fn runtime_recover_hid_attempts_cycle_when_enabled() { - with_var("LESAVKA_ALLOW_GADGET_CYCLE", Some("1"), || { - let rt = Runtime::new().expect("create runtime"); - rt.block_on(async { - let kb_tmp = NamedTempFile::new().expect("temp keyboard file"); - let ms_tmp = NamedTempFile::new().expect("temp mouse file"); + with_isolated_gadget_roots(|| { + with_var("LESAVKA_ALLOW_GADGET_CYCLE", Some("1"), || { + with_var("LESAVKA_ALLOW_EXTERNAL_UVC_GADGET_CYCLE", Some("1"), || { + let rt = Runtime::new().expect("create runtime"); + rt.block_on(async { + let kb_tmp = NamedTempFile::new().expect("temp keyboard file"); + let ms_tmp = NamedTempFile::new().expect("temp mouse file"); - let kb = tokio::fs::OpenOptions::new() - .write(true) - .open(kb_tmp.path()) - .await - .expect("open temp kb"); - let ms = tokio::fs::OpenOptions::new() - .write(true) - .open(ms_tmp.path()) - .await - .expect("open temp ms"); + let kb = tokio::fs::OpenOptions::new() + .write(true) + .open(kb_tmp.path()) + .await + .expect("open temp kb"); + let ms = tokio::fs::OpenOptions::new() + .write(true) + .open(ms_tmp.path()) + .await + .expect("open temp ms"); - let kb = Arc::new(Mutex::new(Some(kb))); - let ms = Arc::new(Mutex::new(Some(ms))); - let did_cycle = Arc::new(AtomicBool::new(false)); - let err = std::io::Error::from_raw_os_error(libc::EPIPE); + let kb = Arc::new(Mutex::new(Some(kb))); + let ms = Arc::new(Mutex::new(Some(ms))); + let did_cycle = Arc::new(AtomicBool::new(false)); + let err = std::io::Error::from_raw_os_error(libc::EPIPE); - runtime_support::recover_hid_if_needed( - &err, - UsbGadget::new("lesavka"), - kb.clone(), - ms.clone(), - kb_tmp.path().to_string_lossy().to_string(), - ms_tmp.path().to_string_lossy().to_string(), - did_cycle.clone(), - ) - .await; + runtime_support::recover_hid_if_needed( + &err, + UsbGadget::new("lesavka"), + kb.clone(), + ms.clone(), + kb_tmp.path().to_string_lossy().to_string(), + ms_tmp.path().to_string_lossy().to_string(), + did_cycle.clone(), + ) + .await; - tokio::time::sleep(Duration::from_millis(2_300)).await; - assert!( - !did_cycle.load(Ordering::SeqCst), - "cycle-enabled recovery should eventually clear lock" - ); + assert!( + did_cycle.load(Ordering::SeqCst), + "transport error should acquire recovery lock before async recovery" + ); + + tokio::time::sleep(Duration::from_millis(2_300)).await; + assert!( + !did_cycle.load(Ordering::SeqCst), + "cycle-enabled recovery should eventually clear lock" + ); + }); + }); }); }); }