From 52fe1328f6b13a2d2844d2b465c30953020988bd Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 12 Feb 2020 16:12:14 -0500 Subject: [PATCH] ZHA tests refactoring (#31744) * Refactor ZHA fixtures. Patch Zigpy radio libs instead of ZHA when setting up fixtures. Use new fixtures for binary_sensor.zha platform. * Update ZHA api tests. * Update ZHA channels and discovery tests. * Update ZHA cover tests. * Update device action/trigger tests. * Update device_tracker.zha platform tests. * Update fan.zha platform tests. * Update ZHA gateway tests. * Update lock.zha platform tests. * Update switch.zha platform tests. * Update sensor.zha platform tests. * Update light.zha platform tests. * Use MockConfigEntry. * Address PR comments. --- tests/components/zha/common.py | 64 +++++---- tests/components/zha/conftest.py | 148 ++++++++++---------- tests/components/zha/test_api.py | 6 +- tests/components/zha/test_binary_sensor.py | 18 +-- tests/components/zha/test_channels.py | 11 +- tests/components/zha/test_cover.py | 16 +-- tests/components/zha/test_device_action.py | 11 +- tests/components/zha/test_device_tracker.py | 16 +-- tests/components/zha/test_device_trigger.py | 7 +- tests/components/zha/test_discover.py | 21 +-- tests/components/zha/test_fan.py | 54 +++---- tests/components/zha/test_gateway.py | 8 +- tests/components/zha/test_light.py | 17 +-- tests/components/zha/test_lock.py | 11 +- tests/components/zha/test_sensor.py | 17 +-- tests/components/zha/test_switch.py | 14 +- 16 files changed, 187 insertions(+), 252 deletions(-) diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index 97668bef2ea..a9f040eda68 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -10,27 +10,10 @@ import zigpy.zcl.clusters.general import zigpy.zcl.foundation as zcl_f import zigpy.zdo.types -from homeassistant.components.zha.core.const import ( - DATA_ZHA, - DATA_ZHA_BRIDGE_ID, - DATA_ZHA_CONFIG, - DATA_ZHA_DISPATCHERS, -) +import homeassistant.components.zha.core.const as zha_const from homeassistant.util import slugify -class FakeApplication: - """Fake application for mocking zigpy.""" - - def __init__(self): - """Init fake application.""" - self.ieee = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32") - self.nwk = 0x087D - - -APPLICATION = FakeApplication() - - class FakeEndpoint: """Fake endpoint for moking zigpy.""" @@ -71,14 +54,15 @@ def patch_cluster(cluster): cluster.read_attributes = CoroutineMock() cluster.read_attributes_raw = Mock() cluster.unbind = CoroutineMock(return_value=[0]) + cluster.write_attributes = CoroutineMock(return_value=[0]) class FakeDevice: """Fake device for mocking zigpy.""" - def __init__(self, ieee, manufacturer, model, node_desc=None): + def __init__(self, app, ieee, manufacturer, model, node_desc=None): """Init fake device.""" - self._application = APPLICATION + self._application = app self.ieee = zigpy.types.EUI64.convert(ieee) self.nwk = 0xB79C self.zdo = Mock() @@ -98,6 +82,14 @@ class FakeDevice: self.node_desc = zigpy.zdo.types.NodeDescriptor.deserialize(node_desc)[0] +def get_zha_gateway(hass): + """Return ZHA gateway from hass.data.""" + try: + return hass.data[zha_const.DATA_ZHA][zha_const.DATA_ZHA_GATEWAY] + except KeyError: + return None + + def make_attribute(attrid, value, status=0): """Make an attribute.""" attr = zcl_f.Attribute() @@ -107,14 +99,6 @@ def make_attribute(attrid, value, status=0): return attr -async def async_setup_entry(hass, config_entry): - """Mock setup entry for zha.""" - hass.data[DATA_ZHA][DATA_ZHA_CONFIG] = {} - hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS] = [] - hass.data[DATA_ZHA][DATA_ZHA_BRIDGE_ID] = APPLICATION.ieee - return True - - async def find_entity_id(domain, zha_device, hass): """Find the entity id under the testing. @@ -133,7 +117,7 @@ async def find_entity_id(domain, zha_device, hass): return None -async def async_enable_traffic(hass, zha_gateway, zha_devices): +async def async_enable_traffic(hass, zha_devices): """Allow traffic to flow through the gateway and the zha device.""" for zha_device in zha_devices: zha_device.update_available(True) @@ -147,3 +131,25 @@ def make_zcl_header(command_id: int, global_command: bool = True) -> zcl_f.ZCLHe else: frc = zcl_f.FrameControl(zcl_f.FrameType.CLUSTER_COMMAND) return zcl_f.ZCLHeader(frc, tsn=1, command_id=command_id) + + +def reset_clusters(clusters): + """Reset mocks on cluster.""" + for cluster in clusters: + cluster.bind.reset_mock() + cluster.configure_reporting.reset_mock() + cluster.write_attributes.reset_mock() + + +async def async_test_rejoin(hass, zigpy_device, clusters, report_counts, ep_id=1): + """Test device rejoins.""" + reset_clusters(clusters) + + zha_gateway = get_zha_gateway(hass) + await zha_gateway.async_device_initialized(zigpy_device) + await hass.async_block_till_done() + for cluster, reports in zip(clusters, report_counts): + assert cluster.bind.call_count == 1 + assert cluster.bind.await_count == 1 + assert cluster.configure_reporting.call_count == reports + assert cluster.configure_reporting.await_count == reports diff --git a/tests/components/zha/conftest.py b/tests/components/zha/conftest.py index f54c6ca8602..26dd2b5da5c 100644 --- a/tests/components/zha/conftest.py +++ b/tests/components/zha/conftest.py @@ -1,19 +1,18 @@ """Test configuration for the ZHA component.""" -import functools from unittest import mock -from unittest.mock import patch import asynctest import pytest import zigpy from zigpy.application import ControllerApplication +import zigpy.group +import zigpy.types -from homeassistant.components.zha.core.const import COMPONENTS, DATA_ZHA, DOMAIN -from homeassistant.components.zha.core.gateway import ZHAGateway -from homeassistant.components.zha.core.store import async_get_registry -from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg +import homeassistant.components.zha.core.const as zha_const +import homeassistant.components.zha.core.registries as zha_regs +from homeassistant.setup import async_setup_component -from .common import FakeDevice, FakeEndpoint, async_setup_entry +from .common import FakeDevice, FakeEndpoint, get_zha_gateway from tests.common import MockConfigEntry @@ -21,52 +20,64 @@ FIXTURE_GRP_ID = 0x1001 FIXTURE_GRP_NAME = "fixture group" -@pytest.fixture(name="config_entry") -async def config_entry_fixture(hass): - """Fixture representing a config entry.""" - config_entry = MockConfigEntry(domain=DOMAIN) - config_entry.add_to_hass(hass) - return config_entry +@pytest.fixture +def zigpy_app_controller(): + """Zigpy ApplicationController fixture.""" + app = mock.MagicMock(spec_set=ControllerApplication) + app.startup = asynctest.CoroutineMock() + app.shutdown = asynctest.CoroutineMock() + groups = zigpy.group.Groups(app) + groups.add_group(FIXTURE_GRP_ID, FIXTURE_GRP_NAME, suppress_event=True) + app.configure_mock(groups=groups) + type(app).ieee = mock.PropertyMock() + app.ieee.return_value = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32") + type(app).nwk = mock.PropertyMock(return_value=zigpy.types.NWK(0x0000)) + type(app).devices = mock.PropertyMock(return_value={}) + return app @pytest.fixture -async def setup_zha(hass, config_entry): - """Load the ZHA component. - - This will init the ZHA component. It loads the component in HA so that - we can test the domains that ZHA supports without actually having a zigbee - network running. - """ - # this prevents needing an actual radio and zigbee network available - with patch("homeassistant.components.zha.async_setup_entry", async_setup_entry): - hass.data[DATA_ZHA] = {} - - # init ZHA - await hass.config_entries.async_forward_entry_setup(config_entry, DOMAIN) - await hass.async_block_till_done() +def zigpy_radio(): + """Zigpy radio mock.""" + radio = mock.MagicMock() + radio.connect = asynctest.CoroutineMock() + return radio -@pytest.fixture(name="zha_gateway") -async def zha_gateway_fixture(hass, config_entry, setup_zha): - """Fixture representing a zha gateway. +@pytest.fixture(name="config_entry") +async def config_entry_fixture(hass): + """Fixture representing a config entry.""" + entry = MockConfigEntry( + version=1, + domain=zha_const.DOMAIN, + data={ + zha_const.CONF_BAUDRATE: zha_const.DEFAULT_BAUDRATE, + zha_const.CONF_RADIO_TYPE: "MockRadio", + zha_const.CONF_USB_PATH: "/dev/ttyUSB0", + }, + ) + entry.add_to_hass(hass) + return entry - Create a ZHAGateway object that can be used to interact with as if we - had a real zigbee network running. - """ - for component in COMPONENTS: - hass.data[DATA_ZHA][component] = hass.data[DATA_ZHA].get(component, {}) - zha_storage = await async_get_registry(hass) - dev_reg = await get_dev_reg(hass) - gateway = ZHAGateway(hass, {}, config_entry) - gateway.zha_storage = zha_storage - gateway.ha_device_registry = dev_reg - gateway.application_controller = mock.MagicMock(spec_set=ControllerApplication) - groups = zigpy.group.Groups(gateway.application_controller) - groups.add_listener(gateway) - groups.add_group(FIXTURE_GRP_ID, FIXTURE_GRP_NAME, suppress_event=True) - gateway.application_controller.configure_mock(groups=groups) - gateway._initialize_groups() - return gateway + +@pytest.fixture +def setup_zha(hass, config_entry, zigpy_app_controller, zigpy_radio): + """Set up ZHA component.""" + zha_config = {zha_const.DOMAIN: {zha_const.CONF_ENABLE_QUIRKS: False}} + + radio_details = { + zha_const.ZHA_GW_RADIO: mock.MagicMock(return_value=zigpy_radio), + zha_const.CONTROLLER: mock.MagicMock(return_value=zigpy_app_controller), + zha_const.ZHA_GW_RADIO_DESCRIPTION: "mock radio", + } + + async def _setup(): + with mock.patch.dict(zha_regs.RADIO_TYPES, {"MockRadio": radio_details}): + status = await async_setup_component(hass, zha_const.DOMAIN, zha_config) + assert status is True + await hass.async_block_till_done() + + return _setup @pytest.fixture @@ -86,7 +97,7 @@ def channel(): @pytest.fixture -def zigpy_device_mock(): +def zigpy_device_mock(zigpy_app_controller): """Make a fake device using the specified cluster classes.""" def _mock_dev( @@ -94,10 +105,12 @@ def zigpy_device_mock(): ieee="00:0d:6f:00:0a:90:69:e7", manufacturer="FakeManufacturer", model="FakeModel", - node_desc=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00", + node_descriptor=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00", ): """Make a fake device using the specified cluster classes.""" - device = FakeDevice(ieee, manufacturer, model, node_desc) + device = FakeDevice( + zigpy_app_controller, ieee, manufacturer, model, node_descriptor + ) for epid, ep in endpoints.items(): endpoint = FakeEndpoint(manufacturer, model, epid) endpoint.device = device @@ -119,19 +132,13 @@ def zigpy_device_mock(): @pytest.fixture -def _zha_device_restored_or_joined(hass, zha_gateway, config_entry): - """Make a restored or joined ZHA devices.""" +def zha_device_joined(hass, setup_zha): + """Return a newly joined ZHA device.""" - async def _zha_device(is_new_join, zigpy_dev): - if is_new_join: - for cmp in COMPONENTS: - await hass.config_entries.async_forward_entry_setup(config_entry, cmp) - await hass.async_block_till_done() - await zha_gateway.async_device_initialized(zigpy_dev) - else: - await zha_gateway.async_device_restored(zigpy_dev) - for cmp in COMPONENTS: - await hass.config_entries.async_forward_entry_setup(config_entry, cmp) + async def _zha_device(zigpy_dev): + await setup_zha() + zha_gateway = get_zha_gateway(hass) + await zha_gateway.async_device_initialized(zigpy_dev) await hass.async_block_till_done() return zha_gateway.get_device(zigpy_dev.ieee) @@ -139,17 +146,16 @@ def _zha_device_restored_or_joined(hass, zha_gateway, config_entry): @pytest.fixture -def zha_device_joined(_zha_device_restored_or_joined): - """Return a newly joined ZHA device.""" - - return functools.partial(_zha_device_restored_or_joined, True) - - -@pytest.fixture -def zha_device_restored(_zha_device_restored_or_joined): +def zha_device_restored(hass, zigpy_app_controller, setup_zha): """Return a restored ZHA device.""" - return functools.partial(_zha_device_restored_or_joined, False) + async def _zha_device(zigpy_dev): + zigpy_app_controller.devices[zigpy_dev.ieee] = zigpy_dev + await setup_zha() + zha_gateway = hass.data[zha_const.DATA_ZHA][zha_const.DATA_ZHA_GATEWAY] + return zha_gateway.get_device(zigpy_dev.ieee) + + return _zha_device @pytest.fixture(params=["zha_device_joined", "zha_device_restored"]) diff --git a/tests/components/zha/test_api.py b/tests/components/zha/test_api.py index c61ecfa8c71..b67a39cd3ab 100644 --- a/tests/components/zha/test_api.py +++ b/tests/components/zha/test_api.py @@ -28,7 +28,7 @@ IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8" @pytest.fixture -async def device_switch(hass, zha_gateway, zigpy_device_mock, zha_device_joined): +async def device_switch(hass, zigpy_device_mock, zha_device_joined): """Test zha switch platform.""" zigpy_device = zigpy_device_mock( @@ -47,7 +47,7 @@ async def device_switch(hass, zha_gateway, zigpy_device_mock, zha_device_joined) @pytest.fixture -async def device_groupable(hass, zha_gateway, zigpy_device_mock, zha_device_joined): +async def device_groupable(hass, zigpy_device_mock, zha_device_joined): """Test zha light platform.""" zigpy_device = zigpy_device_mock( @@ -78,7 +78,7 @@ async def zha_client(hass, hass_ws_client, device_switch, device_groupable): return await hass_ws_client(hass) -async def test_device_clusters(hass, config_entry, zha_gateway, zha_client): +async def test_device_clusters(hass, zha_client): """Test getting device cluster info.""" await zha_client.send_json( {ID: 5, TYPE: "zha/devices/clusters", ATTR_IEEE: IEEE_SWITCH_DEVICE} diff --git a/tests/components/zha/test_binary_sensor.py b/tests/components/zha/test_binary_sensor.py index 4be5f3c1f39..a22bfa54dae 100644 --- a/tests/components/zha/test_binary_sensor.py +++ b/tests/components/zha/test_binary_sensor.py @@ -9,6 +9,7 @@ from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, @@ -65,13 +66,12 @@ async def async_test_iaszone_on_off(hass, cluster, entity_id): @pytest.mark.parametrize( "device, on_off_test, cluster_name, reporting", [ - (DEVICE_IAS, async_test_iaszone_on_off, "ias_zone", False), - (DEVICE_OCCUPANCY, async_test_binary_sensor_on_off, "occupancy", True), + (DEVICE_IAS, async_test_iaszone_on_off, "ias_zone", (0,)), + (DEVICE_OCCUPANCY, async_test_binary_sensor_on_off, "occupancy", (1,)), ], ) async def test_binary_sensor( hass, - zha_gateway, zigpy_device_mock, zha_device_joined_restored, device, @@ -89,7 +89,7 @@ async def test_binary_sensor( # test that the sensors exist and are in the unavailable state assert hass.states.get(entity_id).state == STATE_UNAVAILABLE - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the sensors exist and are in the off state assert hass.states.get(entity_id).state == STATE_OFF @@ -99,13 +99,5 @@ async def test_binary_sensor( await on_off_test(hass, cluster, entity_id) # test rejoin - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_device) - await hass.async_block_till_done() + await async_test_rejoin(hass, zigpy_device, [cluster], reporting) assert hass.states.get(entity_id).state == STATE_OFF - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - if reporting: - assert cluster.configure_reporting.call_count > 0 - assert cluster.configure_reporting.await_count > 0 diff --git a/tests/components/zha/test_channels.py b/tests/components/zha/test_channels.py index c5ad4d3fbc0..ee493ca01a7 100644 --- a/tests/components/zha/test_channels.py +++ b/tests/components/zha/test_channels.py @@ -6,6 +6,8 @@ import homeassistant.components.zha.core.channels as channels import homeassistant.components.zha.core.device as zha_device import homeassistant.components.zha.core.registries as registries +from .common import get_zha_gateway + @pytest.fixture def ieee(): @@ -19,6 +21,13 @@ def nwk(): return t.NWK(0xBEEF) +@pytest.fixture +async def zha_gateway(hass, setup_zha): + """Return ZhaGateway fixture.""" + await setup_zha() + return get_zha_gateway(hass) + + @pytest.mark.parametrize( "cluster_id, bind_count, attrs", [ @@ -63,7 +72,7 @@ def nwk(): ], ) async def test_in_channel_config( - cluster_id, bind_count, attrs, zha_gateway, hass, zigpy_device_mock + cluster_id, bind_count, attrs, hass, zigpy_device_mock, zha_gateway ): """Test ZHA core channel configuration for input clusters.""" zigpy_dev = zigpy_device_mock( diff --git a/tests/components/zha/test_cover.py b/tests/components/zha/test_cover.py index 321ad95e73e..e5883605e34 100644 --- a/tests/components/zha/test_cover.py +++ b/tests/components/zha/test_cover.py @@ -12,6 +12,7 @@ from homeassistant.const import STATE_CLOSED, STATE_OPEN, STATE_UNAVAILABLE from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, @@ -37,9 +38,7 @@ def zigpy_cover_device(zigpy_device_mock): @asynctest.patch( "homeassistant.components.zha.core.channels.closures.WindowCovering.async_initialize" ) -async def test_cover( - m1, hass, zha_gateway, zha_device_joined_restored, zigpy_cover_device -): +async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): """Test zha cover platform.""" async def get_chan_attr(*args, **kwargs): @@ -62,7 +61,7 @@ async def test_cover( assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and device - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) await hass.async_block_till_done() attr = make_attribute(8, 100) @@ -132,12 +131,5 @@ async def test_cover( ) # test rejoin - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_cover_device) - await hass.async_block_till_done() + await async_test_rejoin(hass, zigpy_cover_device, [cluster], (1,)) assert hass.states.get(entity_id).state == STATE_OPEN - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - assert cluster.configure_reporting.call_count == 1 - assert cluster.configure_reporting.await_count == 1 diff --git a/tests/components/zha/test_device_action.py b/tests/components/zha/test_device_action.py index 4f41878f952..8866e6cff55 100644 --- a/tests/components/zha/test_device_action.py +++ b/tests/components/zha/test_device_action.py @@ -23,13 +23,7 @@ COMMAND_SINGLE = "single" @pytest.fixture -def calls(hass): - """Track calls to a mock service.""" - return async_mock_service(hass, "zha", "warning_device_warn") - - -@pytest.fixture -async def device_ias(hass, zha_gateway, zigpy_device_mock, zha_device_joined_restored): +async def device_ias(hass, zigpy_device_mock, zha_device_joined_restored): """IAS device fixture.""" clusters = [general.Basic, security.IasZone, security.IasWd] @@ -67,7 +61,7 @@ async def test_get_actions(hass, device_ias): assert actions == expected_actions -async def test_action(hass, calls, device_ias): +async def test_action(hass, device_ias): """Test for executing a zha device action.""" zigpy_device, zha_device = device_ias @@ -108,6 +102,7 @@ async def test_action(hass, calls, device_ias): ) await hass.async_block_till_done() + calls = async_mock_service(hass, DOMAIN, "warning_device_warn") channel = {ch.name: ch for ch in zha_device.all_channels}[CHANNEL_EVENT_RELAY] channel.zha_send_event(channel.cluster, COMMAND_SINGLE, []) diff --git a/tests/components/zha/test_device_tracker.py b/tests/components/zha/test_device_tracker.py index fe5661c4776..3782cdc09a7 100644 --- a/tests/components/zha/test_device_tracker.py +++ b/tests/components/zha/test_device_tracker.py @@ -15,6 +15,7 @@ import homeassistant.util.dt as dt_util from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, @@ -42,9 +43,7 @@ def zigpy_device_dt(zigpy_device_mock): return zigpy_device_mock(endpoints) -async def test_device_tracker( - hass, zha_gateway, zha_device_joined_restored, zigpy_device_dt -): +async def test_device_tracker(hass, zha_device_joined_restored, zigpy_device_dt): """Test zha device tracker platform.""" zha_device = await zha_device_joined_restored(zigpy_device_dt) @@ -61,7 +60,7 @@ async def test_device_tracker( await hass.async_block_till_done() # allow traffic to flow through the gateway and device - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the state has changed from unavailable to not home assert hass.states.get(entity_id).state == STATE_NOT_HOME @@ -88,12 +87,5 @@ async def test_device_tracker( assert entity.battery_level == 100 # test adding device tracker to the network and HA - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_device_dt) - await hass.async_block_till_done() + await async_test_rejoin(hass, zigpy_device_dt, [cluster], (2,)) assert hass.states.get(entity_id).state == STATE_HOME - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - assert cluster.configure_reporting.call_count == 2 - assert cluster.configure_reporting.await_count == 2 diff --git a/tests/components/zha/test_device_trigger.py b/tests/components/zha/test_device_trigger.py index 56f5c6c85ba..4bb7567d1e6 100644 --- a/tests/components/zha/test_device_trigger.py +++ b/tests/components/zha/test_device_trigger.py @@ -39,8 +39,8 @@ def calls(hass): return async_mock_service(hass, "test", "automation") -@pytest.fixture(params=["zha_device_joined", "zha_device_restored"]) -async def mock_devices(hass, zha_gateway, zigpy_device_mock, request): +@pytest.fixture +async def mock_devices(hass, zigpy_device_mock, zha_device_joined_restored): """IAS device fixture.""" zigpy_device = zigpy_device_mock( @@ -53,8 +53,7 @@ async def mock_devices(hass, zha_gateway, zigpy_device_mock, request): }, ) - join_or_restore = request.getfixturevalue(request.param) - zha_device = await join_or_restore(zigpy_device) + zha_device = await zha_device_joined_restored(zigpy_device) zha_device.update_available(True) await hass.async_block_till_done() return zigpy_device, zha_device diff --git a/tests/components/zha/test_discover.py b/tests/components/zha/test_discover.py index 9ed88c86e51..a194453bd65 100644 --- a/tests/components/zha/test_discover.py +++ b/tests/components/zha/test_discover.py @@ -1,6 +1,5 @@ """Test zha device discovery.""" -import asyncio import re from unittest import mock @@ -11,6 +10,7 @@ import homeassistant.components.zha.core.discovery as disc import homeassistant.components.zha.core.gateway as core_zha_gw import homeassistant.helpers.entity_registry +from .common import get_zha_gateway from .zha_devices_list import DEVICES NO_TAIL_ID = re.compile("_\\d$") @@ -18,12 +18,7 @@ NO_TAIL_ID = re.compile("_\\d$") @pytest.mark.parametrize("device", DEVICES) async def test_devices( - device, - zha_gateway: core_zha_gw.ZHAGateway, - hass, - config_entry, - zigpy_device_mock, - monkeypatch, + device, hass, zigpy_device_mock, monkeypatch, zha_device_joined_restored ): """Test device discovery.""" @@ -32,7 +27,7 @@ async def test_devices( "00:11:22:33:44:55:66:77", device["manufacturer"], device["model"], - node_desc=device["node_descriptor"], + node_descriptor=device["node_descriptor"], ) _dispatch = mock.MagicMock(wraps=disc.async_dispatch_discovery_info) @@ -45,14 +40,7 @@ async def test_devices( "homeassistant.components.zha.core.discovery._async_create_cluster_channel", wraps=disc._async_create_cluster_channel, ): - await zha_gateway.async_device_restored(zigpy_device) - await hass.async_block_till_done() - tasks = [ - hass.config_entries.async_forward_entry_setup(config_entry, component) - for component in zha_const.COMPONENTS - ] - await asyncio.gather(*tasks) - + await zha_device_joined_restored(zigpy_device) await hass.async_block_till_done() entity_ids = hass.states.async_entity_ids() @@ -61,6 +49,7 @@ async def test_devices( ent for ent in entity_ids if ent.split(".")[0] in zha_const.COMPONENTS } + zha_gateway = get_zha_gateway(hass) zha_dev = zha_gateway.get_device(zigpy_device.ieee) event_channels = { # pylint: disable=protected-access ch.id for ch in zha_dev._relay_channels.values() diff --git a/tests/components/zha/test_fan.py b/tests/components/zha/test_fan.py index 48222993a3a..0cf3e3e954d 100644 --- a/tests/components/zha/test_fan.py +++ b/tests/components/zha/test_fan.py @@ -1,5 +1,5 @@ """Test zha fan.""" -from unittest.mock import call, patch +from unittest.mock import call import pytest import zigpy.zcl.clusters.hvac as hvac @@ -18,13 +18,12 @@ from homeassistant.const import ( from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, ) -from tests.common import mock_coro - @pytest.fixture def zigpy_device(zigpy_device_mock): @@ -35,7 +34,7 @@ def zigpy_device(zigpy_device_mock): return zigpy_device_mock(endpoints) -async def test_fan(hass, zha_gateway, zha_device_joined_restored, zigpy_device): +async def test_fan(hass, zha_device_joined_restored, zigpy_device): """Test zha fan platform.""" zha_device = await zha_device_joined_restored(zigpy_device) @@ -47,7 +46,7 @@ async def test_fan(hass, zha_gateway, zha_device_joined_restored, zigpy_device): assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and device - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the state has changed from unavailable to off assert hass.states.get(entity_id).state == STATE_OFF @@ -66,44 +65,25 @@ async def test_fan(hass, zha_gateway, zha_device_joined_restored, zigpy_device): assert hass.states.get(entity_id).state == STATE_OFF # turn on from HA - with patch( - "zigpy.zcl.Cluster.write_attributes", - return_value=mock_coro([zcl_f.Status.SUCCESS, zcl_f.Status.SUCCESS]), - ): - # turn on via UI - await async_turn_on(hass, entity_id) - assert len(cluster.write_attributes.mock_calls) == 1 - assert cluster.write_attributes.call_args == call({"fan_mode": 2}) + cluster.write_attributes.reset_mock() + await async_turn_on(hass, entity_id) + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call({"fan_mode": 2}) # turn off from HA - with patch( - "zigpy.zcl.Cluster.write_attributes", - return_value=mock_coro([zcl_f.Status.SUCCESS, zcl_f.Status.SUCCESS]), - ): - # turn off via UI - await async_turn_off(hass, entity_id) - assert len(cluster.write_attributes.mock_calls) == 1 - assert cluster.write_attributes.call_args == call({"fan_mode": 0}) + cluster.write_attributes.reset_mock() + await async_turn_off(hass, entity_id) + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call({"fan_mode": 0}) # change speed from HA - with patch( - "zigpy.zcl.Cluster.write_attributes", - return_value=mock_coro([zcl_f.Status.SUCCESS, zcl_f.Status.SUCCESS]), - ): - # turn on via UI - await async_set_speed(hass, entity_id, speed=fan.SPEED_HIGH) - assert len(cluster.write_attributes.mock_calls) == 1 - assert cluster.write_attributes.call_args == call({"fan_mode": 3}) + cluster.write_attributes.reset_mock() + await async_set_speed(hass, entity_id, speed=fan.SPEED_HIGH) + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call({"fan_mode": 3}) # test adding new fan to the network and HA - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_device) - await hass.async_block_till_done() - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - assert cluster.configure_reporting.call_count == 1 - assert cluster.configure_reporting.await_count == 1 + await async_test_rejoin(hass, zigpy_device, [cluster], (1,)) async def async_turn_on(hass, entity_id, speed=None): diff --git a/tests/components/zha/test_gateway.py b/tests/components/zha/test_gateway.py index 45f47a1fa87..74aed6f5872 100644 --- a/tests/components/zha/test_gateway.py +++ b/tests/components/zha/test_gateway.py @@ -2,7 +2,7 @@ import pytest import zigpy.zcl.clusters.general as general -from .common import async_enable_traffic +from .common import async_enable_traffic, get_zha_gateway @pytest.fixture @@ -27,13 +27,13 @@ async def zha_dev_basic(hass, zha_device_restored, zigpy_dev_basic): return zha_device -async def test_device_left(hass, zha_gateway, zigpy_dev_basic, zha_dev_basic): +async def test_device_left(hass, zigpy_dev_basic, zha_dev_basic): """Device leaving the network should become unavailable.""" assert zha_dev_basic.available is False - await async_enable_traffic(hass, zha_gateway, [zha_dev_basic]) + await async_enable_traffic(hass, [zha_dev_basic]) assert zha_dev_basic.available is True - zha_gateway.device_left(zigpy_dev_basic) + get_zha_gateway(hass).device_left(zigpy_dev_basic) assert zha_dev_basic.available is False diff --git a/tests/components/zha/test_light.py b/tests/components/zha/test_light.py index 5df379d1d7a..e21c22d30cf 100644 --- a/tests/components/zha/test_light.py +++ b/tests/components/zha/test_light.py @@ -14,6 +14,7 @@ from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, @@ -73,7 +74,7 @@ LIGHT_COLOR = { [(LIGHT_ON_OFF, (1, 0, 0)), (LIGHT_LEVEL, (1, 1, 0)), (LIGHT_COLOR, (1, 1, 3))], ) async def test_light( - hass, zha_gateway, zigpy_device_mock, zha_device_joined_restored, device, reporting, + hass, zigpy_device_mock, zha_device_joined_restored, device, reporting, ): """Test zha light platform.""" @@ -92,7 +93,7 @@ async def test_light( assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and device - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the lights were created and are off assert hass.states.get(entity_id).state == STATE_OFF @@ -121,17 +122,7 @@ async def test_light( clusters.append(cluster_level) if cluster_color: clusters.append(cluster_color) - for cluster in clusters: - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_device) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_OFF - for cluster, reporting_count in zip(clusters, reporting): - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - assert cluster.configure_reporting.call_count == reporting_count - assert cluster.configure_reporting.await_count == reporting_count + await async_test_rejoin(hass, zigpy_device, clusters, reporting) async def async_test_on_off_from_light(hass, cluster, entity_id): diff --git a/tests/components/zha/test_lock.py b/tests/components/zha/test_lock.py index 2c5dc9f41ba..0442ea497d7 100644 --- a/tests/components/zha/test_lock.py +++ b/tests/components/zha/test_lock.py @@ -23,8 +23,8 @@ LOCK_DOOR = 0 UNLOCK_DOOR = 1 -@pytest.fixture(params=["zha_device_joined", "zha_device_restored"]) -async def lock(hass, zha_gateway, zigpy_device_mock, request): +@pytest.fixture +async def lock(hass, zigpy_device_mock, zha_device_joined_restored): """Lock cluster fixture.""" zigpy_device = zigpy_device_mock( @@ -37,12 +37,11 @@ async def lock(hass, zha_gateway, zigpy_device_mock, request): }, ) - join_or_restore = request.getfixturevalue(request.param) - zha_device = await join_or_restore(zigpy_device) + zha_device = await zha_device_joined_restored(zigpy_device) return zha_device, zigpy_device.endpoints[1].door_lock -async def test_lock(hass, zha_gateway, lock): +async def test_lock(hass, lock): """Test zha lock platform.""" zha_device, cluster = lock @@ -53,7 +52,7 @@ async def test_lock(hass, zha_gateway, lock): assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and device - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the state has changed from unavailable to unlocked assert hass.states.get(entity_id).state == STATE_UNLOCKED diff --git a/tests/components/zha/test_sensor.py b/tests/components/zha/test_sensor.py index b78ea8a3583..b81e8f02c12 100644 --- a/tests/components/zha/test_sensor.py +++ b/tests/components/zha/test_sensor.py @@ -25,6 +25,7 @@ from homeassistant.util import dt as dt_util from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, @@ -102,7 +103,6 @@ async def async_test_electrical_measurement(hass, cluster, entity_id): ) async def test_sensor( hass, - zha_gateway, zigpy_device_mock, zha_device_joined_restored, cluster_id, @@ -128,7 +128,7 @@ async def test_sensor( assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and devices - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the sensor now have a state of unknown assert hass.states.get(entity_id).state == STATE_UNKNOWN @@ -137,15 +137,7 @@ async def test_sensor( await test_func(hass, cluster, entity_id) # test rejoin - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_device) - await hass.async_block_till_done() - await test_func(hass, cluster, entity_id) - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - assert cluster.configure_reporting.call_count == report_count - assert cluster.configure_reporting.await_count == report_count + await async_test_rejoin(hass, zigpy_device, [cluster], (report_count,)) async def send_attribute_report(hass, cluster, attrid, value): @@ -232,7 +224,6 @@ async def test_temp_uom( expected, restore, hass_ms, - zha_gateway, core_rs, zigpy_device_mock, zha_device_restored, @@ -267,7 +258,7 @@ async def test_temp_uom( assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and devices - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the sensors now have a state of unknown if not restore: diff --git a/tests/components/zha/test_switch.py b/tests/components/zha/test_switch.py index f70538f65e8..a088283834b 100644 --- a/tests/components/zha/test_switch.py +++ b/tests/components/zha/test_switch.py @@ -10,6 +10,7 @@ from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from .common import ( async_enable_traffic, + async_test_rejoin, find_entity_id, make_attribute, make_zcl_header, @@ -34,7 +35,7 @@ def zigpy_device(zigpy_device_mock): return zigpy_device_mock(endpoints) -async def test_switch(hass, zha_gateway, zha_device_joined_restored, zigpy_device): +async def test_switch(hass, zha_device_joined_restored, zigpy_device): """Test zha switch platform.""" zha_device = await zha_device_joined_restored(zigpy_device) @@ -46,7 +47,7 @@ async def test_switch(hass, zha_gateway, zha_device_joined_restored, zigpy_devic assert hass.states.get(entity_id).state == STATE_UNAVAILABLE # allow traffic to flow through the gateway and device - await async_enable_traffic(hass, zha_gateway, [zha_device]) + await async_enable_traffic(hass, [zha_device]) # test that the state has changed from unavailable to off assert hass.states.get(entity_id).state == STATE_OFF @@ -93,11 +94,4 @@ async def test_switch(hass, zha_gateway, zha_device_joined_restored, zigpy_devic ) # test joining a new switch to the network and HA - cluster.bind.reset_mock() - cluster.configure_reporting.reset_mock() - await zha_gateway.async_device_initialized(zigpy_device) - await hass.async_block_till_done() - assert cluster.bind.call_count == 1 - assert cluster.bind.await_count == 1 - assert cluster.configure_reporting.call_count == 1 - assert cluster.configure_reporting.await_count == 1 + await async_test_rejoin(hass, zigpy_device, [cluster], (1,))