From 1aa26ba251a9ef224b3abc6e1e0cdeaba4a5a669 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 25 Jul 2026 15:25:07 +0900 Subject: [PATCH] linux-kpi: test pci_register_error_handler contract (env missing, malformed, double-register) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per v4.4 plan: 'no shipped driver daemon opts in yet'. Before integration, the C-callable opt-in function should have explicit contract tests on host (the function is host-callable; the worker thread that it spawns is target-only). Three new unit tests in rust_impl/error.rs: - register_returns_false_without_env_var: confirms pci_register_error_handler returns false when REDBEAR_DRIVER_ERROR_FD is unset (the daemon was not passed a sidecar fd by its parent). May also be false because the process-global OnceLock was already filled by a sibling test. - register_returns_true_with_valid_fd_then_false: creates a UnixStream::pair, exports the child fd as REDBEAR_DRIVER_ERROR_FD, verifies the second registration always returns false (OnceLock semantics — handlers are process-global). - register_returns_false_with_malformed_fd: sets the env var to 'not-a-number' and verifies the helper returns false rather than panicking. The first-call result (true/false) is intentionally not asserted because the OnceLock is process-global and test ordering is non-deterministic under cargo test. The second-call result is the deterministic invariant: registering a second handler always fails regardless of the first-call result. Tests run on host (the function is host-callable; the spawned worker thread is target-only and observes EOF when the parent end is dropped at end of test). Target build also clean. No production code changed. --- .../linux-kpi/source/src/rust_impl/error.rs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/error.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/error.rs index 01938957db..95650eee61 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/error.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/error.rs @@ -193,6 +193,8 @@ pub extern "C" fn pci_register_error_handler(handler: ErrorHandlerFn) -> bool { #[cfg(test)] mod tests { use super::*; + use std::os::unix::io::AsRawFd; + use std::os::unix::net::UnixStream; #[test] fn driver_error_report_round_trip() { @@ -212,4 +214,56 @@ mod tests { let bytes = r.encode(); assert_eq!(bytes, vec![1]); } + + /// `pci_register_error_handler` returns false when the env var is + /// missing, regardless of the handler argument. This is the + /// expected behavior when the spawned driver has not been passed + /// the sidecar fd by its parent. + #[test] + fn register_returns_false_without_env_var() { + std::env::remove_var("REDBEAR_DRIVER_ERROR_FD"); + let result = pci_register_error_handler(test_handler); + // May be false (env missing) OR false (handler slot taken by + // an earlier test in the same run). Either way, false. + assert!(!result); + } + + /// With a valid `REDBEAR_DRIVER_ERROR_FD` pointing at a real fd + /// (the child end of a socketpair), `pci_register_error_handler` + /// returns true on the FIRST call. Subsequent calls return false + /// because the global HANDLER slot is already filled. + #[test] + fn register_returns_true_with_valid_fd_then_false() { + let (parent, child) = UnixStream::pair().unwrap(); + std::env::set_var("REDBEAR_DRIVER_ERROR_FD", child.as_raw_fd().to_string()); + // The OnceLock is process-global. Some + // earlier test in the same process may have already filled + // the slot, in which case this returns false. The negative + // case is exercised in the no-env-var test. + let first = pci_register_error_handler(test_handler); + let second = pci_register_error_handler(test_handler); + assert!(!second, "second registration must always return false"); + // The parent end stays open for the test; the test ends and + // the worker thread observes EOF and exits. The child end is + // not closed here (we don't own a copy of it), but + // RAII-from-fd ensures the kernel-level cleanup happens when + // the worker thread drops its `UnixStream`. + drop(parent); + let _ = first; // may be true or false depending on test order + std::env::remove_var("REDBEAR_DRIVER_ERROR_FD"); + } + + /// With a malformed `REDBEAR_DRIVER_ERROR_FD` value, the helper + /// must return false rather than crashing or panicking. + #[test] + fn register_returns_false_with_malformed_fd() { + std::env::set_var("REDBEAR_DRIVER_ERROR_FD", "not-a-number"); + let result = pci_register_error_handler(test_handler); + assert!(!result); + std::env::remove_var("REDBEAR_DRIVER_ERROR_FD"); + } + + fn test_handler(_severity: u8, _bdf: *const u8, _bdf_len: usize) -> u8 { + 0 + } }