Spaces:
Running
Running
Download tests/test_loop.py from idormoy/focus_buddy: direct link, hf CLI and curl.
- Browser
- Download file 5.54 kB
-
https://huggingface.co/spaces/idormoy/focus_buddy/resolve/main/tests/test_loop.py
- Command line
-
hf download hf://spaces/idormoy/focus_buddy/tests/test_loop.py
-
curl -L -o test_loop.py https://huggingface.co/spaces/idormoy/focus_buddy/resolve/main/tests/test_loop.py
5.54 kB
| import threading | |
| from conftest import FakeBody, FakeNudgeWriter, FakeSpeech, FakeVision | |
| from focus_buddy.loop import FocusLoop | |
| from focus_buddy.observations import Habit, Observation | |
| WORKING = Observation() | |
| PHONE = Observation(looking_at_phone=True, looking_at_screen=False) | |
| NAILS = Observation(biting_nails=True) | |
| def build(settings, observations, frames=10, nudge_writer=None): | |
| body, speech = FakeBody(frames=frames), FakeSpeech() | |
| loop = FocusLoop( | |
| body=body, | |
| vision=FakeVision(observations), | |
| speech=speech, | |
| settings=settings, | |
| nudge_writer=nudge_writer, | |
| ) | |
| return loop, body, speech | |
| class TestSpeaking: | |
| def test_stays_quiet_while_you_work(self, settings): | |
| loop, _, speech = build(settings, [WORKING]) | |
| for _ in range(5): | |
| loop.tick() | |
| assert speech.spoken == [] | |
| def test_nudges_on_a_detected_habit(self, settings): | |
| loop, body, speech = build(settings, [PHONE]) | |
| loop.tick() | |
| assert len(speech.spoken) == 1 | |
| assert "phone" in speech.spoken[0].lower() | |
| assert len(body.played) == 1 | |
| def test_cooldown_suppresses_a_second_nudge(self, settings): | |
| settings.nudge_cooldown_s = 3600.0 | |
| loop, _, speech = build(settings, [PHONE]) | |
| loop.tick() | |
| loop.tick() | |
| assert len(speech.spoken) == 1 | |
| def test_no_frame_means_no_work(self, settings): | |
| loop, _, speech = build(settings, [PHONE], frames=0) | |
| assert loop.tick() is None | |
| assert speech.spoken == [] | |
| class TestMemoryIntegration: | |
| def test_counts_the_episode_it_nudged_about(self, settings): | |
| loop, _, _ = build(settings, [NAILS]) | |
| loop.tick() | |
| assert loop.memory.counters[Habit.BITING_NAILS] == 1 | |
| def test_summary_is_spoken_when_due(self, settings): | |
| settings.summary_every_s = 0.0 | |
| loop, _, speech = build(settings, [WORKING]) | |
| loop.tick() | |
| assert any("You've been working" in line for line in speech.spoken) | |
| class TestLLMNudges: | |
| def test_uses_a_valid_model_nudge(self, settings): | |
| settings.use_llm_nudges = True | |
| writer = FakeNudgeWriter("Hey, maybe set the phone aside now.") | |
| loop, _, speech = build(settings, [PHONE], nudge_writer=writer) | |
| loop.tick() | |
| assert speech.spoken == ["Hey, maybe set the phone aside now."] | |
| assert writer.calls == ["phone"] | |
| def test_falls_back_when_the_model_talks_about_the_wrong_habit(self, settings): | |
| settings.use_llm_nudges = True | |
| writer = FakeNudgeWriter("Hey, you are touching your face again.") | |
| loop, _, speech = build(settings, [PHONE], nudge_writer=writer) | |
| loop.tick() | |
| # Rejected, so a template about the phone is spoken instead. | |
| assert "phone" in speech.spoken[0].lower() | |
| assert speech.spoken[0] != writer.reply | |
| def test_falls_back_when_the_model_raises(self, settings): | |
| settings.use_llm_nudges = True | |
| writer = FakeNudgeWriter(explode=True) | |
| loop, _, speech = build(settings, [PHONE], nudge_writer=writer) | |
| loop.tick() | |
| assert len(speech.spoken) == 1 | |
| def test_templates_are_used_when_llm_nudges_are_off(self, settings): | |
| writer = FakeNudgeWriter("Hey, maybe set the phone aside now.") | |
| loop, _, _ = build(settings, [PHONE], nudge_writer=writer) | |
| loop.tick() | |
| assert writer.calls == [] | |
| class TestLifecycle: | |
| def test_run_stops_when_the_event_is_set(self, settings): | |
| loop, body, _ = build(settings, [WORKING], frames=1000) | |
| stop = threading.Event() | |
| original_tick = loop.tick | |
| ticks = [] | |
| def counting_tick(): | |
| ticks.append(1) | |
| if len(ticks) >= 3: | |
| stop.set() | |
| return original_tick() | |
| loop.tick = counting_tick | |
| loop.run(stop) | |
| assert len(ticks) == 3 | |
| def test_run_closes_everything_it_owns(self, settings): | |
| writer = FakeNudgeWriter() | |
| loop, body, _ = build(settings, [WORKING], nudge_writer=writer) | |
| stop = threading.Event() | |
| stop.set() | |
| loop.run(stop) | |
| assert body.closed and writer.closed | |
| class TestBodyOwnership: | |
| """A settings reload rebuilds the backends but keeps the robot connection.""" | |
| def _loop(self, owns_body): | |
| from focus_buddy.config import Settings, SpeechEngine | |
| from focus_buddy.loop import FocusLoop | |
| body, vision, speech = FakeBody(), FakeVision([Observation()]), FakeSpeech() | |
| loop = ( | |
| FocusLoop( | |
| body=body, | |
| vision=vision, | |
| speech=speech, | |
| settings=Settings(speech=SpeechEngine.NONE), | |
| ) | |
| if owns_body | |
| else FocusLoop( | |
| body=body, | |
| vision=vision, | |
| speech=speech, | |
| settings=Settings(speech=SpeechEngine.NONE), | |
| owns_body=False, | |
| ) | |
| ) | |
| return loop, body, vision | |
| def test_by_default_the_loop_closes_the_body(self): | |
| loop, body, vision = self._loop(True) | |
| loop.close() | |
| assert body.closed is True and vision.closed is True | |
| def test_a_borrowed_body_is_left_open(self): | |
| """Reconnecting to the robot cost ~20s and a wobble toggle for nothing.""" | |
| loop, body, vision = self._loop(False) | |
| loop.close() | |
| assert body.closed is False | |
| # The things that do depend on settings are still torn down. | |
| assert vision.closed is True | |