core/tests/components/modbus/conftest.py

191 lines
5.8 KiB
Python

"""The tests for the Modbus sensor component."""
import copy
from dataclasses import dataclass
from datetime import timedelta
import logging
from unittest import mock
from freezegun.api import FrozenDateTimeFactory
from pymodbus.exceptions import ModbusException
import pytest
from homeassistant.components.modbus.const import MODBUS_DOMAIN as DOMAIN, TCP
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_TYPE
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from tests.common import async_fire_time_changed, mock_restore_cache
TEST_MODBUS_NAME = "modbusTest"
TEST_ENTITY_NAME = "test entity"
TEST_MODBUS_HOST = "modbusHost"
TEST_PORT_TCP = 5501
TEST_PORT_SERIAL = "usb01"
@dataclass
class ReadResult:
"""Storage class for register read results."""
def __init__(self, register_words):
"""Init."""
self.registers = register_words
self.bits = register_words
self.value = register_words
def isError(self):
"""Set error state."""
return False
@pytest.fixture(name="mock_pymodbus")
def mock_pymodbus_fixture():
"""Mock pymodbus."""
mock_pb = mock.MagicMock()
with mock.patch(
"homeassistant.components.modbus.modbus.ModbusTcpClient",
return_value=mock_pb,
autospec=True,
), mock.patch(
"homeassistant.components.modbus.modbus.ModbusSerialClient",
return_value=mock_pb,
autospec=True,
), mock.patch(
"homeassistant.components.modbus.modbus.ModbusUdpClient",
return_value=mock_pb,
autospec=True,
):
yield mock_pb
@pytest.fixture(name="check_config_loaded")
def check_config_loaded_fixture():
"""Set default for check_config_loaded."""
return True
@pytest.fixture(name="register_words")
def register_words_fixture():
"""Set default for register_words."""
return [0x00, 0x00]
@pytest.fixture(name="config_addon")
def config_addon_fixture():
"""Add entra configuration items."""
return None
@pytest.fixture(name="do_exception")
def do_exception_fixture():
"""Remove side_effect to pymodbus calls."""
return False
@pytest.fixture(name="mock_modbus")
async def mock_modbus_fixture(
hass, caplog, register_words, check_config_loaded, config_addon, do_config
):
"""Load integration modbus using mocked pymodbus."""
conf = copy.deepcopy(do_config)
for key in conf:
if config_addon:
conf[key][0].update(config_addon)
caplog.set_level(logging.WARNING)
config = {
DOMAIN: [
{
CONF_TYPE: TCP,
CONF_HOST: TEST_MODBUS_HOST,
CONF_PORT: TEST_PORT_TCP,
CONF_NAME: TEST_MODBUS_NAME,
**conf,
}
]
}
mock_pb = mock.MagicMock()
with mock.patch(
"homeassistant.components.modbus.modbus.ModbusTcpClient",
return_value=mock_pb,
autospec=True,
):
now = dt_util.utcnow()
with mock.patch(
"homeassistant.helpers.event.dt_util.utcnow",
return_value=now,
autospec=True,
):
result = await async_setup_component(hass, DOMAIN, config)
assert result or not check_config_loaded
await hass.async_block_till_done()
yield mock_pb
@pytest.fixture(name="mock_pymodbus_exception")
async def mock_pymodbus_exception_fixture(hass, do_exception, mock_modbus):
"""Trigger update call with time_changed event."""
if do_exception:
exc = ModbusException("fail read_coils")
mock_modbus.read_coils.side_effect = exc
mock_modbus.read_discrete_inputs.side_effect = exc
mock_modbus.read_input_registers.side_effect = exc
mock_modbus.read_holding_registers.side_effect = exc
@pytest.fixture(name="mock_pymodbus_return")
async def mock_pymodbus_return_fixture(hass, register_words, mock_modbus):
"""Trigger update call with time_changed event."""
read_result = ReadResult(register_words) if register_words else None
mock_modbus.read_coils.return_value = read_result
mock_modbus.read_discrete_inputs.return_value = read_result
mock_modbus.read_input_registers.return_value = read_result
mock_modbus.read_holding_registers.return_value = read_result
mock_modbus.write_register.return_value = read_result
mock_modbus.write_registers.return_value = read_result
mock_modbus.write_coil.return_value = read_result
mock_modbus.write_coils.return_value = read_result
@pytest.fixture(name="mock_do_cycle")
async def mock_do_cycle_fixture(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_pymodbus_exception,
mock_pymodbus_return,
) -> FrozenDateTimeFactory:
"""Trigger update call with time_changed event."""
freezer.tick(timedelta(seconds=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
return freezer
async def do_next_cycle(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, cycle: int
) -> None:
"""Trigger update call with time_changed event."""
freezer.tick(timedelta(seconds=cycle))
async_fire_time_changed(hass)
await hass.async_block_till_done()
@pytest.fixture(name="mock_test_state")
async def mock_test_state_fixture(hass, request):
"""Mock restore cache."""
mock_restore_cache(hass, request.param)
return request.param
@pytest.fixture(name="mock_ha")
async def mock_ha_fixture(hass, mock_pymodbus_return):
"""Load homeassistant to allow service calls."""
assert await async_setup_component(hass, "homeassistant", {})
await hass.async_block_till_done()
@pytest.fixture(name="caplog_setup_text")
async def caplog_setup_text_fixture(caplog):
"""Return setup log of integration."""
return caplog.text