core/tests/components/homeassistant_hardware/test_helpers.py

186 lines
6.5 KiB
Python

"""Test hardware helpers."""
import logging
from unittest.mock import AsyncMock, MagicMock, Mock, call
import pytest
from homeassistant.components.homeassistant_hardware.const import DATA_COMPONENT
from homeassistant.components.homeassistant_hardware.helpers import (
async_notify_firmware_info,
async_register_firmware_info_callback,
async_register_firmware_info_provider,
)
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
FIRMWARE_INFO_EZSP = FirmwareInfo(
device="/dev/serial/by-id/device1",
firmware_type=ApplicationType.EZSP,
firmware_version=None,
source="zha",
owners=[AsyncMock(is_running=AsyncMock(return_value=True))],
)
FIRMWARE_INFO_SPINEL = FirmwareInfo(
device="/dev/serial/by-id/device2",
firmware_type=ApplicationType.SPINEL,
firmware_version=None,
source="otbr",
owners=[AsyncMock(is_running=AsyncMock(return_value=True))],
)
async def test_dispatcher_registration(hass: HomeAssistant) -> None:
"""Test HardwareInfoDispatcher registration."""
await async_setup_component(hass, "homeassistant_hardware", {})
# Mock provider 1 with a synchronous method to pull firmware info
provider1_config_entry = MockConfigEntry(
domain="zha",
unique_id="some_unique_id1",
data={},
)
provider1_config_entry.add_to_hass(hass)
provider1_config_entry.mock_state(hass, ConfigEntryState.LOADED)
provider1_firmware = MagicMock(spec=["get_firmware_info"])
provider1_firmware.get_firmware_info = MagicMock(return_value=FIRMWARE_INFO_EZSP)
async_register_firmware_info_provider(hass, "zha", provider1_firmware)
# Mock provider 2 with an asynchronous method to pull firmware info
provider2_config_entry = MockConfigEntry(
domain="otbr",
unique_id="some_unique_id2",
data={},
)
provider2_config_entry.add_to_hass(hass)
provider2_config_entry.mock_state(hass, ConfigEntryState.LOADED)
provider2_firmware = MagicMock(spec=["async_get_firmware_info"])
provider2_firmware.async_get_firmware_info = AsyncMock(
return_value=FIRMWARE_INFO_SPINEL
)
async_register_firmware_info_provider(hass, "otbr", provider2_firmware)
# Double registration won't work
with pytest.raises(ValueError, match="Domain zha is already registered"):
async_register_firmware_info_provider(hass, "zha", provider1_firmware)
# We can iterate over the results
info = [i async for i in hass.data[DATA_COMPONENT].iter_firmware_info()]
assert info == [
FIRMWARE_INFO_EZSP,
FIRMWARE_INFO_SPINEL,
]
callback1 = Mock()
cancel1 = async_register_firmware_info_callback(
hass, "/dev/serial/by-id/device1", callback1
)
callback2 = Mock()
cancel2 = async_register_firmware_info_callback(
hass, "/dev/serial/by-id/device2", callback2
)
# And receive notification callbacks
await async_notify_firmware_info(hass, "zha", firmware_info=FIRMWARE_INFO_EZSP)
await async_notify_firmware_info(hass, "otbr", firmware_info=FIRMWARE_INFO_SPINEL)
await async_notify_firmware_info(hass, "zha", firmware_info=FIRMWARE_INFO_EZSP)
cancel1()
await async_notify_firmware_info(hass, "zha", firmware_info=FIRMWARE_INFO_EZSP)
await async_notify_firmware_info(hass, "otbr", firmware_info=FIRMWARE_INFO_SPINEL)
cancel2()
assert callback1.mock_calls == [
call(FIRMWARE_INFO_EZSP),
call(FIRMWARE_INFO_EZSP),
]
assert callback2.mock_calls == [
call(FIRMWARE_INFO_SPINEL),
call(FIRMWARE_INFO_SPINEL),
]
async def test_dispatcher_iter_error_handling(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test HardwareInfoDispatcher ignoring errors from firmware info providers."""
await async_setup_component(hass, "homeassistant_hardware", {})
provider1_config_entry = MockConfigEntry(
domain="zha",
unique_id="some_unique_id1",
data={},
)
provider1_config_entry.add_to_hass(hass)
provider1_config_entry.mock_state(hass, ConfigEntryState.LOADED)
provider1_firmware = MagicMock(spec=["get_firmware_info"])
provider1_firmware.get_firmware_info = MagicMock(side_effect=Exception("Boom!"))
async_register_firmware_info_provider(hass, "zha", provider1_firmware)
provider2_config_entry = MockConfigEntry(
domain="otbr",
unique_id="some_unique_id2",
data={},
)
provider2_config_entry.add_to_hass(hass)
provider2_config_entry.mock_state(hass, ConfigEntryState.LOADED)
provider2_firmware = MagicMock(spec=["async_get_firmware_info"])
provider2_firmware.async_get_firmware_info = AsyncMock(
return_value=FIRMWARE_INFO_SPINEL
)
async_register_firmware_info_provider(hass, "otbr", provider2_firmware)
with caplog.at_level(logging.ERROR):
info = [i async for i in hass.data[DATA_COMPONENT].iter_firmware_info()]
assert info == [FIRMWARE_INFO_SPINEL]
assert "Error while getting firmware info from" in caplog.text
async def test_dispatcher_callback_error_handling(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test HardwareInfoDispatcher ignoring errors from firmware info callbacks."""
await async_setup_component(hass, "homeassistant_hardware", {})
provider1_config_entry = MockConfigEntry(
domain="zha",
unique_id="some_unique_id1",
data={},
)
provider1_config_entry.add_to_hass(hass)
provider1_config_entry.mock_state(hass, ConfigEntryState.LOADED)
provider1_firmware = MagicMock(spec=["get_firmware_info"])
provider1_firmware.get_firmware_info = MagicMock(return_value=FIRMWARE_INFO_EZSP)
async_register_firmware_info_provider(hass, "zha", provider1_firmware)
callback1 = Mock(side_effect=Exception("Some error"))
async_register_firmware_info_callback(hass, "/dev/serial/by-id/device1", callback1)
callback2 = Mock()
async_register_firmware_info_callback(hass, "/dev/serial/by-id/device1", callback2)
with caplog.at_level(logging.ERROR):
await async_notify_firmware_info(hass, "zha", firmware_info=FIRMWARE_INFO_EZSP)
assert "Error while notifying firmware info listener" in caplog.text
assert callback1.mock_calls == [call(FIRMWARE_INFO_EZSP)]
assert callback2.mock_calls == [call(FIRMWARE_INFO_EZSP)]