Skip to content

Unit Test Results

406 total | 406 passed | 0 failed2026-05-11 10:26

By complexity

Level Count Description
smoke 15 no-crash / lifecycle only
format 46 string or schema format checks
behavioral 317 actual module behavior: output, state, counts
integration 29 multi-module pipeline or cross-device

See Testing standards for the full classification guide.

test_artnet.cpp

7/7 passedtest_artnet.cpp

  • smoke ArtNetOutModule - lifecycle without layer (should not crash)
  • behavioral ArtNetOutModule - sends packets when wired to a DriverLayer with pixels
  • behavioral ArtNetOutModule - multi-universe: 170 pixels use 1 universe, 171 use 2
  • behavioral ArtNetOutModule - packet header bytes are correct Art-Net OpDmx
  • smoke ArtNetInModule - lifecycle without layer (should not crash)
  • format ArtNetInModule - healthReport format
  • integration ArtNetOut loopback to ArtNetIn on 127.0.0.1

Controls and KV Store

33/33 passedtest_controls_and_kv.cpp

  • behavioral StatefulModuleBase - addControl registers controls correctly
  • behavioral StatefulModuleBase - clearControls removes non-system control descriptors
  • behavioral StatefulModuleBase - setControl float writes through to field
  • behavioral StatefulModuleBase - setControl uint8 writes through to field
  • behavioral StatefulModuleBase - setControl uint32 writes through to field
  • behavioral StatefulModuleBase - setControl bool writes through to field
  • behavioral StatefulModuleBase - setControl unknown key is a no-op
  • behavioral StatefulModuleBase - setControl JsonVariant writes through to field
  • behavioral StatefulModuleBase - onUpdate is called after setControl
  • behavioral StatefulModuleBase - onUpdate is NOT called for unknown key
  • format StatefulModuleBase - getSchema produces correct JSON structure
  • format StatefulModuleBase - getSchema reflects current field value after setControl
  • format SineEffectModule - getSchema exposes frequency and amplitude
  • behavioral SineEffectModule - setControl updates live frequency
  • format PreviewModule - getSchema has only enabled control
  • behavioral KvStore - setFloat / getFloat round-trip
  • behavioral KvStore - setUint8 / getUint8 round-trip
  • behavioral KvStore - setBool / getBool round-trip
  • behavioral KvStore - getFloat returns default for unknown key
  • behavioral KvStore - getUint8 returns default for unknown key
  • behavioral KvStore - getBool returns default for unknown key
  • behavioral KvStore - getFloat returns default when key holds different type
  • behavioral KvStore - exists returns true for present key, false for absent
  • behavioral KvStore - setFloat updates an existing entry
  • behavioral KvStore - setFloat returns false when table is full
  • behavioral KvStore - clear removes all entries
  • behavioral clearControls - rebuild preserves value of re-registered control
  • behavioral clearControls - values of removed controls are not applied to unrelated fields
  • behavioral clearControls - system control enabled survives rebuild
  • format clearControls - schemaDirty_ is set and can be cleared
  • format clearControls - burst of rebuilds produces exactly one schemaDirty flag
  • behavioral SineEffectModule - rebuildControls switches to Ripples parameters
  • behavioral SineEffectModule - frequency preserved across type switch and back

Coord3D and Pixel Addressing

7/7 passedtest_coord3d.cpp

  • behavioral Coord3D - default construction is (0,0,0)
  • behavioral Coord3D - equality and inequality
  • behavioral coords_to_index - flat 2D grid (depth=1)
  • behavioral coords_to_index - 3D volume
  • behavioral index_to_coords - flat 2D grid (depth=1)
  • behavioral index_to_coords - 3D round-trip with coords_to_index
  • behavioral coords_to_index - matches EffectsLayer pixel layout

test_effects_2d.cpp

