Add media player test to Cambridge Audio (#125780)
* Add media player tests to Cambridge Audio * Add media player tests to Cambridge Audio * Remove unnecessary test case * Move state_update call out of mock * Update tests/components/cambridge_audio/test_media_player.py --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>pull/126382/head
parent
98728d37a6
commit
610e9239a4
|
@ -3,13 +3,13 @@
|
|||
from collections.abc import Generator
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from aiostreammagic.models import Info
|
||||
from aiostreammagic.models import Info, NowPlaying, PlayState, Source, State
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.cambridge_audio.const import DOMAIN
|
||||
from homeassistant.const import CONF_HOST
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
from tests.common import MockConfigEntry, load_fixture, load_json_array_fixture
|
||||
from tests.components.smhi.common import AsyncMock
|
||||
|
||||
|
||||
|
@ -39,7 +39,20 @@ def mock_stream_magic_client() -> Generator[AsyncMock]:
|
|||
client = mock_client.return_value
|
||||
client.host = "192.168.20.218"
|
||||
client.info = Info.from_json(load_fixture("get_info.json", DOMAIN))
|
||||
client.sources = [
|
||||
Source.from_dict(x)
|
||||
for x in load_json_array_fixture("get_sources.json", DOMAIN)
|
||||
]
|
||||
client.state = State.from_json(load_fixture("get_state.json", DOMAIN))
|
||||
client.play_state = PlayState.from_json(
|
||||
load_fixture("get_play_state.json", DOMAIN)
|
||||
)
|
||||
client.now_playing = NowPlaying.from_json(
|
||||
load_fixture("get_now_playing.json", DOMAIN)
|
||||
)
|
||||
client.is_connected = Mock(return_value=True)
|
||||
client.position_last_updated = client.play_state.position
|
||||
client.unregister_state_update_callbacks = AsyncMock(return_value=True)
|
||||
|
||||
yield client
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
"""Constants for Cambridge Audio integration tests."""
|
||||
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
|
||||
DEVICE_NAME = "cambridge_audio_cxnv2"
|
||||
ENTITY_ID = f"{MP_DOMAIN}.{DEVICE_NAME}"
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"state": "PLAYING",
|
||||
"source": {
|
||||
"id": "AIRPLAY",
|
||||
"name": "AirPlay"
|
||||
},
|
||||
"allow_apd": false,
|
||||
"listening_on": "Listening on Cambridge Audio CXNv2 - AirPlay",
|
||||
"display": {
|
||||
"line1": "Holiday",
|
||||
"line2": "Green Day",
|
||||
"line3": "Greatest Hits: God's Favorite Band",
|
||||
"format": "44.1kHz/16bit ALAC",
|
||||
"mqa": "none",
|
||||
"playback_source": "iPhone",
|
||||
"class": "stream.service.airplay",
|
||||
"art_file": "/tmp/current/AlbumArtFile-811-363",
|
||||
"art_url": "http://192.168.20.218:80/album-art-2d89?id=1:246",
|
||||
"progress": {
|
||||
"position": 216,
|
||||
"duration": 232
|
||||
}
|
||||
},
|
||||
"controls": ["play_pause", "track_next", "track_previous"]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"state": "play",
|
||||
"position": 179,
|
||||
"presettable": false,
|
||||
"mode_repeat": "off",
|
||||
"mode_shuffle": "off",
|
||||
"metadata": {
|
||||
"class": "md.track",
|
||||
"source": "AIRPLAY",
|
||||
"name": "AirPlay",
|
||||
"duration": 232,
|
||||
"album": "Greatest Hits: God's Favorite Band",
|
||||
"artist": "Green Day",
|
||||
"title": "Holiday",
|
||||
"art_url": "http://192.168.20.218:80/album-art-2d89?id=1:246",
|
||||
"mqa": "none",
|
||||
"codec": "ALAC",
|
||||
"lossless": true,
|
||||
"sample_rate": 44100,
|
||||
"bit_depth": 16
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
[
|
||||
{
|
||||
"id": "IR",
|
||||
"name": "Internet Radio",
|
||||
"default_name": "Internet Radio",
|
||||
"class": "stream.radio",
|
||||
"nameable": false,
|
||||
"ui_selectable": false,
|
||||
"description": "Internet Radio",
|
||||
"description_locale": "Internet Radio",
|
||||
"preferred_order": 9
|
||||
},
|
||||
{
|
||||
"id": "USB_AUDIO",
|
||||
"name": "USB Audio",
|
||||
"default_name": "USB Audio",
|
||||
"class": "digital.usb",
|
||||
"nameable": true,
|
||||
"ui_selectable": true,
|
||||
"description": "USB Audio",
|
||||
"description_locale": "USB Audio",
|
||||
"preferred_order": 1
|
||||
},
|
||||
{
|
||||
"id": "SPDIF_COAX",
|
||||
"name": "D2",
|
||||
"default_name": "D2",
|
||||
"class": "digital.coax",
|
||||
"nameable": true,
|
||||
"ui_selectable": false,
|
||||
"description": "Digital Co-axial",
|
||||
"description_locale": "Digital Co-axial",
|
||||
"preferred_order": 3
|
||||
},
|
||||
{
|
||||
"id": "SPDIF_TOSLINK",
|
||||
"name": "D1",
|
||||
"default_name": "D1",
|
||||
"class": "digital.toslink",
|
||||
"nameable": true,
|
||||
"ui_selectable": false,
|
||||
"description": "Digital Optical",
|
||||
"description_locale": "Digital Optical",
|
||||
"preferred_order": 2
|
||||
},
|
||||
{
|
||||
"id": "MEDIA_PLAYER",
|
||||
"name": "Media Library",
|
||||
"default_name": "Media Library",
|
||||
"class": "stream.media",
|
||||
"nameable": false,
|
||||
"ui_selectable": true,
|
||||
"description": "Media Player",
|
||||
"description_locale": "Media Player",
|
||||
"preferred_order": 10
|
||||
},
|
||||
{
|
||||
"id": "AIRPLAY",
|
||||
"name": "AirPlay",
|
||||
"default_name": "AirPlay",
|
||||
"class": "stream.service.airplay",
|
||||
"nameable": false,
|
||||
"ui_selectable": true,
|
||||
"description": "AirPlay",
|
||||
"description_locale": "AirPlay",
|
||||
"preferred_order": 11
|
||||
},
|
||||
{
|
||||
"id": "SPOTIFY",
|
||||
"name": "Spotify",
|
||||
"default_name": "Spotify",
|
||||
"class": "stream.service.spotify",
|
||||
"nameable": false,
|
||||
"ui_selectable": true,
|
||||
"description": "Spotify",
|
||||
"description_locale": "Spotify",
|
||||
"preferred_order": 6,
|
||||
"normalisation": "off"
|
||||
},
|
||||
{
|
||||
"id": "CAST",
|
||||
"name": "Chromecast built-in",
|
||||
"default_name": "Chromecast built-in",
|
||||
"class": "stream.service.cast",
|
||||
"nameable": false,
|
||||
"ui_selectable": true,
|
||||
"description": "Chromecast built-in",
|
||||
"description_locale": "Chromecast built-in",
|
||||
"preferred_order": 8
|
||||
},
|
||||
{
|
||||
"id": "ROON",
|
||||
"name": "Roon Ready",
|
||||
"default_name": "Roon Ready",
|
||||
"class": "stream.service.roon",
|
||||
"nameable": false,
|
||||
"ui_selectable": false,
|
||||
"description": "Roon Ready",
|
||||
"description_locale": "Roon Ready",
|
||||
"preferred_order": 5
|
||||
},
|
||||
{
|
||||
"id": "TIDAL",
|
||||
"name": "TIDAL Connect",
|
||||
"default_name": "TIDAL Connect",
|
||||
"class": "stream.service.tidal",
|
||||
"nameable": false,
|
||||
"ui_selectable": false,
|
||||
"description": "TIDAL",
|
||||
"description_locale": "TIDAL",
|
||||
"preferred_order": 7
|
||||
}
|
||||
]
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"source": "AIRPLAY",
|
||||
"power": true,
|
||||
"pre_amp_mode": false,
|
||||
"pre_amp_state": "disabled_user",
|
||||
"cbus": "off"
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
"""Tests for the Cambridge Audio integration."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from aiostreammagic import TransportControl
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
DOMAIN as MP_DOMAIN,
|
||||
MediaPlayerEntityFeature,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
SERVICE_MEDIA_NEXT_TRACK,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||
STATE_BUFFERING,
|
||||
STATE_IDLE,
|
||||
STATE_OFF,
|
||||
STATE_PAUSED,
|
||||
STATE_PLAYING,
|
||||
STATE_STANDBY,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
from .const import ENTITY_ID
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def mock_state_update(client: AsyncMock) -> None:
|
||||
"""Trigger a callback in the media player."""
|
||||
await client.register_state_update_callbacks.call_args[0][0](client)
|
||||
|
||||
|
||||
async def test_entity_supported_features(
|
||||
hass: HomeAssistant,
|
||||
mock_stream_magic_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entity attributes."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
await mock_state_update(mock_stream_magic_client)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
attrs = state.attributes
|
||||
|
||||
# Ensure volume isn't available when pre-amp is disabled
|
||||
assert not mock_stream_magic_client.state.pre_amp_mode
|
||||
assert (
|
||||
MediaPlayerEntityFeature.VOLUME_SET
|
||||
| MediaPlayerEntityFeature.VOLUME_STEP
|
||||
| MediaPlayerEntityFeature.VOLUME_MUTE
|
||||
not in attrs[ATTR_SUPPORTED_FEATURES]
|
||||
)
|
||||
|
||||
# Check for basic media controls
|
||||
assert {
|
||||
TransportControl.PLAY_PAUSE,
|
||||
TransportControl.TRACK_NEXT,
|
||||
TransportControl.TRACK_PREVIOUS,
|
||||
}.issubset(mock_stream_magic_client.now_playing.controls)
|
||||
assert (
|
||||
MediaPlayerEntityFeature.PLAY
|
||||
| MediaPlayerEntityFeature.PAUSE
|
||||
| MediaPlayerEntityFeature.NEXT_TRACK
|
||||
| MediaPlayerEntityFeature.PREVIOUS_TRACK
|
||||
in attrs[ATTR_SUPPORTED_FEATURES]
|
||||
)
|
||||
assert (
|
||||
MediaPlayerEntityFeature.SHUFFLE_SET
|
||||
| MediaPlayerEntityFeature.REPEAT_SET
|
||||
| MediaPlayerEntityFeature.SEEK
|
||||
not in attrs[ATTR_SUPPORTED_FEATURES]
|
||||
)
|
||||
|
||||
mock_stream_magic_client.now_playing.controls = [
|
||||
TransportControl.TOGGLE_REPEAT,
|
||||
TransportControl.TOGGLE_SHUFFLE,
|
||||
TransportControl.SEEK,
|
||||
]
|
||||
await mock_state_update(mock_stream_magic_client)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
attrs = state.attributes
|
||||
|
||||
assert (
|
||||
MediaPlayerEntityFeature.SHUFFLE_SET
|
||||
| MediaPlayerEntityFeature.REPEAT_SET
|
||||
| MediaPlayerEntityFeature.SEEK
|
||||
in attrs[ATTR_SUPPORTED_FEATURES]
|
||||
)
|
||||
|
||||
mock_stream_magic_client.state.pre_amp_mode = True
|
||||
await mock_state_update(mock_stream_magic_client)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
attrs = state.attributes
|
||||
assert (
|
||||
MediaPlayerEntityFeature.VOLUME_SET
|
||||
| MediaPlayerEntityFeature.VOLUME_STEP
|
||||
| MediaPlayerEntityFeature.VOLUME_MUTE
|
||||
in attrs[ATTR_SUPPORTED_FEATURES]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("power_state", "play_state", "media_player_state"),
|
||||
[
|
||||
(True, "NETWORK", STATE_STANDBY),
|
||||
(False, "NETWORK", STATE_STANDBY),
|
||||
(False, "play", STATE_OFF),
|
||||
(True, "play", STATE_PLAYING),
|
||||
(True, "pause", STATE_PAUSED),
|
||||
(True, "connecting", STATE_BUFFERING),
|
||||
(True, "stop", STATE_IDLE),
|
||||
(True, "ready", STATE_IDLE),
|
||||
],
|
||||
)
|
||||
async def test_entity_state(
|
||||
hass: HomeAssistant,
|
||||
mock_stream_magic_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
power_state: bool,
|
||||
play_state: str,
|
||||
media_player_state: str,
|
||||
) -> None:
|
||||
"""Test media player state."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
mock_stream_magic_client.state.power = power_state
|
||||
mock_stream_magic_client.play_state.state = play_state
|
||||
await mock_state_update(mock_stream_magic_client)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state.state == media_player_state
|
||||
|
||||
|
||||
async def test_media_play_pause_stop(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_stream_magic_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test media next previous track service."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
data = {ATTR_ENTITY_ID: ENTITY_ID}
|
||||
|
||||
# Test for play/pause command when separate play and pause controls are unavailable
|
||||
await hass.services.async_call(MP_DOMAIN, SERVICE_MEDIA_PAUSE, data, True)
|
||||
mock_stream_magic_client.play_pause.assert_called_once()
|
||||
|
||||
await hass.services.async_call(MP_DOMAIN, SERVICE_MEDIA_PLAY, data, True)
|
||||
assert mock_stream_magic_client.play_pause.call_count == 2
|
||||
|
||||
# Test for separate play and pause controls
|
||||
mock_stream_magic_client.now_playing.controls = [
|
||||
TransportControl.PLAY,
|
||||
TransportControl.PAUSE,
|
||||
]
|
||||
await mock_state_update(mock_stream_magic_client)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(MP_DOMAIN, SERVICE_MEDIA_PAUSE, data, True)
|
||||
mock_stream_magic_client.pause.assert_called_once()
|
||||
|
||||
await hass.services.async_call(MP_DOMAIN, SERVICE_MEDIA_PLAY, data, True)
|
||||
mock_stream_magic_client.play.assert_called_once()
|
||||
|
||||
|
||||
async def test_media_next_previous_track(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_stream_magic_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test media next previous track service."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
data = {ATTR_ENTITY_ID: ENTITY_ID}
|
||||
|
||||
await hass.services.async_call(MP_DOMAIN, SERVICE_MEDIA_NEXT_TRACK, data, True)
|
||||
|
||||
mock_stream_magic_client.next_track.assert_called_once()
|
||||
|
||||
await hass.services.async_call(MP_DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data, True)
|
||||
|
||||
mock_stream_magic_client.previous_track.assert_called_once()
|
Loading…
Reference in New Issue