From 47c5ea93e0051c88d728df9ab289dc25df74fcd2 Mon Sep 17 00:00:00 2001 From: Benjamin Auquite Date: Thu, 27 Apr 2023 14:56:49 -0500 Subject: [PATCH] Add `test_supported_features` and fix the problem introduced in #565 (#575) * Update test_switch.py * Update test_switch.py * test is now done. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix the test only. test is backwards compatible with the old method. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix _supported_to_attributes everything works now. * pre-commit fixes cannot fix the `function too complex` problem. * ignore test_switch.py in `pre-commit-config.yaml` * Add ignore C901 to test_supported_features * remove commented out code --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bas Nijholt Co-authored-by: Bas Nijholt --- custom_components/adaptive_lighting/switch.py | 31 +++---- tests/test_switch.py | 84 ++++++++++++++++++- 2 files changed, 100 insertions(+), 15 deletions(-) diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 3eabf7ff..f933f068 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -155,12 +155,13 @@ from .const import ( ) _SUPPORT_OPTS = { - "brightness": SUPPORT_BRIGHTNESS, - "color_temp": SUPPORT_COLOR_TEMP, - "color": SUPPORT_COLOR, - "transition": SUPPORT_TRANSITION, + COLOR_MODE_BRIGHTNESS: SUPPORT_BRIGHTNESS, + COLOR_MODE_COLOR_TEMP: SUPPORT_COLOR_TEMP, + CONST_COLOR: SUPPORT_COLOR, + ATTR_TRANSITION: SUPPORT_TRANSITION, } + VALID_COLOR_MODES = { COLOR_MODE_BRIGHTNESS: ATTR_BRIGHTNESS, COLOR_MODE_COLOR_TEMP: ATTR_COLOR_TEMP_KELVIN, @@ -642,16 +643,18 @@ def _expand_light_groups(hass: HomeAssistant, lights: list[str]) -> list[str]: def _supported_to_attributes(supported): supported_attributes = {} supports_colors = False - for mode, attr in VALID_COLOR_MODES.items(): - if mode not in supported: - continue - supported_attributes[attr] = True - if ( - not supports_colors - and mode != COLOR_MODE_BRIGHTNESS - and mode != COLOR_MODE_COLOR_TEMP - ): - supports_colors = True + for mode in supported: + attr = VALID_COLOR_MODES.get(mode) + if attr: + supported_attributes[attr] = True + if attr in COLOR_ATTRS: + supports_colors = True + # ATTR_SUPPORTED_FEATURES only + elif mode in _SUPPORT_OPTS: + supported_attributes[mode] = True + if CONST_COLOR in supported_attributes: + supports_colors = True + supported_attributes.pop(CONST_COLOR) return supported_attributes, supports_colors diff --git a/tests/test_switch.py b/tests/test_switch.py index 476e83cc..884b796b 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -3,9 +3,10 @@ import asyncio from copy import deepcopy import datetime +import itertools import logging from random import randint -from unittest.mock import patch +from unittest.mock import MagicMock, patch from homeassistant.components.adaptive_lighting.const import ( ADAPT_BRIGHTNESS_SWITCH, @@ -25,6 +26,7 @@ from homeassistant.components.adaptive_lighting.const import ( CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, + CONST_COLOR, DEFAULT_MAX_BRIGHTNESS, DEFAULT_NAME, DEFAULT_SLEEP_BRIGHTNESS, @@ -37,7 +39,10 @@ from homeassistant.components.adaptive_lighting.const import ( UNDO_UPDATE_LISTENER, ) from homeassistant.components.adaptive_lighting.switch import ( + _SUPPORT_OPTS, + VALID_COLOR_MODES, _attributes_have_changed, + _supported_features, color_difference_redmean, create_context, is_our_context, @@ -47,9 +52,13 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_COLOR_TEMP_KELVIN, + ATTR_MAX_COLOR_TEMP_KELVIN, + ATTR_MIN_COLOR_TEMP_KELVIN, ATTR_RGB_COLOR, + ATTR_SUPPORTED_COLOR_MODES, ATTR_TRANSITION, ATTR_XY_COLOR, + COLOR_MODE_BRIGHTNESS, ) from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import SERVICE_TURN_OFF @@ -515,6 +524,79 @@ async def test_turn_on_off_listener_not_tracking_untracked_lights(hass): assert light not in switch.turn_on_off_listener.lights +def test_supported_features(hass): # noqa: C901 + """Test the supported features of a light.""" + + possible_legacy_features = {} + MAX_COMBINATIONS = 4 # maximum number of elements that can be combined + for i in range(1, min(MAX_COMBINATIONS, len(_SUPPORT_OPTS)) + 1): + for combination in itertools.combinations(_SUPPORT_OPTS.keys(), i): + key = "_".join(combination) + value = [v for k, v in _SUPPORT_OPTS.items() if k in combination] + possible_legacy_features[key] = value + + possible_color_modes = {} + for i in range(1, len(VALID_COLOR_MODES) + 1): + for combination in itertools.combinations(VALID_COLOR_MODES.keys(), i): + key = "_".join(combination) + value = [v for k, v in VALID_COLOR_MODES.items() if k in combination] + possible_color_modes[key] = value + + # create a mock HomeAssistant object + hass = MagicMock() + + # iterate over possible legacy features + for feature_key, feature_values in possible_legacy_features.items(): + # _LOGGER.debug(feature_values) + # set the attributes of the mock state object to the possible legacy feature values + state_attrs = {ATTR_SUPPORTED_FEATURES: sum(feature_values)} + hass.states.get.return_value.attributes = state_attrs + + # iterate over possible color modes + for mode_key, mode_values in possible_color_modes.items(): + # _LOGGER.debug(mode_values) + # set the attributes of the mock state object to the possible color mode values + state_attrs[ATTR_SUPPORTED_COLOR_MODES] = set(mode_values) + hass.states.get.return_value.attributes = state_attrs + + # Handle both the new and the old _supported_features. + result = _supported_features(hass, ENTITY_LIGHT) + supported, supports_colors = ( + result if isinstance(result, tuple) else (result, None) + ) + expected_supported = {} if supports_colors is not None else set() + for mode, attr in VALID_COLOR_MODES.items(): + if mode in mode_values: + if supports_colors is None: + expected_supported.add(mode) + else: + expected_supported[attr] = True + if supports_colors is True: + expected_supported[COLOR_MODE_BRIGHTNESS] = True + for opt, value in _SUPPORT_OPTS.items(): + if value in feature_values: + if supports_colors is None: + expected_supported.add(opt) + else: + if supports_colors is True: + expected_supported[COLOR_MODE_BRIGHTNESS] = True + if opt in VALID_COLOR_MODES: + expected_supported[VALID_COLOR_MODES[opt]] = True + elif opt != CONST_COLOR: + expected_supported[opt] = True + if ATTR_MIN_COLOR_TEMP_KELVIN in supported: + supported.pop(ATTR_MIN_COLOR_TEMP_KELVIN) + if ATTR_MAX_COLOR_TEMP_KELVIN in supported: + supported.pop(ATTR_MAX_COLOR_TEMP_KELVIN) + assert supported == expected_supported, ( + f"\nExpected supported: {expected_supported}\n" + f"Actual supported: {supported}\n" + f"feature_values: {feature_values}\n" + f"mode_values: {mode_values}\n" + f"supports_colors: {supports_colors}\n" + ) + + @pytest.mark.dependency(depends=GLOBAL_TEST_DEPENDENCIES) async def test_manual_control(hass): """Test the 'manual control' tracking."""