7/7 passedtest_effects_2d.cpp

  • behavioral GameOfLifeEffect - glider moves (+1,+1) after 4 generations
  • behavioral GameOfLifeEffect - extinction triggers re-seed
  • behavioral GameOfLifeEffect - renders non-zero pixels after loop
  • behavioral NoiseEffect2D - renders non-zero pixels
  • behavioral NoiseEffect2D - output varies with position (scale>0 produces spatial variation)
  • behavioral DistortionWaves2DEffect - renders non-zero pixels
  • behavioral DistortionWaves2DEffect - spatial variation across pixels

Health Checks

8/8 passedtest_health_checks.cpp

  • format SineEffectModule - healthReport format after one tick
  • format PreviewModule - healthReport format after one tick
  • behavioral Health check - SineEffectModule and Preview checksums match
  • behavioral Health check - zero-residue: PreviewModule state reset after teardown
  • format Scheduler - printHealthReports fires for participating modules
  • behavioral EffectsLayer - disableSelf marks setup failed and skips loop
  • behavioral EffectsLayer - setupOk resets to true on re-setup after disableSelf
  • behavioral DriverLayer - disableSelf marks setup failed

HTTP Server

17/17 passedtest_http_server.cpp

  • format SineEffectModule - getSchema includes category 'effect'
  • format EffectsLayer - getSchema includes category 'layer'
  • format DriverLayer - getSchema includes category 'layer'
  • behavioral SineEffectModule - setControl updates frequency live
  • behavioral SineEffectModule - setControl ignores unknown key
  • behavioral ModuleManager - getModulesJson emits id + name + category + controls
  • behavioral ModuleManager - setModuleControl returns true for valid id+key
  • behavioral ModuleManager - setModuleControl returns false for unknown module id
  • behavioral ModuleManager - setModuleControl returns false for unknown key
  • behavioral ModuleManager - flushIfDirty does not flush before debounce window
  • behavioral ModuleManager - getModulesJson reflects live value after setModuleControl
  • behavioral ModuleManager - flushIfDirty writes state file when debounce is 0
  • behavioral ModuleManager - addModule creates a root module at runtime
  • behavioral ModuleManager - addModule creates a child module with parent_id
  • behavioral ModuleManager - removeModule returns HasChildren when children exist
  • behavioral ModuleManager - removeModule returns NotFound for unknown id
  • behavioral ModuleManager - validateControls: all slider controls have valid min < max

REST and WebSocket Integration

14/14 passedtest_integration.cpp

  • integration integration/A: GET /api/modules returns 200 with a JSON array
  • integration integration/A: POST /api/control with valid id+key returns 200 ok:true
  • integration integration/A: POST /api/control with unknown module id returns 404
  • integration integration/A: POST /api/control with unknown key returns 404
  • integration integration/A: POST /api/control with malformed JSON returns 400
  • integration integration/A: GET /api/types returns array of known type names
  • integration integration/B: pixel snapshot header is stable across consecutive ticks
  • integration integration/B: pixel snapshot contains non-zero pixels after SineEffectModule runs
  • integration integration/B: GET /api/modules + pixel snapshot are consistent
  • integration integration/D: POST /api/control value appears in WS state within 600 ms
  • integration integration/E: first WS connection receives a valid state frame within 3 s
  • integration integration/E: WS state frame contains expected module ids
  • integration integration/D4: binary WS frame header byte and size match pixelSnapshot
  • integration integration/schema: WS receives schema frame after first and second rebuildControls

Effect and Driver Layers

