Testing¶
Lince is developed test-first. The suite currently contains 263 test cases
(262 pass + 1 conditional skip), with -Werror and a strict warning set that
must stay clean.
Framework and build integration¶
- Framework: Catch2 v3.5.3, fetched automatically via CMake
FetchContent. - CMake option:
LINCE_BUILD_TESTS(defaultON). - Discovery:
catch_discover_tests(lince_tests)withSKIP_REGULAR_EXPRESSIONso thatSKIP(...)macros are reported as CTest skips rather than failures.
Running the suite¶
# Configure (tests enabled by default)
cmake -S . -B build -G Ninja
# Build
cmake --build build
# Run everything through CTest
ctest --test-dir build --output-on-failure
# Run the Catch2 binary directly
./build/tests/lince_tests
# Run only unit tests
./build/tests/lince_tests "[unit]"
# Run only integration tests
./build/tests/lince_tests "[integration]"
Because catch_discover_tests is used, CTest knows about each individual
TEST_CASE as a separate CTest test.
Directory layout¶
tests/unit/ — Module tests¶
Fast, deterministic, no I/O. Each file targets one module or one concern.
| File | Coverage |
|---|---|
test_types.cpp |
Strong types (PhysAddr, VirtAddr, CoreId, SimTimeNs, Result<T>) |
test_address_range.cpp |
AddressRange half-open MMIO dispatch ranges |
test_module_versions.cpp |
Build-time dependency / version checks |
test_defaults.cpp |
Default implementations (StdoutLogger, NullFaultInjector, …) |
test_ram.cpp |
Ram raw byte storage |
test_system_bus.cpp |
SystemBus routing, big-endian typed access, region validation |
test_dma.cpp |
DMA reads/writes via IBusMaster |
test_cpu_state.cpp |
CpuState register windows, PSR/WIM/TBR, trap entry/exit |
test_cpu_state_fpu.cpp |
FPU register state and FSR logic |
test_softfloat_context.cpp |
Berkeley SoftFloat 3e integration |
test_decoder.cpp |
SPARC V8 integer instruction decoder |
test_fpu_decoder.cpp |
FPU instruction decoder |
test_handlers_alu.cpp |
Integer ALU handlers (ADD, SUB, logical, shifts, …) |
test_handlers_branch.cpp |
Branch / Bicc handlers and delay slots |
test_handlers_loadstore.cpp |
Load/store handlers |
test_handlers_regwin.cpp |
SAVE / RESTORE handlers |
test_handlers_privileged.cpp |
Privileged instructions (RD/WR special registers, RETT) |
test_fpu_handlers_loadstore.cpp |
FPU load/store handlers |
test_fpu_handlers_moves.cpp |
FPU register-move handlers |
test_fpu_handlers_arith.cpp |
FPU arithmetic handlers |
test_fpu_handlers_compare.cpp |
FPU compare handlers |
test_fpu_handlers_branch.cpp |
FPU branch (FBfcc) handlers |
test_fpu_traps.cpp |
FPU trap generation |
test_traps.cpp |
Trap dispatch, enter_trap() / leave_trap() |
test_step.cpp |
step() fetch-decode-execute loop |
test_memctrl.cpp |
MemCtrl (FTMCTRL stub registers) |
test_irqmp.cpp |
IrqMP multi-core interrupt controller |
test_gptimer.cpp |
GPTimer prescaler, sub-timers, watchdog |
test_apbuart.cpp |
ApbUart RX FIFO, status, control, scaler |
test_cpu_bus_bridge.cpp |
CpuBusBridge CPU-side bus adapter |
test_elf_loader.cpp |
ELF32 loading and segment placement |
test_emulator.cpp |
Emulator public API (lifecycle, config, reset) |
test_event_scheduler.cpp |
EventScheduler timed event queue |
test_gdb_stub_codec.cpp |
GDB Remote Serial Protocol packet codec |
tests/integration/ — End-to-end tests¶
Full Emulator instances running real or hand-crafted binaries.
| File | Coverage |
|---|---|
test_bare_metal.cpp |
Hand-encoded SPARC binaries (NOP sled, error-mode detection, APBUart MMIO) |
test_demo_dma_device.cpp |
DemoDmaDevice attached via public add_peripheral, DMA + IRQ exercised |
test_hello_uart_elf.cpp |
Cross-compiled hello_uart.elf; asserts 'A' appears on captured UART |
test_rtems_boot.cpp |
RTEMS 5 hello-world.elf boot test |
test_rtems_sptests.cpp |
Parametrised RTEMS 5 sptests (sp01–sp12) |
test_rtems_fptests.cpp |
RTEMS floating-point test fptest01.elf |
test_gdb_stub_protocol.cpp |
In-process GDB RSP over TCP (scripted session) |
test_gdb_stub_rtems.cpp |
Real sparc-rtems5-gdb front-end against Emulator (conditional) |
tests/asm/ — SPARC assembly programs¶
Real .S files compiled with the RCC SPARC cross-compiler:
hello_uart.S— Minimal assembly that enablesCTRL.TEon the APBUart and writes'A'.
The tests/asm/CMakeLists.txt looks for sparc-gaisler-rtems5-gcc on PATH.
If found, it compiles hello_uart.S with -mcpu=leon3 -msoft-float
-nostdlib -nostartfiles and a minimal linker script placing .text at
0x40000000. If the compiler is absent, the integration test SKIPS at
runtime.
tests/rtems/ — RTEMS binaries¶
tests/rtems/bin/hello-world.elf— Pre-built RTEMS 5 hello-world.tests/rtems/bin/fptest01.elf— Pre-built RTEMS FP test.tests/rtems/sptests/bin/— Expected location for sptest ELFs (sp01.elf,sp02.elf, …). The directory is empty in the repo; binaries are supplied externally in the development / CI environment.
CMake bakes these paths as compile definitions so the integration tests can locate them at runtime.
tests/support/ — Test fixtures¶
| File | Purpose |
|---|---|
dummy_peripheral.cpp/.hpp |
DummyPeripheral — an IPeripheral test fixture with MMIO registers and DMA triggers, used by bus and DMA unit tests. |
capturing_char_device.hpp |
Thread-safe ICharacterDevice that records all transmitted bytes into a std::string for assertions. |
sparc_encoders.hpp |
Constexpr helpers for encoding SPARC V8 instructions (SETHI, CALL, ALU imm, Bicc, JMPL, SAVE/RESTORE, RETT) so step-level tests can plant instructions without raw hex constants. |
test_bus.hpp |
FakeBus / MemBus (4 KiB BE RAM helpers) / ErrorBus (always returns BusError). |
Test counts and the conditional skip¶
- 263 total test cases discovered by CTest.
- 262 pass unconditionally.
- 1 conditional skip:
test_rtems_boot.
The skip fires when the RTEMS hello-world ELF is not found at the expected path. It does not indicate a bug — it simply means the cross-compiled payload is absent. All other tests run and pass on every CI run.
RTEMS sptests¶
The test_rtems_sptests.cpp integration test parametrises over ten sptests:
Pass criterion: the captured UART output contains the exact string
*** END OF TEST. The test does not require a clean halt or a specific
HaltReason; the functional criterion is the end-of-test banner alone
(Decision 36).
If an individual ELF is missing, that case SKIPs cleanly.
Conventions and patterns¶
-
Test names describe behaviour.
TEST_CASE("ADD sets condition codes on overflow")notTEST_CASE("test_add_3"). -
Capture output for assertions. Integration tests that exercise the APBUart inject a
CapturingCharDeviceand assert on the captured string. -
Conditional skips for optional dependencies. Tests that require external artifacts (cross-compiler, RTEMS ELFs, GDB) use
SKIP(...)with a descriptive message. This keeps CI green when toolchains are absent. -
Helper buses in unit tests. Core unit tests bypass the full
Emulatorand useFakeBus/MemBus/ErrorBusto feed instructions directly toCpuState. -
Sparc encoders for instruction planting.
sparc_encoders.hppprovides constexpr functions likeenc_add_imm,enc_bicc,enc_save_imm, etc., so tests construct valid SPARC words without magic hex constants. -
DMA fixture pattern.
DummyPeripheralvalidates bus mastering independently of real peripherals.