python-kasa/kasa/tests/test_feature.py
Teemu R 64da736717
Add generic interface for accessing device features (#741)
This adds a generic interface for all device classes to introspect available device features,
that is necessary to make it easier to support a wide variety of supported devices with different set of features.
This will allow constructing generic interfaces (e.g., in homeassistant) that fetch and change these features without hard-coding the API calls.

`Device.features()` now returns a mapping of `<identifier, Feature>` where the `Feature` contains all necessary information (like the name, the icon, a way to get and change the setting) to present and change the defined feature through its interface.
2024-02-15 16:25:08 +01:00

80 lines
2.6 KiB
Python

import pytest
from kasa import Feature, FeatureType
@pytest.fixture
def dummy_feature() -> Feature:
# create_autospec for device slows tests way too much, so we use a dummy here
class DummyDevice:
pass
feat = Feature(
device=DummyDevice(), # type: ignore[arg-type]
name="dummy_feature",
attribute_getter="dummygetter",
attribute_setter="dummysetter",
container=None,
icon="mdi:dummy",
type=FeatureType.BinarySensor,
)
return feat
def test_feature_api(dummy_feature: Feature):
"""Test all properties of a dummy feature."""
assert dummy_feature.device is not None
assert dummy_feature.name == "dummy_feature"
assert dummy_feature.attribute_getter == "dummygetter"
assert dummy_feature.attribute_setter == "dummysetter"
assert dummy_feature.container is None
assert dummy_feature.icon == "mdi:dummy"
assert dummy_feature.type == FeatureType.BinarySensor
def test_feature_value(dummy_feature: Feature):
"""Verify that property gets accessed on *value* access."""
dummy_feature.attribute_getter = "test_prop"
dummy_feature.device.test_prop = "dummy" # type: ignore[attr-defined]
assert dummy_feature.value == "dummy"
def test_feature_value_container(mocker, dummy_feature: Feature):
"""Test that container's attribute is accessed when expected."""
class DummyContainer:
@property
def test_prop(self):
return "dummy"
dummy_feature.container = DummyContainer()
dummy_feature.attribute_getter = "test_prop"
mock_dev_prop = mocker.patch.object(
dummy_feature, "test_prop", new_callable=mocker.PropertyMock, create=True
)
assert dummy_feature.value == "dummy"
mock_dev_prop.assert_not_called()
def test_feature_value_callable(dev, dummy_feature: Feature):
"""Verify that callables work as *attribute_getter*."""
dummy_feature.attribute_getter = lambda x: "dummy value"
assert dummy_feature.value == "dummy value"
async def test_feature_setter(dev, mocker, dummy_feature: Feature):
"""Verify that *set_value* calls the defined method."""
mock_set_dummy = mocker.patch.object(dummy_feature.device, "set_dummy", create=True)
dummy_feature.attribute_setter = "set_dummy"
await dummy_feature.set_value("dummy value")
mock_set_dummy.assert_called_with("dummy value")
async def test_feature_setter_read_only(dummy_feature):
"""Verify that read-only feature raises an exception when trying to change it."""
dummy_feature.attribute_setter = None
with pytest.raises(ValueError):
await dummy_feature.set_value("value for read only feature")