29/29 passedtest_layers.cpp

  • smoke EffectsLayer - zero residue after teardown
  • integration DriverLayer - zero sources produces all-black blend
  • integration DriverLayer - single producer passes through unchanged
  • integration DriverLayer - two producers blend to pixel-wise saturating add
  • smoke DriverLayer - zero residue after teardown
  • integration PreviewModule - reads blended output from DriverLayer
  • behavioral GridLayout - extent() returns configured dimensions
  • behavioral GridLayout - extent() via setProps
  • behavioral DriverLayer - props-only sizing (no Layout children, backward compat)
  • behavioral DriverLayer - GridLayout child overrides props size via onChildrenReady
  • behavioral DriverLayer - onChildrenReady called via addChild (addModule path)
  • behavioral EffectsLayer - onSizeChanged ignored when useDriverSize is false
  • behavioral EffectsLayer - full-extent driver sizing (start=0 end=1)
  • behavioral EffectsLayer - partial driver sizing via start/end props
  • behavioral EffectsLayer - resizes when driver resizes via onChildrenReady
  • behavioral EffectsLayer - EffectsLayer before DriverLayer setup ordering
  • behavioral GridLayout - saveState/loadState round-trips width/height/depth
  • integration DriverLayer - partial EffectsLayer (startX=0.5) does not OOB blend
  • behavioral EffectsLayer - saveState/loadState round-trips start/end fractions
  • behavioral EffectsLayer - onUpdate resizes buffer when start/end slider changes
  • behavioral GridLayout - requestMappings straight (identity for non-serpentine)
  • behavioral GridLayout - requestMappings serpentine reverses odd rows
  • behavioral EffectsLayer preferredCore returns 0
  • behavioral DriverLayer preferredCore returns 1
  • format DriverLayer healthReport contains expected fields
  • format DriverLayer healthReport reports source count
  • behavioral StatefulModuleBase timing_ accumulates self loop() time
  • behavioral StatefulModuleBase parent self-timing excludes child time
  • behavioral StatefulModuleBase timing resets on runSetup

test_layouts.cpp

21/21 passedtest_layouts.cpp

  • behavioral RingLayout - extent is (2*radius+1) squared by 1
  • behavioral RingLayout - physCount matches ledCount
  • behavioral RingLayout - LED 0 maps to rightmost grid cell (angle=0)
  • behavioral RingLayout - LEDs are distributed around the circle (no clustering at one point)
  • behavioral WheelLayout - extent is (2*ledsPerSpoke+1) squared by 1
  • behavioral WheelLayout - physCount equals spokes x ledsPerSpoke
  • behavioral WheelLayout - spoke 0 innermost LED is one step right of centre
  • behavioral WheelLayout - spoke 0 outermost LED is 15 steps right of centre
  • behavioral XmasTreeLayout - extent is (2h-1) x h x (2h-1)
  • behavioral XmasTreeLayout - depth equals width (square footprint)
  • behavioral XmasTreeLayout - physCount equals strings * ledsPerString
  • behavioral XmasTreeLayout - first LED maps to bottom layer (y=0)
  • behavioral XmasTreeLayout - last LED maps to tip at centre top (y=height-1, x=cx, z=cz)
  • behavioral XmasTreeLayout - all virtual indices within 3D grid bounds
  • behavioral XmasTreeLayout - two strings produce double the LED count
  • behavioral Multi-layout: two offset GridLayouts produce union bounding box
  • behavioral Multi-layout: composed PhysMap routes each sub-layout to its virtual sub-region
  • behavioral Multi-layout: physMap covers entire union canvas without gaps
  • behavioral GridLayout - growing dimensions updates mappingCount
  • behavioral GridLayout - shrinking dimensions updates mappingCount
  • format GridLayout - healthReport reflects current dimensions after resize

Logger

10/10 passedtest_logger.cpp

  • behavioral Logger - logLevelFromString parses all levels
  • behavioral Logger - logLevelFromString rejects unknown string
  • behavioral Logger - logLevelToString round-trips all levels
  • behavioral Logger - g_logLevel gate: LOG_SETUP suppressed below Setup level
  • behavioral Logger - WS push function receives stripped message
  • behavioral Logger - WS push not called when fn is null
  • behavioral Logger ring - logPush stores entries in order
  • behavioral Logger ring - overflow evicts oldest entries
  • behavioral Logger ring - empty string is not stored
  • behavioral Logger ring - logPush calls WS push fn

Memory and Filesystem

3/3 passedtest_memory_stats.cpp

  • behavioral MemoryStats - filesystem stats are available
  • behavioral MemoryStats - memory sanity checks
  • behavioral MemoryStats - multiple snapshots are consistent

