From 64bdf2d35b2b9276eb9c8f2a64d8b8510270a5c6 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Mon, 6 Apr 2020 01:09:20 +0200 Subject: [PATCH] Modbus: isolate common test functions (#33447) Since all entity test functions are going to use the modbus class, isolate the common parts in conftest.py, and thereby make it simpler to write additional test cases. cleaned up test_modbus_sensor.py while splitting the code. --- tests/components/modbus/conftest.py | 96 +++++++++++ tests/components/modbus/test_modbus_sensor.py | 157 +++++++++--------- 2 files changed, 177 insertions(+), 76 deletions(-) create mode 100644 tests/components/modbus/conftest.py diff --git a/tests/components/modbus/conftest.py b/tests/components/modbus/conftest.py new file mode 100644 index 00000000000..043236c503c --- /dev/null +++ b/tests/components/modbus/conftest.py @@ -0,0 +1,96 @@ +"""The tests for the Modbus sensor component.""" +from datetime import timedelta +import logging +from unittest import mock + +import pytest + +from homeassistant.components.modbus.const import ( + CALL_TYPE_REGISTER_INPUT, + CONF_REGISTER, + CONF_REGISTER_TYPE, + CONF_REGISTERS, + DEFAULT_HUB, + MODBUS_DOMAIN, +) +from homeassistant.const import CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL +from homeassistant.setup import async_setup_component +import homeassistant.util.dt as dt_util + +from tests.common import MockModule, async_fire_time_changed, mock_integration + +_LOGGER = logging.getLogger(__name__) + + +@pytest.fixture() +def mock_hub(hass): + """Mock hub.""" + mock_integration(hass, MockModule(MODBUS_DOMAIN)) + hub = mock.MagicMock() + hub.name = "hub" + hass.data[MODBUS_DOMAIN] = {DEFAULT_HUB: hub} + return hub + + +common_register_config = {CONF_NAME: "test-config", CONF_REGISTER: 1234} + + +class ReadResult: + """Storage class for register read results.""" + + def __init__(self, register_words): + """Init.""" + self.registers = register_words + + +read_result = None + + +async def simulate_read_registers(unit, address, count): + """Simulate modbus register read.""" + del unit, address, count # not used in simulation, but in real connection + global read_result + return read_result + + +async def run_test( + hass, mock_hub, register_config, entity_domain, register_words, expected +): + """Run test for given config and check that sensor outputs expected result.""" + + # Full sensor configuration + sensor_name = "modbus_test_sensor" + scan_interval = 5 + config = { + entity_domain: { + CONF_PLATFORM: "modbus", + CONF_SCAN_INTERVAL: scan_interval, + CONF_REGISTERS: [ + dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config) + ], + } + } + + # Setup inputs for the sensor + global read_result + read_result = ReadResult(register_words) + if register_config.get(CONF_REGISTER_TYPE) == CALL_TYPE_REGISTER_INPUT: + mock_hub.read_input_registers = simulate_read_registers + else: + mock_hub.read_holding_registers = simulate_read_registers + + # Initialize sensor + now = dt_util.utcnow() + with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): + assert await async_setup_component(hass, entity_domain, config) + + # Trigger update call with time_changed event + now += timedelta(seconds=scan_interval + 1) + with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): + async_fire_time_changed(hass, now) + await hass.async_block_till_done() + + # Check state + entity_id = f"{entity_domain}.{sensor_name}" + state = hass.states.get(entity_id).state + assert state == expected diff --git a/tests/components/modbus/test_modbus_sensor.py b/tests/components/modbus/test_modbus_sensor.py index 1c4094387a9..6207a363937 100644 --- a/tests/components/modbus/test_modbus_sensor.py +++ b/tests/components/modbus/test_modbus_sensor.py @@ -1,8 +1,5 @@ """The tests for the Modbus sensor component.""" -from datetime import timedelta -from unittest import mock - -import pytest +import logging from homeassistant.components.modbus.const import ( CALL_TYPE_REGISTER_HOLDING, @@ -11,78 +8,18 @@ from homeassistant.components.modbus.const import ( CONF_DATA_TYPE, CONF_OFFSET, CONF_PRECISION, - CONF_REGISTER, CONF_REGISTER_TYPE, - CONF_REGISTERS, CONF_REVERSE_ORDER, CONF_SCALE, DATA_TYPE_FLOAT, DATA_TYPE_INT, DATA_TYPE_UINT, - DEFAULT_HUB, - MODBUS_DOMAIN, ) -from homeassistant.const import CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL -from homeassistant.setup import async_setup_component -import homeassistant.util.dt as dt_util +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from tests.common import MockModule, async_fire_time_changed, mock_integration +from .conftest import run_test - -@pytest.fixture() -def mock_hub(hass): - """Mock hub.""" - mock_integration(hass, MockModule(MODBUS_DOMAIN)) - hub = mock.MagicMock() - hub.name = "hub" - hass.data[MODBUS_DOMAIN] = {DEFAULT_HUB: hub} - return hub - - -common_register_config = {CONF_NAME: "test-config", CONF_REGISTER: 1234} - - -class ReadResult: - """Storage class for register read results.""" - - def __init__(self, register_words): - """Init.""" - self.registers = register_words - - -async def run_test(hass, mock_hub, register_config, register_words, expected): - """Run test for given config and check that sensor outputs expected result.""" - - # Full sensor configuration - sensor_name = "modbus_test_sensor" - scan_interval = 5 - config = { - MODBUS_DOMAIN: { - CONF_PLATFORM: "modbus", - CONF_SCAN_INTERVAL: scan_interval, - CONF_REGISTERS: [ - dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config) - ], - } - } - - # Setup inputs for the sensor - read_result = ReadResult(register_words) - if register_config.get(CONF_REGISTER_TYPE) == CALL_TYPE_REGISTER_INPUT: - mock_hub.read_input_registers.return_value = read_result - else: - mock_hub.read_holding_registers.return_value = read_result - - # Initialize sensor - now = dt_util.utcnow() - with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): - assert await async_setup_component(hass, MODBUS_DOMAIN, config) - - # Trigger update call with time_changed event - now += timedelta(seconds=scan_interval + 1) - with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): - async_fire_time_changed(hass, now) - await hass.async_block_till_done() +_LOGGER = logging.getLogger(__name__) async def test_simple_word_register(hass, mock_hub): @@ -94,14 +31,26 @@ async def test_simple_word_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test(hass, mock_hub, register_config, register_words=[0], expected="0") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[0], + expected="0", + ) async def test_optional_conf_keys(hass, mock_hub): """Test handling of optional configuration keys.""" register_config = {} await run_test( - hass, mock_hub, register_config, register_words=[0x8000], expected="-32768" + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[0x8000], + expected="-32768", ) @@ -114,7 +63,14 @@ async def test_offset(hass, mock_hub): CONF_OFFSET: 13, CONF_PRECISION: 0, } - await run_test(hass, mock_hub, register_config, register_words=[7], expected="20") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[7], + expected="20", + ) async def test_scale_and_offset(hass, mock_hub): @@ -126,7 +82,14 @@ async def test_scale_and_offset(hass, mock_hub): CONF_OFFSET: 13, CONF_PRECISION: 0, } - await run_test(hass, mock_hub, register_config, register_words=[7], expected="34") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[7], + expected="34", + ) async def test_ints_can_have_precision(hass, mock_hub): @@ -139,7 +102,12 @@ async def test_ints_can_have_precision(hass, mock_hub): CONF_PRECISION: 4, } await run_test( - hass, mock_hub, register_config, register_words=[7], expected="34.0000" + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[7], + expected="34.0000", ) @@ -152,7 +120,14 @@ async def test_floats_get_rounded_correctly(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test(hass, mock_hub, register_config, register_words=[1], expected="2") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[1], + expected="2", + ) async def test_parameters_as_strings(hass, mock_hub): @@ -164,7 +139,14 @@ async def test_parameters_as_strings(hass, mock_hub): CONF_OFFSET: "5", CONF_PRECISION: "1", } - await run_test(hass, mock_hub, register_config, register_words=[9], expected="18.5") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[9], + expected="18.5", + ) async def test_floating_point_scale(hass, mock_hub): @@ -176,7 +158,14 @@ async def test_floating_point_scale(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 2, } - await run_test(hass, mock_hub, register_config, register_words=[1], expected="2.40") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[1], + expected="2.40", + ) async def test_floating_point_offset(hass, mock_hub): @@ -188,7 +177,14 @@ async def test_floating_point_offset(hass, mock_hub): CONF_OFFSET: -10.3, CONF_PRECISION: 1, } - await run_test(hass, mock_hub, register_config, register_words=[2], expected="-8.3") + await run_test( + hass, + mock_hub, + register_config, + SENSOR_DOMAIN, + register_words=[2], + expected="-8.3", + ) async def test_signed_two_word_register(hass, mock_hub): @@ -204,6 +200,7 @@ async def test_signed_two_word_register(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected="-1985229329", ) @@ -222,6 +219,7 @@ async def test_unsigned_two_word_register(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0x89ABCDEF), ) @@ -238,6 +236,7 @@ async def test_reversed(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0xCDEF89AB), ) @@ -256,6 +255,7 @@ async def test_four_word_register(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF, 0x0123, 0x4567], expected="9920249030613615975", ) @@ -274,6 +274,7 @@ async def test_four_word_register_precision_is_intact_with_int_params(hass, mock hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF], expected="163971058432973793", ) @@ -292,6 +293,7 @@ async def test_four_word_register_precision_is_lost_with_float_params(hass, mock hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF], expected="163971058432973792", ) @@ -311,6 +313,7 @@ async def test_two_word_input_register(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0x89ABCDEF), ) @@ -330,6 +333,7 @@ async def test_two_word_holding_register(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0x89ABCDEF), ) @@ -349,6 +353,7 @@ async def test_float_data_type(hass, mock_hub): hass, mock_hub, register_config, + SENSOR_DOMAIN, register_words=[16286, 1617], expected="1.23457", )