Skip to content

Latest commit

 

History

History
130 lines (107 loc) · 9.53 KB

testing.md

File metadata and controls

130 lines (107 loc) · 9.53 KB

Test Framework

Overview

In-kernel test harness. No external test runner required. 890 tests across 50+ suites compiled directly into the kernel image. Tests execute in kernel mode and can exercise all subsystems including privileged operations.

Two execution paths:

  1. Boot-time: test shell command (built-in) calls testing::run_all() — prints PASS/FAIL per test, returns summary count.
  2. Automated: neodev test boots QEMU headless with serial output, waits for test completion line, parses PASS/FAIL counts from serial console.

Registration

Global test list in src/testing.rs. Each subsystem exports a register_*_tests() function (e.g., register_neofs_tests()). The central testing::register_tests() function calls all module registration functions to populate the static test array.

Macro API

test_case!("my_test", {
    // test body
    test_eq!(a, b);
    test_ne!(a, b);
    test_true!(cond);
    test_fail!("message");
});
  • test_case!(name, { body }): Registers a named test. Clean completion = PASS; an assertion returns Err = FAIL.
  • test_eq!(a, b): Asserts a == b; returns Err with both values on mismatch.
  • test_ne!(a, b): Asserts a != b; returns Err on equality.
  • test_true!(cond): Asserts condition is true.
  • test_fail!(msg): Always returns Err with the formatted message (for testing error paths).

Execution

testing::run_all() iterates the global test array and runs each test in sequence with interrupts disabled (hal::without_interrupts), printing TEST <name> ... PASS or FAIL: <reason> to serial. It returns (passed, failed). Tests signal failure by returning Err(msg) — there is no catch_unwind. The test built-in shell command prints a summary line: TESTS: X total, Y passed, Z failed.

neodev test waits for a regex match on the final summary line, then exits with code 0 if 0 failures, code 1 otherwise.

Harness caveats

  • Disk image freshness. Some kernel tests mutate the NeoFS volume (mkdir/rmdir, unlink, rename, copy-on-write). After interactive boots (neodev run) the disk_image.img may be left dirty, which produces false FS-test failures (e.g. 752/754) even though no code regressed. Rebuild the image before measuring:

    neodev build --quick --image
    neodev test

    Expected result is all tests passing (e.g. 890/890 PASS); any failure is a regression.

  • Serial log growth. neodev test writes qemu_output.log (gitignored). Very large logs (hundreds of MB) can destabilise the run; rotate/delete it if a run exits early.

  • Interactive input in automation. QEMU -monitor sendkey delivery is intermittent; for keyboard E2E, use a persistent monitor connection with an info status warm-up and a short read timeout. See docs/investigation/kbd-smp-queue-validation.md (Phase 10).

Full Suite Table