test_modifiers.cpp

36/36 passedtest_modifiers.cpp

  • behavioral Coord3D masked sentinel round-trips through isMasked
  • behavioral MirrorModifier modifyDims halves each active axis (rounding up)
  • behavioral MirrorModifier modifyDims rounds up for odd sizes
  • behavioral MirrorModifier modifyXYZ reflects coordinates above midpoint
  • behavioral MirrorModifier isStatic returns true
  • behavioral MirrorModifier all-axes: dims reduce to 1/8 for 8x8x8
  • behavioral CheckerboardModifier masks pixels where (x+y+z) is odd
  • behavioral CheckerboardModifier does not change dims
  • behavioral CheckerboardModifier isStatic returns true
  • behavioral ScrollModifier isStatic: true when all speeds 0, false when any non-zero
  • behavioral ScrollModifier does not change dims
  • behavioral ScrollModifier wraps coordinate by tick and speed
  • behavioral EffectsLayer with no modifiers: hasStaticModifierChain is true
  • behavioral EffectsLayer with no modifiers: applyModifiers is identity
  • behavioral DriverLayer with MirrorModifier(X): physical output mirrors left half
  • behavioral DriverLayer with CheckerboardModifier: odd pixels are black
  • behavioral DriverLayer with ScrollModifier: output changes across frames
  • behavioral DriverLayer rebuilds maps when MirrorModifier control changes at runtime
  • behavioral Disabling a MirrorModifier reverts to identity output
  • integration DriverLayer two static layers blend additively
  • behavioral RotateModifier isStatic: true at speed=0, false when speed!=0
  • behavioral RotateModifier modifyDims: identity at 0 degrees
  • behavioral RotateModifier modifyDims: 90 degrees swaps width and height
  • behavioral RotateModifier modifyDims: 45 degrees on 8x8 expands to bounding box
  • behavioral RotateModifier modifyXYZ: identity at 0 degrees
  • behavioral RotateModifier modifyXYZ: 180 degrees maps (0,0) to (w-1,h-1)
  • behavioral RotateModifier modifyXYZ: 90 degrees maps corners correctly on 4x8
  • behavioral RotateModifier modifyXYZ: center pixel maps to center of bbox
  • behavioral RotateModifier dynamic: angle advances with tick
  • behavioral TileModifier is always static
  • behavioral TileModifier modifyDims: each axis divided by repeat count
  • behavioral TileModifier modifyDims: ceiling division on odd region size
  • behavioral TileModifier modifyDims: repeats=1 leaves all dims unchanged
  • behavioral TileModifier modifyXYZ: wraps XY coordinates via modulo
  • behavioral TileModifier modifyXYZ: Z axis tiling
  • behavioral TileModifier bakes into EffectsLayer as a static modifier chain

Module Manager

29/29 passedtest_module_manager.cpp

  • behavioral TypeRegistry - registered types are findable
  • behavioral TypeRegistry - create returns correct type
  • behavioral TypeRegistry - create returns nullptr for unknown type
  • behavioral EffectsLayer - setProps sets width and height
  • behavioral SineEffect - setProps and saveState/loadState round-trip
  • behavioral ModuleManager - loads config and registers all modules
  • integration ModuleManager - wires modules correctly (pipeline runs without crash)
  • behavioral ModuleManager - addModule happy path
  • behavioral ModuleManager - addModule duplicate id rejected
  • behavioral ModuleManager - addModule unknown type rejected
  • behavioral ModuleManager - removeModule happy path
  • behavioral ModuleManager - removeModule not found returns NotFound
  • behavioral ModuleManager - removeModule add then remove ok
  • behavioral TypeRegistry - getTypes returns all registered type names
  • behavioral ModuleManager - addModule with valid parent_id succeeds
  • behavioral ModuleManager - addModule with invalid parent_id rejected
  • behavioral ModuleManager - removeModule with children returns HasChildren
  • behavioral ModuleManager - parent_id round-tripped through getModulesJson
  • behavioral ModuleManager - child module NOT registered in Scheduler (roots-only)
  • behavioral ModuleManager - child wired into parent's children_ list
  • behavioral ModuleManager - addModule child: added to parent, not Scheduler
  • behavioral ModuleManager - removeModule child: detached from parent, not Scheduler
  • behavioral ModuleManager - auto-creates full default set when no top-level modules exist
  • behavioral ModuleManager - does NOT auto-create default set when any top-level module exists
  • behavioral replaceModule - leaf effect child changes type, preserves id
  • behavioral replaceModule - layout child triggers DriverLayer remap
  • behavioral replaceModule - parent with children re-wires children to new instance
  • behavioral replaceModule - unknown id returns false
  • behavioral replaceModule - unknown type returns false and leaves original intact

