Update IDs after firmware upgrade in HEOS (#23641)

* Initial work

* Update tests
This commit is contained in:
Andrew Sayre 2019-05-06 10:53:11 -05:00 committed by Martin Hjelmare
parent 73aadbe8bc
commit bf649e373c
6 changed files with 223 additions and 75 deletions

View file

@ -20,7 +20,7 @@ def config_entry_fixture():
@pytest.fixture(name="controller")
def controller_fixture(
players, favorites, input_sources, playlists, dispatcher):
players, favorites, input_sources, playlists, change_data, dispatcher):
"""Create a mock Heos controller fixture."""
with patch("pyheos.Heos", autospec=True) as mock:
mock_heos = mock.return_value
@ -32,6 +32,7 @@ def controller_fixture(
mock_heos.get_favorites.return_value = favorites
mock_heos.get_input_sources.return_value = input_sources
mock_heos.get_playlists.return_value = playlists
mock_heos.load_players.return_value = change_data
mock_heos.is_signed_in = True
mock_heos.signed_in_username = "user@user.com"
yield mock_heos
@ -149,3 +150,23 @@ def playlists_fixture() -> Sequence[HeosSource]:
playlist.type = const.TYPE_PLAYLIST
playlist.name = "Awesome Music"
return [playlist]
@pytest.fixture(name="change_data")
def change_data_fixture() -> Dict:
"""Create player change data for testing."""
return {
const.DATA_MAPPED_IDS: {},
const.DATA_NEW: []
}
@pytest.fixture(name="change_data_mapped_ids")
def change_data_mapped_ids_fixture() -> Dict:
"""Create player change data for testing."""
return {
const.DATA_MAPPED_IDS: {
101: 1
},
const.DATA_NEW: []
}

View file

@ -1,13 +1,14 @@
"""Tests for the init module."""
import asyncio
from asynctest import patch
from asynctest import Mock, patch
from pyheos import CommandError, const
import pytest
from homeassistant.components.heos import async_setup_entry, async_unload_entry
from homeassistant.components.heos import (
ControllerManager, async_setup_entry, async_unload_entry)
from homeassistant.components.heos.const import (
DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN)
DATA_CONTROLLER_MANAGER, DATA_SOURCE_MANAGER, DOMAIN)
from homeassistant.components.media_player.const import (
DOMAIN as MEDIA_PLAYER_DOMAIN)
from homeassistant.const import CONF_HOST
@ -74,7 +75,7 @@ async def test_async_setup_entry_loads_platforms(
assert controller.get_favorites.call_count == 1
assert controller.get_input_sources.call_count == 1
controller.disconnect.assert_not_called()
assert hass.data[DOMAIN][DATA_CONTROLLER] == controller
assert hass.data[DOMAIN][DATA_CONTROLLER_MANAGER].controller == controller
assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].favorites == favorites
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].inputs == input_sources
@ -97,7 +98,7 @@ async def test_async_setup_entry_not_signed_in_loads_platforms(
assert controller.get_favorites.call_count == 0
assert controller.get_input_sources.call_count == 1
controller.disconnect.assert_not_called()
assert hass.data[DOMAIN][DATA_CONTROLLER] == controller
assert hass.data[DOMAIN][DATA_CONTROLLER_MANAGER].controller == controller
assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].favorites == {}
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].inputs == input_sources
@ -139,12 +140,13 @@ async def test_async_setup_entry_player_failure(
async def test_unload_entry(hass, config_entry, controller):
"""Test entries are unloaded correctly."""
hass.data[DOMAIN] = {DATA_CONTROLLER: controller}
controller_manager = Mock(ControllerManager)
hass.data[DOMAIN] = {DATA_CONTROLLER_MANAGER: controller_manager}
with patch.object(hass.config_entries, 'async_forward_entry_unload',
return_value=True) as unload:
assert await async_unload_entry(hass, config_entry)
await hass.async_block_till_done()
assert controller.disconnect.call_count == 1
assert controller_manager.disconnect.call_count == 1
assert unload.call_count == 1
assert DOMAIN not in hass.data

View file

@ -5,7 +5,7 @@ from pyheos import CommandError, const
from homeassistant.components.heos import media_player
from homeassistant.components.heos.const import (
DATA_SOURCE_MANAGER, DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED)
DATA_SOURCE_MANAGER, DOMAIN, SIGNAL_HEOS_UPDATED)
from homeassistant.components.media_player.const import (
ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, ATTR_MEDIA_ALBUM_NAME,
ATTR_MEDIA_ARTIST, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE,
@ -66,7 +66,7 @@ async def test_state_attributes(hass, config_entry, config, controller):
hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list
async def test_updates_start_from_signals(
async def test_updates_from_signals(
hass, config_entry, config, controller, favorites):
"""Tests dispatched signals update player."""
await setup_platform(hass, config_entry, config)
@ -102,48 +102,53 @@ async def test_updates_start_from_signals(
assert state.attributes[ATTR_MEDIA_DURATION] == 360
assert state.attributes[ATTR_MEDIA_POSITION] == 1
# Test controller player change updates
player.available = False
player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED, {})
await hass.async_block_till_done()
state = hass.states.get('media_player.test_player')
assert state.state == STATE_UNAVAILABLE
async def test_updates_from_connection_event(
hass, config_entry, config, controller, input_sources, caplog):
hass, config_entry, config, controller, caplog):
"""Tests player updates from connection event after connection failure."""
# Connected
await setup_platform(hass, config_entry, config)
player = controller.players[1]
event = asyncio.Event()
async def set_signal():
event.set()
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_UPDATED, set_signal)
# Connected
player.available = True
player.heos.dispatcher.send(
const.SIGNAL_HEOS_EVENT, const.EVENT_CONNECTED)
await hass.async_block_till_done()
await event.wait()
state = hass.states.get('media_player.test_player')
assert state.state == STATE_IDLE
assert player.refresh.call_count == 1
# Connected handles refresh failure
player.reset_mock()
player.refresh.side_effect = CommandError(None, "Failure", 1)
player.heos.dispatcher.send(
const.SIGNAL_HEOS_EVENT, const.EVENT_CONNECTED)
await hass.async_block_till_done()
state = hass.states.get('media_player.test_player')
assert player.refresh.call_count == 1
assert "Unable to refresh player" in caplog.text
assert controller.load_players.call_count == 1
# Disconnected
event.clear()
player.reset_mock()
controller.load_players.reset_mock()
player.available = False
player.heos.dispatcher.send(
const.SIGNAL_HEOS_EVENT, const.EVENT_DISCONNECTED)
await hass.async_block_till_done()
await event.wait()
state = hass.states.get('media_player.test_player')
assert state.state == STATE_UNAVAILABLE
assert player.refresh.call_count == 0
assert controller.load_players.call_count == 0
# Connected handles refresh failure
event.clear()
player.reset_mock()
controller.load_players.reset_mock()
controller.load_players.side_effect = CommandError(None, "Failure", 1)
player.available = True
player.heos.dispatcher.send(
const.SIGNAL_HEOS_EVENT, const.EVENT_CONNECTED)
await event.wait()
state = hass.states.get('media_player.test_player')
assert state.state == STATE_IDLE
assert controller.load_players.call_count == 1
assert "Unable to refresh players" in caplog.text
async def test_updates_from_sources_updated(
@ -156,7 +161,7 @@ async def test_updates_from_sources_updated(
async def set_signal():
event.set()
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_SOURCES_UPDATED, set_signal)
SIGNAL_HEOS_UPDATED, set_signal)
input_sources.clear()
player.heos.dispatcher.send(
@ -168,6 +173,65 @@ async def test_updates_from_sources_updated(
assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list
async def test_updates_from_players_changed(
hass, config_entry, config, controller, change_data,
caplog):
"""Test player updates from changes to available players."""
await setup_platform(hass, config_entry, config)
player = controller.players[1]
event = asyncio.Event()
async def set_signal():
event.set()
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_UPDATED, set_signal)
assert hass.states.get('media_player.test_player').state == STATE_IDLE
player.state = const.PLAY_STATE_PLAY
player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED,
change_data)
await event.wait()
assert hass.states.get('media_player.test_player').state == STATE_PLAYING
async def test_updates_from_players_changed_new_ids(
hass, config_entry, config, controller, change_data_mapped_ids,
caplog):
"""Test player updates from changes to available players."""
await setup_platform(hass, config_entry, config)
device_registry = await hass.helpers.device_registry.async_get_registry()
entity_registry = await hass.helpers.entity_registry.async_get_registry()
player = controller.players[1]
event = asyncio.Event()
# Assert device registry matches current id
assert device_registry.async_get_device(
{(DOMAIN, 1)}, [])
# Assert entity registry matches current id
assert entity_registry.async_get_entity_id(
MEDIA_PLAYER_DOMAIN, DOMAIN, '1') == "media_player.test_player"
# Trigger update
async def set_signal():
event.set()
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_UPDATED, set_signal)
player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED,
change_data_mapped_ids)
await event.wait()
# Assert device registry identifiers were updated
assert len(device_registry.devices) == 1
assert device_registry.async_get_device(
{(DOMAIN, 101)}, [])
# Assert entity registry unique id was updated
assert len(entity_registry.entities) == 1
assert entity_registry.async_get_entity_id(
MEDIA_PLAYER_DOMAIN, DOMAIN, '101') == "media_player.test_player"
async def test_updates_from_user_changed(
hass, config_entry, config, controller):
"""Tests player updates from changes in user."""
@ -178,7 +242,7 @@ async def test_updates_from_user_changed(
async def set_signal():
event.set()
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_SOURCES_UPDATED, set_signal)
SIGNAL_HEOS_UPDATED, set_signal)
controller.is_signed_in = False
controller.signed_in_username = None