feat(bridge): add wifi/serial bridge runtime and UI
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Deferred sequence start on beat / downbeat."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -86,3 +87,176 @@ def test_downbeat_start_counts_trigger_beat(monkeypatch):
|
||||
assert sp._beat_run["lane_states"][0]["beatCount"] == 1
|
||||
sp.stop()
|
||||
|
||||
|
||||
def _prime_all_lanes_test_mocks(monkeypatch, deliver_log, route_log, reset_log):
|
||||
import types
|
||||
|
||||
async def fake_deliver(_bridge, messages, _macs, _devices, **_kw):
|
||||
for raw in messages:
|
||||
deliver_log.append(json.loads(raw))
|
||||
return ([], 0)
|
||||
|
||||
def fake_clear(lane_index):
|
||||
route_log.append(("clear", lane_index))
|
||||
|
||||
monkeypatch.setattr(sp, "_resolve_lane_device_names", lambda _i, _c: ["dev-a"])
|
||||
monkeypatch.setattr(
|
||||
"util.driver_delivery.deliver_json_messages",
|
||||
fake_deliver,
|
||||
)
|
||||
fake_transport = types.ModuleType("models.transport")
|
||||
fake_transport.get_current_bridge = lambda: object()
|
||||
monkeypatch.setitem(sys.modules, "models.transport", fake_transport)
|
||||
monkeypatch.setattr(
|
||||
sp,
|
||||
"_reset_after_sequence_change",
|
||||
lambda: reset_log.extend(["route", "audio"]),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"util.beat_driver_route.clear_sequence_manual_lane_route",
|
||||
fake_clear,
|
||||
)
|
||||
|
||||
|
||||
def test_prime_all_lanes_delivery_order(monkeypatch):
|
||||
"""Sequence start: step-0 presets, select, rest presets, then beat/route reset."""
|
||||
deliver_log: list = []
|
||||
route_log: list = []
|
||||
reset_log: list = []
|
||||
_prime_all_lanes_test_mocks(monkeypatch, deliver_log, route_log, reset_log)
|
||||
|
||||
ctx = {
|
||||
"lanes": [
|
||||
[
|
||||
{"preset_id": "p1", "beats": 2},
|
||||
{"preset_id": "p2", "beats": 2},
|
||||
]
|
||||
],
|
||||
"lane_states": [{"stepIdx": 0, "beatCount": 0, "done": False}],
|
||||
"num_lanes": 1,
|
||||
"sequence_doc": {
|
||||
"lanes": [
|
||||
[
|
||||
{"preset_id": "p1", "beats": 2},
|
||||
{"preset_id": "p2", "beats": 2},
|
||||
]
|
||||
],
|
||||
"group_ids": ["g1"],
|
||||
},
|
||||
"zone_doc": {"group_ids": ["g1"], "brightness": 200},
|
||||
"presets_map": {
|
||||
"p1": {
|
||||
"pattern": "solid",
|
||||
"colors": ["#FF0000"],
|
||||
"auto": True,
|
||||
},
|
||||
"p2": {
|
||||
"pattern": "solid",
|
||||
"colors": ["#00FF00"],
|
||||
"auto": True,
|
||||
},
|
||||
},
|
||||
"devices": object(),
|
||||
"groups": object(),
|
||||
"settings": {},
|
||||
"palette_colors": [],
|
||||
}
|
||||
|
||||
asyncio.run(sp._prime_all_lanes(ctx))
|
||||
|
||||
assert len(deliver_log) == 3
|
||||
step0_msg = deliver_log[0]
|
||||
select_msg = deliver_log[1]
|
||||
rest_msg = deliver_log[2]
|
||||
assert set(step0_msg["presets"]) == {"p1"}
|
||||
assert select_msg["select"] == ["p1"]
|
||||
assert set(rest_msg["presets"]) == {"p2"}
|
||||
for body in deliver_log:
|
||||
assert not ("presets" in body and "select" in body)
|
||||
assert route_log == [("clear", 0)]
|
||||
assert reset_log == ["route", "audio"]
|
||||
assert ctx.get("_sequence_primed") is True
|
||||
|
||||
|
||||
def test_prime_all_lanes_single_preset(monkeypatch):
|
||||
"""One preset in the lane: step-0 presets, select, reset (no rest phase)."""
|
||||
deliver_log: list = []
|
||||
route_log: list = []
|
||||
reset_log: list = []
|
||||
_prime_all_lanes_test_mocks(monkeypatch, deliver_log, route_log, reset_log)
|
||||
|
||||
ctx = {
|
||||
"lanes": [[{"preset_id": "p1", "beats": 2}]],
|
||||
"lane_states": [{"stepIdx": 0, "beatCount": 0, "done": False}],
|
||||
"num_lanes": 1,
|
||||
"sequence_doc": {
|
||||
"lanes": [[{"preset_id": "p1", "beats": 2}]],
|
||||
"group_ids": ["g1"],
|
||||
},
|
||||
"zone_doc": {"group_ids": ["g1"], "brightness": 200},
|
||||
"presets_map": {
|
||||
"p1": {
|
||||
"pattern": "solid",
|
||||
"colors": ["#FF0000"],
|
||||
"auto": True,
|
||||
}
|
||||
},
|
||||
"devices": object(),
|
||||
"groups": object(),
|
||||
"settings": {},
|
||||
"palette_colors": [],
|
||||
}
|
||||
|
||||
asyncio.run(sp._prime_all_lanes(ctx))
|
||||
|
||||
assert len(deliver_log) == 2
|
||||
assert set(deliver_log[0]["presets"]) == {"p1"}
|
||||
assert deliver_log[1]["select"] == ["p1"]
|
||||
assert route_log == [("clear", 0)]
|
||||
assert reset_log == ["route", "audio"]
|
||||
|
||||
|
||||
def test_prime_all_lanes_merges_same_groups(monkeypatch):
|
||||
"""Lanes sharing group ids get one step-0 broadcast, not one per lane."""
|
||||
deliver_log: list = []
|
||||
route_log: list = []
|
||||
reset_log: list = []
|
||||
_prime_all_lanes_test_mocks(monkeypatch, deliver_log, route_log, reset_log)
|
||||
|
||||
ctx = {
|
||||
"lanes": [
|
||||
[{"preset_id": "p1", "beats": 2}],
|
||||
[{"preset_id": "p2", "beats": 2}],
|
||||
],
|
||||
"lane_states": [
|
||||
{"stepIdx": 0, "beatCount": 0, "done": False},
|
||||
{"stepIdx": 0, "beatCount": 0, "done": False},
|
||||
],
|
||||
"num_lanes": 2,
|
||||
"sequence_doc": {
|
||||
"lanes": [
|
||||
[{"preset_id": "p1", "beats": 2}],
|
||||
[{"preset_id": "p2", "beats": 2}],
|
||||
],
|
||||
"lanes_group_ids": [["g1"], ["g1"]],
|
||||
},
|
||||
"zone_doc": {"group_ids": ["g1"], "brightness": 200},
|
||||
"presets_map": {
|
||||
"p1": {"pattern": "solid", "colors": ["#FF0000"], "auto": True},
|
||||
"p2": {"pattern": "solid", "colors": ["#00FF00"], "auto": True},
|
||||
},
|
||||
"devices": object(),
|
||||
"groups": object(),
|
||||
"settings": {},
|
||||
"palette_colors": [],
|
||||
}
|
||||
|
||||
asyncio.run(sp._prime_all_lanes(ctx))
|
||||
|
||||
preset_msgs = [b for b in deliver_log if "presets" in b]
|
||||
select_msgs = [b for b in deliver_log if "select" in b]
|
||||
assert len(preset_msgs) == 1
|
||||
assert set(preset_msgs[0]["presets"]) == {"p1", "p2"}
|
||||
assert preset_msgs[0]["groups"] == ["g1"]
|
||||
assert len(select_msgs) == 2
|
||||
assert route_log == [("clear", 0), ("clear", 1)]
|
||||
|
||||
Reference in New Issue
Block a user