Network and WiFi

37/37 passedtest_network.cpp

  • behavioral NetworkModule - default device_name on PC starts with MM- and has length 7
  • behavioral NetworkModule - setProps overrides device_name
  • behavioral NetworkModule - loadState/saveState round-trip for device_name
  • format NetworkModule - getSchema includes device_name and mac_address
  • format NetworkModule - category is network
  • format NetworkModule - healthReport contains device= and mac=
  • behavioral EditStr - setControl round-trip via JsonVariant
  • behavioral EditStr - setControl(float) returns false for EditStr type
  • format EditStr - getSchema includes value
  • smoke WifiStaModule - lifecycle on PC does not crash
  • behavioral WifiStaModule - password present in getControlValues
  • format WifiStaModule - password in getSchema has value field
  • behavioral WifiStaModule - saveState/loadState round-trip for ssid
  • format WifiStaModule - category is network
  • behavioral WifiStaModule - setControl ssid triggers pendingConnect via loop
  • behavioral WifiStaModule - password value is empty when unset, matches when set
  • behavioral WifiStaModule - password control uiType is password
  • smoke WifiApModule - lifecycle on PC does not crash
  • behavioral WifiApModule - setInput network accepted without crash
  • format WifiApModule - category is network
  • smoke EthernetModule - lifecycle does not crash
  • behavioral EthernetModule - status is unsupported on PC (no Ethernet hardware)
  • format EthernetModule - category is network
  • format EthernetModule - healthReport is eth=unsupported on PC
  • behavioral EthernetModule - isConnected returns false on PC
  • behavioral EthernetModule - loadState/saveState round-trip for mode and static_ip
  • behavioral EthernetModule - default static_subnet is 255.255.255.0
  • smoke DeviceDiscoveryModule - lifecycle on PC does not crash
  • format DeviceDiscoveryModule - category is network
  • behavioral DeviceDiscoveryModule - setup registers broadcast_interval, status, and 8 device controls
  • format DeviceDiscoveryModule - healthReport starts with devices=0
  • behavioral DeviceDiscoveryModule - setInput network accepted without crash
  • behavioral DeviceDiscoveryModule - broadcast_interval saveState/loadState round-trip
  • behavioral DeviceDiscoveryModule - getControlValues includes status field
  • behavioral WifiApModule - onUpdate enabled false sets status to disabled
  • behavioral WifiApModule - onUpdate enabled true after false restores AP (no WiFi on PC)
  • behavioral WifiStaModule - onUpdate enabled false sets status to disabled

test_observability.cpp

