Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
Marcel van der Veldt
9b25bf8e09 cleanup 2024-10-31 07:57:55 +01:00
Marcel van der Veldt
588661c15f add a few more tests for actions 2024-10-31 01:03:35 +01:00
Marcel van der Veldt
99e9cd507a add base tests 2024-10-31 00:49:25 +01:00
Marcel van der Veldt
62caeb1000 Implement base tests 2024-10-31 00:04:57 +01:00
Marcel van der Veldt
fac22c3171 Add test foundation for Music Assistant integration 2024-10-30 21:58:01 +01:00
Marcel van der Veldt
d2ee3ab957 Add client fixture 2024-10-30 19:54:19 +01:00
7 changed files with 993 additions and 3 deletions

View file

@ -152,6 +152,8 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
self._attr_supported_features = SUPPORTED_FEATURES
if PlayerFeature.SYNC in self.player.supported_features:
self._attr_supported_features |= MediaPlayerEntityFeature.GROUPING
if PlayerFeature.VOLUME_MUTE in self.player.supported_features:
self._attr_supported_features |= MediaPlayerEntityFeature.VOLUME_MUTE
self._attr_device_class = MediaPlayerDeviceClass.SPEAKER
self._prev_time: float = 0
@ -219,7 +221,9 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
)
)
]
self._attr_group_members = group_members_entity_ids
# NOTE: we sort the group_members for now,
# until the MA API returns them sorted (group_childs is now a set)
self._attr_group_members = sorted(group_members_entity_ids)
self._attr_volume_level = (
player.volume_level / 100 if player.volume_level is not None else None
)

View file

@ -0,0 +1,86 @@
"""Provide common test tools."""
from __future__ import annotations
import json
from typing import Any
from unittest.mock import MagicMock
from music_assistant_models.enums import EventType
from music_assistant_models.player import Player
from music_assistant_models.player_queue import PlayerQueue
from syrupy import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, load_fixture
def load_and_parse_fixture(fixture: str) -> dict[str, Any]:
"""Load and parse a fixture."""
return json.loads(load_fixture(f"music_assistant/{fixture}.json"))
async def setup_integration_from_fixtures(
hass: HomeAssistant,
music_assistant_client: MagicMock,
) -> None:
"""Set up MusicAssistant integration with fixture data."""
players = create_players_from_fixture()
music_assistant_client.players._players = {x.player_id: x for x in players}
player_queues = create_player_queues_from_fixture()
music_assistant_client.player_queues._queues = {
x.queue_id: x for x in player_queues
}
config_entry = MockConfigEntry(
domain="music_assistant", data={"url": "http://mock-music_assistant-server-url"}
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
def create_players_from_fixture() -> list[Player]:
"""Create MA Players from fixture."""
fixture_data = load_and_parse_fixture("players")
return [Player.from_dict(player_data) for player_data in fixture_data]
def create_player_queues_from_fixture() -> list[Player]:
"""Create MA PlayerQueues from fixture."""
fixture_data = load_and_parse_fixture("player_queues")
return [
PlayerQueue.from_dict(player_queue_data) for player_queue_data in fixture_data
]
async def trigger_subscription_callback(
hass: HomeAssistant,
client: MagicMock,
event: EventType = EventType.PLAYER_UPDATED,
data: Any = None,
) -> None:
"""Trigger a subscription callback."""
# trigger callback on all subscribers
for sub in client.subscribe_events.call_args_list:
callback = sub.kwargs["callback"]
event_filter = sub.kwargs.get("event_filter")
if event_filter in (None, event):
callback(event, data)
await hass.async_block_till_done()
def snapshot_music_assistant_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
platform: Platform,
) -> None:
"""Snapshot MusicAssistant entities."""
entities = hass.states.async_all(platform)
for entity_state in entities:
entity_entry = entity_registry.async_get(entity_state.entity_id)
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
assert entity_state == snapshot(name=f"{entity_entry.entity_id}-state")

View file

