Skip to content

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 (default ON).
  • Discovery: catch_discover_tests(lince_tests) with SKIP_REGULAR_EXPRESSION so that SKIP(...) 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 (sp01sp12)
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 enables CTRL.TE on 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:

sp01, sp02, sp03, sp04, sp05, sp06, sp07, sp08, sp11, sp12

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

  1. Test names describe behaviour. TEST_CASE("ADD sets condition codes on overflow") not TEST_CASE("test_add_3").

  2. Capture output for assertions. Integration tests that exercise the APBUart inject a CapturingCharDevice and assert on the captured string.

  3. 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.

  4. Helper buses in unit tests. Core unit tests bypass the full Emulator and use FakeBus / MemBus / ErrorBus to feed instructions directly to CpuState.

  5. Sparc encoders for instruction planting. sparc_encoders.hpp provides constexpr functions like enc_add_imm, enc_bicc, enc_save_imm, etc., so tests construct valid SPARC words without magic hex constants.

  6. DMA fixture pattern. DummyPeripheral validates bus mastering independently of real peripherals.