16/16 passedtest_observability.cpp

  • behavioral ModuleTiming record accumulates min/max/total/count correctly
  • behavioral ModuleTiming avgMs divides totalUs by count * 1000
  • behavioral ModuleTiming minMs and maxMs convert microseconds to milliseconds
  • behavioral ModuleTiming fps = count * 1e6 / totalUs
  • behavioral ModuleTiming reset clears all fields
  • behavioral ModuleTiming avgMs and fps return 0 when count is 0
  • behavioral Scheduler timing accumulator tracks SpinModule within 5%
  • behavioral Timing balance: total module time + idle == tick budget within 2%
  • behavioral setupHeapDelta_ is 0 on PC because pal::free_heap_bytes() stubs to 0
  • behavioral Frag formula: largest == 50% of free gives frag == 50 (below threshold)
  • behavioral Frag formula: largest == 49% of free gives frag == 51 (above threshold)
  • behavioral Frag formula: zero totalFree returns 0 (guard against divide-by-zero)
  • behavioral Frag formula: zero largestBlock gives frag == 100
  • behavioral Frag formula: largest == totalFree gives frag == 0 (fully contiguous)
  • behavioral Scheduler memWarnings and timingSpikes start at 0
  • behavioral Scheduler timingSpikes increments when module exceeds 5x rolling average

test_physmap.cpp

18/18 passedtest_physmap.cpp

  • behavioral PhysMap - default constructed is empty
  • behavioral PhysMap - resize initialises all slots to NO_VIRT
  • behavioral PhysMap - set and at round-trip
  • behavioral PhysMap - out-of-range at returns NO_VIRT
  • behavioral PhysMap - out-of-range set is a no-op
  • behavioral PhysMap - clear resets to empty
  • behavioral PhysMap - resize again re-initialises
  • behavioral PhysMap - 1:0 mapping (NO_VIRT slot)
  • behavioral PhysMap - 1:1 identity mapping
  • behavioral PhysMap - 1:1 shuffled mapping
  • behavioral PhysMap - 1:N mapping (shared virtual index)
  • behavioral GridLayout buildPhysMap - straight 2x2 is identity
  • behavioral GridLayout buildPhysMap - straight 3x2 is identity
  • behavioral GridLayout buildPhysMap - serpentine 4x2: row 0 forward, row 1 reversed
  • behavioral GridLayout buildPhysMap - straight same as buildMappings identity
  • integration DriverLayer with straight GridLayout: output byte-identical to no-layout blend
  • behavioral DriverLayer with serpentine GridLayout: physical pixel at strip 4 matches logical (3,1)
  • behavioral DriverLayer without Layout still produces output (identity path)

Preview Pipeline

3/3 passedtest_preview_e2e.cpp

  • integration Preview pipeline - non-zero output after 50 ticks
  • integration Preview pipeline - checksum varies across ticks
  • integration Preview pipeline - PreviewModule listed with fps>0 via HTTP

test_producer_consumer.cpp

7/7 passedtest_producer_consumer.cpp

  • behavioral buffer is null before setup
  • behavioral declareBuffer registers pointer, length and elemSize
  • behavioral bufferPtr gives read access to the underlying array
  • behavioral producer_ is null before wiring
  • behavioral setInput('producer', ...) wires the producer
  • behavioral setInput ignores unknown keys
  • behavioral consumer reads producer buffer after wiring

test_reorder.cpp

12/12 passedtest_reorder.cpp

  • behavioral Scheduler::reorderModules - execution order changes after reorder
  • behavioral Scheduler::reorderModules - timing entry moves with its module
  • behavioral Scheduler::reorderModules - wrong size returns false
  • behavioral Scheduler::reorderModules - unknown pointer returns false
  • behavioral ModuleManager::reorderChildren - root order changes in JSON and loop
  • behavioral ModuleManager::reorderChildren - root: unknown id returns false
  • behavioral ModuleManager::reorderChildren - root: wrong count returns false
  • behavioral ModuleManager::reorderChildren - child order changes in JSON
  • behavioral ModuleManager::reorderChildren - child: parent childCount unchanged
  • behavioral ModuleManager::reorderChildren - child: unknown parent returns false
  • behavioral ModuleManager::reorderChildren - child: unknown child id returns false
  • behavioral EffectsLayer modifierGen bumps after modifier reorder

Scenario Replay

2/2 passedtest_scenarios.cpp

  • behavioral scenario replay - all scenarios build and run without crash
  • integration scenario replay - base-pipeline fps bound is met

Scheduler

