Skip to main content

Crate globally_ordered_mock_mmio

Crate globally_ordered_mock_mmio 

Source
Expand description

Globally-ordered mock for MMIO driver testing in Rust.

Built on top of the MMIO region abstractions in //sdk/lib/driver/mmio/rust, this crate provides a high-density, readable, trace-like API for testing drivers that interact with memory-mapped I/O (MMIO).

§Usage guide

§Initialize the test double

Create a MockMmioRegionBuilder instance covering the expected MMIO region size in bytes.

use globally_ordered_mock_mmio::MockMmioRegionBuilder;

let mock_builder = MockMmioRegionBuilder::new(0x1000);

§Declare expectations

Record expected MMIO operations using the ExpectedTraceBuilder API.

§Typed register operations

Use ExpectedTraceBuilder::read and ExpectedTraceBuilder::write for expectations on register types defined with mmio::register!. The methods deduce byte offsets and access widths.

use globally_ordered_mock_mmio::MockMmioRegionBuilder;
use mmio::{ReadableRegister, WritableRegister};

let mock_builder = MockMmioRegionBuilder::new(0x1000);
mock_builder.expect(|t| {
    // Expect a read from Status returning 0x0000_0001
    t.read::<registers::Status>(0x0000_0001);

    // Expect a write to Command with value 0x0000_0002
    t.write::<registers::Command>(0x0000_0002);
});

// Performed by the code under test.
let mut mmio = mock_builder.build();
assert_eq!(registers::Status::read(&mmio).0, 0x0000_0001);
registers::Command(0x0000_0002).write(&mut mmio);

§Indexed register operations

Use ExpectedTraceBuilder::read_indexed and ExpectedTraceBuilder::write_indexed for expectations on indexed register types defined with #[indexed_register(...)].

use globally_ordered_mock_mmio::MockMmioRegionBuilder;
use mmio::{ReadableIndexedRegister, WritableIndexedRegister};

let mock_builder = MockMmioRegionBuilder::new(0x1000);
mock_builder.expect(|t| {
    // Expect a read from IndexedData at index 2 returning 0x30
    t.read_indexed::<registers::IndexedData>(2, 0x30);

    // Expect a write to IndexedData at index 3 with value 0x40
    t.write_indexed::<registers::IndexedData>(3, 0x40);
});

// Performed by the code under test.
let mut mmio = mock_builder.build();
assert_eq!(registers::IndexedData::read_index(&mmio, 2).0, 0x30);
registers::IndexedData(0x40).write_index(&mut mmio, 3);

§Successful polling

Use ExpectedTraceBuilder::poll to model a status register that changes value across successive reads.

use globally_ordered_mock_mmio::MockMmioRegionBuilder;
use mmio::ReadableRegister;

let mock_builder = MockMmioRegionBuilder::new(0x1000);
mock_builder.expect(|t| {
    // First two reads return 0x01 (busy); subsequent reads return 0x02 (ready)
    t.poll::<registers::Status>([0x0000_0001, 0x0000_0001, 0x0000_0002]);
});

// Performed by the code under test.
let mmio = mock_builder.build();
while registers::Status::read(&mmio).0 != 0x0000_0002 {}

§Timed out polling

Use ExpectedTraceBuilder::poll_indefinitely to model hardware remaining busy until the driver times out and executes recovery.

use globally_ordered_mock_mmio::MockMmioRegionBuilder;
use mmio::{ReadableRegister, WritableRegister};

let mock_builder = MockMmioRegionBuilder::new(0x1000);
mock_builder.expect(|t| {
    // Returns 0x01 indefinitely until the driver executes a non-matching access
    t.poll_indefinitely::<registers::Status>(0x0000_0001);
    t.write::<registers::Command>(0x0000_0001); // Reset command after timeout
});

// Performed by the code under test.
let mut mmio = mock_builder.build();
for _ in 0..10 {
    assert_eq!(registers::Status::read(&mmio).0, 0x0000_0001);
}
registers::Command(0x0000_0001).write(&mut mmio);

§Raw numeric offsets

When the reference test data is a trace containing raw MMIO offsets and values, pass the raw offset to ExpectedTraceBuilder::at and express the operation and value using the RawOffsetTraceBuilder methods, such as RawOffsetTraceBuilder::read32 and RawOffsetTraceBuilder::write32.

use globally_ordered_mock_mmio::MockMmioRegionBuilder;
use mmio::Mmio;

let mock_builder = MockMmioRegionBuilder::new(0x1000);
mock_builder.expect(|t| {
    t.at(0x0).read8(0x12);
    t.at(0x1).write8(0x34);
    t.at(0x8).read32(0x1234_5678);
    t.at(0xc).write32(0x9abc_def0);
});

// Performed by the code under test.
let mut mmio = mock_builder.build();
assert_eq!(mmio.load8(0x0), 0x12);
mmio.store8(0x1, 0x34);
assert_eq!(mmio.load32(0x8), 0x1234_5678);
mmio.store32(0xc, 0x9abc_def0);

§Memory barriers

Use ExpectedTraceBuilder::write_barrier to expect a write barrier between two MMIO accesses.

use globally_ordered_mock_mmio::MockMmioRegionBuilder;
use mmio::Mmio;

let mock_builder = MockMmioRegionBuilder::new(0x1000);
mock_builder.expect(|t| {
    t.at(0x0).write32(0x1234_5678);
    t.write_barrier();
    t.at(0x4).write32(0x9abc_def0);
});

// Performed by the code under test.
let mut mmio = mock_builder.build();
mmio.store32(0x0, 0x1234_5678);
mmio.write_barrier();
mmio.store32(0x4, 0x9abc_def0);

§Obtain and use the MMIO region

Call MockMmioRegionBuilder::build to obtain a MockMmioRegion, which is an mmio::region::MmioRegion implementation.

The returned region implements mmio::Mmio and mmio::MmioSplit, allowing driver code to split sub-regions across multiple threads. Accesses from all threads are checked against a single global expectation list, in the order in which the accesses reach the mock.

The mock does not impose any ordering on accesses issued by different threads. Tests that exercise concurrent driver code must establish their own ordering, for example by having the threads synchronize on a channel.

§Verification and teardown

Expectations are verified on Drop, after the MockMmioRegionBuilder and all the MockMmioRegions it produced are dropped. Any unretired expectation results in a test failure. No explicit verification call is needed.

Verification is skipped while the thread is already panicking, so that the failure that caused the panic is not masked.

Structs§

ExpectedTraceBuilder
Builder for recording expected MMIO access sequences within a closure.
MockMmioRegionBuilder
Strict thread-safe mock (interaction testing) for MMIO (MmioRegion).
RawOffsetTraceBuilder
Builder for recording expected MMIO accesses at a fixed raw byte offset.

Functions§

mmio_operand_to_u64
Returns the numerical value for any MmioOperand.

Type Aliases§

MockMmioRegion
MmioRegion produced by MockMmioRegionBuilder.