hilt 0.1.0

Renode-based hardware-in-the-loop test fixtures for embedded Rust projects
Documentation
//! `hilt` — hardware-in-the-loop test fixtures for embedded Rust.
//!
//! Runs compiled firmware inside a [Renode](https://renode.io) simulation in a
//! Podman/Docker container, captures its output, and lets host tests assert on
//! it. It supports three firmware-verification styles, and mixes them freely:
//!
//! * **Self-reporting firmware** — a generic Cortex-M board ([`CpuInit::VectorTable`]).
//!   `hilt` reads the ELF's vector table, sets `SP`/`PC`, and hooks a marker
//!   symbol to log `HIL OK`. Assert with [`HilOutput::passed`] /
//!   [`assert_hil_ok!`]. Great for one firmware image per test.
//! * **Live CAN interaction** — board-described platforms ([`CpuInit::Board`])
//!   wired into a CAN hub, with any machine optionally bridged to a host
//!   SocketCAN interface ([`MachineSpec::with_socketcan_bridge`]). Host code
//!   then exchanges frames with the firmware over `vcan` and asserts on
//!   protocol behavior. Multiple machines are supported; Linux hosts only.
//! * **Live UART interaction** — any machine's UART exposed to the host as a
//!   raw TCP socket ([`MachineSpec::with_uart_bridge`]). Host code connects to
//!   `127.0.0.1:<port>` and exchanges bytes with the firmware's serial line;
//!   works on macOS/Windows hosts too.
//!
//! # Quick start (single self-reporting firmware)
//!
//! ```no_run
//! use hilt::{HilConfig, Platform, RenodeRunner, assert_hil_ok};
//!
//! let output = RenodeRunner::new(HilConfig::single(
//!     Platform::rp2040(),
//!     "target/thumbv6m-none-eabi/release/examples/hil_keyboard",
//! ))
//! .run();
//! assert_hil_ok!(output);
//! ```
//!
//! # Quick start (multi-machine CAN with a host bridge)
//!
//! ```no_run
//! use hilt::{HilConfig, MachineSpec, Platform, RenodeRunner};
//!
//! let machines = vec![
//!     MachineSpec::new("controller", "controller.elf", Platform::stm32h7())
//!         .with_socketcan_bridge(),
//!     MachineSpec::new("sensor", "sensor.elf", Platform::stm32h7()),
//! ];
//! // hilt::setup_vcan("vcan0");  // once, with privileges
//! let runner = RenodeRunner::new(HilConfig::multi(machines));
//! std::thread::spawn(move || runner.run());
//! // ... open vcan0 and exercise the firmware ...
//! ```
//!
//! External requirements: `podman` or `docker`; for [`CpuInit::VectorTable`]
//! boards also `rust-objdump`/`rust-nm` (`rustup component add llvm-tools`).
//!
//! [`CpuInit::VectorTable`]: crate::CpuInit::VectorTable
//! [`CpuInit::Board`]: crate::CpuInit::Board

#![warn(missing_docs)]

pub mod config;
pub mod matrix;
pub mod output;
pub mod platform;
pub mod runner;

pub use config::{
    default_renode_image, HilConfig, MachineSpec, UartBridge, DEFAULT_MARKER, DEFAULT_RENODE_IMAGE,
    DEFAULT_SOCKETCAN_IFACE, RENODE_ARM64_IMAGE, RENODE_IMAGE_ENV,
};
pub use matrix::{
    build_test_plan, generate_matrix_test_resc, parse_key_log, parse_tapdance_log, MatrixTestKey,
    MatrixTestPlan,
};
pub use output::{HilOutput, HIL_OK_MARKER};
pub use platform::{CpuInit, Platform, ReplSource};
pub use runner::{
    container_runtime_available, detect_container_runtime, extract_elf_info, find_symbol_address,
    interface_available, renode_available, renode_bin, run_renode_container,
    run_with_timeout_and_cleanup, setup_vcan, Artifact, ElfInfo, GuestBuild, RenodeRunner,
    RENODE_ARM64_VERSION, RUNTIME_ENV,
};
// `assert_hil_ok!` is exported at the crate root by `#[macro_export]`.

/// Serializes tests that read or mutate process-global env vars (which are
/// shared across cargo's parallel test threads).
#[cfg(test)]
pub(crate) fn test_env_lock() -> std::sync::MutexGuard<'static, ()> {
    static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
    LOCK.lock()
        .unwrap_or_else(std::sync::PoisonError::into_inner)
}