diff --git a/src/cthulhu/sound.py b/src/cthulhu/sound.py index ed3bb6b..e04d41a 100644 --- a/src/cthulhu/sound.py +++ b/src/cthulhu/sound.py @@ -128,6 +128,10 @@ class Player: def stop(self, _element: Any = None) -> None: """Stops current sound playback.""" + with self._workerLock: + if self._workerProcess is None or self._workerProcess.poll() is not None: + return + self._sendWorkerCommand({"action": "stop"}, waitForResponse=False) def shutdown(self) -> None: diff --git a/src/cthulhu/speech.py b/src/cthulhu/speech.py index 7d18353..604aeeb 100644 --- a/src/cthulhu/speech.py +++ b/src/cthulhu/speech.py @@ -694,6 +694,8 @@ def stop() -> None: _speechserver.stop() # type: ignore if _echoSpeechserver and _echoSpeechserver != _speechserver: _echoSpeechserver.stop() # type: ignore + player = sound.getPlayer() + player.stop() def shutdown() -> None: debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Shutting down', True) diff --git a/tests/test_presentation_interrupt_sound_regressions.py b/tests/test_presentation_interrupt_sound_regressions.py new file mode 100644 index 0000000..6a44e2c --- /dev/null +++ b/tests/test_presentation_interrupt_sound_regressions.py @@ -0,0 +1,30 @@ +import sys +import unittest +from pathlib import Path +from unittest import mock + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) + +from cthulhu import speech + + +class PresentationInterruptSoundRegressionTests(unittest.TestCase): + def test_speech_stop_also_stops_sound_playback(self): + speechServer = mock.Mock() + echoServer = mock.Mock() + soundPlayer = mock.Mock() + + with ( + mock.patch.object(speech, "_speechserver", speechServer), + mock.patch.object(speech, "_echoSpeechserver", echoServer), + mock.patch.object(speech.sound, "getPlayer", return_value=soundPlayer), + ): + speech.stop() + + speechServer.stop.assert_called_once_with() + echoServer.stop.assert_called_once_with() + soundPlayer.stop.assert_called_once_with() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_sound_recovery.py b/tests/test_sound_recovery.py index d6d9560..e69a9e4 100644 --- a/tests/test_sound_recovery.py +++ b/tests/test_sound_recovery.py @@ -38,8 +38,11 @@ from cthulhu import sound_sink class _FakeProcess: + def __init__(self, returnCode=None): + self.returnCode = returnCode + def poll(self): - return None + return self.returnCode class SoundSinkTests(unittest.TestCase): @@ -107,6 +110,23 @@ class PlayerRecoveryTests(unittest.TestCase): self.assertEqual(stopReasons, ["lost audio sink"]) self.assertEqual(startedSinks, [settings.SOUND_SINK_AUTO]) + def test_stop_does_not_start_worker_when_worker_is_not_running(self): + player = sound.Player() + + with mock.patch.object(player, "_sendWorkerCommand") as sendCommand: + player.stop() + + sendCommand.assert_not_called() + + def test_stop_sends_command_to_running_worker(self): + player = sound.Player() + player._workerProcess = _FakeProcess() + + with mock.patch.object(player, "_sendWorkerCommand") as sendCommand: + player.stop() + + sendCommand.assert_called_once_with({"action": "stop"}, waitForResponse=False) + if __name__ == "__main__": unittest.main()