linux-kpi: test pci_register_error_handler contract (env missing, malformed, double-register)

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<ErrorHandlerFn> 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.
This commit is contained in:
2026-07-25 15:25:07 +09:00
parent 3995591b77
commit 1aa26ba251
@@ -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<ErrorHandlerFn> 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
}
}