@ -1,16 +1,23 @@
"""Music Assistant test fixtures."""
from collections.abc import Generator
from unittest.mock import patch
import asyncio
from collections.abc import AsyncGenerator, Generator
from unittest.mock import MagicMock, patch
from music_assistant_client.music import Music
from music_assistant_client.player_queues import PlayerQueues
from music_assistant_client.players import Players
from music_assistant_models.api import ServerInfoMessage
import pytest
from homeassistant.components.music_assistant.config_flow import CONF_URL
from homeassistant.components.music_assistant.const import DOMAIN
from homeassistant.core import HomeAssistant
from tests.common import AsyncMock, MockConfigEntry, load_fixture
MOCK_SERVER_ID = "1234"
@pytest.fixture
def mock_get_server_info() -> Generator[AsyncMock]:
@ -24,6 +31,60 @@ def mock_get_server_info() -> Generator[AsyncMock]:
yield mock_get_server_info
@pytest.fixture(name="music_assistant_client")
async def music_assistant_client_fixture() -> AsyncGenerator[MagicMock]:
"""Fixture for a Music Assistant client."""
with patch(
"homeassistant.components.music_assistant.MusicAssistantClient", autospec=True
) as client_class:
client = client_class.return_value
async def connect() -> None:
"""Mock connect."""
await asyncio.sleep(0)
async def listen(init_ready: asyncio.Event | None) -> None:
"""Mock listen."""
if init_ready is not None:
init_ready.set()
listen_block = asyncio.Event()
await listen_block.wait()
pytest.fail("Listen was not cancelled!")
client.connect = AsyncMock(side_effect=connect)
client.start_listening = AsyncMock(side_effect=listen)
client.server_info = ServerInfoMessage(
server_id=MOCK_SERVER_ID,
server_version="0.0.0",
schema_version=1,
min_supported_schema_version=1,
base_url="http://localhost:8095",
homeassistant_addon=False,
onboard_done=True,
)
client.connection = MagicMock()
client.connection.connected = True
client.players = Players(client)
client.player_queues = PlayerQueues(client)
client.music = Music(client)
client.server_url = client.server_info.base_url
client.get_media_item_image_url = MagicMock(return_value=None)
yield client
@pytest.fixture(name="integration")
async def integration_fixture(
hass: HomeAssistant, music_assistant_client: MagicMock
) -> MockConfigEntry:
"""Set up the Music Assistant integration."""
entry = MockConfigEntry(domain=DOMAIN, data={CONF_URL: "http://localhost:8095"})
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Mock a config entry."""

View file

@ -0,0 +1,326 @@
[
{
"queue_id": "00:00:00:00:00:01",
"active": false,
"display_name": "Test Player 1",
"available": true,
"items": 0,
"shuffle_enabled": false,
"repeat_mode": "off",
"dont_stop_the_music_enabled": false,
"current_index": null,
"index_in_buffer": null,
"elapsed_time": 0,
"elapsed_time_last_updated": 1730118302.163217,
"state": "idle",
"current_item": null,
"next_item": null,
"radio_source": [],
"flow_mode": false,
"resume_pos": 0
},
{
"queue_id": "00:00:00:00:00:02",
"active": false,
"display_name": "My Super Test Player 2",
"available": true,
"items": 0,
"shuffle_enabled": false,
"repeat_mode": "off",
"dont_stop_the_music_enabled": false,
"current_index": null,
"index_in_buffer": null,
"elapsed_time": 0,
"elapsed_time_last_updated": 0,
"state": "idle",
"current_item": null,
"next_item": null,
"radio_source": [],
"flow_mode": false,
"resume_pos": 0
},
{
"queue_id": "test_group_player_1",
"active": true,
"display_name": "Test Group Player 1",
"available": true,
"items": 1094,
"shuffle_enabled": true,
"repeat_mode": "all",
"dont_stop_the_music_enabled": true,
"current_index": 26,
"index_in_buffer": 26,
"elapsed_time": 232.08810877799988,
"elapsed_time_last_updated": 1730313109.5659513,
"state": "playing",
"current_item": {
"queue_id": "test_group_player_1",
"queue_item_id": "5d95dc5be77e4f7eb4939f62cfef527b",
"name": "Guns N' Roses - November Rain",
"duration": 536,
"sort_index": 2109,
"streamdetails": {
"provider": "spotify",
"item_id": "3YRCqOhFifThpSRFJ1VWFM",
"audio_format": {
"content_type": "ogg",
"sample_rate": 44100,
"bit_depth": 16,
"channels": 2,
"output_format_str": "ogg",
"bit_rate": 0
},
"media_type": "track",
"stream_type": "custom",
"stream_title": null,
"duration": 536,
"size": null,
"can_seek": true,
"loudness": -12.47,
"loudness_album": null,
"prefer_album_loudness": false,
"volume_normalization_mode": "fallback_dynamic",
"target_loudness": -17,
"strip_silence_begin": false,
"strip_silence_end": true,
"stream_error": null
},
"media_item": {
"item_id": "3YRCqOhFifThpSRFJ1VWFM",
"provider": "spotify",
"name": "November Rain",
"version": "",
"sort_name": "november rain",
"uri": "spotify://track/3YRCqOhFifThpSRFJ1VWFM",
"external_ids": [["isrc", "USGF19141510"]],
"media_type": "track",
"provider_mappings": [
{
"item_id": "3YRCqOhFifThpSRFJ1VWFM",
"provider_domain": "spotify",
"provider_instance": "spotify",
"available": true,
"audio_format": {
"content_type": "ogg",
"sample_rate": 44100,
"bit_depth": 16,
"channels": 2,
"output_format_str": "ogg",
"bit_rate": 320
},
"url": "https://open.spotify.com/track/3YRCqOhFifThpSRFJ1VWFM",
"details": null
}
],
"metadata": {
"description": null,
"review": null,
"explicit": false,
"images": [
{
"type": "thumb",
"path": "https://i.scdn.co/image/ab67616d0000b273e44963b8bb127552ac761873",
"provider": "spotify",
"remotely_accessible": true
}
],
"genres": null,
"mood": null,
"style": null,
"copyright": null,
"lyrics": null,
"label": null,
"links": null,
"chapters": null,
"performers": null,
"preview": "https://p.scdn.co/mp3-preview/98deb9c370bbaa350be058b3470fbe3bc1e28d9d?cid=2eb96f9b37494be1824999d58028a305",
"popularity": 77,
"last_refresh": null
},
"favorite": false,
"position": 1372,
"duration": 536,
"artists": [
{
"item_id": "3qm84nBOXUEQ2vnTfUTTFC",
"provider": "spotify",
"name": "Guns N' Roses",
"version": "",
"sort_name": "guns n' roses",
"uri": "spotify://artist/3qm84nBOXUEQ2vnTfUTTFC",
"external_ids": [],
"media_type": "artist",
"available": true,
"image": null
}
],
"album": {
"item_id": "0CxPbTRARqKUYighiEY9Sz",
"provider": "spotify",
"name": "Use Your Illusion I",
"version": "",
"sort_name": "use your illusion i",
"uri": "spotify://album/0CxPbTRARqKUYighiEY9Sz",
"external_ids": [],
"media_type": "album",
"available": true,
"image": {
"type": "thumb",
"path": "https://i.scdn.co/image/ab67616d0000b273e44963b8bb127552ac761873",
"provider": "spotify",
"remotely_accessible": true
}
},
"disc_number": 1,
"track_number": 10
},
"image": {
"type": "thumb",
"path": "https://i.scdn.co/image/ab67616d0000b273e44963b8bb127552ac761873",
"provider": "spotify",
"remotely_accessible": true
},
"index": 0
},
"next_item": {
"queue_id": "test_group_player_1",
"queue_item_id": "990ae8f29cdf4fb588d679b115621f55",
"name": "The Stranglers - Golden Brown",
"duration": 207,
"sort_index": 1138,
"streamdetails": {
"provider": "qobuz",
"item_id": "1004735",
"audio_format": {
"content_type": "flac",
"sample_rate": 44100,
"bit_depth": 16,
"channels": 2,
"output_format_str": "flac",
"bit_rate": 0
},
"media_type": "track",
"stream_type": "http",
"stream_title": null,
"duration": 207,
"size": null,
"can_seek": true,
"loudness": -14.23,
"loudness_album": null,
"prefer_album_loudness": true,
"volume_normalization_mode": "fallback_dynamic",
"target_loudness": -17,
"strip_silence_begin": true,
"strip_silence_end": true,
"stream_error": null
},
"media_item": {
"item_id": "1004735",
"provider": "qobuz",
"name": "Golden Brown",
"version": "",
"sort_name": "golden brown",
"uri": "qobuz://track/1004735",
"external_ids": [["isrc", "GBAYE8100053"]],
"media_type": "track",
"provider_mappings": [
{
"item_id": "1004735",
"provider_domain": "qobuz",
"provider_instance": "qobuz",
"available": true,
"audio_format": {
"content_type": "flac",
"sample_rate": 44100,
"bit_depth": 16,
"channels": 2,
"output_format_str": "flac",
"bit_rate": 0
},
"url": "https://open.qobuz.com/track/1004735",
"details": null
}
],
"metadata": {
"description": null,
"review": null,
"explicit": null,
"images": [
{
"type": "thumb",
"path": "https://static.qobuz.com/images/covers/59/88/0724353468859_600.jpg",
"provider": "qobuz",
"remotely_accessible": true
}
],
"genres": null,
"mood": null,
"style": null,
"copyright": "© 2001 Parlophone Records Ltd, a Warner Music Group Company ℗ 1981 Parlophone Records Ltd, a Warner Music Group Company",
"lyrics": null,
"label": null,
"links": null,
"chapters": null,
"performers": [
"Dave Greenfield, Composer, Producer, Keyboards, Vocals",
"Jean",
"Hugh Cornwell, Composer, Producer, Guitar, Vocals",
"Jean Jacques Burnel, Producer, Bass Guitar, Vocals",
"Jet Black, Composer, Producer, Drums, Percussion",
"Jacques Burnell, Composer",
"The Stranglers, MainArtist"
],
"preview": null,
"popularity": null,
"last_refresh": null
},
"favorite": false,
"position": 183,
"duration": 207,
"artists": [
{
"item_id": "26779",
"provider": "qobuz",
"name": "The Stranglers",
"version": "",
"sort_name": "stranglers, the",
"uri": "qobuz://artist/26779",
"external_ids": [],
"media_type": "artist",
"available": true,
"image": null
}
],
"album": {
"item_id": "0724353468859",
"provider": "qobuz",
"name": "La Folie",
"version": "",
"sort_name": "folie, la",
"uri": "qobuz://album/0724353468859",
"external_ids": [["barcode", "0724353468859"]],
"media_type": "album",
"available": true,
"image": {
"type": "thumb",
"path": "https://static.qobuz.com/images/covers/59/88/0724353468859_600.jpg",
"provider": "qobuz",
"remotely_accessible": true
}
},
"disc_number": 1,
"track_number": 9
},
"image": {
"type": "thumb",
"path": "https://static.qobuz.com/images/covers/59/88/0724353468859_600.jpg",
"provider": "qobuz",
"remotely_accessible": true
},
"index": 0
},
"radio_source": [],
"flow_mode": false,
"resume_pos": 0
}
]

View file

@ -0,0 +1,147 @@
[
{
"player_id": "00:00:00:00:00:01",
"provider": "test",
"type": "player",
"name": "Test Player 1",
"available": true,
"powered": false,
"device_info": {
"model": "Test Model",
"address": "192.168.1.1",
"manufacturer": "Test Manufacturer"
},
"supported_features": [
"volume_set",
"volume_mute",
"pause",
"sync",
"power",
"enqueue"
],
"elapsed_time": 0,
"elapsed_time_last_updated": 0,
"state": "idle",
"volume_level": 20,
"volume_muted": false,
"group_childs": [],
"active_source": "00:00:00:00:00:01",
"active_group": null,
"current_media": null,
"synced_to": null,
"enabled_by_default": true,
"needs_poll": false,
"poll_interval": 30,
"enabled": true,
"hidden": false,
"icon": "mdi-speaker",
"group_volume": 20,
"display_name": "Test Player 1",
"extra_data": {},
"announcement_in_progress": false
},
{
"player_id": "00:00:00:00:00:02",
"provider": "test",
"type": "player",
"name": "Test Player 2",
"available": true,
"powered": true,
"device_info": {
"model": "Test Model",
"address": "192.168.1.2",
"manufacturer": "Test Manufacturer"
},
"supported_features": [
"volume_set",
"volume_mute",
"pause",
"sync",
"power",
"enqueue"
],
"elapsed_time": 0,
"elapsed_time_last_updated": 0,
"state": "playing",
"volume_level": 20,
"volume_muted": false,
"group_childs": [],
"active_source": "spotify",
"active_group": null,
"current_media": {
"uri": "spotify://track/5d95dc5be77e4f7eb4939f62cfef527b",
"media_type": "track",
"title": "Test Track",
"artist": "Test Artist",
"album": "Test Album",
"image_url": null,
"duration": 300,
"queue_id": null,
"queue_item_id": null,
"custom_data": null
},
"synced_to": null,
"enabled_by_default": true,
"needs_poll": false,
"poll_interval": 30,
"enabled": true,
"hidden": false,
"icon": "mdi-speaker",
"group_volume": 20,
"display_name": "My Super Test Player 2",
"extra_data": {},
"announcement_in_progress": false
},
{
"player_id": "test_group_player_1",
"provider": "player_group",
"type": "group",
"name": "Test Group Player 1",
"available": true,
"powered": true,
"device_info": {
"model": "Sync Group",
"address": "",
"manufacturer": "Test"
},
"supported_features": [
"volume_set",
"volume_mute",
"pause",
"sync",
"power",
"enqueue"
],
"elapsed_time": 0.0,
"elapsed_time_last_updated": 1730315437.9904983,
"state": "idle",
"volume_level": 6,
"volume_muted": false,
"group_childs": ["00:00:00:00:00:01", "00:00:00:00:00:02"],
"active_source": "test_group_player_1",
"active_group": null,
"current_media": {
"uri": "http://192.168.1.1:8097/single/test_group_player_1/5d95dc5be77e4f7eb4939f62cfef527b.flac?ts=1730313038",
"media_type": "unknown",
"title": null,
"artist": null,
"album": null,
"image_url": null,
"duration": null,
"queue_id": "test_group_player_1",
"queue_item_id": "5d95dc5be77e4f7eb4939f62cfef527b",
"custom_data": null
},
"synced_to": null,
"enabled_by_default": true,
"needs_poll": true,
"poll_interval": 30,
"enabled": true,
"hidden": false,
"icon": "mdi-speaker-multiple",
"group_volume": 6,
"display_name": "Test Group Player 1",
"extra_data": {},
"announcement_in_progress": false
}
]

View file

@ -0,0 +1,190 @@
# serializer version: 1
# name: test_media_player[media_player.my_super_test_player_2-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'media_player',
'entity_category': None,
'entity_id': 'media_player.my_super_test_player_2',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
'original_icon': 'mdi:speaker',
'original_name': None,
'platform': 'music_assistant',
'previous_unique_id': None,
'supported_features': <MediaPlayerEntityFeature: 4126655>,
'translation_key': None,
'unique_id': '00:00:00:00:00:02',
'unit_of_measurement': None,
})
# ---
# name: test_media_player[media_player.my_super_test_player_2-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'active_queue': None,
'app_id': 'spotify',
'device_class': 'speaker',
'entity_picture_local': None,
'friendly_name': 'My Super Test Player 2',
'group_members': list([
]),
'icon': 'mdi:speaker',
'is_volume_muted': False,
'mass_player_type': 'player',
'media_album_name': 'Test Album',
'media_artist': 'Test Artist',
'media_content_id': 'spotify://track/5d95dc5be77e4f7eb4939f62cfef527b',
'media_content_type': <MediaType.MUSIC: 'music'>,
'media_duration': 300,
'media_position': 0,
'media_title': 'Test Track',
'supported_features': <MediaPlayerEntityFeature: 4126655>,
'volume_level': 0.2,
}),
'context': <ANY>,
'entity_id': 'media_player.my_super_test_player_2',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'playing',
})
# ---
# name: test_media_player[media_player.test_group_player_1-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'media_player',
'entity_category': None,
'entity_id': 'media_player.test_group_player_1',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
'original_icon': 'mdi:speaker-multiple',
'original_name': None,
'platform': 'music_assistant',
'previous_unique_id': None,
'supported_features': <MediaPlayerEntityFeature: 4126655>,
'translation_key': None,
'unique_id': 'test_group_player_1',
'unit_of_measurement': None,
})
# ---
# name: test_media_player[media_player.test_group_player_1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'active_queue': 'test_group_player_1',
'app_id': 'music_assistant',
'device_class': 'speaker',
'entity_picture_local': None,
'friendly_name': 'Test Group Player 1',
'group_members': list([
'media_player.my_super_test_player_2',
'media_player.test_player_1',
]),
'icon': 'mdi:speaker-multiple',
'is_volume_muted': False,
'mass_player_type': 'group',
'media_album_name': 'Use Your Illusion I',
'media_artist': "Guns N' Roses",
'media_content_id': 'spotify://track/3YRCqOhFifThpSRFJ1VWFM',
'media_content_type': <MediaType.MUSIC: 'music'>,
'media_duration': 536,
'media_position': 232,
'media_position_updated_at': datetime.datetime(2024, 10, 30, 18, 31, 49, 565951, tzinfo=datetime.timezone.utc),
'media_title': 'November Rain',
'repeat': 'all',
'shuffle': True,
'supported_features': <MediaPlayerEntityFeature: 4126655>,
'volume_level': 0.06,
}),
'context': <ANY>,
'entity_id': 'media_player.test_group_player_1',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'idle',
})
# ---
# name: test_media_player[media_player.test_player_1-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'media_player',
'entity_category': None,
'entity_id': 'media_player.test_player_1',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
'original_icon': 'mdi:speaker',
'original_name': None,
'platform': 'music_assistant',
'previous_unique_id': None,
'supported_features': <MediaPlayerEntityFeature: 4126655>,
'translation_key': None,
'unique_id': '00:00:00:00:00:01',
'unit_of_measurement': None,
})
# ---
# name: test_media_player[media_player.test_player_1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'active_queue': '00:00:00:00:00:01',
'device_class': 'speaker',
'friendly_name': 'Test Player 1',
'group_members': list([
]),
'icon': 'mdi:speaker',
'mass_player_type': 'player',
'supported_features': <MediaPlayerEntityFeature: 4126655>,
}),
'context': <ANY>,
'entity_id': 'media_player.test_player_1',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View file

@ -0,0 +1,176 @@
"""Test Music Assistant media player entities."""
from unittest.mock import MagicMock, call
from syrupy import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .common import setup_integration_from_fixtures, snapshot_music_assistant_entities
async def test_media_player(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
music_assistant_client: MagicMock,
) -> None:
"""Test media player."""
await setup_integration_from_fixtures(hass, music_assistant_client)
snapshot_music_assistant_entities(
hass, entity_registry, snapshot, Platform.MEDIA_PLAYER
)
async def test_media_player_actions(
hass: HomeAssistant,
music_assistant_client: MagicMock,
) -> None:
"""Test media_player entity actions."""
await setup_integration_from_fixtures(hass, music_assistant_client)
entity_id = "media_player.test_player_1"
mass_player_id = "00:00:00:00:00:01"
state = hass.states.get(entity_id)
assert state
# test basic actions (play/stop/pause etc.)
for action, cmd in (
("media_play", "play"),
("media_pause", "pause"),
("media_stop", "stop"),
("media_previous_track", "previous"),
("media_next_track", "next"),
("volume_up", "volume_up"),
("volume_down", "volume_down"),
):
await hass.services.async_call(
"media_player",
action,
{
"entity_id": entity_id,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
f"players/cmd/{cmd}", player_id=mass_player_id
)
music_assistant_client.send_command.reset_mock()
# test seek action
await hass.services.async_call(
"media_player",
"media_seek",
{
"entity_id": entity_id,
"seek_position": 100,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"players/cmd/seek", player_id=mass_player_id, position=100
)
music_assistant_client.send_command.reset_mock()
# test volume action
await hass.services.async_call(
"media_player",
"volume_set",
{
"entity_id": entity_id,
"volume_level": 0.5,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"players/cmd/volume_set", player_id=mass_player_id, volume_level=50
)
music_assistant_client.send_command.reset_mock()
# test volume mute action
await hass.services.async_call(
"media_player",
"volume_mute",
{
"entity_id": entity_id,
"is_volume_muted": True,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"players/cmd/volume_mute", player_id=mass_player_id, muted=True
)
music_assistant_client.send_command.reset_mock()
# test turn_on /turn_off action
for action, pwr in (
("turn_on", True),
("turn_off", False),
):
await hass.services.async_call(
"media_player",
action,
{
"entity_id": entity_id,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"players/cmd/power", player_id=mass_player_id, powered=pwr
)
music_assistant_client.send_command.reset_mock()
# test shuffle action
await hass.services.async_call(
"media_player",
"shuffle_set",
{
"entity_id": entity_id,
"shuffle": True,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"player_queues/shuffle", queue_id=mass_player_id, shuffle_enabled=True
)
music_assistant_client.send_command.reset_mock()
# test repeat action
await hass.services.async_call(
"media_player",
"repeat_set",
{
"entity_id": entity_id,
"repeat": "one",
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"player_queues/repeat", queue_id=mass_player_id, repeat_mode="one"
)
music_assistant_client.send_command.reset_mock()
# test clear playlist action
await hass.services.async_call(
"media_player",
"clear_playlist",
{
"entity_id": entity_id,
},
blocking=True,
)
assert music_assistant_client.send_command.call_count == 1
assert music_assistant_client.send_command.call_args == call(
"player_queues/clear", queue_id=mass_player_id
)
music_assistant_client.send_command.reset_mock()