480 lines
19 KiB
Python
480 lines
19 KiB
Python
"""Philips Hue lights platform tests."""
|
|
|
|
import logging
|
|
import unittest
|
|
import unittest.mock as mock
|
|
from unittest.mock import call, MagicMock, patch
|
|
|
|
from homeassistant.components import hue
|
|
import homeassistant.components.light.hue as hue_light
|
|
|
|
from tests.common import get_test_home_assistant, MockDependency
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class TestSetup(unittest.TestCase):
|
|
"""Test the Hue light platform."""
|
|
|
|
def setUp(self): # pylint: disable=invalid-name
|
|
"""Setup things to be run when tests are started."""
|
|
self.hass = get_test_home_assistant()
|
|
self.skip_teardown_stop = False
|
|
|
|
def tearDown(self):
|
|
"""Stop everything that was started."""
|
|
if not self.skip_teardown_stop:
|
|
self.hass.stop()
|
|
|
|
def setup_mocks_for_update_lights(self):
|
|
"""Set up all mocks for update_lights tests."""
|
|
self.mock_bridge = MagicMock()
|
|
self.mock_bridge.allow_hue_groups = False
|
|
self.mock_api = MagicMock()
|
|
self.mock_bridge.get_api.return_value = self.mock_api
|
|
self.mock_bridge_type = MagicMock()
|
|
self.mock_lights = []
|
|
self.mock_groups = []
|
|
self.mock_add_devices = MagicMock()
|
|
hue_light.setup_data(self.hass)
|
|
|
|
def setup_mocks_for_process_lights(self):
|
|
"""Set up all mocks for process_lights tests."""
|
|
self.mock_bridge = MagicMock()
|
|
self.mock_api = MagicMock()
|
|
self.mock_api.get.return_value = {}
|
|
self.mock_bridge.get_api.return_value = self.mock_api
|
|
self.mock_bridge_type = MagicMock()
|
|
hue_light.setup_data(self.hass)
|
|
|
|
def setup_mocks_for_process_groups(self):
|
|
"""Set up all mocks for process_groups tests."""
|
|
self.mock_bridge = MagicMock()
|
|
self.mock_bridge.get_group.return_value = {
|
|
'name': 'Group 0', 'state': {'any_on': True}}
|
|
self.mock_api = MagicMock()
|
|
self.mock_api.get.return_value = {}
|
|
self.mock_bridge.get_api.return_value = self.mock_api
|
|
self.mock_bridge_type = MagicMock()
|
|
hue_light.setup_data(self.hass)
|
|
|
|
def test_setup_platform_no_discovery_info(self):
|
|
"""Test setup_platform without discovery info."""
|
|
self.hass.data[hue.DOMAIN] = {}
|
|
mock_add_devices = MagicMock()
|
|
|
|
hue_light.setup_platform(self.hass, {}, mock_add_devices)
|
|
|
|
mock_add_devices.assert_not_called()
|
|
|
|
def test_setup_platform_no_bridge_id(self):
|
|
"""Test setup_platform without a bridge."""
|
|
self.hass.data[hue.DOMAIN] = {}
|
|
mock_add_devices = MagicMock()
|
|
|
|
hue_light.setup_platform(self.hass, {}, mock_add_devices, {})
|
|
|
|
mock_add_devices.assert_not_called()
|
|
|
|
def test_setup_platform_one_bridge(self):
|
|
"""Test setup_platform with one bridge."""
|
|
mock_bridge = MagicMock()
|
|
self.hass.data[hue.DOMAIN] = {'10.0.0.1': mock_bridge}
|
|
mock_add_devices = MagicMock()
|
|
|
|
with patch('homeassistant.components.light.hue.' +
|
|
'unthrottled_update_lights') as mock_update_lights:
|
|
hue_light.setup_platform(
|
|
self.hass, {}, mock_add_devices,
|
|
{'bridge_id': '10.0.0.1'})
|
|
mock_update_lights.assert_called_once_with(
|
|
self.hass, mock_bridge, mock_add_devices)
|
|
|
|
def test_setup_platform_multiple_bridges(self):
|
|
"""Test setup_platform wuth multiple bridges."""
|
|
mock_bridge = MagicMock()
|
|
mock_bridge2 = MagicMock()
|
|
self.hass.data[hue.DOMAIN] = {
|
|
'10.0.0.1': mock_bridge,
|
|
'192.168.0.10': mock_bridge2,
|
|
}
|
|
mock_add_devices = MagicMock()
|
|
|
|
with patch('homeassistant.components.light.hue.' +
|
|
'unthrottled_update_lights') as mock_update_lights:
|
|
hue_light.setup_platform(
|
|
self.hass, {}, mock_add_devices,
|
|
{'bridge_id': '10.0.0.1'})
|
|
hue_light.setup_platform(
|
|
self.hass, {}, mock_add_devices,
|
|
{'bridge_id': '192.168.0.10'})
|
|
|
|
mock_update_lights.assert_has_calls([
|
|
call(self.hass, mock_bridge, mock_add_devices),
|
|
call(self.hass, mock_bridge2, mock_add_devices),
|
|
])
|
|
|
|
@MockDependency('phue')
|
|
def test_update_lights_with_no_lights(self, mock_phue):
|
|
"""Test the update_lights function when no lights are found."""
|
|
self.setup_mocks_for_update_lights()
|
|
|
|
with patch('homeassistant.components.light.hue.get_bridge_type',
|
|
return_value=self.mock_bridge_type):
|
|
with patch('homeassistant.components.light.hue.process_lights',
|
|
return_value=[]) as mock_process_lights:
|
|
with patch('homeassistant.components.light.hue.process_groups',
|
|
return_value=self.mock_groups) \
|
|
as mock_process_groups:
|
|
hue_light.unthrottled_update_lights(
|
|
self.hass, self.mock_bridge, self.mock_add_devices)
|
|
|
|
mock_process_lights.assert_called_once_with(
|
|
self.hass, self.mock_api, self.mock_bridge,
|
|
self.mock_bridge_type, mock.ANY)
|
|
mock_process_groups.assert_not_called()
|
|
self.mock_add_devices.assert_not_called()
|
|
|
|
@MockDependency('phue')
|
|
def test_update_lights_with_some_lights(self, mock_phue):
|
|
"""Test the update_lights function with some lights."""
|
|
self.setup_mocks_for_update_lights()
|
|
self.mock_lights = ['some', 'light']
|
|
|
|
with patch('homeassistant.components.light.hue.get_bridge_type',
|
|
return_value=self.mock_bridge_type):
|
|
with patch('homeassistant.components.light.hue.process_lights',
|
|
return_value=self.mock_lights) as mock_process_lights:
|
|
with patch('homeassistant.components.light.hue.process_groups',
|
|
return_value=self.mock_groups) \
|
|
as mock_process_groups:
|
|
hue_light.unthrottled_update_lights(
|
|
self.hass, self.mock_bridge, self.mock_add_devices)
|
|
|
|
mock_process_lights.assert_called_once_with(
|
|
self.hass, self.mock_api, self.mock_bridge,
|
|
self.mock_bridge_type, mock.ANY)
|
|
mock_process_groups.assert_not_called()
|
|
self.mock_add_devices.assert_called_once_with(
|
|
self.mock_lights)
|
|
|
|
@MockDependency('phue')
|
|
def test_update_lights_no_groups(self, mock_phue):
|
|
"""Test the update_lights function when no groups are found."""
|
|
self.setup_mocks_for_update_lights()
|
|
self.mock_bridge.allow_hue_groups = True
|
|
self.mock_lights = ['some', 'light']
|
|
|
|
with patch('homeassistant.components.light.hue.get_bridge_type',
|
|
return_value=self.mock_bridge_type):
|
|
with patch('homeassistant.components.light.hue.process_lights',
|
|
return_value=self.mock_lights) as mock_process_lights:
|
|
with patch('homeassistant.components.light.hue.process_groups',
|
|
return_value=self.mock_groups) \
|
|
as mock_process_groups:
|
|
hue_light.unthrottled_update_lights(
|
|
self.hass, self.mock_bridge, self.mock_add_devices)
|
|
|
|
mock_process_lights.assert_called_once_with(
|
|
self.hass, self.mock_api, self.mock_bridge,
|
|
self.mock_bridge_type, mock.ANY)
|
|
mock_process_groups.assert_called_once_with(
|
|
self.hass, self.mock_api, self.mock_bridge,
|
|
self.mock_bridge_type, mock.ANY)
|
|
self.mock_add_devices.assert_called_once_with(
|
|
self.mock_lights)
|
|
|
|
@MockDependency('phue')
|
|
def test_update_lights_with_lights_and_groups(self, mock_phue):
|
|
"""Test the update_lights function with both lights and groups."""
|
|
self.setup_mocks_for_update_lights()
|
|
self.mock_bridge.allow_hue_groups = True
|
|
self.mock_lights = ['some', 'light']
|
|
self.mock_groups = ['and', 'groups']
|
|
|
|
with patch('homeassistant.components.light.hue.get_bridge_type',
|
|
return_value=self.mock_bridge_type):
|
|
with patch('homeassistant.components.light.hue.process_lights',
|
|
return_value=self.mock_lights) as mock_process_lights:
|
|
with patch('homeassistant.components.light.hue.process_groups',
|
|
return_value=self.mock_groups) \
|
|
as mock_process_groups:
|
|
hue_light.unthrottled_update_lights(
|
|
self.hass, self.mock_bridge, self.mock_add_devices)
|
|
|
|
mock_process_lights.assert_called_once_with(
|
|
self.hass, self.mock_api, self.mock_bridge,
|
|
self.mock_bridge_type, mock.ANY)
|
|
mock_process_groups.assert_called_once_with(
|
|
self.hass, self.mock_api, self.mock_bridge,
|
|
self.mock_bridge_type, mock.ANY)
|
|
self.mock_add_devices.assert_called_once_with(
|
|
self.mock_lights)
|
|
|
|
def test_process_lights_api_error(self):
|
|
"""Test the process_lights function when the bridge errors out."""
|
|
self.setup_mocks_for_process_lights()
|
|
self.mock_api.get.return_value = None
|
|
|
|
ret = hue_light.process_lights(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals([], ret)
|
|
self.assertEquals(
|
|
{},
|
|
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS])
|
|
|
|
def test_process_lights_no_lights(self):
|
|
"""Test the process_lights function when bridge returns no lights."""
|
|
self.setup_mocks_for_process_lights()
|
|
|
|
ret = hue_light.process_lights(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals([], ret)
|
|
self.assertEquals(
|
|
{},
|
|
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS])
|
|
|
|
@patch('homeassistant.components.light.hue.HueLight')
|
|
def test_process_lights_some_lights(self, mock_hue_light):
|
|
"""Test the process_lights function with multiple groups."""
|
|
self.setup_mocks_for_process_lights()
|
|
self.mock_api.get.return_value = {
|
|
1: {'state': 'on'}, 2: {'state': 'off'}}
|
|
|
|
ret = hue_light.process_lights(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals(len(ret), 2)
|
|
mock_hue_light.assert_has_calls([
|
|
call(
|
|
1, {'state': 'on'}, self.mock_bridge, mock.ANY,
|
|
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
|
|
self.mock_bridge.allow_in_emulated_hue),
|
|
call(
|
|
2, {'state': 'off'}, self.mock_bridge, mock.ANY,
|
|
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
|
|
self.mock_bridge.allow_in_emulated_hue),
|
|
])
|
|
self.assertEquals(
|
|
len(self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS]),
|
|
2)
|
|
|
|
@patch('homeassistant.components.light.hue.HueLight')
|
|
def test_process_lights_new_light(self, mock_hue_light):
|
|
"""
|
|
Test the process_lights function with new groups.
|
|
|
|
Test what happens when we already have a light and a new one shows up.
|
|
"""
|
|
self.setup_mocks_for_process_lights()
|
|
self.mock_api.get.return_value = {
|
|
1: {'state': 'on'}, 2: {'state': 'off'}}
|
|
self.hass.data[
|
|
hue_light.DATA_KEY][hue_light.DATA_LIGHTS][1] = MagicMock()
|
|
|
|
ret = hue_light.process_lights(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals(len(ret), 1)
|
|
mock_hue_light.assert_has_calls([
|
|
call(
|
|
2, {'state': 'off'}, self.mock_bridge, mock.ANY,
|
|
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
|
|
self.mock_bridge.allow_in_emulated_hue),
|
|
])
|
|
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS][
|
|
1].schedule_update_ha_state.assert_called_once_with()
|
|
self.assertEquals(
|
|
len(self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTS]),
|
|
2)
|
|
|
|
def test_process_groups_api_error(self):
|
|
"""Test the process_groups function when the bridge errors out."""
|
|
self.setup_mocks_for_process_groups()
|
|
self.mock_api.get.return_value = None
|
|
|
|
ret = hue_light.process_groups(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals([], ret)
|
|
self.assertEquals(
|
|
{},
|
|
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS])
|
|
|
|
def test_process_groups_no_state(self):
|
|
"""Test the process_groups function when bridge returns no status."""
|
|
self.setup_mocks_for_process_groups()
|
|
self.mock_bridge.get_group.return_value = {'name': 'Group 0'}
|
|
|
|
ret = hue_light.process_groups(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals([], ret)
|
|
self.assertEquals(
|
|
{},
|
|
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS])
|
|
|
|
@patch('homeassistant.components.light.hue.HueLight')
|
|
def test_process_groups_some_groups(self, mock_hue_light):
|
|
"""Test the process_groups function with multiple groups."""
|
|
self.setup_mocks_for_process_groups()
|
|
self.mock_api.get.return_value = {
|
|
1: {'state': 'on'}, 2: {'state': 'off'}}
|
|
|
|
ret = hue_light.process_groups(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals(len(ret), 2)
|
|
mock_hue_light.assert_has_calls([
|
|
call(
|
|
1, {'state': 'on'}, self.mock_bridge, mock.ANY,
|
|
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
|
|
self.mock_bridge.allow_in_emulated_hue, True),
|
|
call(
|
|
2, {'state': 'off'}, self.mock_bridge, mock.ANY,
|
|
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
|
|
self.mock_bridge.allow_in_emulated_hue, True),
|
|
])
|
|
self.assertEquals(
|
|
len(self.hass.data[
|
|
hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS]),
|
|
2)
|
|
|
|
@patch('homeassistant.components.light.hue.HueLight')
|
|
def test_process_groups_new_group(self, mock_hue_light):
|
|
"""
|
|
Test the process_groups function with new groups.
|
|
|
|
Test what happens when we already have a light and a new one shows up.
|
|
"""
|
|
self.setup_mocks_for_process_groups()
|
|
self.mock_api.get.return_value = {
|
|
1: {'state': 'on'}, 2: {'state': 'off'}}
|
|
self.hass.data[
|
|
hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS][1] = MagicMock()
|
|
|
|
ret = hue_light.process_groups(
|
|
self.hass, self.mock_api, self.mock_bridge, self.mock_bridge_type,
|
|
None)
|
|
|
|
self.assertEquals(len(ret), 1)
|
|
mock_hue_light.assert_has_calls([
|
|
call(
|
|
2, {'state': 'off'}, self.mock_bridge, mock.ANY,
|
|
self.mock_bridge_type, self.mock_bridge.allow_unreachable,
|
|
self.mock_bridge.allow_in_emulated_hue, True),
|
|
])
|
|
self.hass.data[hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS][
|
|
1].schedule_update_ha_state.assert_called_once_with()
|
|
self.assertEquals(
|
|
len(self.hass.data[
|
|
hue_light.DATA_KEY][hue_light.DATA_LIGHTGROUPS]),
|
|
2)
|
|
|
|
|
|
class TestHueLight(unittest.TestCase):
|
|
"""Test the HueLight class."""
|
|
|
|
def setUp(self): # pylint: disable=invalid-name
|
|
"""Setup things to be run when tests are started."""
|
|
self.hass = get_test_home_assistant()
|
|
self.skip_teardown_stop = False
|
|
|
|
self.light_id = 42
|
|
self.mock_info = MagicMock()
|
|
self.mock_bridge = MagicMock()
|
|
self.mock_update_lights = MagicMock()
|
|
self.mock_bridge_type = MagicMock()
|
|
self.mock_allow_unreachable = MagicMock()
|
|
self.mock_is_group = MagicMock()
|
|
self.mock_allow_in_emulated_hue = MagicMock()
|
|
self.mock_is_group = False
|
|
|
|
def tearDown(self):
|
|
"""Stop everything that was started."""
|
|
if not self.skip_teardown_stop:
|
|
self.hass.stop()
|
|
|
|
def buildLight(
|
|
self, light_id=None, info=None, update_lights=None, is_group=None):
|
|
"""Helper to build a HueLight object with minimal fuss."""
|
|
return hue_light.HueLight(
|
|
light_id if light_id is not None else self.light_id,
|
|
info if info is not None else self.mock_info,
|
|
self.mock_bridge,
|
|
(update_lights
|
|
if update_lights is not None
|
|
else self.mock_update_lights),
|
|
self.mock_bridge_type,
|
|
self.mock_allow_unreachable, self.mock_allow_in_emulated_hue,
|
|
is_group if is_group is not None else self.mock_is_group)
|
|
|
|
def test_unique_id_for_light(self):
|
|
"""Test the unique_id method with lights."""
|
|
class_name = "<class 'homeassistant.components.light.hue.HueLight'>"
|
|
|
|
light = self.buildLight(info={'uniqueid': 'foobar'})
|
|
self.assertEquals(
|
|
class_name+'.foobar',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={})
|
|
self.assertEquals(
|
|
class_name+'.Unnamed Device.Light.42',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={'name': 'my-name'})
|
|
self.assertEquals(
|
|
class_name+'.my-name.Light.42',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={'type': 'my-type'})
|
|
self.assertEquals(
|
|
class_name+'.Unnamed Device.my-type.42',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={'name': 'a name', 'type': 'my-type'})
|
|
self.assertEquals(
|
|
class_name+'.a name.my-type.42',
|
|
light.unique_id)
|
|
|
|
def test_unique_id_for_group(self):
|
|
"""Test the unique_id method with groups."""
|
|
class_name = "<class 'homeassistant.components.light.hue.HueLight'>"
|
|
|
|
light = self.buildLight(info={'uniqueid': 'foobar'}, is_group=True)
|
|
self.assertEquals(
|
|
class_name+'.foobar',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={}, is_group=True)
|
|
self.assertEquals(
|
|
class_name+'.Unnamed Device.Group.42',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={'name': 'my-name'}, is_group=True)
|
|
self.assertEquals(
|
|
class_name+'.my-name.Group.42',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(info={'type': 'my-type'}, is_group=True)
|
|
self.assertEquals(
|
|
class_name+'.Unnamed Device.my-type.42',
|
|
light.unique_id)
|
|
|
|
light = self.buildLight(
|
|
info={'name': 'a name', 'type': 'my-type'},
|
|
is_group=True)
|
|
self.assertEquals(
|
|
class_name+'.a name.my-type.42',
|
|
light.unique_id)
|