From a82f587e37d2c3db091e352a45254e524be1bf1f Mon Sep 17 00:00:00 2001 From: SukramJ Date: Sun, 19 Dec 2021 15:32:31 +0100 Subject: [PATCH] Add coverage config (#76) * Add coverage config - small fixes - refactor tests * Update changelog.txt --- .coverage | Bin 0 -> 53248 bytes .coveragerc | 23 ++++++++++ changelog.txt | 4 +- cov_hahomematic.sh | 1 + hahomematic/entity.py | 4 +- tests/conftest.py | 104 +++++++++++++++++++++++++++--------------- tests/test_central.py | 6 +-- 7 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 .coverage create mode 100644 .coveragerc create mode 100755 cov_hahomematic.sh diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..6faed61fb7f861d60a93f4916f386e2686d86582 GIT binary patch literal 53248 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 literal 0 HcmV?d00001 diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..07f066a0 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,23 @@ +[run] +source = hahomematic + +omit = + hahomematic/__init__.py + + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # TYPE_CHECKING and @overload blocks are never executed during pytest run + if TYPE_CHECKING: + @overload diff --git a/changelog.txt b/changelog.txt index fa3d8c7a..02e7727e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,8 @@ Version 0.1.0 (2021-12-xx) - Bump version to 0.1.0 -- Remove interface_id from get_entity_name -- Remove interface_id from get_custom_entity_name +- Remove interface_id from get_entity_name and get_custom_entity_name - Add initial test +- Add coverage config Version 0.0.22 (2021-12-16) - Resolve names without interface diff --git a/cov_hahomematic.sh b/cov_hahomematic.sh new file mode 100755 index 00000000..08325bb0 --- /dev/null +++ b/cov_hahomematic.sh @@ -0,0 +1 @@ +pytest --cov=hahomematic --cov-config=.coveragerc --cov-report html tests/ \ No newline at end of file diff --git a/hahomematic/entity.py b/hahomematic/entity.py index 4f98930b..8011d48c 100644 --- a/hahomematic/entity.py +++ b/hahomematic/entity.py @@ -421,7 +421,7 @@ def __init__( self._device_enum = device_enum self._device_desc = device_def self._entity_def = entity_def - self._channel_no = channel_no + self.channel_no = channel_no self.name = get_custom_entity_name( central=self._central, address=self.address, @@ -437,7 +437,7 @@ def _init_entities(self) -> None: fields_rep = self._device_desc.get(hm_entity_definition.ED_FIELDS_REP, {}) # Add repeating fields for (f_name, p_name) in fields_rep.items(): - f_address = f"{self.address}:{self._channel_no}" + f_address = f"{self.address}:{self.channel_no}" entity = self._device.get_hm_entity(f_address, p_name) self._add_entity(f_name, entity) # Add device fields diff --git a/tests/conftest.py b/tests/conftest.py index 6e146b48..9a349a22 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,17 @@ +from __future__ import annotations + import asyncio import logging +from typing import Any import pydevccu import pytest from hahomematic import config, const -from hahomematic.central_unit import CentralConfig +from hahomematic.central_unit import CentralConfig, CentralUnit from hahomematic.client import ClientConfig +from hahomematic.device import HmDevice +from hahomematic.entity import CustomEntity, GenericEntity from hahomematic.helpers import get_device_address from hahomematic.xml_rpc_server import register_xml_rpc_server @@ -14,7 +19,7 @@ CCU_HOST = "127.0.0.1" CCU_USERNAME = None CCU_PASSWORD = None -got_devices = False +GOT_DEVICES = False # content of conftest.py def pytest_configure(config): @@ -29,26 +34,38 @@ def pytest_unconfigure(config): # pragma: no cover del sys._called_from_test +@pytest.yield_fixture(scope="session") +def loop() -> asyncio.AbstractEventLoop: + """Yield running event_loop""" + event_loop = asyncio.get_event_loop_policy().new_event_loop() + yield event_loop + event_loop.close() + + @pytest.fixture -async def pydev_ccu(loop): +def pydev_ccu() -> pydevccu.Server: """Defines the virtual ccu""" - ccu = pydevccu.Server(persistance=True, logic={"startupdelay": 1, "interval": 30}) + ccu = pydevccu.Server() ccu.start() yield ccu ccu.stop() @pytest.fixture -async def central(loop, pydev_ccu): +async def central( + loop: asyncio.AbstractEventLoop, pydev_ccu: pydevccu.Server +) -> CentralUnit: """Yield central""" - SLEEPCOUNTER = 0 + sleep_counter = 0 + global GOT_DEVICES + GOT_DEVICES = False - def systemcallback(self, src, *args): - if args and args[0] and len(args[0]) > 0: - global got_devices - got_devices = True + def systemcallback(src, *args): + if src == "devicesCreated" and args and args[0] and len(args[0]) > 0: + global GOT_DEVICES + GOT_DEVICES = True - central = CentralConfig( + central_unit = CentralConfig( name="ccu-dev", entry_id="123", loop=loop, @@ -60,39 +77,33 @@ def systemcallback(self, src, *args): ).get_central() config.INIT_TIMEOUT = 10 config.CACHE_DIR = "cache" - central.callback_system_event = systemcallback + central_unit.callback_system_event = systemcallback client1 = await ClientConfig( - central=central, + central=central_unit, name="hm", port=2001, ).get_client() # Clients have to exist prior to creating the devices - central.create_devices() + central_unit.create_devices() # Once the central_1 is running we subscribe to receive messages. await client1.proxy_init() - await central.init_hub() - while not got_devices and SLEEPCOUNTER < 300: + await central_unit.init_hub() + while not GOT_DEVICES and sleep_counter < 300: print("Waiting for devices") - SLEEPCOUNTER += 1 + sleep_counter += 1 await asyncio.sleep(1) - yield central + yield central_unit - await central.stop() + await central_unit.stop() -@pytest.yield_fixture(scope="session") -def loop(request): - """Yield running event_loop""" - event_loop = asyncio.get_event_loop_policy().new_event_loop() - yield event_loop - event_loop.close() - - -async def get_value_from_central(central, address, parameter, do_load=False): +async def get_value_from_generic_entity( + central_unit: CentralUnit, address: str, parameter: str, do_load: bool = False +) -> Any: """Return the device value.""" - hm_entity = get_hm_entity(central, address, parameter) + hm_entity = await get_hm_genertic_entity(central_unit, address, parameter) assert hm_entity if do_load: await hm_entity.load_data() @@ -100,19 +111,40 @@ async def get_value_from_central(central, address, parameter, do_load=False): return hm_entity.state -def get_hm_device(central, address): +def get_hm_device(central_unit: CentralUnit, address: str) -> HmDevice | None: """Return the hm_device.""" d_address = get_device_address(address) - return central.hm_devices.get(d_address) + return central_unit.hm_devices.get(d_address) + + +async def get_hm_genertic_entity( + central_unit: CentralUnit, address: str, parameter: str, do_load: bool = False +) -> GenericEntity | None: + """Return the hm generic_entity.""" + hm_device = get_hm_device(central_unit, address) + assert hm_device + hm_entity = hm_device.entities.get((address, parameter)) + if hm_entity and do_load: + await hm_entity.load_data() + return hm_entity -def get_hm_entity(central, address, parameter): - """Return the hm_entity.""" - hm_device = get_hm_device(central, address) +async def get_hm_custom_entity( + central_unit: CentralUnit, address: str, channel_no: int, do_load: bool = False +) -> CustomEntity | None: + """Return the hm custom_entity.""" + hm_device = get_hm_device(central_unit, address) assert hm_device - return hm_device.entities.get((address, parameter)) + for custom_entity in hm_device.custom_entities: + if custom_entity.channel_no == channel_no: + if do_load: + await custom_entity.load_data() + return custom_entity + return None -def send_device_value_to_ccu(pydev_ccu, address, parameter, value): +def send_device_value_to_ccu( + pydev_ccu: pydevccu.Server, address: str, parameter: str, value: Any +) -> None: """Send the device value to ccu.""" pydev_ccu.setValue(address, parameter, value, force=False) diff --git a/tests/test_central.py b/tests/test_central.py index 632b671d..1a89957e 100644 --- a/tests/test_central.py +++ b/tests/test_central.py @@ -2,7 +2,7 @@ from typing import Any from unittest.mock import patch -from conftest import get_value_from_central, send_device_value_to_ccu +from conftest import get_value_from_generic_entity, send_device_value_to_ccu import pytest from hahomematic.helpers import get_device_address @@ -26,12 +26,12 @@ async def test_device_set_data(central, pydev_ccu, loop) -> None: """Test callback.""" assert central assert pydev_ccu - old_value = await get_value_from_central( + old_value = await get_value_from_generic_entity( central, "VCU6354483:1", "SET_POINT_TEMPERATURE" ) assert old_value is None send_device_value_to_ccu(pydev_ccu, "VCU6354483:1", "SET_POINT_TEMPERATURE", 19.0) - new_value = await get_value_from_central( + new_value = await get_value_from_generic_entity( central, "VCU6354483:1", "SET_POINT_TEMPERATURE" ) assert new_value == 19.0