4/4 passedtest_scheduler.cpp

  • integration phase 1 sanity
  • behavioral Scheduler loopCore runs only matching-core modules
  • behavioral Scheduler loop() runs all modules via loopCore(0) then loopCore(1)
  • behavioral Scheduler loopCore timing is tracked per module

Sine Effect

3/3 passedtest_sine_effect.cpp

  • behavioral SineEffectModule - deterministic output for fixed frequency/amplitude
  • smoke SineEffectModule - zero residue after teardown
  • integration PreviewModule - consumer sees producer frame checksum via layers

Stateful Module

23/24 passedtest_stateful_module.cpp

  • behavioral sprint9/B: SystemStatusModule has 'progress' type controls
  • behavioral sprint9/B: SystemStatusModule progress controls have min=0 and max>0
  • format sprint9/D: SystemStatusModule string controls have string value in schema
  • behavioral sprint9/E: WifiApModule loadState/saveState round-trip for ap_password
  • smoke sprint9/F: ModuleManager with disableStatePersistence does not write state files
  • behavioral sprint9/G: SineEffectModule frequency and amplitude are uint8_t
  • format sprint9/G: SineEffectModule getSchema includes integer=true for frequency
  • behavioral sprint9/G: SineEffectModule setControl with integer value clamps to uint8_t range
  • behavioral sprint9/G: SineEffectModule saveState/loadState round-trips integer frequency
  • behavioral sprint7/D: meta() const on uninitialised store returns empty document
  • behavioral sprint7/D: meta() stores and retrieves non-control fields
  • behavioral sprint7/D: meta() store survives teardown/runSetup cycle
  • behavioral R4S9/A: defVal captured from field initializer, not overwritten by setControl
  • behavioral R4S9/A: defVal is unchanged after saveState/loadState round-trip
  • format R7S9: select control - getSchema emits options array and value
  • behavioral R7S9: select control - setControl updates value
  • behavioral R7S9: select control - setControl rejects out-of-range via clamp
  • behavioral R7S9: select control - saveState and loadState round-trip
  • behavioral R7S9: select control - addControlValue dynamic form
  • behavioral R7S9: select control - hot-reload frees owned options without leak
  • behavioral R7S9: SineEffect - waveform select registered with 4 options
  • behavioral R7S9: SineEffect - square waveform produces different checksum than sine
  • behavioral R7S9: LinesEffect - axis select registered with 4 options
  • behavioral R7S9: LinesEffect - axis=x produces different output than axis=all

System Status

27/27 passedtest_system_info.cpp

  • behavioral LogLevel enum ordering is cumulative
  • behavioral logLevelFromString parses all valid names
  • behavioral logLevelFromString rejects unknown names and nullptr
  • behavioral EffectsLayer heapSize equals 2 * width * height * sizeof(projectMM::RGB)
  • behavioral DriverLayer heapSize equals width * height * sizeof(projectMM::RGB) (single physical buffer)
  • behavioral EffectsLayer heapSize is non-zero for default-constructed (10x10x10) instance
  • format EffectsLayer healthReport contains geometry and counters
  • format healthReport respects buffer length limit
  • smoke SystemStatusModule lifecycle: setup / loop / teardown
  • behavioral SystemStatusModule exposes key controls by name
  • format SystemStatusModule has no heap allocation
  • format SystemStatusModule classSize is sizeof(SystemStatusModule)
  • format SystemStatusModule healthReport writes non-empty string
  • format SystemStatusModule healthReport contains expected keys after loop1s sampling
  • behavioral fillMemoryJson returns all expected top-level keys
  • behavioral fillMemoryJson modules array is non-empty and has id + setup_delta_kb fields
  • behavioral fillMemoryJson numeric values are non-negative on all platforms
  • behavioral pal::ota_begin PC stub returns true and sets handle to 0
  • behavioral pal::ota_write PC stub returns true for non-empty data
  • behavioral pal::ota_end PC stub returns true
  • smoke pal::ota_abort PC stub does not crash
  • behavioral g_otaStatus initial value is 'idle'
  • behavioral g_otaStatus and g_otaPct can be written and read back
  • smoke FirmwareUpdateModule lifecycle: setup / loop / loop1s / teardown
  • behavioral FirmwareUpdateModule exposes update_status and update_pct controls
  • behavioral FirmwareUpdateModule loop reflects g_otaStatus changes
  • format FirmwareUpdateModule has no heap allocation

