1 parent
4a0695ab57
commit
9b2a8b4b30
24 files changed
+3002
-24
No files matched your search
@@ -0,0 +1,255 @@
|
||||
"""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()
|
||||
Reference in new issue
Block a user