From a77add1b77c7a297d41b6a38aad580b89b634652 Mon Sep 17 00:00:00 2001 From: Luke Lashley Date: Wed, 8 May 2024 18:33:23 -0400 Subject: [PATCH] Add better testing to vacuum platform (#112523) * Add better testing to vacuum platform * remove state strings * some of the MR comments * move MockVacuum * remove manifest extra * fix linting * fix other linting * Fix create entity calls * Format * remove create_entity * change to match notify --------- Co-authored-by: Martin Hjelmare --- tests/components/vacuum/__init__.py | 83 +++++++++++ tests/components/vacuum/conftest.py | 23 +++ tests/components/vacuum/test_init.py | 203 ++++++++++++++++++++++++++- 3 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 tests/components/vacuum/conftest.py diff --git a/tests/components/vacuum/__init__.py b/tests/components/vacuum/__init__.py index b62949e6e8a669..98a02155b651c1 100644 --- a/tests/components/vacuum/__init__.py +++ b/tests/components/vacuum/__init__.py @@ -1 +1,84 @@ """The tests for vacuum platforms.""" + +from typing import Any + +from homeassistant.components.vacuum import ( + DOMAIN, + STATE_CLEANING, + STATE_DOCKED, + STATE_IDLE, + STATE_PAUSED, + STATE_RETURNING, + StateVacuumEntity, + VacuumEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from tests.common import MockEntity + + +class MockVacuum(MockEntity, StateVacuumEntity): + """Mock vacuum class.""" + + _attr_supported_features = ( + VacuumEntityFeature.PAUSE + | VacuumEntityFeature.STOP + | VacuumEntityFeature.RETURN_HOME + | VacuumEntityFeature.FAN_SPEED + | VacuumEntityFeature.BATTERY + | VacuumEntityFeature.CLEAN_SPOT + | VacuumEntityFeature.MAP + | VacuumEntityFeature.STATE + | VacuumEntityFeature.START + ) + _attr_battery_level = 99 + _attr_fan_speed_list = ["slow", "fast"] + + def __init__(self, **values: Any) -> None: + """Initialize a mock vacuum entity.""" + super().__init__(**values) + self._attr_state = STATE_DOCKED + self._attr_fan_speed = "slow" + + def stop(self, **kwargs: Any) -> None: + """Stop cleaning.""" + self._attr_state = STATE_IDLE + + def return_to_base(self, **kwargs: Any) -> None: + """Return to base.""" + self._attr_state = STATE_RETURNING + + def clean_spot(self, **kwargs: Any) -> None: + """Clean a spot.""" + self._attr_state = STATE_CLEANING + + def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: + """Set the fan speed.""" + self._attr_fan_speed = fan_speed + + def start(self) -> None: + """Start cleaning.""" + self._attr_state = STATE_CLEANING + + def pause(self) -> None: + """Pause cleaning.""" + self._attr_state = STATE_PAUSED + + +async def help_async_setup_entry_init( + hass: HomeAssistant, config_entry: ConfigEntry +) -> bool: + """Set up test config entry.""" + await hass.config_entries.async_forward_entry_setup(config_entry, DOMAIN) + return True + + +async def help_async_unload_entry( + hass: HomeAssistant, config_entry: ConfigEntry +) -> bool: + """Unload test config emntry.""" + return await hass.config_entries.async_unload_platforms( + config_entry, [Platform.VACUUM] + ) diff --git a/tests/components/vacuum/conftest.py b/tests/components/vacuum/conftest.py new file mode 100644 index 00000000000000..e99879d2c35bd6 --- /dev/null +++ b/tests/components/vacuum/conftest.py @@ -0,0 +1,23 @@ +"""Fixtures for Vacuum platform tests.""" + +from collections.abc import Generator + +import pytest + +from homeassistant.config_entries import ConfigFlow +from homeassistant.core import HomeAssistant + +from tests.common import mock_config_flow, mock_platform + + +class MockFlow(ConfigFlow): + """Test flow.""" + + +@pytest.fixture +def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]: + """Mock config flow.""" + mock_platform(hass, "test.config_flow") + + with mock_config_flow("test", MockFlow): + yield diff --git a/tests/components/vacuum/test_init.py b/tests/components/vacuum/test_init.py index 7a42913afbfcbb..efd2a63f0f7aa5 100644 --- a/tests/components/vacuum/test_init.py +++ b/tests/components/vacuum/test_init.py @@ -2,9 +2,210 @@ from __future__ import annotations -from homeassistant.components.vacuum import StateVacuumEntity, VacuumEntityFeature +from typing import Any + +import pytest + +from homeassistant.components.vacuum import ( + DOMAIN, + SERVICE_CLEAN_SPOT, + SERVICE_LOCATE, + SERVICE_PAUSE, + SERVICE_RETURN_TO_BASE, + SERVICE_SEND_COMMAND, + SERVICE_SET_FAN_SPEED, + SERVICE_START, + SERVICE_STOP, + STATE_CLEANING, + STATE_IDLE, + STATE_PAUSED, + STATE_RETURNING, + StateVacuumEntity, + VacuumEntityFeature, +) from homeassistant.core import HomeAssistant +from . import MockVacuum, help_async_setup_entry_init, help_async_unload_entry + +from tests.common import ( + MockConfigEntry, + MockModule, + mock_integration, + setup_test_component_platform, +) + + +@pytest.mark.parametrize( + ("service", "expected_state"), + [ + (SERVICE_CLEAN_SPOT, STATE_CLEANING), + (SERVICE_PAUSE, STATE_PAUSED), + (SERVICE_RETURN_TO_BASE, STATE_RETURNING), + (SERVICE_START, STATE_CLEANING), + (SERVICE_STOP, STATE_IDLE), + ], +) +async def test_state_services( + hass: HomeAssistant, config_flow_fixture: None, service: str, expected_state: str +) -> None: + """Test get vacuum service that affect state.""" + mock_vacuum = MockVacuum( + name="Testing", + entity_id="vacuum.testing", + ) + config_entry = MockConfigEntry(domain="test") + config_entry.add_to_hass(hass) + + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=help_async_setup_entry_init, + async_unload_entry=help_async_unload_entry, + ), + ) + setup_test_component_platform(hass, DOMAIN, [mock_vacuum], from_config_entry=True) + assert await hass.config_entries.async_setup(config_entry.entry_id) + + await hass.services.async_call( + DOMAIN, + service, + {"entity_id": mock_vacuum.entity_id}, + blocking=True, + ) + vacuum_state = hass.states.get(mock_vacuum.entity_id) + + assert vacuum_state.state == expected_state + + +async def test_fan_speed(hass: HomeAssistant, config_flow_fixture: None) -> None: + """Test set vacuum fan speed.""" + mock_vacuum = MockVacuum( + name="Testing", + entity_id="vacuum.testing", + ) + config_entry = MockConfigEntry(domain="test") + config_entry.add_to_hass(hass) + + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=help_async_setup_entry_init, + async_unload_entry=help_async_unload_entry, + ), + ) + setup_test_component_platform(hass, DOMAIN, [mock_vacuum], from_config_entry=True) + assert await hass.config_entries.async_setup(config_entry.entry_id) + + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_FAN_SPEED, + {"entity_id": mock_vacuum.entity_id, "fan_speed": "high"}, + blocking=True, + ) + + assert mock_vacuum.fan_speed == "high" + + +async def test_locate(hass: HomeAssistant, config_flow_fixture: None) -> None: + """Test vacuum locate.""" + + calls = [] + + class MockVacuumWithLocation(MockVacuum): + def __init__(self, calls: list[str], **kwargs) -> None: + super().__init__() + self._attr_supported_features = ( + self.supported_features | VacuumEntityFeature.LOCATE + ) + self._calls = calls + + def locate(self, **kwargs: Any) -> None: + self._calls.append("locate") + + mock_vacuum = MockVacuumWithLocation( + name="Testing", entity_id="vacuum.testing", calls=calls + ) + config_entry = MockConfigEntry(domain="test") + config_entry.add_to_hass(hass) + + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=help_async_setup_entry_init, + async_unload_entry=help_async_unload_entry, + ), + ) + setup_test_component_platform(hass, DOMAIN, [mock_vacuum], from_config_entry=True) + assert await hass.config_entries.async_setup(config_entry.entry_id) + + await hass.services.async_call( + DOMAIN, + SERVICE_LOCATE, + {"entity_id": mock_vacuum.entity_id}, + blocking=True, + ) + + assert "locate" in calls + + +async def test_send_command(hass: HomeAssistant, config_flow_fixture: None) -> None: + """Test Vacuum send command.""" + + strings = [] + + class MockVacuumWithSendCommand(MockVacuum): + def __init__(self, strings: list[str], **kwargs) -> None: + super().__init__() + self._attr_supported_features = ( + self.supported_features | VacuumEntityFeature.SEND_COMMAND + ) + self._strings = strings + + def send_command( + self, + command: str, + params: dict[str, Any] | list[Any] | None = None, + **kwargs: Any, + ) -> None: + if command == "add_str": + self._strings.append(params["str"]) + + mock_vacuum = MockVacuumWithSendCommand( + name="Testing", entity_id="vacuum.testing", strings=strings + ) + config_entry = MockConfigEntry(domain="test") + config_entry.add_to_hass(hass) + + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=help_async_setup_entry_init, + async_unload_entry=help_async_unload_entry, + ), + ) + setup_test_component_platform(hass, DOMAIN, [mock_vacuum], from_config_entry=True) + assert await hass.config_entries.async_setup(config_entry.entry_id) + + await hass.services.async_call( + DOMAIN, + SERVICE_SEND_COMMAND, + { + "entity_id": mock_vacuum.entity_id, + "command": "add_str", + "params": {"str": "test"}, + }, + blocking=True, + ) + + assert "test" in strings + async def test_supported_features_compat(hass: HomeAssistant) -> None: """Test StateVacuumEntity using deprecated feature constants features."""