* Mark executor jobs as background unless created from a tracked task If the current task is not tracked the executor job should not be a background task to avoid delaying startup and shutdown. Currently any executor job created in a untracked task or background task would end up being tracked and delaying startup/shutdown * import exec has the same issue * Avoid tracking import executor jobs There is no reason to track these jobs as they are always awaited and we do not want to support fire and forget import executor jobs * fix xiaomi_miio * lots of fire time changed without background await * revert changes moved to other PR * more * more * more * m * m * p * fix fire and forget tests * scrape * sonos * system * more * capture callback before block * coverage * more * more races * more races * more * missed some * more fixes * missed some more * fix * remove unneeded * one more race * two
273 lines
9.5 KiB
Python
273 lines
9.5 KiB
Python
"""The tests for the Mikrotik device tracker platform."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
from typing import Any
|
|
|
|
from freezegun import freeze_time
|
|
import pytest
|
|
|
|
from homeassistant.components import mikrotik
|
|
import homeassistant.components.device_tracker as device_tracker
|
|
from homeassistant.const import STATE_UNAVAILABLE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|
from homeassistant.util.dt import utcnow
|
|
|
|
from . import (
|
|
DEVICE_2_WIRELESS,
|
|
DEVICE_3_DHCP_NUMERIC_NAME,
|
|
DEVICE_3_WIRELESS,
|
|
DEVICE_4_DHCP,
|
|
DEVICE_4_WIFIWAVE2,
|
|
DHCP_DATA,
|
|
MOCK_DATA,
|
|
MOCK_OPTIONS,
|
|
WIRELESS_DATA,
|
|
setup_mikrotik_entry,
|
|
)
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed, patch
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_device_registry_devices(hass: HomeAssistant) -> None:
|
|
"""Create device registry devices so the device tracker entities are enabled."""
|
|
dev_reg = dr.async_get(hass)
|
|
config_entry = MockConfigEntry(domain="something_else")
|
|
config_entry.add_to_hass(hass)
|
|
|
|
for idx, device in enumerate(
|
|
(
|
|
"00:00:00:00:00:01",
|
|
"00:00:00:00:00:02",
|
|
"00:00:00:00:00:03",
|
|
"00:00:00:00:00:04",
|
|
)
|
|
):
|
|
dev_reg.async_get_or_create(
|
|
name=f"Device {idx}",
|
|
config_entry_id=config_entry.entry_id,
|
|
connections={(dr.CONNECTION_NETWORK_MAC, device)},
|
|
)
|
|
|
|
|
|
def mock_command(
|
|
self, cmd: str, params: dict[str, Any] | None = None, suppress_errors: bool = False
|
|
) -> Any:
|
|
"""Mock the Mikrotik command method."""
|
|
if cmd == mikrotik.const.MIKROTIK_SERVICES[mikrotik.const.IS_WIRELESS]:
|
|
return True
|
|
if cmd == mikrotik.const.MIKROTIK_SERVICES[mikrotik.const.DHCP]:
|
|
return DHCP_DATA
|
|
if cmd == mikrotik.const.MIKROTIK_SERVICES[mikrotik.const.WIRELESS]:
|
|
return WIRELESS_DATA
|
|
return {}
|
|
|
|
|
|
async def test_device_trackers(
|
|
hass: HomeAssistant, mock_device_registry_devices
|
|
) -> None:
|
|
"""Test device_trackers created by mikrotik."""
|
|
|
|
# test devices are added from wireless list only
|
|
await setup_mikrotik_entry(hass)
|
|
|
|
device_1 = hass.states.get("device_tracker.device_1")
|
|
assert device_1
|
|
assert device_1.state == "home"
|
|
assert device_1.attributes["ip"] == "0.0.0.1"
|
|
assert device_1.attributes["mac"] == "00:00:00:00:00:01"
|
|
assert device_1.attributes["host_name"] == "Device_1"
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2 is None
|
|
|
|
with patch.object(mikrotik.hub.MikrotikData, "command", new=mock_command):
|
|
# test device_2 is added after connecting to wireless network
|
|
WIRELESS_DATA.append(DEVICE_2_WIRELESS)
|
|
|
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=10))
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "home"
|
|
assert device_2.attributes["ip"] == "0.0.0.2"
|
|
assert device_2.attributes["mac"] == "00:00:00:00:00:02"
|
|
assert device_2.attributes["host_name"] == "Device_2"
|
|
|
|
# test state remains home if last_seen within consider_home_interval
|
|
del WIRELESS_DATA[1] # device 2 is removed from wireless list
|
|
with freeze_time(utcnow() + timedelta(minutes=4)):
|
|
async_fire_time_changed(hass, utcnow() + timedelta(minutes=4))
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "home"
|
|
|
|
# test state changes to away if last_seen past consider_home_interval
|
|
with freeze_time(utcnow() + timedelta(minutes=6)):
|
|
async_fire_time_changed(hass, utcnow() + timedelta(minutes=6))
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "not_home"
|
|
|
|
|
|
async def test_force_dhcp(hass: HomeAssistant, mock_device_registry_devices) -> None:
|
|
"""Test updating hub that supports wireless with forced dhcp method."""
|
|
|
|
# hub supports wireless by default, force_dhcp is enabled to override
|
|
await setup_mikrotik_entry(hass, force_dhcp=False)
|
|
device_1 = hass.states.get("device_tracker.device_1")
|
|
assert device_1
|
|
assert device_1.state == "home"
|
|
# device_2 is not on the wireless list but it is still added from DHCP
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "home"
|
|
|
|
|
|
async def test_hub_not_support_wireless(
|
|
hass: HomeAssistant, mock_device_registry_devices
|
|
) -> None:
|
|
"""Test device_trackers created when hub doesn't support wireless."""
|
|
|
|
await setup_mikrotik_entry(hass, support_wireless=False)
|
|
device_1 = hass.states.get("device_tracker.device_1")
|
|
assert device_1
|
|
assert device_1.state == "home"
|
|
# device_2 is added from DHCP
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "home"
|
|
|
|
|
|
async def test_arp_ping_success(
|
|
hass: HomeAssistant, mock_device_registry_devices
|
|
) -> None:
|
|
"""Test arp ping devices to confirm they are connected."""
|
|
|
|
with patch.object(mikrotik.hub.MikrotikData, "do_arp_ping", return_value=True):
|
|
await setup_mikrotik_entry(hass, arp_ping=True, force_dhcp=True)
|
|
|
|
# test wired device_2 show as home if arp ping returns True
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "home"
|
|
|
|
|
|
async def test_arp_ping_timeout(
|
|
hass: HomeAssistant, mock_device_registry_devices
|
|
) -> None:
|
|
"""Test arp ping timeout so devices are shown away."""
|
|
with patch.object(mikrotik.hub.MikrotikData, "do_arp_ping", return_value=False):
|
|
await setup_mikrotik_entry(hass, arp_ping=True, force_dhcp=True)
|
|
|
|
# test wired device_2 show as not_home if arp ping times out
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2
|
|
assert device_2.state == "not_home"
|
|
|
|
|
|
async def test_device_trackers_numerical_name(
|
|
hass: HomeAssistant, mock_device_registry_devices
|
|
) -> None:
|
|
"""Test device_trackers created by mikrotik with numerical device name."""
|
|
|
|
await setup_mikrotik_entry(
|
|
hass, dhcp_data=[DEVICE_3_DHCP_NUMERIC_NAME], wireless_data=[DEVICE_3_WIRELESS]
|
|
)
|
|
|
|
device_3 = hass.states.get("device_tracker.123")
|
|
assert device_3
|
|
assert device_3.state == "home"
|
|
assert device_3.attributes["friendly_name"] == "123"
|
|
assert device_3.attributes["ip"] == "0.0.0.3"
|
|
assert device_3.attributes["mac"] == "00:00:00:00:00:03"
|
|
assert device_3.attributes["host_name"] == "123"
|
|
|
|
|
|
async def test_hub_wifiwave2(hass: HomeAssistant, mock_device_registry_devices) -> None:
|
|
"""Test device_trackers created when hub supports wifiwave2."""
|
|
|
|
await setup_mikrotik_entry(
|
|
hass,
|
|
dhcp_data=[DEVICE_4_DHCP],
|
|
wifiwave2_data=[DEVICE_4_WIFIWAVE2],
|
|
support_wireless=False,
|
|
support_wifiwave2=True,
|
|
)
|
|
|
|
device_4 = hass.states.get("device_tracker.device_4")
|
|
assert device_4
|
|
assert device_4.state == "home"
|
|
assert device_4.attributes["friendly_name"] == "Device_4"
|
|
assert device_4.attributes["ip"] == "0.0.0.4"
|
|
assert device_4.attributes["mac"] == "00:00:00:00:00:04"
|
|
assert device_4.attributes["host_name"] == "Device_4"
|
|
|
|
|
|
async def test_restoring_devices(
|
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
|
) -> None:
|
|
"""Test restoring existing device_tracker entities if not detected on startup."""
|
|
config_entry = MockConfigEntry(
|
|
domain=mikrotik.DOMAIN, data=MOCK_DATA, options=MOCK_OPTIONS
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
|
|
entity_registry.async_get_or_create(
|
|
device_tracker.DOMAIN,
|
|
mikrotik.DOMAIN,
|
|
"00:00:00:00:00:01",
|
|
suggested_object_id="device_1",
|
|
config_entry=config_entry,
|
|
)
|
|
entity_registry.async_get_or_create(
|
|
device_tracker.DOMAIN,
|
|
mikrotik.DOMAIN,
|
|
"00:00:00:00:00:02",
|
|
suggested_object_id="device_2",
|
|
config_entry=config_entry,
|
|
)
|
|
entity_registry.async_get_or_create(
|
|
device_tracker.DOMAIN,
|
|
mikrotik.DOMAIN,
|
|
"00:00:00:00:00:03",
|
|
suggested_object_id="device_3",
|
|
config_entry=config_entry,
|
|
)
|
|
|
|
await setup_mikrotik_entry(hass)
|
|
|
|
# test device_2 which is not in wireless list is restored
|
|
device_1 = hass.states.get("device_tracker.device_1")
|
|
assert device_1 is not None
|
|
assert device_1.state == "home"
|
|
device_2 = hass.states.get("device_tracker.device_2")
|
|
assert device_2 is not None
|
|
assert device_2.state == "not_home"
|
|
# device_3 is not on the DHCP list or wireless list
|
|
# so it won't be restored.
|
|
device_3 = hass.states.get("device_tracker.device_3")
|
|
assert device_3 is None
|
|
|
|
|
|
async def test_update_failed(hass: HomeAssistant, mock_device_registry_devices) -> None:
|
|
"""Test failing to connect during update."""
|
|
|
|
await setup_mikrotik_entry(hass)
|
|
|
|
with patch.object(
|
|
mikrotik.hub.MikrotikData, "command", side_effect=mikrotik.errors.CannotConnect
|
|
):
|
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=10))
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
device_1 = hass.states.get("device_tracker.device_1")
|
|
assert device_1
|
|
assert device_1.state == STATE_UNAVAILABLE
|