diff --git a/.coveragerc b/.coveragerc index 4bb0a13751a..2afc0300d8e 100644 --- a/.coveragerc +++ b/.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/* diff --git a/tests/components/sonos/conftest.py b/tests/components/sonos/conftest.py index f7f8d67589f..f9adf1be8f9 100644 --- a/tests/components/sonos/conftest.py +++ b/tests/components/sonos/conftest.py @@ -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.""" diff --git a/tests/components/sonos/test_config_flow.py b/tests/components/sonos/test_config_flow.py index 9677ee73759..eee644abeba 100644 --- a/tests/components/sonos/test_config_flow.py +++ b/tests/components/sonos/test_config_flow.py @@ -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" diff --git a/tests/components/sonos/test_init.py b/tests/components/sonos/test_init.py index bf4b5d5e7cc..71f89f6d880 100644 --- a/tests/components/sonos/test_init.py +++ b/tests/components/sonos/test_init.py @@ -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() diff --git a/tests/components/sonos/test_media_player.py b/tests/components/sonos/test_media_player.py index 9fb1d7639eb..a73ff22aba6 100644 --- a/tests/components/sonos/test_media_player.py +++ b/tests/components/sonos/test_media_player.py @@ -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 diff --git a/tests/components/sonos/test_plex_playback.py b/tests/components/sonos/test_plex_playback.py index f9bedbfe1f7..d9f809e8395 100644 --- a/tests/components/sonos/test_plex_playback.py +++ b/tests/components/sonos/test_plex_playback.py @@ -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"}' diff --git a/tests/components/sonos/test_sensor.py b/tests/components/sonos/test_sensor.py index a45d587cc08..633bf750962 100644 --- a/tests/components/sonos/test_sensor.py +++ b/tests/components/sonos/test_sensor.py @@ -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) diff --git a/tests/components/sonos/test_switch.py b/tests/components/sonos/test_switch.py index 9c25e2b8cc7..63ac9de6065 100644 --- a/tests/components/sonos/test_switch.py +++ b/tests/components/sonos/test_switch.py @@ -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