Add tests for System Monitor (#107891)
* Add tests * no coordinator * Coverage * processes * test init * util * test icon * Add tests * Mod tests * Add tests * Test attributes * snapshots * icon * test disk mounts * fixes * svmem * cache_clear * test icon * reset icon test * test_processor_temperature * fix tests on macos --------- Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
749ef45727
commit
5b3e1306f8
7 changed files with 1147 additions and 16 deletions
|
@ -1316,9 +1316,6 @@ omit =
|
|||
homeassistant/components/system_bridge/notify.py
|
||||
homeassistant/components/system_bridge/sensor.py
|
||||
homeassistant/components/system_bridge/update.py
|
||||
homeassistant/components/systemmonitor/__init__.py
|
||||
homeassistant/components/systemmonitor/sensor.py
|
||||
homeassistant/components/systemmonitor/util.py
|
||||
homeassistant/components/tado/__init__.py
|
||||
homeassistant/components/tado/binary_sensor.py
|
||||
homeassistant/components/tado/climate.py
|
||||
|
|
|
@ -4,12 +4,12 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from functools import cache
|
||||
from functools import cache, lru_cache
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import Any, Literal
|
||||
|
||||
import psutil
|
||||
import voluptuous as vol
|
||||
|
@ -56,10 +56,6 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
CONF_ARG = "arg"
|
||||
|
||||
if sys.maxsize > 2**32:
|
||||
CPU_ICON = "mdi:cpu-64-bit"
|
||||
else:
|
||||
CPU_ICON = "mdi:cpu-32-bit"
|
||||
|
||||
SENSOR_TYPE_NAME = 0
|
||||
SENSOR_TYPE_UOM = 1
|
||||
|
@ -70,6 +66,14 @@ SENSOR_TYPE_MANDATORY_ARG = 4
|
|||
SIGNAL_SYSTEMMONITOR_UPDATE = "systemmonitor_update"
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_cpu_icon() -> Literal["mdi:cpu-64-bit", "mdi:cpu-32-bit"]:
|
||||
"""Return cpu icon."""
|
||||
if sys.maxsize > 2**32:
|
||||
return "mdi:cpu-64-bit"
|
||||
return "mdi:cpu-32-bit"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SysMonitorSensorEntityDescription(SensorEntityDescription):
|
||||
"""Description for System Monitor sensor entities."""
|
||||
|
@ -121,19 +125,19 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = {
|
|||
"load_15m": SysMonitorSensorEntityDescription(
|
||||
key="load_15m",
|
||||
name="Load (15m)",
|
||||
icon=CPU_ICON,
|
||||
icon=get_cpu_icon(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"load_1m": SysMonitorSensorEntityDescription(
|
||||
key="load_1m",
|
||||
name="Load (1m)",
|
||||
icon=CPU_ICON,
|
||||
icon=get_cpu_icon(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"load_5m": SysMonitorSensorEntityDescription(
|
||||
key="load_5m",
|
||||
name="Load (5m)",
|
||||
icon=CPU_ICON,
|
||||
icon=get_cpu_icon(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"memory_free": SysMonitorSensorEntityDescription(
|
||||
|
@ -210,14 +214,14 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = {
|
|||
"process": SysMonitorSensorEntityDescription(
|
||||
key="process",
|
||||
name="Process",
|
||||
icon=CPU_ICON,
|
||||
icon=get_cpu_icon(),
|
||||
mandatory_arg=True,
|
||||
),
|
||||
"processor_use": SysMonitorSensorEntityDescription(
|
||||
key="processor_use",
|
||||
name="Processor use",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon=CPU_ICON,
|
||||
icon=get_cpu_icon(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"processor_temperature": SysMonitorSensorEntityDescription(
|
||||
|
@ -751,7 +755,11 @@ def _getloadavg() -> tuple[float, float, float]:
|
|||
|
||||
def _read_cpu_temperature() -> float | None:
|
||||
"""Attempt to read CPU / processor temperature."""
|
||||
temps = psutil.sensors_temperatures()
|
||||
try:
|
||||
temps = psutil.sensors_temperatures()
|
||||
except AttributeError:
|
||||
# Linux, macOS
|
||||
return None
|
||||
|
||||
for name, entries in temps.items():
|
||||
for i, entry in enumerate(entries, start=1):
|
||||
|
|
|
@ -1,11 +1,61 @@
|
|||
"""Fixtures for the System Monitor integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import namedtuple
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
import socket
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from psutil import NoSuchProcess, Process
|
||||
from psutil._common import sdiskpart, sdiskusage, shwtemp, snetio, snicaddr, sswap
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.systemmonitor.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
# Different depending on platform so making according to Linux
|
||||
svmem = namedtuple(
|
||||
"svmem",
|
||||
[
|
||||
"total",
|
||||
"available",
|
||||
"percent",
|
||||
"used",
|
||||
"free",
|
||||
"active",
|
||||
"inactive",
|
||||
"buffers",
|
||||
"cached",
|
||||
"shared",
|
||||
"slab",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_sys_platform() -> Generator[None, None, None]:
|
||||
"""Mock sys platform to Linux."""
|
||||
with patch("sys.platform", "linux"):
|
||||
yield
|
||||
|
||||
|
||||
class MockProcess(Process):
|
||||
"""Mock a Process class."""
|
||||
|
||||
def __init__(self, name: str, ex: bool = False) -> None:
|
||||
"""Initialize the process."""
|
||||
super().__init__(1)
|
||||
self._name = name
|
||||
self._ex = ex
|
||||
|
||||
def name(self):
|
||||
"""Return a name."""
|
||||
if self._ex:
|
||||
raise NoSuchProcess(1, self._name)
|
||||
return self._name
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
|
@ -15,3 +65,184 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
|||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Mock ConfigEntry."""
|
||||
return MockConfigEntry(
|
||||
title="System Monitor",
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
options={
|
||||
"sensor": {"process": ["python3", "pip"]},
|
||||
"resources": [
|
||||
"disk_use_percent_/",
|
||||
"disk_use_percent_/home/notexist/",
|
||||
"memory_free_",
|
||||
"network_out_eth0",
|
||||
"process_python3",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_added_config_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_util: Mock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> MockConfigEntry:
|
||||
"""Mock ConfigEntry that's been added to HA."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert DOMAIN in hass.config_entries.async_domains()
|
||||
return mock_config_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_process() -> list[MockProcess]:
|
||||
"""Mock process."""
|
||||
_process_python = MockProcess("python3")
|
||||
_process_pip = MockProcess("pip")
|
||||
return [_process_python, _process_pip]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_psutil(mock_process: list[MockProcess]) -> Mock:
|
||||
"""Mock psutil."""
|
||||
with patch(
|
||||
"homeassistant.components.systemmonitor.sensor.psutil",
|
||||
autospec=True,
|
||||
) as mock_psutil:
|
||||
mock_psutil.disk_usage.return_value = sdiskusage(
|
||||
500 * 1024**2, 300 * 1024**2, 200 * 1024**2, 60.0
|
||||
)
|
||||
mock_psutil.swap_memory.return_value = sswap(
|
||||
100 * 1024**2, 60 * 1024**2, 40 * 1024**2, 60.0, 1, 1
|
||||
)
|
||||
mock_psutil.virtual_memory.return_value = svmem(
|
||||
100 * 1024**2,
|
||||
40 * 1024**2,
|
||||
40.0,
|
||||
60 * 1024**2,
|
||||
30 * 1024**2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
)
|
||||
mock_psutil.net_io_counters.return_value = {
|
||||
"eth0": snetio(100 * 1024**2, 100 * 1024**2, 50, 50, 0, 0, 0, 0),
|
||||
"eth1": snetio(200 * 1024**2, 200 * 1024**2, 150, 150, 0, 0, 0, 0),
|
||||
"vethxyzxyz": snetio(300 * 1024**2, 300 * 1024**2, 150, 150, 0, 0, 0, 0),
|
||||
}
|
||||
mock_psutil.net_if_addrs.return_value = {
|
||||
"eth0": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"192.168.1.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
"eth1": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"192.168.10.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
"vethxyzxyz": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"172.16.10.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
}
|
||||
mock_psutil.cpu_percent.return_value = 10.0
|
||||
mock_psutil.boot_time.return_value = 1703973338.0
|
||||
mock_psutil.process_iter.return_value = mock_process
|
||||
# sensors_temperatures not available on MacOS so we
|
||||
# need to override the spec
|
||||
mock_psutil.sensors_temperatures = Mock()
|
||||
mock_psutil.sensors_temperatures.return_value = {
|
||||
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
||||
}
|
||||
mock_psutil.NoSuchProcess = NoSuchProcess
|
||||
yield mock_psutil
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_util(mock_process) -> Mock:
|
||||
"""Mock psutil."""
|
||||
with patch(
|
||||
"homeassistant.components.systemmonitor.util.psutil", autospec=True
|
||||
) as mock_util:
|
||||
mock_util.net_if_addrs.return_value = {
|
||||
"eth0": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"192.168.1.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
"eth1": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"192.168.10.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
"vethxyzxyz": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"172.16.10.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
}
|
||||
mock_process = [MockProcess("python3")]
|
||||
mock_util.process_iter.return_value = mock_process
|
||||
# sensors_temperatures not available on MacOS so we
|
||||
# need to override the spec
|
||||
mock_util.sensors_temperatures = Mock()
|
||||
mock_util.sensors_temperatures.return_value = {
|
||||
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
||||
}
|
||||
mock_util.disk_partitions.return_value = [
|
||||
sdiskpart("test", "/", "ext4", "", 1, 1),
|
||||
sdiskpart("test2", "/media/share", "ext4", "", 1, 1),
|
||||
sdiskpart("test3", "/incorrect", "", "", 1, 1),
|
||||
sdiskpart("proc", "/proc/run", "proc", "", 1, 1),
|
||||
]
|
||||
mock_util.disk_usage.return_value = sdiskusage(10, 10, 0, 0)
|
||||
yield mock_util
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_os() -> Mock:
|
||||
"""Mock os."""
|
||||
with patch("homeassistant.components.systemmonitor.sensor.os") as mock_os, patch(
|
||||
"homeassistant.components.systemmonitor.util.os"
|
||||
) as mock_os_util:
|
||||
mock_os_util.name = "nt"
|
||||
mock_os.getloadavg.return_value = (1, 2, 3)
|
||||
yield mock_os
|
||||
|
|
399
tests/components/systemmonitor/snapshots/test_sensor.ambr
Normal file
399
tests/components/systemmonitor/snapshots/test_sensor.ambr
Normal file
|
@ -0,0 +1,399 @@
|
|||
# serializer version: 1
|
||||
# name: test_sensor[System Monitor Disk free / - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Disk free /',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk free / - state]
|
||||
'0.2'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk free /media/share - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Disk free /media/share',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk free /media/share - state]
|
||||
'0.2'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use (percent) / - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Disk use (percent) /',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use (percent) / - state]
|
||||
'60.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use (percent) /home/notexist/ - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Disk use (percent) /home/notexist/',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use (percent) /home/notexist/ - state]
|
||||
'60.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use (percent) /media/share - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Disk use (percent) /media/share',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use (percent) /media/share - state]
|
||||
'60.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use / - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Disk use /',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use / - state]
|
||||
'0.3'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use /media/share - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Disk use /media/share',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Disk use /media/share - state]
|
||||
'0.3'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv4 address eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor IPv4 address eth0',
|
||||
'icon': 'mdi:ip-network',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv4 address eth0 - state]
|
||||
'192.168.1.1'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv4 address eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor IPv4 address eth1',
|
||||
'icon': 'mdi:ip-network',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv4 address eth1 - state]
|
||||
'192.168.10.1'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv6 address eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor IPv6 address eth0',
|
||||
'icon': 'mdi:ip-network',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv6 address eth0 - state]
|
||||
'unknown'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv6 address eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor IPv6 address eth1',
|
||||
'icon': 'mdi:ip-network',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor IPv6 address eth1 - state]
|
||||
'unknown'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Last boot - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'System Monitor Last boot',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Last boot - state]
|
||||
'2023-12-30T21:55:38+00:00'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (15m) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (15m)',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (15m) - state]
|
||||
'3'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (1m) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (1m)',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (1m) - state]
|
||||
'1'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (5m) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (5m)',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (5m) - state]
|
||||
'2'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Memory free - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Memory free',
|
||||
'icon': 'mdi:memory',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Memory free - state]
|
||||
'40.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Memory use (percent) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Memory use (percent)',
|
||||
'icon': 'mdi:memory',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Memory use (percent) - state]
|
||||
'40.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Memory use - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Memory use',
|
||||
'icon': 'mdi:memory',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Memory use - state]
|
||||
'60.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network in eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Network in eth0',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network in eth0 - state]
|
||||
'100.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network in eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Network in eth1',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network in eth1 - state]
|
||||
'200.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network out eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Network out eth0',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network out eth0 - state]
|
||||
'100.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network out eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Network out eth1',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network out eth1 - state]
|
||||
'200.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput in eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_rate',
|
||||
'friendly_name': 'System Monitor Network throughput in eth0',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfDataRate.MEGABYTES_PER_SECOND: 'MB/s'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput in eth0 - state]
|
||||
'unknown'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput in eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_rate',
|
||||
'friendly_name': 'System Monitor Network throughput in eth1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfDataRate.MEGABYTES_PER_SECOND: 'MB/s'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput in eth1 - state]
|
||||
'unknown'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput out eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_rate',
|
||||
'friendly_name': 'System Monitor Network throughput out eth0',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfDataRate.MEGABYTES_PER_SECOND: 'MB/s'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput out eth0 - state]
|
||||
'unknown'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput out eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_rate',
|
||||
'friendly_name': 'System Monitor Network throughput out eth1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfDataRate.MEGABYTES_PER_SECOND: 'MB/s'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Network throughput out eth1 - state]
|
||||
'unknown'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets in eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Packets in eth0',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets in eth0 - state]
|
||||
'50'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets in eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Packets in eth1',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets in eth1 - state]
|
||||
'150'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets out eth0 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Packets out eth0',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets out eth0 - state]
|
||||
'50'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets out eth1 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Packets out eth1',
|
||||
'icon': 'mdi:server-network',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Packets out eth1 - state]
|
||||
'150'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Process pip - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Process pip',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Process pip - state]
|
||||
'on'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Process python3 - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Process python3',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Process python3 - state]
|
||||
'on'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Processor temperature - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'System Monitor Processor temperature',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Processor temperature - state]
|
||||
'50.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Processor use - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Processor use',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Processor use - state]
|
||||
'10'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap free - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Swap free',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap free - state]
|
||||
'40.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap use (percent) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Swap use (percent)',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap use (percent) - state]
|
||||
'60.0'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap use - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
'friendly_name': 'System Monitor Swap use',
|
||||
'icon': 'mdi:harddisk',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfInformation.MEBIBYTES: 'MiB'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap use - state]
|
||||
'60.0'
|
||||
# ---
|
60
tests/components/systemmonitor/test_init.py
Normal file
60
tests/components/systemmonitor/test_init.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
"""Test for System Monitor init."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.systemmonitor.const import CONF_PROCESS
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import STATE_OFF
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
|
||||
async def test_load_unload_entry(
|
||||
hass: HomeAssistant, mock_added_config_entry: ConfigEntry
|
||||
) -> None:
|
||||
"""Test load and unload an entry."""
|
||||
|
||||
assert mock_added_config_entry.state == ConfigEntryState.LOADED
|
||||
assert await hass.config_entries.async_unload(mock_added_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_added_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_adding_processor_to_options(
|
||||
hass: HomeAssistant, mock_added_config_entry: ConfigEntry
|
||||
) -> None:
|
||||
"""Test options listener."""
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_systemd")
|
||||
assert process_sensor is None
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
mock_added_config_entry.entry_id
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_PROCESS: ["python3", "pip", "systemd"],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"sensor": {
|
||||
CONF_PROCESS: ["python3", "pip", "systemd"],
|
||||
},
|
||||
"resources": [
|
||||
"disk_use_percent_/",
|
||||
"disk_use_percent_/home/notexist/",
|
||||
"memory_free_",
|
||||
"network_out_eth0",
|
||||
"process_python3",
|
||||
],
|
||||
}
|
||||
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_systemd")
|
||||
assert process_sensor is not None
|
||||
assert process_sensor.state == STATE_OFF
|
346
tests/components/systemmonitor/test_sensor.py
Normal file
346
tests/components/systemmonitor/test_sensor.py
Normal file
|
@ -0,0 +1,346 @@
|
|||
"""Test System Monitor sensor."""
|
||||
from datetime import timedelta
|
||||
import socket
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from psutil._common import shwtemp, snetio, snicaddr
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.systemmonitor.sensor import (
|
||||
_read_cpu_temperature,
|
||||
get_cpu_icon,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import MockProcess, svmem
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_sensor(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_added_config_entry: ConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the sensor."""
|
||||
memory_sensor = hass.states.get("sensor.system_monitor_memory_free")
|
||||
assert memory_sensor is not None
|
||||
assert memory_sensor.state == "40.0"
|
||||
assert memory_sensor.attributes == {
|
||||
"state_class": "measurement",
|
||||
"unit_of_measurement": "MiB",
|
||||
"device_class": "data_size",
|
||||
"icon": "mdi:memory",
|
||||
"friendly_name": "System Monitor Memory free",
|
||||
}
|
||||
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
||||
assert process_sensor is not None
|
||||
assert process_sensor.state == STATE_ON
|
||||
|
||||
for entity in er.async_entries_for_config_entry(
|
||||
entity_registry, mock_added_config_entry.entry_id
|
||||
):
|
||||
state = hass.states.get(entity.entity_id)
|
||||
assert state.state == snapshot(name=f"{state.name} - state")
|
||||
assert state.attributes == snapshot(name=f"{state.name} - attributes")
|
||||
|
||||
|
||||
async def test_sensor_not_loading_veth_networks(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_added_config_entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Test the sensor."""
|
||||
network_sensor_1 = hass.states.get("sensor.system_monitor_network_out_eth1")
|
||||
network_sensor_2 = hass.states.get(
|
||||
"sensor.sensor.system_monitor_network_out_vethxyzxyz"
|
||||
)
|
||||
assert network_sensor_1 is not None
|
||||
assert network_sensor_1.state == "200.0"
|
||||
assert network_sensor_2 is None
|
||||
|
||||
|
||||
async def test_sensor_icon(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_util: Mock,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the sensor icon for 32bit/64bit system."""
|
||||
|
||||
get_cpu_icon.cache_clear()
|
||||
with patch("sys.maxsize", 2**32):
|
||||
assert get_cpu_icon() == "mdi:cpu-32-bit"
|
||||
get_cpu_icon.cache_clear()
|
||||
with patch("sys.maxsize", 2**64):
|
||||
assert get_cpu_icon() == "mdi:cpu-64-bit"
|
||||
|
||||
|
||||
async def test_sensor_yaml(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_util: Mock,
|
||||
) -> None:
|
||||
"""Test the sensor imported from YAML."""
|
||||
config = {
|
||||
"sensor": {
|
||||
"platform": "systemmonitor",
|
||||
"resources": [
|
||||
{"type": "disk_use_percent"},
|
||||
{"type": "disk_use_percent", "arg": "/media/share"},
|
||||
{"type": "memory_free", "arg": "/"},
|
||||
{"type": "network_out", "arg": "eth0"},
|
||||
{"type": "process", "arg": "python3"},
|
||||
],
|
||||
}
|
||||
}
|
||||
assert await async_setup_component(hass, "sensor", config)
|
||||
await hass.async_block_till_done()
|
||||
memory_sensor = hass.states.get("sensor.system_monitor_memory_free")
|
||||
assert memory_sensor is not None
|
||||
assert memory_sensor.state == "40.0"
|
||||
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
||||
assert process_sensor is not None
|
||||
assert process_sensor.state == STATE_ON
|
||||
|
||||
|
||||
async def test_sensor_yaml_fails_missing_argument(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_util: Mock,
|
||||
) -> None:
|
||||
"""Test the sensor imported from YAML fails on missing mandatory argument."""
|
||||
config = {
|
||||
"sensor": {
|
||||
"platform": "systemmonitor",
|
||||
"resources": [
|
||||
{"type": "network_in"},
|
||||
],
|
||||
}
|
||||
}
|
||||
assert await async_setup_component(hass, "sensor", config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Mandatory 'arg' is missing for sensor type 'network_in'" in caplog.text
|
||||
|
||||
|
||||
async def test_sensor_updating(
|
||||
hass: HomeAssistant,
|
||||
mock_added_config_entry: ConfigEntry,
|
||||
mock_psutil: Mock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the sensor."""
|
||||
memory_sensor = hass.states.get("sensor.system_monitor_memory_free")
|
||||
assert memory_sensor is not None
|
||||
assert memory_sensor.state == "40.0"
|
||||
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
||||
assert process_sensor is not None
|
||||
assert process_sensor.state == STATE_ON
|
||||
|
||||
mock_psutil.virtual_memory.side_effect = Exception("Failed to update")
|
||||
freezer.tick(timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
memory_sensor = hass.states.get("sensor.system_monitor_memory_free")
|
||||
assert memory_sensor is not None
|
||||
assert memory_sensor.state == STATE_UNAVAILABLE
|
||||
|
||||
mock_psutil.virtual_memory.side_effect = None
|
||||
mock_psutil.virtual_memory.return_value = svmem(
|
||||
100 * 1024**2,
|
||||
25 * 1024**2,
|
||||
25.0,
|
||||
60 * 1024**2,
|
||||
30 * 1024**2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
)
|
||||
freezer.tick(timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
memory_sensor = hass.states.get("sensor.system_monitor_memory_free")
|
||||
assert memory_sensor is not None
|
||||
assert memory_sensor.state == "25.0"
|
||||
|
||||
|
||||
async def test_sensor_process_fails(
|
||||
hass: HomeAssistant,
|
||||
mock_added_config_entry: ConfigEntry,
|
||||
mock_psutil: Mock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test process not exist failure."""
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
||||
assert process_sensor is not None
|
||||
assert process_sensor.state == STATE_ON
|
||||
|
||||
_process = MockProcess("python3", True)
|
||||
|
||||
mock_psutil.process_iter.return_value = [_process]
|
||||
|
||||
freezer.tick(timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
||||
assert process_sensor is not None
|
||||
# assert process_sensor.state == STATE_ON
|
||||
|
||||
assert "Failed to load process with ID: 1, old name: python3" in caplog.text
|
||||
|
||||
|
||||
async def test_sensor_network_sensors(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_added_config_entry: ConfigEntry,
|
||||
mock_psutil: Mock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test process not exist failure."""
|
||||
network_out_sensor = hass.states.get("sensor.system_monitor_network_out_eth1")
|
||||
packets_out_sensor = hass.states.get("sensor.system_monitor_packets_out_eth1")
|
||||
throughput_network_out_sensor = hass.states.get(
|
||||
"sensor.system_monitor_network_throughput_out_eth1"
|
||||
)
|
||||
|
||||
assert network_out_sensor is not None
|
||||
assert packets_out_sensor is not None
|
||||
assert throughput_network_out_sensor is not None
|
||||
assert network_out_sensor.state == "200.0"
|
||||
assert packets_out_sensor.state == "150"
|
||||
assert throughput_network_out_sensor.state == STATE_UNKNOWN
|
||||
|
||||
mock_psutil.net_io_counters.return_value = {
|
||||
"eth0": snetio(200 * 1024**2, 200 * 1024**2, 100, 100, 0, 0, 0, 0),
|
||||
"eth1": snetio(400 * 1024**2, 400 * 1024**2, 300, 300, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
freezer.tick(timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
network_out_sensor = hass.states.get("sensor.system_monitor_network_out_eth1")
|
||||
packets_out_sensor = hass.states.get("sensor.system_monitor_packets_out_eth1")
|
||||
throughput_network_out_sensor = hass.states.get(
|
||||
"sensor.system_monitor_network_throughput_out_eth1"
|
||||
)
|
||||
|
||||
assert network_out_sensor is not None
|
||||
assert packets_out_sensor is not None
|
||||
assert throughput_network_out_sensor is not None
|
||||
assert network_out_sensor.state == "400.0"
|
||||
assert packets_out_sensor.state == "300"
|
||||
assert float(throughput_network_out_sensor.state) == pytest.approx(3.493, rel=0.1)
|
||||
|
||||
mock_psutil.net_io_counters.return_value = {
|
||||
"eth0": snetio(100 * 1024**2, 100 * 1024**2, 50, 50, 0, 0, 0, 0),
|
||||
}
|
||||
mock_psutil.net_if_addrs.return_value = {
|
||||
"eth0": [
|
||||
snicaddr(
|
||||
socket.AF_INET,
|
||||
"192.168.1.1",
|
||||
"255.255.255.0",
|
||||
"255.255.255.255",
|
||||
None,
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
freezer.tick(timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
network_out_sensor = hass.states.get("sensor.system_monitor_network_out_eth1")
|
||||
packets_out_sensor = hass.states.get("sensor.system_monitor_packets_out_eth1")
|
||||
throughput_network_out_sensor = hass.states.get(
|
||||
"sensor.system_monitor_network_throughput_out_eth1"
|
||||
)
|
||||
|
||||
assert network_out_sensor is not None
|
||||
assert packets_out_sensor is not None
|
||||
assert throughput_network_out_sensor is not None
|
||||
assert network_out_sensor.state == STATE_UNKNOWN
|
||||
assert packets_out_sensor.state == STATE_UNKNOWN
|
||||
assert throughput_network_out_sensor.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_missing_cpu_temperature(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_util: Mock,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test the sensor when temperature missing."""
|
||||
mock_psutil.sensors_temperatures.return_value = {
|
||||
"not_exist": [shwtemp("not_exist", 50.0, 60.0, 70.0)]
|
||||
}
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Cannot read CPU / processor temperature information" in caplog.text
|
||||
temp_sensor = hass.states.get("sensor.system_monitor_processor_temperature")
|
||||
assert temp_sensor is None
|
||||
|
||||
|
||||
async def test_processor_temperature() -> None:
|
||||
"""Test the disk failures."""
|
||||
|
||||
with patch("sys.platform", "linux"), patch(
|
||||
"homeassistant.components.systemmonitor.sensor.psutil"
|
||||
) as mock_psutil:
|
||||
mock_psutil.sensors_temperatures.return_value = {
|
||||
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
||||
}
|
||||
temperature = _read_cpu_temperature()
|
||||
assert temperature == 50.0
|
||||
|
||||
with patch("sys.platform", "nt"), patch(
|
||||
"homeassistant.components.systemmonitor.sensor.psutil",
|
||||
) as mock_psutil:
|
||||
mock_psutil.sensors_temperatures.side_effect = AttributeError(
|
||||
"sensors_temperatures not exist"
|
||||
)
|
||||
temperature = _read_cpu_temperature()
|
||||
assert temperature is None
|
||||
|
||||
with patch("sys.platform", "darwin"), patch(
|
||||
"homeassistant.components.systemmonitor.sensor.psutil"
|
||||
) as mock_psutil:
|
||||
mock_psutil.sensors_temperatures.return_value = {
|
||||
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
||||
}
|
||||
temperature = _read_cpu_temperature()
|
||||
assert temperature == 50.0
|
90
tests/components/systemmonitor/test_util.py
Normal file
90
tests/components/systemmonitor/test_util.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""Test System Monitor utils."""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from psutil._common import sdiskpart
|
||||
import pytest
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "error_text"),
|
||||
[
|
||||
(PermissionError("No permission"), "No permission for running user to access"),
|
||||
(OSError("OS error"), "was excluded because of: OS error"),
|
||||
],
|
||||
)
|
||||
async def test_disk_setup_failure(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_util: Mock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
side_effect: Exception,
|
||||
error_text: str,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test the disk failures."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.systemmonitor.util.psutil.disk_usage",
|
||||
side_effect=side_effect,
|
||||
):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
disk_sensor = hass.states.get("sensor.system_monitor_disk_free_media_share")
|
||||
assert disk_sensor is None
|
||||
|
||||
assert error_text in caplog.text
|
||||
|
||||
|
||||
async def test_disk_util(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
mock_util: Mock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the disk failures."""
|
||||
|
||||
mock_util.disk_partitions.return_value = [
|
||||
sdiskpart("test", "/", "ext4", "", 1, 1), # Should be ok
|
||||
sdiskpart("test2", "/media/share", "ext4", "", 1, 1), # Should be ok
|
||||
sdiskpart("test3", "/incorrect", "", "", 1, 1), # Should be skipped as no type
|
||||
sdiskpart(
|
||||
"proc", "/proc/run", "proc", "", 1, 1
|
||||
), # Should be skipped as in skipped disk types
|
||||
sdiskpart(
|
||||
"test4",
|
||||
"/tmpfs/", # noqa: S108
|
||||
"tmpfs",
|
||||
"",
|
||||
1,
|
||||
1,
|
||||
), # Should be skipped as in skipped disk types
|
||||
sdiskpart("test5", "E:", "cd", "cdrom", 1, 1), # Should be skipped as cdrom
|
||||
]
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
disk_sensor1 = hass.states.get("sensor.system_monitor_disk_free")
|
||||
disk_sensor2 = hass.states.get("sensor.system_monitor_disk_free_media_share")
|
||||
disk_sensor3 = hass.states.get("sensor.system_monitor_disk_free_incorrect")
|
||||
disk_sensor4 = hass.states.get("sensor.system_monitor_disk_free_proc_run")
|
||||
disk_sensor5 = hass.states.get("sensor.system_monitor_disk_free_tmpfs")
|
||||
disk_sensor6 = hass.states.get("sensor.system_monitor_disk_free_e")
|
||||
assert disk_sensor1 is not None
|
||||
assert disk_sensor2 is not None
|
||||
assert disk_sensor3 is None
|
||||
assert disk_sensor4 is None
|
||||
assert disk_sensor5 is None
|
||||
assert disk_sensor6 is None
|
Loading…
Add table
Reference in a new issue