Add basic test support to Homematic IP Cloud (#27228)

* Add basic test support to Homematic IP Cloud

* move test data address comments
This commit is contained in:
SukramJ 2019-10-06 11:54:26 +02:00 committed by Martin Hjelmare
parent 2c6a869bc6
commit 476f24e451
9 changed files with 5672 additions and 10 deletions

View file

@ -115,6 +115,7 @@ class HomematicipAccesspointStatus(HomematicipGenericDevice):
def __init__(self, home: AsyncHome) -> None: def __init__(self, home: AsyncHome) -> None:
"""Initialize access point device.""" """Initialize access point device."""
home.modelType = "HmIP-HAP"
super().__init__(home, home) super().__init__(home, home)
@property @property

View file

@ -0,0 +1,75 @@
"""Initializer helpers for HomematicIP fake server."""
from unittest.mock import MagicMock, patch
from homematicip.aio.connection import AsyncConnection
import pytest
from homeassistant import config_entries
from homeassistant.components.homematicip_cloud import (
DOMAIN as HMIPC_DOMAIN,
const as hmipc,
hap as hmip_hap,
)
from homeassistant.core import HomeAssistant
from .helper import AUTH_TOKEN, HAPID, HomeTemplate
from tests.common import MockConfigEntry, mock_coro
@pytest.fixture(name="mock_connection")
def mock_connection_fixture():
"""Return a mockked connection."""
connection = MagicMock(spec=AsyncConnection)
def _rest_call_side_effect(path, body=None):
return path, body
connection._restCall.side_effect = _rest_call_side_effect # pylint: disable=W0212
connection.api_call.return_value = mock_coro(True)
return connection
@pytest.fixture(name="default_mock_home")
def default_mock_home_fixture(mock_connection):
"""Create a fake homematic async home."""
return HomeTemplate(connection=mock_connection).init_home().get_async_home_mock()
@pytest.fixture(name="hmip_config_entry")
def hmip_config_entry_fixture():
"""Create a fake config entriy for homematic ip cloud."""
entry_data = {
hmipc.HMIPC_HAPID: HAPID,
hmipc.HMIPC_AUTHTOKEN: AUTH_TOKEN,
hmipc.HMIPC_NAME: "",
}
config_entry = MockConfigEntry(
version=1,
domain=HMIPC_DOMAIN,
title=HAPID,
data=entry_data,
source="import",
connection_class=config_entries.CONN_CLASS_CLOUD_PUSH,
system_options={"disable_new_entities": False},
)
return config_entry
@pytest.fixture(name="default_mock_hap")
async def default_mock_hap_fixture(
hass: HomeAssistant, default_mock_home, hmip_config_entry
):
"""Create a fake homematic access point."""
hass.config.components.add(HMIPC_DOMAIN)
hap = hmip_hap.HomematicipHAP(hass, hmip_config_entry)
with patch.object(hap, "get_hap", return_value=mock_coro(default_mock_home)):
assert await hap.async_setup() is True
hass.data[HMIPC_DOMAIN] = {HAPID: hap}
await hass.async_block_till_done()
return hap

View file

@ -0,0 +1,128 @@
"""Helper for HomematicIP Cloud Tests."""
import json
from unittest.mock import Mock
from homematicip.aio.class_maps import (
TYPE_CLASS_MAP,
TYPE_GROUP_MAP,
TYPE_SECURITY_EVENT_MAP,
)
from homematicip.aio.home import AsyncHome
from homematicip.home import Home
from tests.common import load_fixture
HAPID = "Mock_HAP"
AUTH_TOKEN = "1234"
HOME_JSON = "homematicip_cloud.json"
def get_and_check_entity_basics(
hass, default_mock_hap, entity_id, entity_name, device_model
):
"""Get and test basic device."""
ha_entity = hass.states.get(entity_id)
assert ha_entity is not None
assert ha_entity.attributes["model_type"] == device_model
assert ha_entity.name == entity_name
hmip_device = default_mock_hap.home.template.search_mock_device_by_id(
ha_entity.attributes["id"]
)
assert hmip_device is not None
return ha_entity, hmip_device
async def async_manipulate_test_data(
hass, hmip_device, attribute, new_value, channel=1
):
"""Set new value on hmip device."""
if channel == 1:
setattr(hmip_device, attribute, new_value)
functional_channel = hmip_device.functionalChannels[channel]
setattr(functional_channel, attribute, new_value)
hmip_device.fire_update_event()
await hass.async_block_till_done()
class HomeTemplate(Home):
"""
Home template as builder for home mock.
It is based on the upstream libs home class to generate hmip devices
and groups based on the given homematicip_cloud.json.
All further testing activities should be done by using the AsyncHome mock,
that is generated by get_async_home_mock(self).
The class also generated mocks of devices and groups for further testing.
"""
_typeClassMap = TYPE_CLASS_MAP
_typeGroupMap = TYPE_GROUP_MAP
_typeSecurityEventMap = TYPE_SECURITY_EVENT_MAP
def __init__(self, connection=None):
"""Init template with connection."""
super().__init__(connection=connection)
self.mock_devices = []
self.mock_groups = []
def init_home(self, json_path=HOME_JSON):
"""Init template with json."""
json_state = json.loads(load_fixture(HOME_JSON), encoding="UTF-8")
self.update_home(json_state=json_state, clearConfig=True)
self._generate_mocks()
return self
def _generate_mocks(self):
"""Generate mocks for groups and devices."""
for device in self.devices:
self.mock_devices.append(_get_mock(device))
for group in self.groups:
self.mock_groups.append(_get_mock(group))
def search_mock_device_by_id(self, device_id):
"""Search a device by given id."""
for device in self.mock_devices:
if device.id == device_id:
return device
return None
def search_mock_group_by_id(self, group_id):
"""Search a group by given id."""
for group in self.mock_groups:
if group.id == group_id:
return group
return None
def get_async_home_mock(self):
"""
Create Mock for Async_Home. based on template to be used for testing.
It adds collections of mocked devices and groups to the home objects,
and sets reuired attributes.
"""
mock_home = Mock(
check_connection=self._connection,
id=HAPID,
connected=True,
dutyCycle=self.dutyCycle,
devices=self.mock_devices,
groups=self.mock_groups,
weather=self.weather,
location=self.location,
label="home label",
template=self,
spec=AsyncHome,
)
mock_home.name = ""
return mock_home
def _get_mock(instance):
"""Create a mock and copy instance attributes over mock."""
mock = Mock(spec=instance, wraps=instance)
mock.__dict__.update(instance.__dict__)
return mock

View file

@ -0,0 +1,41 @@
"""Tests for HomematicIP Cloud lights."""
import logging
from tests.components.homematicip_cloud.helper import (
async_manipulate_test_data,
get_and_check_entity_basics,
)
_LOGGER = logging.getLogger(__name__)
async def test_hmip_sam(hass, default_mock_hap):
"""Test HomematicipLight."""
entity_id = "binary_sensor.garagentor"
entity_name = "Garagentor"
device_model = "HmIP-SAM"
ha_entity, hmip_device = get_and_check_entity_basics(
hass, default_mock_hap, entity_id, entity_name, device_model
)
assert ha_entity.state == "on"
assert ha_entity.attributes["acceleration_sensor_mode"] == "FLAT_DECT"
assert ha_entity.attributes["acceleration_sensor_neutral_position"] == "VERTICAL"
assert ha_entity.attributes["acceleration_sensor_sensitivity"] == "SENSOR_RANGE_4G"
assert ha_entity.attributes["acceleration_sensor_trigger_angle"] == 45
service_call_counter = len(hmip_device.mock_calls)
await async_manipulate_test_data(
hass, hmip_device, "accelerationSensorTriggered", False
)
ha_entity = hass.states.get(entity_id)
assert ha_entity.state == "off"
assert len(hmip_device.mock_calls) == service_call_counter + 1
await async_manipulate_test_data(
hass, hmip_device, "accelerationSensorTriggered", True
)
ha_entity = hass.states.get(entity_id)
assert ha_entity.state == "on"
assert len(hmip_device.mock_calls) == service_call_counter + 2

View file

@ -1,8 +1,7 @@
"""Tests for HomematicIP Cloud config flow.""" """Tests for HomematicIP Cloud config flow."""
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.homematicip_cloud import hap as hmipc from homeassistant.components.homematicip_cloud import config_flow, const, hap as hmipc
from homeassistant.components.homematicip_cloud import config_flow, const
from tests.common import MockConfigEntry, mock_coro from tests.common import MockConfigEntry, mock_coro

View file

@ -3,9 +3,9 @@ from unittest.mock import Mock, patch
import pytest import pytest
from homeassistant.components.homematicip_cloud import const, errors, hap as hmipc
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.components.homematicip_cloud import hap as hmipc
from homeassistant.components.homematicip_cloud import const, errors
from tests.common import mock_coro, mock_coro_func from tests.common import mock_coro, mock_coro_func
@ -94,8 +94,8 @@ async def test_hap_setup_connection_error():
), pytest.raises(ConfigEntryNotReady): ), pytest.raises(ConfigEntryNotReady):
await hap.async_setup() await hap.async_setup()
assert len(hass.async_add_job.mock_calls) == 0 assert not hass.async_add_job.mock_calls
assert len(hass.config_entries.flow.async_init.mock_calls) == 0 assert not hass.config_entries.flow.async_init.mock_calls
async def test_hap_reset_unloads_entry_if_setup(): async def test_hap_reset_unloads_entry_if_setup():
@ -114,7 +114,7 @@ async def test_hap_reset_unloads_entry_if_setup():
assert await hap.async_setup() is True assert await hap.async_setup() is True
assert hap.home is home assert hap.home is home
assert len(hass.services.async_register.mock_calls) == 0 assert not hass.services.async_register.mock_calls
assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 8 assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 8
hass.config_entries.async_forward_entry_unload.return_value = mock_coro(True) hass.config_entries.async_forward_entry_unload.return_value = mock_coro(True)

