From ff6812a7983cdff444a92d3a2fe41e8eccde0699 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Thu, 21 Mar 2024 10:49:32 +0100 Subject: [PATCH] Split light fixture from implementation to only import when fixture is actually used (#113892) * Split light fixture from implementation to only import when fixture is actually used * Non-local import --- tests/components/conftest.py | 27 +++- tests/components/group/test_light.py | 28 ++--- tests/components/light/common.py | 92 ++++++++++++++ .../components/light/test_device_condition.py | 5 +- tests/components/light/test_init.py | 48 ++++---- tests/conftest.py | 4 - tests/fixtures/pytest/__init__.py | 1 - tests/fixtures/pytest/light.py | 115 ------------------ 8 files changed, 160 insertions(+), 160 deletions(-) delete mode 100644 tests/fixtures/pytest/__init__.py delete mode 100644 tests/fixtures/pytest/light.py diff --git a/tests/components/conftest.py b/tests/components/conftest.py index 24fdc7743ce..8b21f8cd1a8 100644 --- a/tests/components/conftest.py +++ b/tests/components/conftest.py @@ -1,11 +1,16 @@ """Fixtures for component testing.""" from collections.abc import Generator -from typing import Any +from typing import TYPE_CHECKING, Any from unittest.mock import MagicMock, patch import pytest +from homeassistant.const import STATE_OFF, STATE_ON + +if TYPE_CHECKING: + from tests.components.light.common import MockLight, SetupLightPlatformCallable + @pytest.fixture(scope="session", autouse=True) def patch_zeroconf_multiple_catcher() -> Generator[None, None, None]: @@ -101,3 +106,23 @@ def prevent_ffmpeg_subprocess() -> Generator[None, None, None]: "homeassistant.components.ffmpeg.FFVersion.get_version", return_value="6.0" ): yield + + +@pytest.fixture +def mock_light_entities() -> list["MockLight"]: + """Return mocked light entities.""" + from tests.components.light.common import MockLight + + return [ + MockLight("Ceiling", STATE_ON), + MockLight("Ceiling", STATE_OFF), + MockLight(None, STATE_OFF), + ] + + +@pytest.fixture +def setup_light_platform() -> "SetupLightPlatformCallable": + """Return a callable to set up the mock light entity component.""" + from tests.components.light.common import setup_light_platform + + return setup_light_platform diff --git a/tests/components/group/test_light.py b/tests/components/group/test_light.py index d48474955a5..dca6002ccb7 100644 --- a/tests/components/group/test_light.py +++ b/tests/components/group/test_light.py @@ -45,7 +45,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component from tests.common import async_capture_events, get_fixture_path -from tests.fixtures.pytest.light import MockLight, SetupLightPlatformCallable +from tests.components.light.common import MockLight, SetupLightPlatformCallable async def test_default_state( @@ -269,7 +269,7 @@ async def test_brightness( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.BRIGHTNESS} @@ -342,7 +342,7 @@ async def test_color_hs( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.HS} @@ -414,7 +414,7 @@ async def test_color_rgb( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.RGB} @@ -488,7 +488,7 @@ async def test_color_rgbw( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.RGBW} @@ -562,7 +562,7 @@ async def test_color_rgbww( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.RGBWW} @@ -636,7 +636,7 @@ async def test_white( MockLight("test1", STATE_ON), MockLight("test2", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.HS, ColorMode.WHITE} @@ -695,7 +695,7 @@ async def test_color_temp( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.COLOR_TEMP} @@ -767,7 +767,7 @@ async def test_emulated_color_temp_group( MockLight("test2", STATE_OFF), MockLight("test3", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.COLOR_TEMP} @@ -835,7 +835,7 @@ async def test_min_max_mireds( MockLight("test1", STATE_ON), MockLight("test2", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.COLOR_TEMP} @@ -1014,7 +1014,7 @@ async def test_supported_color_modes( MockLight("test2", STATE_OFF), MockLight("test3", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.COLOR_TEMP, ColorMode.HS} @@ -1064,7 +1064,7 @@ async def test_color_mode( MockLight("test2", STATE_OFF), MockLight("test3", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {ColorMode.COLOR_TEMP, ColorMode.HS} @@ -1142,7 +1142,7 @@ async def test_color_mode2( MockLight("test5", STATE_ON), MockLight("test6", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity = entities[0] entity.supported_color_modes = {ColorMode.COLOR_TEMP} @@ -1265,7 +1265,7 @@ async def test_service_calls( MockLight("ceiling_lights", STATE_OFF), MockLight("kitchen_lights", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {supported_color_modes} diff --git a/tests/components/light/common.py b/tests/components/light/common.py index f76f1d4146d..519084d9c34 100644 --- a/tests/components/light/common.py +++ b/tests/components/light/common.py @@ -3,6 +3,7 @@ All containing methods are legacy helpers that should not be used by new components. Instead call the service directly. """ +from collections.abc import Callable from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -21,6 +22,8 @@ from homeassistant.components.light import ( ATTR_WHITE, ATTR_XY_COLOR, DOMAIN, + ColorMode, + LightEntity, ) from homeassistant.const import ( ATTR_ENTITY_ID, @@ -29,8 +32,13 @@ from homeassistant.const import ( SERVICE_TURN_OFF, SERVICE_TURN_ON, ) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.loader import bind_hass +from tests.common import MockPlatform, MockToggleEntity, mock_platform + @bind_hass def turn_on( @@ -217,3 +225,87 @@ async def async_toggle( } await hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data, blocking=True) + + +TURN_ON_ARG_TO_COLOR_MODE = { + "hs_color": ColorMode.HS, + "xy_color": ColorMode.XY, + "rgb_color": ColorMode.RGB, + "rgbw_color": ColorMode.RGBW, + "rgbww_color": ColorMode.RGBWW, + "color_temp_kelvin": ColorMode.COLOR_TEMP, +} + + +class MockLight(MockToggleEntity, LightEntity): + """Mock light class.""" + + _attr_max_color_temp_kelvin = 6500 + _attr_min_color_temp_kelvin = 2000 + supported_features = 0 + + brightness = None + color_temp_kelvin = None + hs_color = None + rgb_color = None + rgbw_color = None + rgbww_color = None + xy_color = None + + def __init__( + self, + name, + state, + unique_id=None, + supported_color_modes: set[ColorMode] | None = None, + ): + """Initialize the mock light.""" + super().__init__(name, state, unique_id) + if supported_color_modes is None: + supported_color_modes = {ColorMode.ONOFF} + self._attr_supported_color_modes = supported_color_modes + color_mode = ColorMode.UNKNOWN + if len(supported_color_modes) == 1: + color_mode = next(iter(supported_color_modes)) + self._attr_color_mode = color_mode + + def turn_on(self, **kwargs): + """Turn the entity on.""" + super().turn_on(**kwargs) + for key, value in kwargs.items(): + if key in [ + "brightness", + "hs_color", + "xy_color", + "rgb_color", + "rgbw_color", + "rgbww_color", + "color_temp_kelvin", + ]: + setattr(self, key, value) + if key == "white": + setattr(self, "brightness", value) + if key in TURN_ON_ARG_TO_COLOR_MODE: + self._attr_color_mode = TURN_ON_ARG_TO_COLOR_MODE[key] + + +SetupLightPlatformCallable = Callable[[HomeAssistant, list[MockLight]], None] + + +def setup_light_platform(hass: HomeAssistant, entities: list[MockLight]) -> None: + """Set up the mock light entity platform.""" + + async def async_setup_platform( + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, + ) -> None: + """Set up test light platform.""" + async_add_entities(entities) + + mock_platform( + hass, + f"test.{DOMAIN}", + MockPlatform(async_setup_platform=async_setup_platform), + ) diff --git a/tests/components/light/test_device_condition.py b/tests/components/light/test_device_condition.py index 2dd06589966..fe556fc3207 100644 --- a/tests/components/light/test_device_condition.py +++ b/tests/components/light/test_device_condition.py @@ -22,7 +22,7 @@ from tests.common import ( async_get_device_automations, async_mock_service, ) -from tests.fixtures.pytest.light import SetupLightPlatformCallable +from tests.components.light.common import MockLight, SetupLightPlatformCallable @pytest.fixture(autouse=True, name="stub_blueprint_populate") @@ -325,6 +325,7 @@ async def test_if_fires_on_for_condition( entity_registry: er.EntityRegistry, calls, setup_light_platform: SetupLightPlatformCallable, + mock_light_entities: list[MockLight], ) -> None: """Test for firing if condition is on with delay.""" config_entry = MockConfigEntry(domain="test", data={}) @@ -343,7 +344,7 @@ async def test_if_fires_on_for_condition( point2 = point1 + timedelta(seconds=10) point3 = point2 + timedelta(seconds=10) - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}}) await hass.async_block_till_done() diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 57bd312421c..b66ae5819c5 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -22,7 +22,7 @@ from homeassistant.setup import async_setup_component import homeassistant.util.color as color_util from tests.common import MockEntityPlatform, MockUser, async_mock_service -from tests.fixtures.pytest.light import MockLight, SetupLightPlatformCallable +from tests.components.light.common import MockLight, SetupLightPlatformCallable orig_Profiles = light.Profiles @@ -114,7 +114,7 @@ async def test_services( mock_light_entities: list[MockLight], ) -> None: """Test the provided services.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} @@ -515,7 +515,7 @@ async def test_light_profiles( mock_light_entities: list[MockLight], ) -> None: """Test light profiles.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) profile_mock_data = { "test": (0.4, 0.6, 100, 0), @@ -564,7 +564,7 @@ async def test_default_profiles_group( mock_light_entities: list[MockLight], ) -> None: """Test default turn-on light profile for all lights.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} @@ -790,7 +790,7 @@ async def test_default_profiles_light( mock_light_entities: list[MockLight], ) -> None: """Test default turn-on light profile for a specific light.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} @@ -857,9 +857,10 @@ async def test_light_context( hass: HomeAssistant, hass_admin_user: MockUser, setup_light_platform: SetupLightPlatformCallable, + mock_light_entities: list[MockLight], ) -> None: """Test that light context works.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() @@ -885,9 +886,10 @@ async def test_light_turn_on_auth( hass: HomeAssistant, hass_read_only_user: MockUser, setup_light_platform: SetupLightPlatformCallable, + mock_light_entities: list[MockLight], ) -> None: """Test that light context works.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() @@ -916,7 +918,7 @@ async def test_light_brightness_step( MockLight("Test_1", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_features = light.SUPPORT_BRIGHTNESS @@ -987,7 +989,7 @@ async def test_light_brightness_pct_conversion( mock_light_entities: list[MockLight], ) -> None: """Test that light brightness percent conversion.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) entity = mock_light_entities[0] entity.supported_features = light.SUPPORT_BRIGHTNESS @@ -1183,7 +1185,7 @@ async def test_light_backwards_compatibility_supported_color_modes( entity4.supported_color_modes = None entity4.color_mode = None - setup_light_platform(entities) + setup_light_platform(hass, entities) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() @@ -1272,7 +1274,7 @@ async def test_light_backwards_compatibility_color_mode( entity4.hs_color = (240, 100) entity4.color_temp_kelvin = 10000 - setup_light_platform(entities) + setup_light_platform(hass, entities) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() @@ -1312,7 +1314,7 @@ async def test_light_service_call_rgbw( entity0 = MockLight("Test_rgbw", STATE_ON) entity0.supported_color_modes = {light.ColorMode.RGBW} - setup_light_platform([entity0]) + setup_light_platform(hass, [entity0]) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() @@ -1345,7 +1347,7 @@ async def test_light_state_off( MockLight("Test_ct", STATE_OFF), MockLight("Test_rgbw", STATE_OFF), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {light.ColorMode.ONOFF} @@ -1413,7 +1415,7 @@ async def test_light_state_rgbw( ) -> None: """Test rgbw color conversion in state updates.""" entity0 = MockLight("Test_rgbw", STATE_ON) - setup_light_platform([entity0]) + setup_light_platform(hass, [entity0]) entity0.brightness = 255 entity0.supported_color_modes = {light.ColorMode.RGBW} @@ -1446,7 +1448,7 @@ async def test_light_state_rgbww( ) -> None: """Test rgbww color conversion in state updates.""" entity0 = MockLight("Test_rgbww", STATE_ON) - setup_light_platform([entity0]) + setup_light_platform(hass, [entity0]) entity0.supported_color_modes = {light.ColorMode.RGBWW} entity0.color_mode = light.ColorMode.RGBWW @@ -1488,7 +1490,7 @@ async def test_light_service_call_color_conversion( MockLight("Test_rgbww", STATE_ON), MockLight("Test_temperature", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {light.ColorMode.HS} @@ -1932,7 +1934,7 @@ async def test_light_service_call_color_conversion_named_tuple( MockLight("Test_rgbw", STATE_ON), MockLight("Test_rgbww", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {light.ColorMode.HS} @@ -2008,7 +2010,7 @@ async def test_light_service_call_color_temp_emulation( MockLight("Test_hs", STATE_ON), MockLight("Test_hs_white", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {light.ColorMode.COLOR_TEMP, light.ColorMode.HS} @@ -2067,7 +2069,7 @@ async def test_light_service_call_color_temp_conversion( MockLight("Test_rgbww_ct", STATE_ON), MockLight("Test_rgbww", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = { @@ -2200,7 +2202,7 @@ async def test_light_mired_color_temp_conversion( MockLight("Test_rgbww_ct", STATE_ON), MockLight("Test_rgbww", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = { @@ -2246,7 +2248,7 @@ async def test_light_service_call_white_mode( """Test color_mode white in service calls.""" entity0 = MockLight("Test_white", STATE_ON) entity0.supported_color_modes = {light.ColorMode.HS, light.ColorMode.WHITE} - setup_light_platform([entity0]) + setup_light_platform(hass, [entity0]) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() @@ -2351,7 +2353,7 @@ async def test_light_state_color_conversion( MockLight("Test_xy", STATE_ON), MockLight("Test_legacy", STATE_ON), ] - setup_light_platform(entities) + setup_light_platform(hass, entities) entity0 = entities[0] entity0.supported_color_modes = {light.ColorMode.HS} @@ -2416,7 +2418,7 @@ async def test_services_filter_parameters( mock_light_entities: list[MockLight], ) -> None: """Test turn_on and turn_off filters unsupported parameters.""" - setup_light_platform() + setup_light_platform(hass, mock_light_entities) assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} diff --git a/tests/conftest.py b/tests/conftest.py index e21a5e3d92b..4eacb8991c0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -114,10 +114,6 @@ asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False)) # Disable fixtures overriding our beautiful policy asyncio.set_event_loop_policy = lambda policy: None -pytest_plugins = [ - "tests.fixtures.pytest.light", -] - def pytest_addoption(parser: pytest.Parser) -> None: """Register custom pytest options.""" diff --git a/tests/fixtures/pytest/__init__.py b/tests/fixtures/pytest/__init__.py deleted file mode 100644 index f792ac8b29e..00000000000 --- a/tests/fixtures/pytest/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Fixtures for tests.""" diff --git a/tests/fixtures/pytest/light.py b/tests/fixtures/pytest/light.py deleted file mode 100644 index e81d2d9ba94..00000000000 --- a/tests/fixtures/pytest/light.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Fixtures for the light entity component tests.""" -from collections.abc import Callable - -import pytest - -from homeassistant.components.light import DOMAIN, ColorMode, LightEntity -from homeassistant.const import STATE_OFF, STATE_ON -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from tests.common import MockPlatform, MockToggleEntity, mock_platform - -TURN_ON_ARG_TO_COLOR_MODE = { - "hs_color": ColorMode.HS, - "xy_color": ColorMode.XY, - "rgb_color": ColorMode.RGB, - "rgbw_color": ColorMode.RGBW, - "rgbww_color": ColorMode.RGBWW, - "color_temp_kelvin": ColorMode.COLOR_TEMP, -} - - -class MockLight(MockToggleEntity, LightEntity): - """Mock light class.""" - - _attr_max_color_temp_kelvin = 6500 - _attr_min_color_temp_kelvin = 2000 - supported_features = 0 - - brightness = None - color_temp_kelvin = None - hs_color = None - rgb_color = None - rgbw_color = None - rgbww_color = None - xy_color = None - - def __init__( - self, - name, - state, - unique_id=None, - supported_color_modes: set[ColorMode] | None = None, - ): - """Initialize the mock light.""" - super().__init__(name, state, unique_id) - if supported_color_modes is None: - supported_color_modes = {ColorMode.ONOFF} - self._attr_supported_color_modes = supported_color_modes - color_mode = ColorMode.UNKNOWN - if len(supported_color_modes) == 1: - color_mode = next(iter(supported_color_modes)) - self._attr_color_mode = color_mode - - def turn_on(self, **kwargs): - """Turn the entity on.""" - super().turn_on(**kwargs) - for key, value in kwargs.items(): - if key in [ - "brightness", - "hs_color", - "xy_color", - "rgb_color", - "rgbw_color", - "rgbww_color", - "color_temp_kelvin", - ]: - setattr(self, key, value) - if key == "white": - setattr(self, "brightness", value) - if key in TURN_ON_ARG_TO_COLOR_MODE: - self._attr_color_mode = TURN_ON_ARG_TO_COLOR_MODE[key] - - -SetupLightPlatformCallable = Callable[[list[MockLight] | None], None] - - -@pytest.fixture -async def mock_light_entities() -> list[MockLight]: - """Return mocked light entities.""" - return [ - MockLight("Ceiling", STATE_ON), - MockLight("Ceiling", STATE_OFF), - MockLight(None, STATE_OFF), - ] - - -@pytest.fixture -async def setup_light_platform( - hass: HomeAssistant, mock_light_entities: list[MockLight] -) -> SetupLightPlatformCallable: - """Set up the mock light entity platform.""" - - def _setup(entities: list[MockLight] | None = None) -> None: - """Set up the mock light entity platform.""" - - async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, - ) -> None: - """Set up test light platform.""" - async_add_entities( - entities if entities is not None else mock_light_entities - ) - - mock_platform( - hass, - f"test.{DOMAIN}", - MockPlatform(async_setup_platform=async_setup_platform), - ) - - return _setup