//! Include-based coverage for USB gadget orchestration helpers. //! //! Scope: include `server/src/gadget.rs` and exercise deterministic helper //! logic and error branches without touching live gadget sysfs state. //! Targets: `server/src/gadget.rs`. //! Why: most gadget logic is file-system and errno handling that should remain //! stable regardless of host environment. #[allow(warnings)] mod gadget_include_contract { include!(env!("LESAVKA_SERVER_GADGET_SRC")); use serial_test::serial; use temp_env::with_var; use tempfile::{NamedTempFile, tempdir}; #[test] fn new_builds_expected_udc_path() { let gadget = UsbGadget::new("lesavka-test"); assert!(gadget.udc_file.ends_with("/lesavka-test/UDC")); } #[test] fn state_errors_for_missing_controller() { let result = UsbGadget::state("definitely-missing-udc"); assert!(result.is_err()); } #[test] fn wait_state_any_times_out_for_missing_controller() { let result = UsbGadget::wait_state_any("definitely-missing-udc", 0); assert!(result.is_err()); } #[test] fn write_attr_writes_value_with_trailing_newline() { let file = NamedTempFile::new().expect("temp file"); UsbGadget::write_attr(file.path(), "configured").expect("write attr"); let content = std::fs::read_to_string(file.path()).expect("read back"); assert_eq!(content, "configured\n"); } #[test] fn wait_udc_present_times_out_for_missing_path() { let result = UsbGadget::wait_udc_present("definitely-missing-udc", 0); assert!(result.is_err()); } #[test] fn probe_platform_udc_is_non_panicking() { let result = UsbGadget::probe_platform_udc(); assert!(result.is_ok()); } #[test] fn find_controller_returns_name_or_error_without_panicking() { let result = UsbGadget::find_controller(); if let Ok(name) = result { assert!(!name.is_empty()); } } #[test] fn is_still_detaching_matches_expected_errno_set() { let busy = anyhow::Error::from(std::io::Error::from_raw_os_error(libc::EBUSY)); let missing = anyhow::Error::from(std::io::Error::from_raw_os_error(libc::ENOENT)); let no_dev = anyhow::Error::from(std::io::Error::from_raw_os_error(libc::ENODEV)); let other = anyhow::Error::from(std::io::Error::from_raw_os_error(libc::EACCES)); assert!(UsbGadget::is_still_detaching(&busy)); assert!(UsbGadget::is_still_detaching(&missing)); assert!(UsbGadget::is_still_detaching(&no_dev)); assert!(!UsbGadget::is_still_detaching(&other)); } #[test] #[serial] fn cycle_handles_missing_gadget_sysfs_gracefully() { let gadget = UsbGadget::new("lesavka-test"); with_var("LESAVKA_GADGET_FORCE_CYCLE", None::<&str>, || { let result = gadget.cycle(); assert!(result.is_err() || result.is_ok()); }); } #[test] #[serial] fn cycle_force_mode_still_returns_without_panicking() { let dir = tempdir().expect("tempdir"); let fake_udc = dir.path().join("UDC"); std::fs::write(&fake_udc, "").expect("create fake udc file"); let gadget = UsbGadget { udc_file: Box::leak(fake_udc.to_string_lossy().to_string().into_boxed_str()), }; with_var("LESAVKA_GADGET_FORCE_CYCLE", Some("1"), || { let result = gadget.cycle(); assert!(result.is_err() || result.is_ok()); }); } }