System Utility Modules

10/10 passedtest_system_utils.cpp

  • smoke TasksModule - lifecycle does not crash
  • format TasksModule - healthReport format contains task_count=
  • format TasksModule - getSchema exposes tasks display and task_count display
  • behavioral TasksModule - task_count is non-negative after setup
  • smoke FileManagerModule - lifecycle does not crash
  • format FileManagerModule - healthReport format contains state_dir= and file_count=
  • format FileManagerModule - getSchema exposes files display, filename text, delete button
  • behavioral FileManagerModule - delete with empty filename sets error result
  • behavioral FileManagerModule - delete nonexistent file sets not-found result
  • behavioral FileManagerModule - delete existing file succeeds

Code Analysis (classSize)

2/2 passedtest_techdebt.cpp

  • format techdebt - classSize per registered type
  • format techdebt - classSize for core infrastructure classes

WebSocket

21/21 passedtest_websocket.cpp

  • behavioral getControlValues returns empty object for module with no controls
  • behavioral getControlValues reflects live field values
  • behavioral getControlValues updates after second setControl
  • behavioral PreviewModule snapshot returns false before first loop()
  • behavioral PreviewModule snapshot returns true after DriverLayer loop() and fills buffer
  • behavioral PreviewModule snapshot header: byte 0 is 0x02 (pixel frame type)
  • behavioral PreviewModule snapshot header: width and height encoded little-endian
  • behavioral PreviewModule snapshot payload size matches width * height * 3
  • behavioral PreviewModule snapshot pixel values are non-zero after SineEffectModule runs
  • behavioral getStateJson returns one entry per module
  • behavioral getStateJson entries have id, controls, and timing fields
  • behavioral getStateJson timing fps is positive after one tick
  • behavioral getStateJson control values reflect live state
  • behavioral pixelSnapshot returns true for preview1 after one tick
  • behavioral pixelSnapshot returns false for unknown module id
  • behavioral getStateJson valid before and after runtime addModule (root)
  • behavioral getStateJson valid before and after runtime removeModule (child)
  • format rebuildControls: schemaDirty set on first type change
  • format rebuildControls: schemaDirty re-set after clear and second type change
  • format rebuildControls: ModuleManager hasSchemaDirty tracks consecutive rebuilds
  • behavioral rebuildControls: getModulesJson control set changes after type switch

Scenarios

Unit tests replay each scenario file in-process via ModuleManager (no HTTP server). The 2 test cases above exercise all scenario files; the table below shows measured fps per step.

On PC, heap stats are 0 (pal::free_heap_kb() returns 0). Timing bounds are checked in-process; fps values are PC throughput only, not representative of ESP32 performance.

Scenario Step fps (PC)
base-pipeline-32x32 add-ripples 239,001
base-pipeline-32x32 resize-32h 186,456
base-pipeline-64x64 add-ripples 245,122
base-pipeline-64x64 resize-32h 195,146
base-pipeline-64x64 resize-64h 55,525
base-pipeline-speed add-ripples 244,526
base-pipeline-speed speed-slow 242,461
base-pipeline-speed speed-default 244,228
base-pipeline-speed speed-fast 243,636
base-pipeline add-status 5,432,432
base-pipeline add-driver 939,252
base-pipeline add-ripples 231,567
base-pipeline set-speed 244,526
four-layers add-ripples 246,022
four-layers add-lines 210,692
four-layers add-gameoflife 158,893
four-layers add-noise 133,822
two-layers add-ripples 246,022
two-layers add-lines 212,698
xmas-tree-3d add-ripples 169,334