Refactor tradfri fan tests (#87999)

This commit is contained in:
Martin Hjelmare 2023-02-13 15:40:21 +01:00 committed by GitHub
parent ae06d9fd5c
commit 8993a36e27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 289 additions and 133 deletions

View File

@ -2,7 +2,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Generator from collections.abc import Generator
from unittest.mock import MagicMock, Mock, PropertyMock, patch from unittest.mock import MagicMock, Mock, patch
import pytest import pytest
@ -79,19 +79,3 @@ def mock_api_factory(mock_api) -> Generator[MagicMock, None, None]:
factory.init.return_value = factory.return_value factory.init.return_value = factory.return_value
factory.return_value.request = mock_api factory.return_value.request = mock_api
yield factory.return_value yield factory.return_value
@pytest.fixture(autouse=True)
def setup(request):
"""Set up patches for pytradfri methods for the fan platform.
This is used in test_fan as well as in test_sensor.
"""
with patch(
"pytradfri.device.AirPurifierControl.raw",
new_callable=PropertyMock,
return_value=[{"mock": "mock"}],
), patch(
"pytradfri.device.AirPurifierControl.air_purifiers",
):
yield

View File

@ -0,0 +1,32 @@
{
"3": {
"0": "IKEA of Sweden",
"1": "STARKVIND Air purifier",
"2": "",
"3": "1.0.033",
"6": 1,
"7": 4364
},
"5750": 10,
"9001": "Test",
"9002": 1633096623,
"9003": 65551,
"9019": 1,
"9020": 1633096633,
"9054": 0,
"15025": [
{
"5900": 0,
"5902": 2,
"5903": 0,
"5904": 259200,
"5905": 0,
"5906": 0,
"5907": 5,
"5908": 10,
"5909": 2,
"5910": 259198,
"9003": 0
}
]
}

View File

@ -1,15 +1,33 @@
"""Tests for Tradfri diagnostics.""" """Tests for Tradfri diagnostics."""
from unittest.mock import MagicMock, Mock from unittest.mock import MagicMock, Mock, PropertyMock, patch
import pytest
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .common import setup_integration from .common import setup_integration
from .test_fan import mock_fan from .test_sensor import mock_fan
from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@pytest.fixture(autouse=True)
def setup(request):
"""Set up patches for pytradfri methods for the fan platform.
This is used in test_fan as well as in test_sensor.
"""
with patch(
"pytradfri.device.AirPurifierControl.raw",
new_callable=PropertyMock,
return_value=[{"mock": "mock"}],
), patch(
"pytradfri.device.AirPurifierControl.air_purifiers",
):
yield
async def test_diagnostics( async def test_diagnostics(
hass: HomeAssistant, hass: HomeAssistant,
hass_client: ClientSessionGenerator, hass_client: ClientSessionGenerator,
@ -34,4 +52,4 @@ async def test_diagnostics(
assert isinstance(result, dict) assert isinstance(result, dict)
assert result["gateway_version"] == "1.2.1234" assert result["gateway_version"] == "1.2.1234"
assert len(result["device_data"]) == 1 assert result["device_data"] == ["model"]

View File

@ -1,144 +1,216 @@
"""Tradfri fan (recognised as air purifiers in the IKEA ecosystem) platform tests.""" """Tradfri fan (recognised as air purifiers in the IKEA ecosystem) platform tests."""
from __future__ import annotations
import json
from typing import Any
from unittest.mock import MagicMock, Mock from unittest.mock import MagicMock, Mock
import pytest import pytest
from pytradfri.const import (
ATTR_AIR_PURIFIER_FAN_SPEED,
ATTR_AIR_PURIFIER_MODE,
ATTR_REACHABLE_STATE,
ROOT_AIR_PURIFIER,
)
from pytradfri.device import Device from pytradfri.device import Device
from pytradfri.device.air_purifier import AirPurifier from pytradfri.device.air_purifier import AirPurifier
from pytradfri.device.air_purifier_control import AirPurifierControl
from .common import setup_integration from homeassistant.components.fan import (
ATTR_PERCENTAGE,
ATTR_PERCENTAGE_STEP,
def mock_fan(test_features=None, test_state=None, device_number=0): ATTR_PRESET_MODE,
"""Mock a tradfri fan/air purifier.""" ATTR_PRESET_MODES,
if test_features is None: DOMAIN as FAN_DOMAIN,
test_features = {} SERVICE_SET_PERCENTAGE,
if test_state is None: SERVICE_SET_PRESET_MODE,
test_state = {} SERVICE_TURN_OFF,
mock_fan_data = Mock(**test_state) SERVICE_TURN_ON,
dev_info_mock = MagicMock()
dev_info_mock.manufacturer = "manufacturer"
dev_info_mock.model_number = "model"
dev_info_mock.firmware_version = "1.2.3"
_mock_fan = Mock(
id=f"mock-fan-id-{device_number}",
reachable=True,
observe=Mock(),
device_info=dev_info_mock,
has_light_control=False,
has_socket_control=False,
has_blind_control=False,
has_signal_repeater_control=False,
has_air_purifier_control=True,
) )
_mock_fan.name = f"tradfri_fan_{device_number}" from homeassistant.components.tradfri.const import DOMAIN
air_purifier_control = AirPurifierControl(_mock_fan) from homeassistant.const import (
ATTR_SUPPORTED_FEATURES,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
# Store the initial state. from .common import setup_integration, trigger_observe_callback
setattr(air_purifier_control, "air_purifiers", [mock_fan_data])
_mock_fan.air_purifier_control = air_purifier_control from tests.common import load_fixture
return _mock_fan
async def test_fan(hass, mock_gateway, mock_api_factory): @pytest.fixture(scope="module")
"""Test that fans are correctly added.""" def air_purifier_response() -> dict[str, Any]:
state = {"fan_speed": 10, "air_quality": 12} """Return an air purifier response."""
return json.loads(load_fixture("air_purifier.json", DOMAIN))
mock_gateway.mock_devices.append(mock_fan(test_state=state))
await setup_integration(hass)
fan_1 = hass.states.get("fan.tradfri_fan_0")
assert fan_1 is not None
assert fan_1.state == "on"
assert fan_1.attributes["percentage"] == 18
assert fan_1.attributes["preset_modes"] == ["Auto"]
assert fan_1.attributes["supported_features"] == 9
async def test_fan_observed(hass, mock_gateway, mock_api_factory): @pytest.fixture
"""Test that fans are correctly observed.""" def air_purifier(air_purifier_response: dict[str, Any]) -> AirPurifier:
state = {"fan_speed": 10, "air_quality": 12} """Return air purifier."""
device = Device(air_purifier_response)
fan = mock_fan(test_state=state) air_purifier_control = device.air_purifier_control
mock_gateway.mock_devices.append(fan) assert air_purifier_control
await setup_integration(hass) return air_purifier_control.air_purifiers[0]
assert len(fan.observe.mock_calls) > 0
async def test_fan_available(hass, mock_gateway, mock_api_factory): async def test_fan_available(
hass: HomeAssistant,
mock_gateway: Mock,
mock_api_factory: MagicMock,
air_purifier: AirPurifier,
) -> None:
"""Test fan available property.""" """Test fan available property."""
entity_id = "fan.test"
fan = mock_fan(test_state={"fan_speed": 10, "air_quality": 12}, device_number=1) device = air_purifier.device
fan.reachable = True mock_gateway.mock_devices.append(device)
fan2 = mock_fan(test_state={"fan_speed": 10, "air_quality": 12}, device_number=2)
fan2.reachable = False
mock_gateway.mock_devices.append(fan)
mock_gateway.mock_devices.append(fan2)
await setup_integration(hass) await setup_integration(hass)
assert hass.states.get("fan.tradfri_fan_1").state == "on" state = hass.states.get(entity_id)
assert hass.states.get("fan.tradfri_fan_2").state == "unavailable" assert state
assert state.state == STATE_OFF
assert state.attributes[ATTR_PERCENTAGE] == 18
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(2.040816)
assert state.attributes[ATTR_PRESET_MODES] == ["Auto"]
assert state.attributes[ATTR_PRESET_MODE] is None
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 9
await trigger_observe_callback(
hass, mock_gateway, device, {ATTR_REACHABLE_STATE: 0}
)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_UNAVAILABLE
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_data, expected_result", (
"service, service_data, device_state, expected_state, "
"expected_percentage, expected_preset_mode"
),
[ [
( (
{"percentage": 50}, SERVICE_SET_PERCENTAGE,
"on", {ATTR_PERCENTAGE: 50},
{
ATTR_AIR_PURIFIER_FAN_SPEED: 25,
ATTR_AIR_PURIFIER_MODE: 25,
},
STATE_ON,
49,
None,
),
(
SERVICE_SET_PERCENTAGE,
{ATTR_PERCENTAGE: 0},
{
ATTR_AIR_PURIFIER_FAN_SPEED: 0,
ATTR_AIR_PURIFIER_MODE: 0,
},
STATE_OFF,
None,
None,
),
(
SERVICE_TURN_ON,
{ATTR_PERCENTAGE: 50},
{
ATTR_AIR_PURIFIER_FAN_SPEED: 25,
ATTR_AIR_PURIFIER_MODE: 25,
},
STATE_ON,
49,
None,
),
(
SERVICE_TURN_ON,
{ATTR_PRESET_MODE: "Auto"},
{
ATTR_AIR_PURIFIER_MODE: 1,
},
STATE_ON,
18,
"Auto",
),
(
SERVICE_TURN_ON,
{},
{
ATTR_AIR_PURIFIER_MODE: 1,
},
STATE_ON,
18,
"Auto",
),
(
SERVICE_SET_PRESET_MODE,
{ATTR_PRESET_MODE: "Auto"},
{
ATTR_AIR_PURIFIER_MODE: 1,
},
STATE_ON,
18,
"Auto",
),
(
SERVICE_TURN_OFF,
{},
{
ATTR_AIR_PURIFIER_FAN_SPEED: 0,
ATTR_AIR_PURIFIER_MODE: 0,
},
STATE_OFF,
None,
None,
), ),
({"percentage": 0}, "off"),
], ],
) )
async def test_set_percentage( async def test_services(
hass, hass: HomeAssistant,
mock_gateway, mock_gateway: Mock,
mock_api_factory, mock_api_factory: MagicMock,
test_data, air_purifier: AirPurifier,
expected_result, service: str,
): service_data: dict[str, Any],
"""Test setting speed of a fan.""" device_state: dict[str, Any],
# Note pytradfri style, not hass. Values not really important. expected_state: str,
initial_state = {"percentage": 10, "fan_speed": 3, "air_quality": 12} expected_percentage: int | None,
# Setup the gateway with a mock fan. expected_preset_mode: str | None,
fan = mock_fan(test_state=initial_state, device_number=0) ) -> None:
mock_gateway.mock_devices.append(fan) """Test fan services."""
entity_id = "fan.test"
device = air_purifier.device
mock_gateway.mock_devices.append(device)
await setup_integration(hass) await setup_integration(hass)
# Use the turn_on service call to change the fan state. state = hass.states.get(entity_id)
assert state
assert state.state == STATE_OFF
assert state.attributes[ATTR_PERCENTAGE] == 18
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(2.040816)
assert state.attributes[ATTR_PRESET_MODES] == ["Auto"]
assert state.attributes[ATTR_PRESET_MODE] is None
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 9
await hass.services.async_call( await hass.services.async_call(
"fan", FAN_DOMAIN,
"set_percentage", service,
{"entity_id": "fan.tradfri_fan_0", **test_data}, {"entity_id": entity_id, **service_data},
blocking=True, blocking=True,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
# Check that the fan is observed. await trigger_observe_callback(
mock_func = fan.observe hass,
assert len(mock_func.mock_calls) > 0 mock_gateway,
_, callkwargs = mock_func.call_args device,
assert "callback" in callkwargs {ROOT_AIR_PURIFIER: [device_state]},
# Callback function to refresh fan state. )
callback = callkwargs["callback"]
responses = mock_gateway.mock_responses state = hass.states.get(entity_id)
mock_gateway_response = responses[0] assert state
assert state.state == expected_state
# A KeyError is raised if we don't this to the response code assert state.attributes[ATTR_PERCENTAGE] == expected_percentage
mock_gateway_response["15025"][0].update({"5908": 10, "5907": 12, "5910": 20}) assert state.attributes[ATTR_PRESET_MODE] == expected_preset_mode
# Use the callback function to update the fan state.
dev = Device(mock_gateway_response)
fan_data = AirPurifier(dev, 0)
fan.air_purifier_control.air_purifiers[0] = fan_data
callback(fan)
await hass.async_block_till_done()
# Check that the state is correct.
state = hass.states.get("fan.tradfri_fan_0")
assert state.state == expected_result

View File

@ -1,18 +1,68 @@
"""Tradfri sensor platform tests.""" """Tradfri sensor platform tests."""
from __future__ import annotations from __future__ import annotations
from unittest.mock import MagicMock, Mock from unittest.mock import MagicMock, Mock, PropertyMock, patch
import pytest
from pytradfri.device.air_purifier_control import AirPurifierControl
from homeassistant.components import tradfri from homeassistant.components import tradfri
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from . import GATEWAY_ID from . import GATEWAY_ID
from .common import setup_integration from .common import setup_integration
from .test_fan import mock_fan
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@pytest.fixture(autouse=True)
def setup(request):
"""Set up patches for pytradfri methods for the fan platform.
This is used in test_fan as well as in test_sensor.
"""
with patch(
"pytradfri.device.AirPurifierControl.raw",
new_callable=PropertyMock,
return_value=[{"mock": "mock"}],
), patch(
"pytradfri.device.AirPurifierControl.air_purifiers",
):
yield
def mock_fan(test_features=None, test_state=None, device_number=0):
"""Mock a tradfri fan/air purifier."""
if test_features is None:
test_features = {}
if test_state is None:
test_state = {}
mock_fan_data = Mock(**test_state)
dev_info_mock = MagicMock()
dev_info_mock.manufacturer = "manufacturer"
dev_info_mock.model_number = "model"
dev_info_mock.firmware_version = "1.2.3"
_mock_fan = Mock(
id=f"mock-fan-id-{device_number}",
reachable=True,
observe=Mock(),
device_info=dev_info_mock,
has_light_control=False,
has_socket_control=False,
has_blind_control=False,
has_signal_repeater_control=False,
has_air_purifier_control=True,
)
_mock_fan.name = f"tradfri_fan_{device_number}"
air_purifier_control = AirPurifierControl(_mock_fan)
# Store the initial state.
setattr(air_purifier_control, "air_purifiers", [mock_fan_data])
_mock_fan.air_purifier_control = air_purifier_control
return _mock_fan
def mock_sensor(test_state: list, device_number=0): def mock_sensor(test_state: list, device_number=0):
"""Mock a tradfri sensor.""" """Mock a tradfri sensor."""
dev_info_mock = MagicMock() dev_info_mock = MagicMock()