Skip to content

What we test

Every unit test grouped by the module or core surface it verifies. Generated from the TEST_CASE / SUBCASE names in test/test_pc/*.cpp by scripts/build/gen_test_list.py — a static parse, no run. Re-run that script after adding or renaming a test.

ArtnetOutModule (Art-Net OpDmx)

test_artnet_packing.cpp — 4 test case(s)

  • ArtNet OpDmx header bytes match the spec for universe 0, 510 DMX
  • ArtNet OpDmx header — universe increments fit in the low byte
  • ArtNet OpDmx header — partial DMX byte count (e.g. last universe)
  • ArtNet OpDmx: 64x64 frame splits into 25 universes (24 full + 1 partial)

MoonModule control system

test_controls.cpp — 11 test case(s)

  • setControl float writes through and fires onUpdate
  • setControl uint8/uint32/bool write through (cast, no clamp)
  • setControl unknown key is a no-op returning false
  • setControl via JsonVariant takes the REST path
  • EditStr control is writable via JsonVariant, not the float path
  • getSchema emits id + a control entry per addControl with min/max/default
  • select control: getSchema carries the options array + index value
  • getControlValues is a flat {key:value} reflecting live fields
  • clearControls + rebuild preserves the live value (pending props)
  • schemaDirty: clearControls marks dirty, clearSchemaDirty resets it
  • defVal is captured from the field initializer, not a later setControl

DataBuffer / DataBufferReader (SPSC)

test_data_buffer.cpp — 8 test case(s)

  • DataBuffer: allocate(0) leaves the buffer invalid
  • DataBuffer: reader returns nullptr before first publish
  • DataBuffer: publish then read returns the filled slot
  • DataBuffer: acquire_write returns the same slot every time
  • DataBuffer: revision increments on each publish
  • DataBuffer: reader returns nullptr after release_read with no new publish
  • DataBuffer: two readers are independent — one release does not affect the other
  • DataBuffer: SPSC cross-core consumer never tears under paced producer

HttpServerModule

test_http.cpp — 3 test case(s)

  • HttpServerModule: GET / serves the gzipped frontend bundle
  • HttpServerModule: GET /api/modules lists registered modules
  • HttpServerModule: unknown route returns 404

REST integration (control + module API)

test_integration.cpp — 1 test case(s)

  • REST integration: control mutation + module API contract end-to-end
    • GET /api/types returns the registered type names
    • POST /api/control on a valid id+key → 200 ok:true
    • POST /api/control unknown module id → 404
    • POST /api/control missing id/key → 400
    • POST /api/control malformed JSON → 400
    • POST /api/modules duplicate id → 409 (the dup-id enforcement point)
    • POST /api/modules unknown type → 201 (v2 add() is permissive)
    • control change is observable via GET /api/modules afterwards

MemTracker + classSize drift

test_memtracker.cpp — 4 test case(s)

  • memtracker: snapshot() is callable and internally consistent
  • memtracker: frag_pct stays within 0..100 and is 0 on a 0 heap
  • techdebt: classSize() == sizeof(T) for every registered module type
  • techdebt: an unregistered type is a base MoonModule sized accordingly

ModuleManager + SystemStatusModule

test_module.cpp — 11 test case(s)

  • ModuleManager: add base module
  • ModuleManager: factory builds concrete type
  • ModuleManager: manager pointer set on add
  • ModuleManager: remove existing
  • ModuleManager: remove non-existent
  • ModuleManager: find
  • ModuleManager: unknown type yields a base module with a truthful type()
  • ModuleManager: remove() erases the whole subtree, not just the root
  • SystemStatusModule: setup/loop/teardown lifecycle does not crash
  • SystemStatusModule: exposes named controls and classSize == sizeof
  • SystemStatusModule: getSchema after loop1s sampling is well-formed

Pal — heap / PSRAM

test_pal_heap.cpp — 4 test case(s)

  • PalHeap: psram_alloc(0) returns null without allocating
  • PalHeap: psram_free(nullptr) is a no-op
  • PalHeap: psram_alloc returns a writable, byte-addressable buffer
  • PalHeap: paired alloc/free does not leak across many cycles

PreviewModule (binary wire format)

test_preview_wire.cpp — 2 test case(s)

  • PreviewModule wire format: 4x4 frame header + body bytes
  • PreviewModule wire format: width and height LE encoding for 128x128

Parent-input model (reparent / reorder)

test_reparent.cpp — 10 test case(s)

  • reparent: parent flag set on the name-matched input
  • reparent: rejected when no input is named for the parent type
  • reparent: wildcard 'source' input matches any parent type
  • reparent: exact name==type wins over generic 'source'
  • reparent: promote to root clears the flag but KEEPS the value
  • reparent: loop order — parent precedes child after reparent
  • reparent: returns false for unknown id
  • reorder children: nested reorder changes child order and loop order
  • reparent: 'layout' input matches a parent by category, not just type
  • reparent: exact name==type wins over a category match

RipplesEffect / PixelEffectBase

test_ripples_lut.cpp — 5 test case(s)

  • RipplesEffect: lifecycle adds and exposes a valid pixel buffer
  • RipplesEffect: geometry change via GridLayoutModule reallocates and bumps revision
  • RipplesEffect: loop20ms writes non-trivial pixel output
  • RipplesEffect: hue_base change recolours the output
  • RipplesEffect: DataRegistry declares buffer for consumers to find

Scenario replay (whole-pipeline)

test_scenarios.cpp — 1 test case(s)

  • Scenarios: every JSON in test/test_pc/scenarios/ replays clean

Scheduler (core affinity)

test_scheduler_affinity.cpp — 2 test case(s)

  • Scheduler affinity: two-core scenario ticks both modules
  • Scheduler affinity: single-core scenario ignores out-of-range modules

StateStoreModule (persistence)

test_state_store.cpp — 1 test case(s)

  • StateStoreModule round-trip — control survives a simulated reboot

67 test cases across 14 files. PC host build (uv run scripts/build/test.py); the same scenario fixtures also replay over REST against a live device via scripts/device/scenario.py.