Files
led-hoop/pico/test/test_chase_via_presets.py

185 lines
5.1 KiB
Python

"""
On-device test that exercises the Chase pattern via Presets.
Usage (from pico/ dir or project root with adjusted paths):
mpremote connect <device> cp src/*.py :
mpremote connect <device> cp src/patterns/*.py :patterns
mpremote connect <device> cp lib/*.py :
mpremote connect <device> cp test/test_chase_via_presets.py :
mpremote connect <device> run test_chase_via_presets.py
"""
import utime
from presets import Presets, Preset
def snapshot_strip_colors(presets, strip_idx=0, max_leds=32):
"""Return a list of (r,g,b) tuples for the first max_leds of the given strip."""
strip = presets.strips[strip_idx]
num = min(strip.num_leds, max_leds)
out = []
for i in range(num):
o = i * 3
g = strip.ar[o]
r = strip.ar[o + 1]
b = strip.ar[o + 2]
out.append((r, g, b))
return out
def expected_chase_color(i, num_leds, step_count, color0, color1, n1, n2, n3, n4):
"""Mirror the position logic from patterns/chase.py for a single logical LED."""
segment_length = n1 + n2
if step_count % 2 == 0:
position = (step_count // 2) * (n3 + n4) + n3
else:
position = ((step_count + 1) // 2) * (n3 + n4)
max_pos = num_leds + segment_length
position = position % max_pos
if position < 0:
position += max_pos
relative_pos = (i - position) % segment_length
if relative_pos < 0:
relative_pos = (relative_pos + segment_length) % segment_length
return color0 if relative_pos < n1 else color1
def test_chase_single_step_via_presets():
presets = Presets()
num_leds = presets.num_leds
if num_leds <= 0:
print("No strips; skipping chase test.")
return
# Simple alternating colors with known lengths.
base_color0 = (10, 0, 0)
base_color1 = (0, 0, 20)
# Use full brightness so apply_brightness is identity.
brightness = 255
n1 = 2
n2 = 3
# Same step size on even/odd for easier reasoning.
n3 = 1
n4 = 1
data = {
"p": "chase",
"c": [base_color0, base_color1],
"b": brightness,
"d": 0,
"a": False, # single-step mode
"n1": n1,
"n2": n2,
"n3": n3,
"n4": n4,
}
name = "test_chase_pattern"
preset = Preset(data)
presets.presets[name] = preset
# Select and run one tick; this should render exactly one chase frame for step 0.
presets.select(name, step=0)
presets.tick()
# Colors after brightness scaling (driver.apply_brightness is used in the pattern).
color0 = presets.apply_brightness(base_color0, brightness)
color1 = presets.apply_brightness(base_color1, brightness)
# Snapshot first few LEDs of strip 0 and compare against expected pattern for step 0.
colors = snapshot_strip_colors(presets, strip_idx=0, max_leds=16)
step_count = 0
for i, actual in enumerate(colors):
expected = expected_chase_color(
i, num_leds, step_count, color0, color1, n1, n2, n3, n4
)
assert (
actual == expected
), "LED %d: got %r, expected %r" % (i, actual, expected)
print("test_chase_single_step_via_presets: OK")
def test_chase_multiple_steps_via_presets():
"""Render several steps and verify pattern advances correctly."""
presets = Presets()
num_leds = presets.num_leds
if num_leds <= 0:
print("No strips; skipping chase multi-step test.")
return
base_color0 = (10, 0, 0)
base_color1 = (0, 0, 20)
brightness = 255
n1 = 2
n2 = 3
n3 = 1
n4 = 1
data = {
"p": "chase",
"c": [base_color0, base_color1],
"b": brightness,
"d": 0,
"a": False,
"n1": n1,
"n2": n2,
"n3": n3,
"n4": n4,
}
name = "test_chase_pattern_multi"
preset = Preset(data)
presets.presets[name] = preset
color0 = presets.apply_brightness(base_color0, brightness)
color1 = presets.apply_brightness(base_color1, brightness)
# In non-auto mode (a=False), the Chase pattern advances one step per
# invocation of the generator, and Presets is expected to call select()
# again for each beat. Emulate that here by re-selecting with an
# explicit step value for each frame we want to test.
for step_count in range(4):
presets.select(name, step=step_count)
presets.tick()
colors = snapshot_strip_colors(presets, strip_idx=0, max_leds=16)
for i, actual in enumerate(colors):
expected = expected_chase_color(
i, num_leds, step_count, color0, color1, n1, n2, n3, n4
)
assert (
actual == expected
), "step %d, LED %d: got %r, expected %r" % (
step_count,
i,
actual,
expected,
)
print("test_chase_multiple_steps_via_presets: OK")
def main():
test_chase_single_step_via_presets()
test_chase_multiple_steps_via_presets()
# Give a brief pause so message is visible if run interactively.
utime.sleep_ms(100)
if __name__ == "__main__":
main()