"""Setup the squeezebox tests.""" from collections.abc import Generator from unittest.mock import AsyncMock, MagicMock, patch import pytest from homeassistant.components.media_player import MediaType from homeassistant.components.squeezebox import const from homeassistant.components.squeezebox.browse_media import ( MEDIA_TYPE_TO_SQUEEZEBOX, SQUEEZEBOX_ID_BY_TYPE, ) from homeassistant.components.squeezebox.const import ( STATUS_QUERY_LIBRARYNAME, STATUS_QUERY_MAC, STATUS_QUERY_UUID, STATUS_QUERY_VERSION, STATUS_SENSOR_INFO_TOTAL_ALBUMS, STATUS_SENSOR_INFO_TOTAL_ARTISTS, STATUS_SENSOR_INFO_TOTAL_DURATION, STATUS_SENSOR_INFO_TOTAL_GENRES, STATUS_SENSOR_INFO_TOTAL_SONGS, STATUS_SENSOR_LASTSCAN, STATUS_SENSOR_OTHER_PLAYER_COUNT, STATUS_SENSOR_PLAYER_COUNT, STATUS_SENSOR_RESCAN, ) from homeassistant.const import CONF_HOST, CONF_PORT, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import format_mac # from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry TEST_HOST = "1.2.3.4" TEST_PORT = "9000" TEST_USE_HTTPS = False SERVER_UUIDS = [ "12345678-1234-1234-1234-123456789012", "87654321-4321-4321-4321-210987654321", ] TEST_MAC = ["aa:bb:cc:dd:ee:ff", "ff:ee:dd:cc:bb:aa"] TEST_PLAYER_NAME = "Test Player" TEST_SERVER_NAME = "Test Server" FAKE_VALID_ITEM_ID = "1234" FAKE_INVALID_ITEM_ID = "4321" FAKE_IP = "42.42.42.42" FAKE_MAC = "deadbeefdead" FAKE_UUID = "deadbeefdeadbeefbeefdeafbeef42" FAKE_PORT = 9000 FAKE_VERSION = "42.0" FAKE_QUERY_RESPONSE = { STATUS_QUERY_UUID: FAKE_UUID, STATUS_QUERY_MAC: FAKE_MAC, STATUS_QUERY_VERSION: FAKE_VERSION, STATUS_SENSOR_RESCAN: 1, STATUS_SENSOR_LASTSCAN: 0, STATUS_QUERY_LIBRARYNAME: "FakeLib", STATUS_SENSOR_INFO_TOTAL_ALBUMS: 4, STATUS_SENSOR_INFO_TOTAL_ARTISTS: 2, STATUS_SENSOR_INFO_TOTAL_DURATION: 500, STATUS_SENSOR_INFO_TOTAL_GENRES: 1, STATUS_SENSOR_INFO_TOTAL_SONGS: 42, STATUS_SENSOR_PLAYER_COUNT: 10, STATUS_SENSOR_OTHER_PLAYER_COUNT: 0, "players_loop": [ { "isplaying": 0, "name": "SqueezeLite-HA-Addon", "seq_no": 0, "modelname": "SqueezeLite-HA-Addon", "playerindex": "status", "model": "squeezelite", "uuid": FAKE_UUID, "canpoweroff": 1, "ip": "192.168.78.86:57700", "displaytype": "none", "playerid": "f9:23:cd:37:c5:ff", "power": 0, "isplayer": 1, "connected": 1, "firmware": "v2.0.0-1488", } ], "count": 1, } @pytest.fixture def mock_setup_entry() -> Generator[AsyncMock]: """Override async_setup_entry.""" with patch( "homeassistant.components.squeezebox.async_setup_entry", return_value=True ) as mock_setup_entry: yield mock_setup_entry @pytest.fixture def config_entry(hass: HomeAssistant) -> MockConfigEntry: """Add the squeezebox mock config entry to hass.""" config_entry = MockConfigEntry( domain=const.DOMAIN, unique_id=SERVER_UUIDS[0], data={ CONF_HOST: TEST_HOST, CONF_PORT: TEST_PORT, const.CONF_HTTPS: TEST_USE_HTTPS, }, ) config_entry.add_to_hass(hass) return config_entry async def mock_async_browse( media_type: MediaType, limit: int, browse_id: tuple | None = None ) -> dict | None: """Mock the async_browse method of pysqueezebox.Player.""" child_types = { "favorites": "favorites", "new music": "album", "albums": "album", "album": "track", "genres": "genre", "genre": "album", "artists": "artist", "artist": "album", "titles": "title", "title": "title", "playlists": "playlist", "playlist": "title", } fake_items = [ { "title": "Fake Item 1", "id": FAKE_VALID_ITEM_ID, "hasitems": False, "isaudio": True, "item_type": child_types[media_type], "artwork_track_id": "b35bb9e9", "url": "file:///var/lib/squeezeboxserver/music/track_1.mp3", }, { "title": "Fake Item 2", "id": FAKE_VALID_ITEM_ID + "_2", "hasitems": media_type == "favorites", "isaudio": True, "item_type": child_types[media_type], "image_url": "http://lms.internal:9000/html/images/favorites.png", "url": "file:///var/lib/squeezeboxserver/music/track_2.mp3", }, { "title": "Fake Item 3", "id": FAKE_VALID_ITEM_ID + "_3", "hasitems": media_type == "favorites", "isaudio": True, "album_id": FAKE_VALID_ITEM_ID if media_type == "favorites" else None, "url": "file:///var/lib/squeezeboxserver/music/track_3.mp3", }, ] if browse_id: search_type, search_id = browse_id if search_id: if search_type == "playlist_id": return ( { "title": "Fake Item 1", "items": fake_items, } if search_id == FAKE_VALID_ITEM_ID else None ) if search_type in SQUEEZEBOX_ID_BY_TYPE.values(): for item in fake_items: if item["id"] == search_id: return { "title": item["title"], "items": [item], } return None if search_type in SQUEEZEBOX_ID_BY_TYPE.values(): return { "title": search_type, "items": fake_items, } return None if media_type in MEDIA_TYPE_TO_SQUEEZEBOX.values(): return { "title": media_type, "items": fake_items, } return None @pytest.fixture def player() -> MagicMock: """Return a mock player.""" return mock_pysqueezebox_player() @pytest.fixture def player_factory() -> MagicMock: """Return a factory for creating mock players.""" return mock_pysqueezebox_player def mock_pysqueezebox_player(uuid: str) -> MagicMock: """Mock a Lyrion Media Server player.""" with patch( "homeassistant.components.squeezebox.Player", autospec=True ) as mock_player: mock_player.async_browse = AsyncMock(side_effect=mock_async_browse) mock_player.generate_image_url_from_track_id = MagicMock( return_value="http://lms.internal:9000/html/images/favorites.png" ) mock_player.name = TEST_PLAYER_NAME mock_player.player_id = uuid mock_player.mode = "stop" mock_player.playlist = None mock_player.album = None mock_player.artist = None mock_player.remote_title = None mock_player.title = None mock_player.image_url = None mock_player.model = "SqueezeLite" return mock_player @pytest.fixture def lms_factory(player_factory: MagicMock) -> MagicMock: """Return a factory for creating mock Lyrion Media Servers with arbitrary number of players.""" return lambda player_count, uuid: mock_pysqueezebox_server( player_factory, player_count, uuid ) @pytest.fixture def lms(player_factory: MagicMock) -> MagicMock: """Mock a Lyrion Media Server with one mock player attached.""" return mock_pysqueezebox_server(player_factory, 1, uuid=TEST_MAC[0]) def mock_pysqueezebox_server( player_factory: MagicMock, player_count: int, uuid: str ) -> MagicMock: """Create a mock Lyrion Media Server with the given number of mock players attached.""" with patch("homeassistant.components.squeezebox.Server", autospec=True) as mock_lms: players = [player_factory(TEST_MAC[index]) for index in range(player_count)] mock_lms.async_get_players = AsyncMock(return_value=players) mock_lms.uuid = uuid mock_lms.name = TEST_SERVER_NAME mock_lms.async_query = AsyncMock(return_value={"uuid": format_mac(uuid)}) mock_lms.async_status = AsyncMock(return_value={"uuid": format_mac(uuid)}) return mock_lms async def configure_squeezebox_media_player_platform( hass: HomeAssistant, config_entry: MockConfigEntry, lms: MagicMock, ) -> None: """Configure a squeezebox config entry with appropriate mocks for media_player.""" with ( patch("homeassistant.components.squeezebox.PLATFORMS", [Platform.MEDIA_PLAYER]), patch("homeassistant.components.squeezebox.Server", return_value=lms), ): await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done(wait_background_tasks=True) @pytest.fixture async def configured_player( hass: HomeAssistant, config_entry: MockConfigEntry, lms: MagicMock ) -> MagicMock: """Fixture mocking calls to pysqueezebox Player from a configured squeezebox.""" await configure_squeezebox_media_player_platform(hass, config_entry, lms) return (await lms.async_get_players())[0] @pytest.fixture async def configured_players( hass: HomeAssistant, config_entry: MockConfigEntry, lms_factory: MagicMock ) -> list[MagicMock]: """Fixture mocking calls to two pysqueezebox Players from a configured squeezebox.""" lms = lms_factory(2, uuid=SERVER_UUIDS[0]) await configure_squeezebox_media_player_platform(hass, config_entry, lms) return await lms.async_get_players()