Suite Count Description
NeoFS - NeoFS v1 removed. NeoFS v2 tests via btree, freelist, snapshot, neodos_dir
NEM 23 v1/v2/v3 parsing, header validation, type decoding, ABI field extraction, category parsing, v3 relocations/symbols/sections
ELF 20 Header validation, segment loading, edge cases (empty, truncated, corrupted), PIE offset/relocation, address space validation
Event Bus 17 v2 queue ops, subscription filters, dynamic payload lifecycle, backpressure, priority dispatch, handler registration/unregistration
Driver State 21 8-state lifecycle (Loaded, Initialized, Registered, Bound, Active, Faulted, Unloaded, Unloading), transition matrix validation, certification, error tracking
Object 14 ObObjectTable create/lookup/destroy, refcount management, close-auto-destroy, namespace path resolution
Page Cache 13 Hash map O(1) lookups, LRU doubly-linked list, create/peek/dirty/invalidate, capacity enforcement, stats, hit_rate, pending_writes
Syscall 13 SSDT dispatch routing, permission table enforcement, handler_close for file+pipe resources, A4.6 spawn/readdir integration
Input 13 Ring buffer operations, VT switching between consoles, independent queue isolation, framebuffer swap, input routing to active VT
Pipe 13 Alloc/free (all 16 slots), write/read data integrity, EOF detection, EPIPE on reader-drop, blocking semantics, fd table integration
Security 12 SID format validation, Token inheritance chain, ACL allow/deny evaluation, SeAccessCheck path coverage, admin bypass, SAM binary serialization
Isolation 12 X4 isolation: constants, bounds checking, alloc/free cycles, driver_id lookup, pointer validation, mode string formatting
Stress 14 Scheduler stress (1000 context switches), syscall storm, memory pressure, buddy allocator fragmentation, handle table exhaustion
URN 11 Parse all URI schemes (file://, device://, registry://, kobj://), resolve each scheme to Ob path, Ob frontend integration
Hot Reload 11 Resource tracking across reloads, registry persistence, state transition (Unloaded→Loaded→Unloaded), error code propagation
PS/2 Kbd Ref 10 Entrypoint validation, lifecycle (init→read→cleanup), key event generation, error handling for invalid scancodes
IRP 11 IRP alloc/free cycling, status transitions Pending→Completed/Error, error code propagation, queue FIFO/wraparound, callback dispatch ordering
ABI 10 Version negotiation (v6/v7), compatibility matrix, edge cases (min>max), warning generation
Slab 9 Per-size alloc/free (16B to 2KB), multi-page slab expansion, realloc fallback, memory reuse after free
Boot Loader 8 NEM driver scan, load, init, activate, unload lifecycle, category ordering (Boot→System→FileSystem→Input→Network)
Framebuffer Ref 8 Entrypoint validation, lifecycle (init→clear→pixel→scroll→cleanup), error handling for null params
Scheduler 7 Priority levels, time-slice accounting, round-robin fairness, aging boost after starvation threshold
Mmap 6 MmapRegion struct layout, flag combinations, address bounds validation, VMA add/remove from process address space
FSCK 10 Framework trait (FsckTrait check/repair). NeoFS v2: B-tree walk + CRC32 node verification, freelist coherency, superblock checksum, corrupt node detection, repair mode. FAT32: geometry validation, FAT chain/cross-link detection, orphan clustering + repair
UTF-8 6 Validation: valid 1-4 byte sequences, overlong rejection, surrogate rejection, continuation byte errors
Work Queue 6 Push/pop ordering, FIFO semantics, empty queue behavior, overflow returns Err, high/low queue isolation, pending flag atomicity
IPI 5 Constants verification, TLB shootdown struct layout, call function struct layout, local-only mask, no-targets edge case
Per-CPU Slab 5 Alloc/free on AP CPUs, refill/drain batching threshold, scalability with CPU count
IRQL 5 Raise/lower transition, page fault invariant (IRQL <= APC_LEVEL), spinlock implicit raise to DISPATCH_LEVEL, nesting depth, preemption disabled at high IRQL
DPC 5 Enqueue/dispatch cycle, IRQ→DPC transition, nesting depth, callback ordering FIFO, stress test (100 enqueue)
KPRCB 5 Struct size assertion, slab cache count (0-3), run queue operations, init sequence, offset sanity (CPU field at correct offset)
IoStack 5 Partition offset passthrough, cache level routing, device read passthrough, offset accumulation across layers
ANSI 3 Color foreground/background escape sequences, cursor position (row/col), clear screen (erase display)
SMP 3 Constants (MAX_CPUS=8), trampoline size < 4 KB, BSP is CPU 0 invariant
PCI Enum 3 Bus 0 device scan, bus 1 empty (no secondary bus), bridge detection config space
Process/Thread 4 Kthread struct layout, ThreadState enum values, Eprocess constructor field initialization
Allocator 8 Box, Vec, String allocation/deallocation, capacity growth, zero-size allocation, aligned allocation
Sync 4 Atomic flags: NEED_RESCHED bit test/set/clear, cross-CPU visibility
Capability 11 Capability flag bit values, CapabilitySet construction, category defaults (kernel vs user), check/enforce logic, escalation rules
Keyboard 5 UTF-8 encoding from scancode, compose key sequences (e.g., dead keys), modifier combinations
IoStack 5 Partition offset passthrough, cache layer routing, device-level read, offset accumulation
Capability 11 Flag values, set operations, category defaults, check/enforce path, escalation detection
Allocator 8 Box/ Vec/String allocation, drop, resize, zero-size edge cases

Adding a New Test

  1. Write test_case!("my_test", { ... }) in the relevant module.
  2. Create a pub fn register_my_tests() function in that module that invokes test_case! (macro registers at call site).
  3. Add a call to register_my_tests() inside testing::register_tests() in src/testing.rs.
  4. Build: cargo build in neodos-kernel/.
  5. Run: neodev test.
  6. Verify the new test appears in the PASS/FAIL output.

The test name appears in the serial log and neodev test output, making it easy to identify failures in CI or manual testing.

HAL tests

  • HAL tests are registered from src/hal/tests.rs (register_hal_tests).
  • Tests that depend on x86_64 state use the x64_ prefix (e.g. x64_cr2_page_fault_addr, x64_msr_raw_safe_match).
  • Device-register MMIO has a smoke test, hal_mmio_roundtrip (src/hal/mmio.rs), which round-trips the hal::mmio helpers over normal mapped memory.
  • Asm placement (HAL assembly confined to src/hal/raw/) is enforced by tooling/grep, not by a kernel test.