Add Bang & Olufsen media_player testing (#120580)

This commit is contained in:
Markus Jacobsen 2024-07-08 22:19:02 +02:00 committed by GitHub
parent 2a31774f91
commit 45843a3112
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 1354 additions and 3 deletions

View file

@ -3,10 +3,26 @@
from collections.abc import Generator from collections.abc import Generator
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from mozart_api.models import BeolinkPeer from mozart_api.models import (
Action,
BeolinkPeer,
ContentItem,
PlaybackContentMetadata,
PlaybackProgress,
PlaybackState,
ProductState,
RemoteMenuItem,
RenderingState,
SoftwareUpdateStatus,
Source,
SourceArray,
SourceTypeEnum,
VolumeState,
)
import pytest import pytest
from homeassistant.components.bang_olufsen.const import DOMAIN from homeassistant.components.bang_olufsen.const import DOMAIN
from homeassistant.core import HomeAssistant
from .const import ( from .const import (
TEST_DATA_CREATE_ENTRY, TEST_DATA_CREATE_ENTRY,
@ -30,10 +46,17 @@ def mock_config_entry():
) )
@pytest.fixture
async def mock_media_player(hass: HomeAssistant, mock_config_entry, mock_mozart_client):
"""Mock media_player entity."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
@pytest.fixture @pytest.fixture
def mock_mozart_client() -> Generator[AsyncMock]: def mock_mozart_client() -> Generator[AsyncMock]:
"""Mock MozartClient.""" """Mock MozartClient."""
with ( with (
patch( patch(
"homeassistant.components.bang_olufsen.MozartClient", autospec=True "homeassistant.components.bang_olufsen.MozartClient", autospec=True
@ -50,6 +73,170 @@ def mock_mozart_client() -> Generator[AsyncMock]:
client.get_beolink_self.return_value = BeolinkPeer( client.get_beolink_self.return_value = BeolinkPeer(
friendly_name=TEST_FRIENDLY_NAME, jid=TEST_JID_1 friendly_name=TEST_FRIENDLY_NAME, jid=TEST_JID_1
) )
client.get_softwareupdate_status = AsyncMock()
client.get_softwareupdate_status.return_value = SoftwareUpdateStatus(
software_version="1.0.0", state=""
)
client.get_product_state = AsyncMock()
client.get_product_state.return_value = ProductState(
volume=VolumeState(),
playback=PlaybackState(
metadata=PlaybackContentMetadata(),
progress=PlaybackProgress(),
source=Source(),
state=RenderingState(value="started"),
),
)
client.get_available_sources = AsyncMock()
client.get_available_sources.return_value = SourceArray(
items=[
# Is in the HIDDEN_SOURCE_IDS constant, so should not be user selectable
Source(
name="AirPlay",
id="airPlay",
is_enabled=True,
is_multiroom_available=False,
),
# The only available source
Source(
name="Tidal",
id="tidal",
is_enabled=True,
is_multiroom_available=True,
),
# Is disabled, so should not be user selectable
Source(
name="Powerlink",
id="pl",
is_enabled=False,
),
]
)
client.get_remote_menu = AsyncMock()
client.get_remote_menu.return_value = {
# Music category, so shouldn't be included in video sources
"b355888b-2cde-5f94-8592-d47b71d52a27": RemoteMenuItem(
action_list=[
Action(
button_name=None,
content_id="netRadio://6629967157728971",
deezer_user_id=None,
gain_db=None,
listening_mode_id=None,
preset_key=None,
queue_item=None,
queue_settings=None,
radio_station_id=None,
source=None,
speaker_group_id=None,
stand_position=None,
stop_duration=None,
tone_name=None,
type="triggerContent",
volume_level=None,
)
],
scene_list=None,
disabled=None,
dynamic_list=None,
first_child_menu_item_id=None,
label="Yle Radio Suomi Helsinki",
next_sibling_menu_item_id="0b4552f8-7ac6-5046-9d44-5410a815b8d6",
parent_menu_item_id="eee0c2d0-2b3a-4899-a708-658475c38926",
available=None,
content=ContentItem(
categories=["music"],
content_uri="netRadio://6629967157728971",
label="Yle Radio Suomi Helsinki",
source=SourceTypeEnum(value="netRadio"),
),
fixed=True,
id="b355888b-2cde-5f94-8592-d47b71d52a27",
),
# Has "hdmi" as category, so should be included in video sources
"b6591565-80f4-4356-bcd9-c92ca247f0a9": RemoteMenuItem(
action_list=[
Action(
button_name=None,
content_id="tv://hdmi_1",
deezer_user_id=None,
gain_db=None,
listening_mode_id=None,
preset_key=None,
queue_item=None,
queue_settings=None,
radio_station_id=None,
source=None,
speaker_group_id=None,
stand_position=None,
stop_duration=None,
tone_name=None,
type="triggerContent",
volume_level=None,
)
],
scene_list=None,
disabled=False,
dynamic_list="none",
first_child_menu_item_id=None,
label="HDMI A",
next_sibling_menu_item_id="0ba98974-7b1f-40dc-bc48-fbacbb0f1793",
parent_menu_item_id="b66c835b-6b98-4400-8f84-6348043792c7",
available=True,
content=ContentItem(
categories=["hdmi"],
content_uri="tv://hdmi_1",
label="HDMI A",
source=SourceTypeEnum(value="tv"),
),
fixed=False,
id="b6591565-80f4-4356-bcd9-c92ca247f0a9",
),
# The parent remote menu item. Has the TV label and should therefore not be included in video sources
"b66c835b-6b98-4400-8f84-6348043792c7": RemoteMenuItem(
action_list=[],
scene_list=None,
disabled=False,
dynamic_list="none",
first_child_menu_item_id="b6591565-80f4-4356-bcd9-c92ca247f0a9",
label="TV",
next_sibling_menu_item_id="0c4547fe-d3cc-4348-a425-473595b8c9fb",
parent_menu_item_id=None,
available=True,
content=None,
fixed=True,
id="b66c835b-6b98-4400-8f84-6348043792c7",
),
# Has an empty content, so should not be included
"64c9da45-3682-44a4-8030-09ed3ef44160": RemoteMenuItem(
action_list=[],
scene_list=None,
disabled=False,
dynamic_list="none",
first_child_menu_item_id=None,
label="ListeningPosition",
next_sibling_menu_item_id=None,
parent_menu_item_id="0c4547fe-d3cc-4348-a425-473595b8c9fb",
available=True,
content=None,
fixed=True,
id="64c9da45-3682-44a4-8030-09ed3ef44160",
),
}
client.post_standby = AsyncMock()
client.set_current_volume_level = AsyncMock()
client.set_volume_mute = AsyncMock()
client.post_playback_command = AsyncMock()
client.seek_to_position = AsyncMock()
client.post_clear_queue = AsyncMock()
client.post_overlay_play = AsyncMock()
client.post_uri_source = AsyncMock()
client.run_provided_scene = AsyncMock()
client.activate_preset = AsyncMock()
client.start_deezer_flow = AsyncMock()
client.add_to_queue = AsyncMock()
client.post_remote_trigger = AsyncMock()
client.set_active_source = AsyncMock()
# Non-REST API client methods # Non-REST API client methods
client.check_device_connection = AsyncMock() client.check_device_connection = AsyncMock()

View file

@ -1,6 +1,25 @@
"""Constants used for testing the bang_olufsen integration.""" """Constants used for testing the bang_olufsen integration."""
from ipaddress import IPv4Address, IPv6Address from ipaddress import IPv4Address, IPv6Address
from unittest.mock import Mock
from mozart_api.exceptions import ApiException
from mozart_api.models import (
Action,
OverlayPlayRequest,
OverlayPlayRequestTextToSpeechTextToSpeech,
PlaybackContentMetadata,
PlaybackError,
PlaybackProgress,
PlayQueueItem,
PlayQueueItemType,
RenderingState,
SceneProperties,
UserFlow,
VolumeLevel,
VolumeMute,
VolumeState,
)
from homeassistant.components.bang_olufsen.const import ( from homeassistant.components.bang_olufsen.const import (
ATTR_FRIENDLY_NAME, ATTR_FRIENDLY_NAME,
@ -8,6 +27,7 @@ from homeassistant.components.bang_olufsen.const import (
ATTR_SERIAL_NUMBER, ATTR_SERIAL_NUMBER,
ATTR_TYPE_NUMBER, ATTR_TYPE_NUMBER,
CONF_BEOLINK_JID, CONF_BEOLINK_JID,
BangOlufsenSource,
) )
from homeassistant.components.zeroconf import ZeroconfServiceInfo from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_NAME from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_NAME
@ -24,7 +44,7 @@ TEST_FRIENDLY_NAME = "Living room Balance"
TEST_TYPE_NUMBER = "1111" TEST_TYPE_NUMBER = "1111"
TEST_ITEM_NUMBER = "1111111" TEST_ITEM_NUMBER = "1111111"
TEST_JID_1 = f"{TEST_TYPE_NUMBER}.{TEST_ITEM_NUMBER}.{TEST_SERIAL_NUMBER}@products.bang-olufsen.com" TEST_JID_1 = f"{TEST_TYPE_NUMBER}.{TEST_ITEM_NUMBER}.{TEST_SERIAL_NUMBER}@products.bang-olufsen.com"
TEST_MEDIA_PLAYER_ENTITY_ID = "media_player.beosound_balance_11111111"
TEST_HOSTNAME_ZEROCONF = TEST_NAME.replace(" ", "-") + ".local." TEST_HOSTNAME_ZEROCONF = TEST_NAME.replace(" ", "-") + ".local."
TEST_TYPE_ZEROCONF = "_bangolufsen._tcp.local." TEST_TYPE_ZEROCONF = "_bangolufsen._tcp.local."
@ -80,3 +100,80 @@ TEST_DATA_ZEROCONF_IPV6 = ZeroconfServiceInfo(
ATTR_ITEM_NUMBER: TEST_ITEM_NUMBER, ATTR_ITEM_NUMBER: TEST_ITEM_NUMBER,
}, },
) )
TEST_AUDIO_SOURCES = [BangOlufsenSource.TIDAL.name]
TEST_VIDEO_SOURCES = ["HDMI A"]
TEST_SOURCES = TEST_AUDIO_SOURCES + TEST_VIDEO_SOURCES
TEST_FALLBACK_SOURCES = [
"Audio Streamer",
"Spotify Connect",
"Line-In",
"Optical",
"B&O Radio",
"Deezer",
"Tidal Connect",
]
TEST_PLAYBACK_METADATA = PlaybackContentMetadata(
album_name="Test album",
artist_name="Test artist",
organization="Test organization",
title="Test title",
total_duration_seconds=123,
track=1,
)
TEST_PLAYBACK_ERROR = PlaybackError(error="Test error")
TEST_PLAYBACK_PROGRESS = PlaybackProgress(progress=123)
TEST_PLAYBACK_STATE_PAUSED = RenderingState(value="paused")
TEST_PLAYBACK_STATE_PLAYING = RenderingState(value="started")
TEST_VOLUME = VolumeState(level=VolumeLevel(level=40))
TEST_VOLUME_HOME_ASSISTANT_FORMAT = 0.4
TEST_PLAYBACK_STATE_TURN_OFF = RenderingState(value="stopped")
TEST_VOLUME_MUTED = VolumeState(
muted=VolumeMute(muted=True), level=VolumeLevel(level=40)
)
TEST_VOLUME_MUTED_HOME_ASSISTANT_FORMAT = True
TEST_SEEK_POSITION_HOME_ASSISTANT_FORMAT = 10.0
TEST_SEEK_POSITION = 10000
TEST_OVERLAY_INVALID_OFFSET_VOLUME_TTS = OverlayPlayRequest(
text_to_speech=OverlayPlayRequestTextToSpeechTextToSpeech(
lang="da-dk", text="Dette er en test"
)
)
TEST_OVERLAY_OFFSET_VOLUME_TTS = OverlayPlayRequest(
text_to_speech=OverlayPlayRequestTextToSpeechTextToSpeech(
lang="en-us", text="This is a test"
),
volume_absolute=60,
)
TEST_RADIO_STATION = SceneProperties(
action_list=[
Action(
type="radio",
radio_station_id="1234567890123456",
)
]
)
TEST_DEEZER_FLOW = UserFlow(user_id="123")
TEST_DEEZER_PLAYLIST = PlayQueueItem(
provider=PlayQueueItemType(value="deezer"),
start_now_from_position=123,
type="playlist",
uri="playlist:1234567890",
)
TEST_DEEZER_TRACK = PlayQueueItem(
provider=PlayQueueItemType(value="deezer"),
start_now_from_position=0,
type="track",
uri="1234567890",
)
# codespell can't see the escaped ', so it thinks the word is misspelled
TEST_DEEZER_INVALID_FLOW = ApiException(
status=400,
reason="Bad Request",
http_resp=Mock(
status=400,
reason="Bad Request",
data='{"message": "Couldn\'t start user flow for me"}', # codespell:ignore
),
)

File diff suppressed because it is too large Load diff