From dce3bde0ab878439b4430d9d29e5dacc48c3fa70 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Tue, 26 Mar 2024 20:20:08 +0100 Subject: [PATCH] Use `setup_test_component_platform` helper for update entity component tests instead of `hass.components` (#114203) * Use `setup_test_component_platform` helper for update entity component tests instead of `hass.components` * Run ruff format --- tests/components/update/common.py | 65 +++++++ tests/components/update/conftest.py | 82 +++++++++ .../components/update/test_device_trigger.py | 20 +-- tests/components/update/test_init.py | 71 ++++---- tests/components/update/test_recorder.py | 10 +- .../custom_components/test/update.py | 159 ------------------ 6 files changed, 190 insertions(+), 217 deletions(-) create mode 100644 tests/components/update/common.py create mode 100644 tests/components/update/conftest.py delete mode 100644 tests/testing_config/custom_components/test/update.py diff --git a/tests/components/update/common.py b/tests/components/update/common.py new file mode 100644 index 00000000000..70b69498f66 --- /dev/null +++ b/tests/components/update/common.py @@ -0,0 +1,65 @@ +"""Common test fixtures for the update component test.""" + +import logging +from typing import Any + +from homeassistant.components.update import UpdateEntity + +from tests.common import MockEntity + +_LOGGER = logging.getLogger(__name__) + + +class MockUpdateEntity(MockEntity, UpdateEntity): + """Mock UpdateEntity class.""" + + @property + def auto_update(self) -> bool: + """Indicate if the device or service has auto update enabled.""" + return self._handle("auto_update") + + @property + def installed_version(self) -> str | None: + """Version currently installed and in use.""" + return self._handle("installed_version") + + @property + def in_progress(self) -> bool | int | None: + """Update installation progress.""" + return self._handle("in_progress") + + @property + def latest_version(self) -> str | None: + """Latest version available for install.""" + return self._handle("latest_version") + + @property + def release_summary(self) -> str | None: + """Summary of the release notes or changelog.""" + return self._handle("release_summary") + + @property + def release_url(self) -> str | None: + """URL to the full release notes of the latest version available.""" + return self._handle("release_url") + + @property + def title(self) -> str | None: + """Title of the software.""" + return self._handle("title") + + def install(self, version: str | None, backup: bool, **kwargs: Any) -> None: + """Install an update.""" + if backup: + _LOGGER.info("Creating backup before installing update") + + if version is not None: + self._values["installed_version"] = version + _LOGGER.info("Installed update with version: %s", version) + else: + self._values["installed_version"] = self.latest_version + _LOGGER.info("Installed latest update") + + def release_notes(self) -> str | None: + """Return the release notes of the latest version.""" + return "Release notes" diff --git a/tests/components/update/conftest.py b/tests/components/update/conftest.py new file mode 100644 index 00000000000..759f243e8db --- /dev/null +++ b/tests/components/update/conftest.py @@ -0,0 +1,82 @@ +"""Fixtures for update component testing.""" + +import pytest + +from homeassistant.components.update import UpdateEntityFeature + +from .common import MockUpdateEntity + + +@pytest.fixture +def mock_update_entities() -> list[MockUpdateEntity]: + """Return a list of mock update entities.""" + return [ + MockUpdateEntity( + name="No Update", + unique_id="no_update", + installed_version="1.0.0", + latest_version="1.0.0", + supported_features=UpdateEntityFeature.INSTALL, + ), + MockUpdateEntity( + name="Update Available", + unique_id="update_available", + installed_version="1.0.0", + latest_version="1.0.1", + supported_features=UpdateEntityFeature.INSTALL, + ), + MockUpdateEntity( + name="Update Unknown", + unique_id="update_unknown", + installed_version="1.0.0", + latest_version=None, + supported_features=UpdateEntityFeature.INSTALL, + ), + MockUpdateEntity( + name="Update Specific Version", + unique_id="update_specific_version", + installed_version="1.0.0", + latest_version="1.0.0", + supported_features=UpdateEntityFeature.INSTALL + | UpdateEntityFeature.SPECIFIC_VERSION, + ), + MockUpdateEntity( + name="Update Backup", + unique_id="update_backup", + installed_version="1.0.0", + latest_version="1.0.1", + supported_features=UpdateEntityFeature.INSTALL + | UpdateEntityFeature.SPECIFIC_VERSION + | UpdateEntityFeature.BACKUP, + ), + MockUpdateEntity( + name="Update Already in Progress", + unique_id="update_already_in_progres", + installed_version="1.0.0", + latest_version="1.0.1", + in_progress=50, + supported_features=UpdateEntityFeature.INSTALL + | UpdateEntityFeature.PROGRESS, + ), + MockUpdateEntity( + name="Update No Install", + unique_id="no_install", + installed_version="1.0.0", + latest_version="1.0.1", + ), + MockUpdateEntity( + name="Update with release notes", + unique_id="with_release_notes", + installed_version="1.0.0", + latest_version="1.0.1", + supported_features=UpdateEntityFeature.RELEASE_NOTES, + ), + MockUpdateEntity( + name="Update with auto update", + unique_id="with_auto_update", + installed_version="1.0.0", + latest_version="1.0.1", + auto_update=True, + supported_features=UpdateEntityFeature.INSTALL, + ), + ] diff --git a/tests/components/update/test_device_trigger.py b/tests/components/update/test_device_trigger.py index 5a22bcec912..31a9ee7b36e 100644 --- a/tests/components/update/test_device_trigger.py +++ b/tests/components/update/test_device_trigger.py @@ -20,7 +20,9 @@ from tests.common import ( async_get_device_automation_capabilities, async_get_device_automations, async_mock_service, + setup_test_component_platform, ) +from tests.components.update.common import MockUpdateEntity @pytest.fixture(autouse=True, name="stub_blueprint_populate") @@ -180,12 +182,10 @@ async def test_if_fires_on_state_change( device_registry: dr.DeviceRegistry, entity_registry: er.EntityRegistry, calls: list[ServiceCall], - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], ) -> None: """Test for turn_on and turn_off triggers firing.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -282,12 +282,10 @@ async def test_if_fires_on_state_change_legacy( device_registry: dr.DeviceRegistry, entity_registry: er.EntityRegistry, calls: list[ServiceCall], - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], ) -> None: """Test for turn_on and turn_off triggers firing.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -352,12 +350,10 @@ async def test_if_fires_on_state_change_with_for( device_registry: dr.DeviceRegistry, entity_registry: er.EntityRegistry, calls: list[ServiceCall], - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], ) -> None: """Test for triggers firing with delay.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() diff --git a/tests/components/update/test_init.py b/tests/components/update/test_init.py index 252bac578a1..02ca605eed4 100644 --- a/tests/components/update/test_init.py +++ b/tests/components/update/test_init.py @@ -50,6 +50,7 @@ from tests.common import ( mock_integration, mock_platform, mock_restore_cache, + setup_test_component_platform, ) from tests.typing import WebSocketGenerator @@ -166,11 +167,10 @@ async def test_update(hass: HomeAssistant) -> None: async def test_entity_with_no_install( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], ) -> None: """Test entity with no updates.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -232,11 +232,10 @@ async def test_entity_with_no_install( async def test_entity_with_no_updates( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], ) -> None: """Test entity with no updates.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -281,12 +280,11 @@ async def test_entity_with_no_updates( async def test_entity_with_auto_update( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity that has auto update feature.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -333,12 +331,11 @@ async def test_entity_with_auto_update( async def test_entity_with_updates_available( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test basic update entity with updates available.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -387,12 +384,11 @@ async def test_entity_with_updates_available( async def test_entity_with_unknown_version( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity that has an unknown version.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -425,12 +421,11 @@ async def test_entity_with_unknown_version( async def test_entity_with_specific_version( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity that support specific version.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -488,12 +483,11 @@ async def test_entity_with_specific_version( async def test_entity_with_backup_support( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity with backup support.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -548,12 +542,11 @@ async def test_entity_with_backup_support( async def test_entity_already_in_progress( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update install already in progress.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -579,15 +572,14 @@ async def test_entity_already_in_progress( async def test_entity_without_progress_support( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity without progress support. In that case, progress is still handled by Home Assistant. """ - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -618,15 +610,14 @@ async def test_entity_without_progress_support( async def test_entity_without_progress_support_raising( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity without progress support that raises during install. In that case, progress is still handled by Home Assistant. """ - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -665,7 +656,7 @@ async def test_entity_without_progress_support_raising( async def test_restore_state( - hass: HomeAssistant, enable_custom_integrations: None + hass: HomeAssistant, mock_update_entities: list[MockUpdateEntity] ) -> None: """Test we restore skipped version state.""" mock_restore_cache( @@ -681,8 +672,7 @@ async def test_restore_state( ), ) - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -697,12 +687,11 @@ async def test_restore_state( async def test_release_notes( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], hass_ws_client: WebSocketGenerator, ) -> None: """Test getting the release notes over the websocket connection.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -723,12 +712,11 @@ async def test_release_notes( async def test_release_notes_entity_not_found( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], hass_ws_client: WebSocketGenerator, ) -> None: """Test getting the release notes for not found entity.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() @@ -750,12 +738,11 @@ async def test_release_notes_entity_not_found( async def test_release_notes_entity_does_not_support_release_notes( hass: HomeAssistant, - enable_custom_integrations: None, + mock_update_entities: list[MockUpdateEntity], hass_ws_client: WebSocketGenerator, ) -> None: """Test getting the release notes for entity that does not support release notes.""" - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() diff --git a/tests/components/update/test_recorder.py b/tests/components/update/test_recorder.py index d8048316900..da63518009e 100644 --- a/tests/components/update/test_recorder.py +++ b/tests/components/update/test_recorder.py @@ -17,17 +17,19 @@ from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util -from tests.common import async_fire_time_changed +from tests.common import async_fire_time_changed, setup_test_component_platform from tests.components.recorder.common import async_wait_recording_done +from tests.components.update.common import MockUpdateEntity async def test_exclude_attributes( - recorder_mock: Recorder, hass: HomeAssistant, enable_custom_integrations: None + recorder_mock: Recorder, + hass: HomeAssistant, + mock_update_entities: list[MockUpdateEntity], ) -> None: """Test update attributes to be excluded.""" now = dt_util.utcnow() - platform = getattr(hass.components, f"test.{DOMAIN}") - platform.init() + setup_test_component_platform(hass, DOMAIN, mock_update_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() state = hass.states.get("update.update_already_in_progress") diff --git a/tests/testing_config/custom_components/test/update.py b/tests/testing_config/custom_components/test/update.py deleted file mode 100644 index 40b5742b220..00000000000 --- a/tests/testing_config/custom_components/test/update.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Provide a mock update platform. - -Call init before using it in your tests to ensure clean test data. -""" - -from __future__ import annotations - -import logging -from typing import Any - -from homeassistant.components.update import UpdateEntity, UpdateEntityFeature - -from tests.common import MockEntity - -ENTITIES = [] - -_LOGGER = logging.getLogger(__name__) - - -class MockUpdateEntity(MockEntity, UpdateEntity): - """Mock UpdateEntity class.""" - - @property - def auto_update(self) -> bool: - """Indicate if the device or service has auto update enabled.""" - return self._handle("auto_update") - - @property - def installed_version(self) -> str | None: - """Version currently installed and in use.""" - return self._handle("installed_version") - - @property - def in_progress(self) -> bool | int | None: - """Update installation progress.""" - return self._handle("in_progress") - - @property - def latest_version(self) -> str | None: - """Latest version available for install.""" - return self._handle("latest_version") - - @property - def release_summary(self) -> str | None: - """Summary of the release notes or changelog.""" - return self._handle("release_summary") - - @property - def release_url(self) -> str | None: - """URL to the full release notes of the latest version available.""" - return self._handle("release_url") - - @property - def title(self) -> str | None: - """Title of the software.""" - return self._handle("title") - - def install(self, version: str | None, backup: bool, **kwargs: Any) -> None: - """Install an update.""" - if backup: - _LOGGER.info("Creating backup before installing update") - - if version is not None: - self._values["installed_version"] = version - _LOGGER.info("Installed update with version: %s", version) - else: - self._values["installed_version"] = self.latest_version - _LOGGER.info("Installed latest update") - - def release_notes(self) -> str | None: - """Return the release notes of the latest version.""" - return "Release notes" - - -def init(empty=False): - """Initialize the platform with entities.""" - global ENTITIES - - ENTITIES = ( - [] - if empty - else [ - MockUpdateEntity( - name="No Update", - unique_id="no_update", - installed_version="1.0.0", - latest_version="1.0.0", - supported_features=UpdateEntityFeature.INSTALL, - ), - MockUpdateEntity( - name="Update Available", - unique_id="update_available", - installed_version="1.0.0", - latest_version="1.0.1", - supported_features=UpdateEntityFeature.INSTALL, - ), - MockUpdateEntity( - name="Update Unknown", - unique_id="update_unknown", - installed_version="1.0.0", - latest_version=None, - supported_features=UpdateEntityFeature.INSTALL, - ), - MockUpdateEntity( - name="Update Specific Version", - unique_id="update_specific_version", - installed_version="1.0.0", - latest_version="1.0.0", - supported_features=UpdateEntityFeature.INSTALL - | UpdateEntityFeature.SPECIFIC_VERSION, - ), - MockUpdateEntity( - name="Update Backup", - unique_id="update_backup", - installed_version="1.0.0", - latest_version="1.0.1", - supported_features=UpdateEntityFeature.INSTALL - | UpdateEntityFeature.SPECIFIC_VERSION - | UpdateEntityFeature.BACKUP, - ), - MockUpdateEntity( - name="Update Already in Progress", - unique_id="update_already_in_progres", - installed_version="1.0.0", - latest_version="1.0.1", - in_progress=50, - supported_features=UpdateEntityFeature.INSTALL - | UpdateEntityFeature.PROGRESS, - ), - MockUpdateEntity( - name="Update No Install", - unique_id="no_install", - installed_version="1.0.0", - latest_version="1.0.1", - ), - MockUpdateEntity( - name="Update with release notes", - unique_id="with_release_notes", - installed_version="1.0.0", - latest_version="1.0.1", - supported_features=UpdateEntityFeature.RELEASE_NOTES, - ), - MockUpdateEntity( - name="Update with auto update", - unique_id="with_auto_update", - installed_version="1.0.0", - latest_version="1.0.1", - auto_update=True, - supported_features=UpdateEntityFeature.INSTALL, - ), - ] - ) - - -async def async_setup_platform( - hass, config, async_add_entities_callback, discovery_info=None -): - """Return mock entities.""" - async_add_entities_callback(ENTITIES)