Improve Sonos tests, begin adding coverage (#61198)
* Update entity registry handling * Add and use fixtures to test setup via config entry * Remove legacy redundant tests * Remove unnecessary mock_coro * Remove unnecessary namespace change * Move zeroconf payload to fixture * Begin adding Sonos to codecov * Mock proper return value * Revert return value for platformpull/61401/head
parent
af91addc6c
commit
9f3a4c3617
11
.coveragerc
11
.coveragerc
|
@ -1003,7 +1003,16 @@ omit =
|
|||
homeassistant/components/somfy/switch.py
|
||||
homeassistant/components/somfy_mylink/__init__.py
|
||||
homeassistant/components/somfy_mylink/cover.py
|
||||
homeassistant/components/sonos/*
|
||||
homeassistant/components/sonos/__init__.py
|
||||
homeassistant/components/sonos/alarms.py
|
||||
homeassistant/components/sonos/entity.py
|
||||
homeassistant/components/sonos/favorites.py
|
||||
homeassistant/components/sonos/helpers.py
|
||||
homeassistant/components/sonos/household_coordinator.py
|
||||
homeassistant/components/sonos/media_browser.py
|
||||
homeassistant/components/sonos/media_player.py
|
||||
homeassistant/components/sonos/speaker.py
|
||||
homeassistant/components/sonos/switch.py
|
||||
homeassistant/components/sony_projector/switch.py
|
||||
homeassistant/components/spc/*
|
||||
homeassistant/components/spider/*
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
"""Configuration for Sonos tests."""
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch as patch
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import ssdp
|
||||
from homeassistant.components import ssdp, zeroconf
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.components.sonos import DOMAIN
|
||||
from homeassistant.const import CONF_HOSTS
|
||||
|
@ -42,6 +42,37 @@ class SonosMockEvent:
|
|||
return self.variables[var_name]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def zeroconf_payload():
|
||||
"""Return a default zeroconf payload."""
|
||||
return zeroconf.ZeroconfServiceInfo(
|
||||
host="192.168.4.2",
|
||||
hostname="Sonos-aaa",
|
||||
name="Sonos-aaa@Living Room._sonos._tcp.local.",
|
||||
port=None,
|
||||
properties={"bootseq": "1234"},
|
||||
type="mock_type",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def async_autosetup_sonos(async_setup_sonos):
|
||||
"""Set up a Sonos integration instance on test run."""
|
||||
await async_setup_sonos()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def async_setup_sonos(hass, config_entry):
|
||||
"""Return a coroutine to set up a Sonos integration instance on demand."""
|
||||
|
||||
async def _wrapper():
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return _wrapper
|
||||
|
||||
|
||||
@pytest.fixture(name="config_entry")
|
||||
def config_entry_fixture():
|
||||
"""Create a mock Sonos config entry."""
|
||||
|
|
|
@ -37,21 +37,14 @@ async def test_user_form(discover_mock: MagicMock, hass: core.HomeAssistant):
|
|||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_zeroconf_form(hass: core.HomeAssistant):
|
||||
"""Test we pass sonos devices to the discovery manager."""
|
||||
async def test_zeroconf_form(hass: core.HomeAssistant, zeroconf_payload):
|
||||
"""Test we pass Zeroconf discoveries to the manager."""
|
||||
|
||||
mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock()
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data=zeroconf.ZeroconfServiceInfo(
|
||||
host="192.168.4.2",
|
||||
hostname="Sonos-aaa",
|
||||
name="Sonos-aaa@Living Room._sonos._tcp.local.",
|
||||
port=None,
|
||||
properties={"bootseq": "1234"},
|
||||
type="mock_type",
|
||||
),
|
||||
data=zeroconf_payload,
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] is None
|
||||
|
@ -128,21 +121,16 @@ async def test_zeroconf_sonos_v1(hass: core.HomeAssistant):
|
|||
assert len(mock_manager.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_zeroconf_form_not_sonos(hass: core.HomeAssistant):
|
||||
async def test_zeroconf_form_not_sonos(hass: core.HomeAssistant, zeroconf_payload):
|
||||
"""Test we abort on non-sonos devices."""
|
||||
mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock()
|
||||
|
||||
zeroconf_payload.hostname = "not-aaa"
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data=zeroconf.ZeroconfServiceInfo(
|
||||
host="192.168.4.2",
|
||||
hostname="not-aaa",
|
||||
name="mock_name",
|
||||
port=None,
|
||||
properties={"bootseq": "1234"},
|
||||
type="mock_type",
|
||||
),
|
||||
data=zeroconf_payload,
|
||||
)
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "not_sonos_device"
|
||||
|
|
|
@ -5,14 +5,11 @@ from homeassistant import config_entries, data_entry_flow
|
|||
from homeassistant.components import sonos
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
async def test_creating_entry_sets_up_media_player(hass):
|
||||
"""Test setting up Sonos loads the media player."""
|
||||
with patch(
|
||||
"homeassistant.components.sonos.media_player.async_setup_entry",
|
||||
return_value=mock_coro(True),
|
||||
) as mock_setup, patch("soco.discover", return_value=True):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
sonos.DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
|
@ -32,7 +29,8 @@ async def test_creating_entry_sets_up_media_player(hass):
|
|||
async def test_configuring_sonos_creates_entry(hass):
|
||||
"""Test that specifying config will create an entry."""
|
||||
with patch(
|
||||
"homeassistant.components.sonos.async_setup_entry", return_value=mock_coro(True)
|
||||
"homeassistant.components.sonos.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup, patch("soco.discover", return_value=True):
|
||||
await async_setup_component(
|
||||
hass,
|
||||
|
@ -47,7 +45,8 @@ async def test_configuring_sonos_creates_entry(hass):
|
|||
async def test_not_configuring_sonos_not_creates_entry(hass):
|
||||
"""Test that no config will not create an entry."""
|
||||
with patch(
|
||||
"homeassistant.components.sonos.async_setup_entry", return_value=mock_coro(True)
|
||||
"homeassistant.components.sonos.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup, patch("soco.discover", return_value=True):
|
||||
await async_setup_component(hass, sonos.DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -9,54 +9,23 @@ from homeassistant.const import STATE_IDLE
|
|||
from homeassistant.core import Context
|
||||
from homeassistant.exceptions import Unauthorized
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
async def setup_platform(hass, config_entry, config):
|
||||
"""Set up the media player platform for testing."""
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_async_setup_entry_hosts(hass, config_entry, config, soco):
|
||||
"""Test static setup."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
speakers = list(hass.data[DATA_SONOS].discovered.values())
|
||||
speaker = speakers[0]
|
||||
assert speaker.soco == soco
|
||||
|
||||
media_player = hass.states.get("media_player.zone_a")
|
||||
assert media_player.state == STATE_IDLE
|
||||
|
||||
|
||||
async def test_async_setup_entry_discover(hass, config_entry, discover):
|
||||
"""Test discovery setup."""
|
||||
await setup_platform(hass, config_entry, {})
|
||||
|
||||
speakers = list(hass.data[DATA_SONOS].discovered.values())
|
||||
speaker = speakers[0]
|
||||
assert speaker.soco.uid == "RINCON_test"
|
||||
|
||||
media_player = hass.states.get("media_player.zone_a")
|
||||
assert media_player.state == STATE_IDLE
|
||||
|
||||
|
||||
async def test_discovery_ignore_unsupported_device(hass, config_entry, soco, caplog):
|
||||
async def test_discovery_ignore_unsupported_device(
|
||||
hass, async_setup_sonos, soco, caplog
|
||||
):
|
||||
"""Test discovery setup."""
|
||||
message = f"GetVolume not supported on {soco.ip_address}"
|
||||
type(soco).volume = PropertyMock(side_effect=NotSupportedException(message))
|
||||
await setup_platform(hass, config_entry, {})
|
||||
|
||||
await async_setup_sonos()
|
||||
|
||||
assert message in caplog.text
|
||||
assert not hass.data[DATA_SONOS].discovered
|
||||
|
||||
|
||||
async def test_services(hass, config_entry, config, hass_read_only_user):
|
||||
async def test_services(hass, async_autosetup_sonos, hass_read_only_user):
|
||||
"""Test join/unjoin requires control access."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
with pytest.raises(Unauthorized):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
@ -67,10 +36,8 @@ async def test_services(hass, config_entry, config, hass_read_only_user):
|
|||
)
|
||||
|
||||
|
||||
async def test_device_registry(hass, config_entry, config, soco):
|
||||
async def test_device_registry(hass, async_autosetup_sonos, soco):
|
||||
"""Test sonos device registered in the device registry."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
reg_device = device_registry.async_get_device(
|
||||
identifiers={("sonos", "RINCON_test")}
|
||||
|
@ -86,10 +53,8 @@ async def test_device_registry(hass, config_entry, config, soco):
|
|||
assert reg_device.name == "Zone A"
|
||||
|
||||
|
||||
async def test_entity_basic(hass, config_entry, discover):
|
||||
async def test_entity_basic(hass, async_autosetup_sonos, discover):
|
||||
"""Test basic state and attributes."""
|
||||
await setup_platform(hass, config_entry, {})
|
||||
|
||||
state = hass.states.get("media_player.zone_a")
|
||||
assert state.state == STATE_IDLE
|
||||
attributes = state.attributes
|
||||
|
|
|
@ -14,16 +14,9 @@ from homeassistant.components.plex.const import PLEX_URI_SCHEME
|
|||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .test_media_player import setup_platform
|
||||
|
||||
|
||||
async def test_plex_play_media(
|
||||
hass,
|
||||
config_entry,
|
||||
config,
|
||||
):
|
||||
async def test_plex_play_media(hass, async_autosetup_sonos):
|
||||
"""Test playing media via the Plex integration."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
media_player = "media_player.zone_a"
|
||||
media_content_id = (
|
||||
'{"library_name": "Music", "artist_name": "Artist", "album_name": "Album"}'
|
||||
|
|
|
@ -1,48 +1,36 @@
|
|||
"""Tests for the Sonos battery sensor platform."""
|
||||
from soco.exceptions import NotSupportedException
|
||||
|
||||
from homeassistant.components.sonos import DOMAIN
|
||||
from homeassistant.components.sonos.binary_sensor import ATTR_BATTERY_POWER_SOURCE
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.helpers import entity_registry as ent_reg
|
||||
|
||||
|
||||
async def setup_platform(hass, config_entry, config):
|
||||
"""Set up the media player platform for testing."""
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_entity_registry_unsupported(hass, config_entry, config, soco):
|
||||
async def test_entity_registry_unsupported(hass, async_setup_sonos, soco):
|
||||
"""Test sonos device without battery registered in the device registry."""
|
||||
soco.get_battery_info.side_effect = NotSupportedException
|
||||
|
||||
await setup_platform(hass, config_entry, config)
|
||||
await async_setup_sonos()
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
assert "media_player.zone_a" in entity_registry.entities
|
||||
assert "sensor.zone_a_battery" not in entity_registry.entities
|
||||
assert "binary_sensor.zone_a_power" not in entity_registry.entities
|
||||
|
||||
|
||||
async def test_entity_registry_supported(hass, config_entry, config, soco):
|
||||
async def test_entity_registry_supported(hass, async_autosetup_sonos, soco):
|
||||
"""Test sonos device with battery registered in the device registry."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
assert "media_player.zone_a" in entity_registry.entities
|
||||
assert "sensor.zone_a_battery" in entity_registry.entities
|
||||
assert "binary_sensor.zone_a_power" in entity_registry.entities
|
||||
|
||||
|
||||
async def test_battery_attributes(hass, config_entry, config, soco):
|
||||
async def test_battery_attributes(hass, async_autosetup_sonos, soco):
|
||||
"""Test sonos device with battery state."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
battery = entity_registry.entities["sensor.zone_a_battery"]
|
||||
battery_state = hass.states.get(battery.entity_id)
|
||||
|
@ -57,16 +45,16 @@ async def test_battery_attributes(hass, config_entry, config, soco):
|
|||
)
|
||||
|
||||
|
||||
async def test_battery_on_S1(hass, config_entry, config, soco, battery_event):
|
||||
async def test_battery_on_S1(hass, async_setup_sonos, soco, battery_event):
|
||||
"""Test battery state updates on a Sonos S1 device."""
|
||||
soco.get_battery_info.return_value = {}
|
||||
|
||||
await setup_platform(hass, config_entry, config)
|
||||
await async_setup_sonos()
|
||||
|
||||
subscription = soco.deviceProperties.subscribe.return_value
|
||||
sub_callback = subscription.callback
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
assert "sensor.zone_a_battery" not in entity_registry.entities
|
||||
assert "binary_sensor.zone_a_power" not in entity_registry.entities
|
||||
|
@ -86,12 +74,12 @@ async def test_battery_on_S1(hass, config_entry, config, soco, battery_event):
|
|||
|
||||
|
||||
async def test_device_payload_without_battery(
|
||||
hass, config_entry, config, soco, battery_event, caplog
|
||||
hass, async_setup_sonos, soco, battery_event, caplog
|
||||
):
|
||||
"""Test device properties event update without battery info."""
|
||||
soco.get_battery_info.return_value = None
|
||||
|
||||
await setup_platform(hass, config_entry, config)
|
||||
await async_setup_sonos()
|
||||
|
||||
subscription = soco.deviceProperties.subscribe.return_value
|
||||
sub_callback = subscription.callback
|
||||
|
@ -106,12 +94,12 @@ async def test_device_payload_without_battery(
|
|||
|
||||
|
||||
async def test_device_payload_without_battery_and_ignored_keys(
|
||||
hass, config_entry, config, soco, battery_event, caplog
|
||||
hass, async_setup_sonos, soco, battery_event, caplog
|
||||
):
|
||||
"""Test device properties event update without battery info and ignored keys."""
|
||||
soco.get_battery_info.return_value = None
|
||||
|
||||
await setup_platform(hass, config_entry, config)
|
||||
await async_setup_sonos()
|
||||
|
||||
subscription = soco.deviceProperties.subscribe.return_value
|
||||
sub_callback = subscription.callback
|
||||
|
@ -125,11 +113,9 @@ async def test_device_payload_without_battery_and_ignored_keys(
|
|||
assert ignored_payload not in caplog.text
|
||||
|
||||
|
||||
async def test_audio_input_sensor(hass, config_entry, config, soco):
|
||||
async def test_audio_input_sensor(hass, async_autosetup_sonos, soco):
|
||||
"""Test sonos device with battery state."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
audio_input_sensor = entity_registry.entities["sensor.zone_a_audio_input_format"]
|
||||
audio_input_state = hass.states.get(audio_input_sensor.entity_id)
|
||||
|
|
|
@ -3,7 +3,6 @@ from copy import copy
|
|||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.sonos import DOMAIN
|
||||
from homeassistant.components.sonos.const import DATA_SONOS_DISCOVERY_MANAGER
|
||||
from homeassistant.components.sonos.switch import (
|
||||
ATTR_DURATION,
|
||||
|
@ -15,8 +14,7 @@ from homeassistant.components.sonos.switch import (
|
|||
)
|
||||
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
|
||||
from homeassistant.const import ATTR_TIME, STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.helpers import entity_registry as ent_reg
|
||||
from homeassistant.util import dt
|
||||
|
||||
from .conftest import SonosMockEvent
|
||||
|
@ -24,18 +22,9 @@ from .conftest import SonosMockEvent
|
|||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
async def setup_platform(hass, config_entry, config):
|
||||
"""Set up the switch platform for testing."""
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_entity_registry(hass, config_entry, config):
|
||||
async def test_entity_registry(hass, async_autosetup_sonos):
|
||||
"""Test sonos device with alarm registered in the device registry."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
assert "media_player.zone_a" in entity_registry.entities
|
||||
assert "switch.sonos_alarm_14" in entity_registry.entities
|
||||
|
@ -47,11 +36,9 @@ async def test_entity_registry(hass, config_entry, config):
|
|||
assert "switch.sonos_zone_a_touch_controls" in entity_registry.entities
|
||||
|
||||
|
||||
async def test_switch_attributes(hass, config_entry, config, soco):
|
||||
async def test_switch_attributes(hass, async_autosetup_sonos, soco):
|
||||
"""Test for correct Sonos switch states."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
alarm = entity_registry.entities["switch.sonos_alarm_14"]
|
||||
alarm_state = hass.states.get(alarm.entity_id)
|
||||
|
@ -125,15 +112,15 @@ async def test_switch_attributes(hass, config_entry, config, soco):
|
|||
|
||||
|
||||
async def test_alarm_create_delete(
|
||||
hass, config_entry, config, soco, alarm_clock, alarm_clock_extended, alarm_event
|
||||
hass, async_setup_sonos, soco, alarm_clock, alarm_clock_extended, alarm_event
|
||||
):
|
||||
"""Test for correct creation and deletion of alarms during runtime."""
|
||||
entity_registry = async_get_entity_registry(hass)
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
one_alarm = copy(alarm_clock.ListAlarms.return_value)
|
||||
two_alarms = copy(alarm_clock_extended.ListAlarms.return_value)
|
||||
|
||||
await setup_platform(hass, config_entry, config)
|
||||
await async_setup_sonos()
|
||||
|
||||
assert "switch.sonos_alarm_14" in entity_registry.entities
|
||||
assert "switch.sonos_alarm_15" not in entity_registry.entities
|
||||
|
|
Loading…
Reference in New Issue