1
0

test_board_led_controller.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """
  2. Unit tests for the board (firmware) LED controller — focused on the 'ball'
  3. tracker: param clamping, key mapping, and status readback.
  4. """
  5. from unittest.mock import patch
  6. from modules.led.board_led_controller import BoardLEDController
  7. from modules.core.state import state
  8. class FakeConn:
  9. """Records /sand_led calls and serves a scriptable settings map."""
  10. def __init__(self, settings=None):
  11. self.led_calls = []
  12. self._settings = settings or {}
  13. def is_connected(self):
  14. return True
  15. def set_led(self, **keys):
  16. self.led_calls.append(keys)
  17. return "ok"
  18. def get_settings(self):
  19. return self._settings
  20. def _controller(conn):
  21. # The controller reaches hardware through state.conn (imported lazily).
  22. return BoardLEDController(), patch.object(state, "conn", conn)
  23. class TestSetBall:
  24. def test_maps_and_forwards_keys(self):
  25. conn = FakeConn()
  26. c, ctx = _controller(conn)
  27. with ctx:
  28. c.set_ball(fgbright=200, bgbright=40, size=8, align=120,
  29. direction="ccw", bg="plasma", color="#FF0040", color2="000028")
  30. assert len(conn.led_calls) == 1
  31. sent = conn.led_calls[0]
  32. assert sent == {
  33. "fgbright": 200, "bgbright": 40, "size": 8, "align": 120,
  34. "direction": "ccw", "bg": "plasma", "color": "FF0040", "color2": "000028",
  35. }
  36. def test_clamps_out_of_range(self):
  37. conn = FakeConn()
  38. c, ctx = _controller(conn)
  39. with ctx:
  40. c.set_ball(size=999, align=400, fgbright=-5, bgbright=500)
  41. sent = conn.led_calls[0]
  42. assert sent["size"] == 200 # 1..200
  43. assert sent["align"] == 359 # 0..359
  44. assert sent["fgbright"] == 0 # 0..255
  45. assert sent["bgbright"] == 255 # 0..255
  46. def test_ignores_invalid_direction(self):
  47. conn = FakeConn()
  48. c, ctx = _controller(conn)
  49. with ctx:
  50. c.set_ball(direction="sideways", size=5)
  51. sent = conn.led_calls[0]
  52. assert "direction" not in sent
  53. assert sent["size"] == 5
  54. def test_empty_params_no_call(self):
  55. conn = FakeConn()
  56. c, ctx = _controller(conn)
  57. with ctx:
  58. result = c.set_ball()
  59. assert conn.led_calls == []
  60. assert result == {"connected": True}
  61. class TestStatusBallReadback:
  62. def test_check_status_exposes_ball(self):
  63. conn = FakeConn(settings={
  64. "LED/Effect": "ball",
  65. "LED/Brightness": "128",
  66. "LED/BallBright": "111",
  67. "LED/BallBgBright": "222",
  68. "LED/BallSize": "17",
  69. "LED/BallBg": "fire",
  70. "LED/Direction": "ccw",
  71. "LED/Align": "88",
  72. })
  73. c, ctx = _controller(conn)
  74. with ctx:
  75. status = c.check_status()
  76. assert status["connected"] is True
  77. assert status["ball"] == {
  78. "fgbright": 111, "bgbright": 222, "size": 17,
  79. "bg": "fire", "direction": "ccw", "align": 88,
  80. }