""" Unit tests for automatic time announcement behavior. """ import datetime import importlib.util from pathlib import Path from unittest.mock import Mock def _load_time_module(): module_path = ( Path(__file__).resolve().parents[2] / "src" / "fenrirscreenreader" / "commands" / "onHeartBeat" / "76000-time.py" ) spec = importlib.util.spec_from_file_location("fenrir_time_command", module_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def _create_environment(output_manager): now = datetime.datetime.now() settings_manager = Mock() settings_manager.get_setting.side_effect = lambda section, setting: { ("time", "on_minutes"): str(now.minute).zfill(2), ("general", "date_format"): "%A, %B %d, %Y", ("general", "time_format"): "%I:%M%P", }[(section, setting)] settings_manager.get_setting_as_int.side_effect = lambda section, setting: { ("time", "delay_sec"): 0, }[(section, setting)] settings_manager.get_setting_as_bool.side_effect = lambda section, setting: { ("time", "enabled"): True, ("time", "present_date"): True, ("time", "present_time"): True, ("time", "interrupt"): False, ("time", "announce"): True, }[(section, setting)] return { "runtime": { "OutputManager": output_manager, "SettingsManager": settings_manager, } } def test_time_announcement_lock_suppresses_second_instance(tmp_path): time_module = _load_time_module() lock_path = tmp_path / "time-announcement.lock" first_output = Mock( interrupt_output=Mock(), play_sound_icon=Mock(), present_text=Mock(), ) second_output = Mock( interrupt_output=Mock(), play_sound_icon=Mock(), present_text=Mock(), ) first_command = time_module.command() second_command = time_module.command() first_command.initialize(_create_environment(first_output)) second_command.initialize(_create_environment(second_output)) first_command._get_announcement_lock_path = lambda: str(lock_path) second_command._get_announcement_lock_path = lambda: str(lock_path) first_command.last_time -= datetime.timedelta(hours=1) second_command.last_time -= datetime.timedelta(hours=1) first_command.run() second_command.run() first_output.play_sound_icon.assert_called_once_with("announce") first_output.present_text.assert_called() second_output.interrupt_output.assert_not_called() second_output.play_sound_icon.assert_not_called() second_output.present_text.assert_not_called()