Web frontend
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
"""Tests for the shared player behaviour, exercised through FakePlayer.
|
||||
|
||||
VlcPlayer adds only the libVLC bindings on top of PlayerBase; it needs a real audio
|
||||
device and is covered by the on-device checklist, not here.
|
||||
device and is covered by the on-device checklist, not here - except for how it works
|
||||
out which track is playing, which is stubbed out at the bottom of this file because
|
||||
getting it wrong is invisible until a front-end displays it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -13,7 +15,7 @@ import pytest
|
||||
|
||||
from musicmouse.bus import EventBus
|
||||
from musicmouse.clock import FakeClock
|
||||
from musicmouse.devices.player import Player, VlcPlayer
|
||||
from musicmouse.devices.player import Player, PlayerBase, VlcPlayer
|
||||
from musicmouse.events import (
|
||||
Event,
|
||||
PlaybackChanged,
|
||||
@@ -295,3 +297,69 @@ def test_fake_player_satisfies_the_player_protocol(player: FakePlayer) -> None:
|
||||
def _vlc_player_satisfies_the_player_protocol(real: VlcPlayer) -> Player:
|
||||
"""Checked by mypy, not at runtime: VlcPlayer needs libVLC to instantiate."""
|
||||
return real
|
||||
|
||||
|
||||
# --------------------------------------------------------- track index without libVLC
|
||||
|
||||
|
||||
class _FakeMedia:
|
||||
def __init__(self, mrl: str) -> None:
|
||||
self._mrl = mrl
|
||||
|
||||
def get_mrl(self) -> str:
|
||||
return self._mrl
|
||||
|
||||
|
||||
class _FakeMediaPlayer:
|
||||
"""Just enough of libVLC's media player to exercise index syncing."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.media: _FakeMedia | None = None
|
||||
|
||||
def get_media(self) -> _FakeMedia | None:
|
||||
return self.media
|
||||
|
||||
|
||||
def _vlc_like(bus: EventBus, playlist: Playlist) -> VlcPlayer:
|
||||
"""A VlcPlayer with its libVLC parts stubbed out, without calling __init__."""
|
||||
player = object.__new__(VlcPlayer)
|
||||
PlayerBase.__init__(player, bus)
|
||||
player._media_player = _FakeMediaPlayer() # type: ignore[assignment]
|
||||
player._playlist = playlist
|
||||
player._mrl_to_index = {f"file://{track.path}": i for i, track in enumerate(playlist.tracks)}
|
||||
return player
|
||||
|
||||
|
||||
async def test_the_track_index_is_read_back_off_the_player(bus: EventBus) -> None:
|
||||
"""Not taken from the event payload.
|
||||
|
||||
``MediaListPlayerNextItemSet`` carries a bare int rather than a Media on some
|
||||
libVLC builds, and trusting it left the index pinned at zero: the UI showed track 1
|
||||
of an album that was audibly on track 3.
|
||||
"""
|
||||
playlist = Playlist("test", tuple(Track(Path(f"/music/{i}.mp3")) for i in range(3)))
|
||||
player = _vlc_like(bus, playlist)
|
||||
events: list[Event] = []
|
||||
bus.subscribe(TrackChanged, events.append)
|
||||
|
||||
player._media_player.media = _FakeMedia("file:///music/2.mp3") # type: ignore[attr-defined]
|
||||
player._on_next_item(object())
|
||||
await bus.drain()
|
||||
|
||||
assert player.track_index == 2
|
||||
assert player.current_track is not None
|
||||
assert player.current_track.path.name == "2.mp3"
|
||||
assert len(events) == 1
|
||||
|
||||
|
||||
async def test_an_unknown_media_leaves_the_index_alone(bus: EventBus) -> None:
|
||||
playlist = Playlist("test", tuple(Track(Path(f"/music/{i}.mp3")) for i in range(3)))
|
||||
player = _vlc_like(bus, playlist)
|
||||
|
||||
player._media_player.media = None # type: ignore[attr-defined]
|
||||
player._on_next_item(object())
|
||||
player._media_player.media = _FakeMedia("file:///elsewhere/x.mp3") # type: ignore[attr-defined]
|
||||
player._on_next_item(object())
|
||||
await bus.drain()
|
||||
|
||||
assert player.track_index == 0
|
||||
|
||||
Reference in New Issue
Block a user