The base AlarmStyle contract carried music_file, forcing SimpleStyle to accept a parameter it never uses. Move style-specific config to the subclass: the base __init__ takes only set_led (**kwargs swallows the rest); each style declares the keyword args it actually uses (BlinkStyle: music_file; SimpleStyle: none). The runner passes config as keyword args and each style keeps only what it needs, so the base contract never grows as styles are added — a future 'talk' style adds talk_file to its own signature, not to the base. README plugin contract updated.
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
from styles.simple import SimpleStyle
|
|
|
|
|
|
def _make_style():
|
|
calls = []
|
|
return SimpleStyle(lambda on: calls.append(on)), calls
|
|
|
|
|
|
def test_simple_ignores_unrelated_config():
|
|
# SimpleStyle must swallow style-specific kwargs it doesn't use (e.g.
|
|
# music_file belongs to blink), so the runner can pass config uniformly.
|
|
style, _ = _make_style()
|
|
style = SimpleStyle(lambda on: None, music_file="ignored.mp3")
|
|
with patch("styles.simple.pygame", MagicMock()):
|
|
style.start()
|
|
assert style.update(0.0, False) is True
|
|
|
|
|
|
def test_simple_keeps_ringing_when_not_pressed():
|
|
style, _ = _make_style()
|
|
with patch("styles.simple.pygame", MagicMock()):
|
|
style.start()
|
|
assert style.update(0.0, False) is True
|
|
|
|
|
|
def test_simple_stops_on_first_press():
|
|
style, _ = _make_style()
|
|
with patch("styles.simple.pygame", MagicMock()):
|
|
style.start()
|
|
assert style.update(0.0, True) is False # press -> stop
|
|
|
|
|
|
def test_simple_led_solid_on():
|
|
style, calls = _make_style()
|
|
with patch("styles.simple.pygame", MagicMock()):
|
|
style.start()
|
|
style.update(0.0, False)
|
|
assert calls == [True, True] # start sets LED on; update keeps it on
|
|
|
|
|
|
def test_simple_beeps_on_interval():
|
|
style, _ = _make_style()
|
|
with patch("styles.simple.pygame", MagicMock()) as pg:
|
|
style.start()
|
|
style.update(100.0, False) # first tick -> beep
|
|
assert pg.mixer.Sound.return_value.play.called
|
|
pg.mixer.Sound.return_value.play.reset_mock()
|
|
|
|
style.update(100.4, False) # before interval -> no beep
|
|
assert not pg.mixer.Sound.return_value.play.called
|
|
|
|
style.update(100.5, False) # at interval -> beep
|
|
assert pg.mixer.Sound.return_value.play.called
|
|
|
|
|
|
def test_simple_never_plays_music():
|
|
style, _ = _make_style()
|
|
with patch("styles.simple.pygame", MagicMock()) as pg:
|
|
style.start()
|
|
style.update(0.0, False)
|
|
style.update(0.0, True) # press -> stop
|
|
assert not pg.mixer.music.play.called
|
|
assert not pg.mixer.music.load.called
|