186 lines
6.5 KiB
Python
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)]
|