Files
linux-voice-assistant/tests/unit/test_mpv_player.py
T
2026-07-18 18:22:25 +02:00

256 lines
8.5 KiB
Python

"""Unit tests for MpvMediaPlayer."""
from unittest.mock import MagicMock, patch
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def make_player(device=None):
"""Return an MpvMediaPlayer with the internal LibMpvPlayer mocked out."""
from linux_voice_assistant.player.state import PlayerState
mock_lib_player = MagicMock()
mock_lib_player.state.return_value = PlayerState.IDLE
with patch("linux_voice_assistant.mpv_player.LibMpvPlayer", return_value=mock_lib_player):
from linux_voice_assistant.mpv_player import MpvMediaPlayer
player = MpvMediaPlayer(device=device)
player._mock = mock_lib_player
return player
# ---------------------------------------------------------------------------
# Initialization
# ---------------------------------------------------------------------------
class TestInit:
def test_player_initialized_with_device(self):
with patch("linux_voice_assistant.mpv_player.LibMpvPlayer") as mock_cls:
mock_cls.return_value = MagicMock()
from linux_voice_assistant.mpv_player import MpvMediaPlayer
MpvMediaPlayer(device="hw:1,0")
mock_cls.assert_called_once_with(device="hw:1,0")
def test_player_initialized_with_none_device(self):
with patch("linux_voice_assistant.mpv_player.LibMpvPlayer") as mock_cls:
mock_cls.return_value = MagicMock()
from linux_voice_assistant.mpv_player import MpvMediaPlayer
MpvMediaPlayer(device=None)
mock_cls.assert_called_once_with(device=None)
def test_done_callback_starts_none(self):
player = make_player()
assert player._done_callback is None
def test_playlist_starts_empty(self):
player = make_player()
assert player._playlist == []
# ---------------------------------------------------------------------------
# play()
# ---------------------------------------------------------------------------
class TestPlay:
def test_play_single_url_calls_lib_player(self):
player = make_player()
player.play("http://example.com/audio.mp3")
player._mock.play.assert_called_once()
def test_play_single_url_passes_correct_url(self):
player = make_player()
player.play("http://example.com/audio.mp3")
args, kwargs = player._mock.play.call_args
assert args[0] == "http://example.com/audio.mp3"
def test_play_list_plays_first_url(self):
player = make_player()
player.play(["http://a.com/1.mp3", "http://a.com/2.mp3"])
args, kwargs = player._mock.play.call_args
assert args[0] == "http://a.com/1.mp3"
def test_play_list_stores_remaining_in_playlist(self):
player = make_player()
player.play(["http://a.com/1.mp3", "http://a.com/2.mp3", "http://a.com/3.mp3"])
assert player._playlist == ["http://a.com/2.mp3", "http://a.com/3.mp3"]
def test_play_single_url_playlist_is_empty(self):
player = make_player()
player.play("http://example.com/audio.mp3")
assert player._playlist == []
def test_play_stores_done_callback(self):
player = make_player()
cb = MagicMock()
player.play("http://example.com/audio.mp3", done_callback=cb)
assert player._done_callback is cb
def test_play_empty_list_does_not_call_lib_player(self):
player = make_player()
player.play([])
player._mock.play.assert_not_called()
def test_play_while_active_stops_previous(self):
from linux_voice_assistant.player.state import PlayerState
player = make_player()
player._done_callback = MagicMock() # simulate active playback
player._mock.state.return_value = PlayerState.PLAYING
player.play("http://example.com/new.mp3")
player._mock.stop.assert_called_once()
def test_play_while_idle_does_not_stop(self):
from linux_voice_assistant.player.state import PlayerState
player = make_player()
player._mock.state.return_value = PlayerState.IDLE
player._done_callback = None
player.play("http://example.com/new.mp3")
player._mock.stop.assert_not_called()
# ---------------------------------------------------------------------------
# _on_track_finished()
# ---------------------------------------------------------------------------
class TestOnTrackFinished:
def test_plays_next_url_when_playlist_has_items(self):
player = make_player()
player._playlist = ["http://a.com/2.mp3"]
player._on_track_finished()
args, _ = player._mock.play.call_args
assert args[0] == "http://a.com/2.mp3"
def test_invokes_done_callback_when_playlist_empty(self):
player = make_player()
cb = MagicMock()
player._done_callback = cb
player._playlist = []
player._on_track_finished()
cb.assert_called_once()
def test_clears_done_callback_after_invoking(self):
player = make_player()
player._done_callback = MagicMock()
player._playlist = []
player._on_track_finished()
assert player._done_callback is None
def test_no_error_when_done_callback_is_none(self):
player = make_player()
player._done_callback = None
player._playlist = []
player._on_track_finished() # should not raise
# ---------------------------------------------------------------------------
# pause() / resume() / stop()
# ---------------------------------------------------------------------------
class TestPauseResumeStop:
def test_pause_delegates_to_lib_player(self):
player = make_player()
player.pause()
player._mock.pause.assert_called_once()
def test_resume_delegates_to_lib_player(self):
player = make_player()
player.resume()
player._mock.resume.assert_called_once()
def test_stop_delegates_to_lib_player(self):
player = make_player()
player.stop()
player._mock.stop.assert_called_once()
def test_stop_invokes_done_callback(self):
player = make_player()
cb = MagicMock()
player._done_callback = cb
player.stop()
cb.assert_called_once()
def test_stop_clears_done_callback(self):
player = make_player()
player._done_callback = MagicMock()
player.stop()
assert player._done_callback is None
def test_stop_no_error_when_no_callback(self):
player = make_player()
player._done_callback = None
player.stop() # should not raise
# ---------------------------------------------------------------------------
# is_playing
# ---------------------------------------------------------------------------
class TestIsPlaying:
def test_true_when_playing(self):
from linux_voice_assistant.player.state import PlayerState
player = make_player()
player._mock.state.return_value = PlayerState.PLAYING
assert player.is_playing is True
def test_true_when_paused(self):
from linux_voice_assistant.player.state import PlayerState
player = make_player()
player._mock.state.return_value = PlayerState.PAUSED
assert player.is_playing is True
def test_true_when_loading(self):
from linux_voice_assistant.player.state import PlayerState
player = make_player()
player._mock.state.return_value = PlayerState.LOADING
assert player.is_playing is True
def test_false_when_idle(self):
from linux_voice_assistant.player.state import PlayerState
player = make_player()
player._mock.state.return_value = PlayerState.IDLE
assert player.is_playing is False
# ---------------------------------------------------------------------------
# set_volume() / duck() / unduck()
# ---------------------------------------------------------------------------
class TestVolume:
def test_set_volume_delegates_to_lib_player(self):
player = make_player()
player.set_volume(75.0)
player._mock.set_volume.assert_called_once_with(75.0)
def test_duck_delegates_to_lib_player(self):
player = make_player()
player.duck(factor=0.3)
player._mock.duck.assert_called_once_with(0.3)
def test_duck_default_factor(self):
player = make_player()
player.duck()
player._mock.duck.assert_called_once_with(0.5)
def test_unduck_delegates_to_lib_player(self):
player = make_player()
player.unduck()
player._mock.unduck.assert_called_once()