View file

@ -2,10 +2,10 @@
from unittest.mock import patch from unittest.mock import patch
from homeassistant.setup import async_setup_component
from homeassistant.components import homematicip_cloud as hmipc from homeassistant.components import homematicip_cloud as hmipc
from homeassistant.setup import async_setup_component
from tests.common import mock_coro, MockConfigEntry from tests.common import MockConfigEntry, mock_coro
async def test_config_with_accesspoint_passed_to_config_entry(hass): async def test_config_with_accesspoint_passed_to_config_entry(hass):
@ -53,7 +53,7 @@ async def test_config_already_registered_not_passed_to_config_entry(hass):
) )
# No flow started # No flow started
assert len(mock_config_entries.flow.mock_calls) == 0 assert not mock_config_entries.flow.mock_calls
async def test_setup_entry_successful(hass): async def test_setup_entry_successful(hass):

View file

@ -0,0 +1,77 @@
"""Tests for HomematicIP Cloud lights."""
import logging
from tests.components.homematicip_cloud.helper import (
async_manipulate_test_data,
get_and_check_entity_basics,
)
_LOGGER = logging.getLogger(__name__)
async def test_hmip_light(hass, default_mock_hap):
"""Test HomematicipLight."""
entity_id = "light.treppe"
entity_name = "Treppe"
device_model = "HmIP-BSL"
ha_entity, hmip_device = get_and_check_entity_basics(
hass, default_mock_hap, entity_id, entity_name, device_model
)
assert ha_entity.state == "on"
service_call_counter = len(hmip_device.mock_calls)
await hass.services.async_call(
"light", "turn_off", {"entity_id": entity_id}, blocking=True
)
assert len(hmip_device.mock_calls) == service_call_counter + 1
assert hmip_device.mock_calls[-1][0] == "turn_off"
await async_manipulate_test_data(hass, hmip_device, "on", False)
ha_entity = hass.states.get(entity_id)
assert ha_entity.state == "off"
await hass.services.async_call(
"light", "turn_on", {"entity_id": entity_id}, blocking=True
)
assert len(hmip_device.mock_calls) == service_call_counter + 3
assert hmip_device.mock_calls[-1][0] == "turn_on"
await async_manipulate_test_data(hass, hmip_device, "on", True)
ha_entity = hass.states.get(entity_id)
assert ha_entity.state == "on"
# HomematicipLightMeasuring
# HomematicipDimmer
async def test_hmip_notification_light(hass, default_mock_hap):
"""Test HomematicipNotificationLight."""
entity_id = "light.treppe_top_notification"
entity_name = "Treppe Top Notification"
device_model = "HmIP-BSL"
ha_entity, hmip_device = get_and_check_entity_basics(
hass, default_mock_hap, entity_id, entity_name, device_model
)
assert ha_entity.state == "off"
service_call_counter = len(hmip_device.mock_calls)
await hass.services.async_call(
"light", "turn_on", {"entity_id": entity_id}, blocking=True
)
assert len(hmip_device.mock_calls) == service_call_counter + 1
assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level"
await async_manipulate_test_data(hass, hmip_device, "dimLevel", 100, 2)
ha_entity = hass.states.get(entity_id)
assert ha_entity.state == "on"
await hass.services.async_call(
"light", "turn_off", {"entity_id": entity_id}, blocking=True
)
assert len(hmip_device.mock_calls) == service_call_counter + 3
assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level"
await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, 2)
ha_entity = hass.states.get(entity_id)
assert ha_entity.state == "off"

5341
tests/fixtures/homematicip_cloud.json vendored Normal file

File diff suppressed because it is too large Load diff