Migrate tests to pytest (#23544)

* Migrate tests to pytest

* Fixup

* Use loop fixture in test_check_config

* Lint
This commit is contained in:
Erik Montnemery 2019-04-30 18:20:38 +02:00 committed by Paulus Schoutsen
parent d71424f285
commit 407e0c58f9
25 changed files with 4744 additions and 4910 deletions

View File

@ -1,25 +1,24 @@
"""The tests for the Entity component helper.""" """The tests for the Entity component helper."""
# pylint: disable=protected-access # pylint: disable=protected-access
import asyncio
from collections import OrderedDict from collections import OrderedDict
import logging import logging
import unittest
from unittest.mock import patch, Mock from unittest.mock import patch, Mock
from datetime import timedelta from datetime import timedelta
import asynctest
import pytest import pytest
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.exceptions import PlatformNotReady from homeassistant.exceptions import PlatformNotReady
from homeassistant.components import group from homeassistant.components import group
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.setup import setup_component, async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from tests.common import ( from tests.common import (
get_test_home_assistant, MockPlatform, MockModule, mock_coro, MockPlatform, MockModule, mock_coro,
async_fire_time_changed, MockEntity, MockConfigEntry, async_fire_time_changed, MockEntity, MockConfigEntry,
mock_entity_platform, mock_integration) mock_entity_platform, mock_integration)
@ -27,178 +26,169 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "test_domain" DOMAIN = "test_domain"
class TestHelpersEntityComponent(unittest.TestCase): async def test_setting_up_group(hass):
"""Test homeassistant.helpers.entity_component module.""" """Set up the setting of a group."""
assert await async_setup_component(hass, 'group', {'group': {}})
component = EntityComponent(_LOGGER, DOMAIN, hass,
group_name='everyone')
def setUp(self): # pylint: disable=invalid-name # No group after setup
"""Initialize a test Home Assistant instance.""" assert len(hass.states.async_entity_ids()) == 0
self.hass = get_test_home_assistant()
def tearDown(self): # pylint: disable=invalid-name await component.async_add_entities([MockEntity()])
"""Clean up the test Home Assistant instance.""" await hass.async_block_till_done()
self.hass.stop()
def test_setting_up_group(self): # group exists
"""Set up the setting of a group.""" assert len(hass.states.async_entity_ids()) == 2
setup_component(self.hass, 'group', {'group': {}}) assert hass.states.async_entity_ids('group') == ['group.everyone']
component = EntityComponent(_LOGGER, DOMAIN, self.hass,
group_name='everyone')
# No group after setup grp = hass.states.get('group.everyone')
assert len(self.hass.states.entity_ids()) == 0
component.add_entities([MockEntity()]) assert grp.attributes.get('entity_id') == \
self.hass.block_till_done() ('test_domain.unnamed_device',)
# group exists # group extended
assert len(self.hass.states.entity_ids()) == 2 await component.async_add_entities([MockEntity(name='goodbye')])
assert self.hass.states.entity_ids('group') == ['group.everyone'] await hass.async_block_till_done()
group = self.hass.states.get('group.everyone') assert len(hass.states.async_entity_ids()) == 3
grp = hass.states.get('group.everyone')
assert group.attributes.get('entity_id') == \ # Ordered in order of added to the group
('test_domain.unnamed_device',) assert grp.attributes.get('entity_id') == \
('test_domain.goodbye', 'test_domain.unnamed_device')
# group extended
component.add_entities([MockEntity(name='goodbye')])
self.hass.block_till_done()
assert len(self.hass.states.entity_ids()) == 3
group = self.hass.states.get('group.everyone')
# Ordered in order of added to the group
assert group.attributes.get('entity_id') == \
('test_domain.goodbye', 'test_domain.unnamed_device')
def test_setup_loads_platforms(self):
"""Test the loading of the platforms."""
component_setup = Mock(return_value=True)
platform_setup = Mock(return_value=None)
mock_integration(self.hass,
MockModule('test_component', setup=component_setup))
# mock the dependencies
mock_integration(self.hass,
MockModule('mod2', dependencies=['test_component']))
mock_entity_platform(self.hass, 'test_domain.mod2',
MockPlatform(platform_setup))
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
assert not component_setup.called
assert not platform_setup.called
component.setup({
DOMAIN: {
'platform': 'mod2',
}
})
self.hass.block_till_done()
assert component_setup.called
assert platform_setup.called
def test_setup_recovers_when_setup_raises(self):
"""Test the setup if exceptions are happening."""
platform1_setup = Mock(side_effect=Exception('Broken'))
platform2_setup = Mock(return_value=None)
mock_entity_platform(self.hass, 'test_domain.mod1',
MockPlatform(platform1_setup))
mock_entity_platform(self.hass, 'test_domain.mod2',
MockPlatform(platform2_setup))
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
assert not platform1_setup.called
assert not platform2_setup.called
component.setup(OrderedDict([
(DOMAIN, {'platform': 'mod1'}),
("{} 2".format(DOMAIN), {'platform': 'non_exist'}),
("{} 3".format(DOMAIN), {'platform': 'mod2'}),
]))
self.hass.block_till_done()
assert platform1_setup.called
assert platform2_setup.called
@patch('homeassistant.helpers.entity_component.EntityComponent'
'._async_setup_platform', return_value=mock_coro())
@patch('homeassistant.setup.async_setup_component',
return_value=mock_coro(True))
def test_setup_does_discovery(self, mock_setup_component, mock_setup):
"""Test setup for discovery."""
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
component.setup({})
discovery.load_platform(self.hass, DOMAIN, 'platform_test',
{'msg': 'discovery_info'}, {DOMAIN: {}})
self.hass.block_till_done()
assert mock_setup.called
assert ('platform_test', {}, {'msg': 'discovery_info'}) == \
mock_setup.call_args[0]
@patch('homeassistant.helpers.entity_platform.'
'async_track_time_interval')
def test_set_scan_interval_via_config(self, mock_track):
"""Test the setting of the scan interval via configuration."""
def platform_setup(hass, config, add_entities, discovery_info=None):
"""Test the platform setup."""
add_entities([MockEntity(should_poll=True)])
mock_entity_platform(self.hass, 'test_domain.platform',
MockPlatform(platform_setup))
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
component.setup({
DOMAIN: {
'platform': 'platform',
'scan_interval': timedelta(seconds=30),
}
})
self.hass.block_till_done()
assert mock_track.called
assert timedelta(seconds=30) == mock_track.call_args[0][2]
def test_set_entity_namespace_via_config(self):
"""Test setting an entity namespace."""
def platform_setup(hass, config, add_entities, discovery_info=None):
"""Test the platform setup."""
add_entities([
MockEntity(name='beer'),
MockEntity(name=None),
])
platform = MockPlatform(platform_setup)
mock_entity_platform(self.hass, 'test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
component.setup({
DOMAIN: {
'platform': 'platform',
'entity_namespace': 'yummy'
}
})
self.hass.block_till_done()
assert sorted(self.hass.states.entity_ids()) == \
['test_domain.yummy_beer', 'test_domain.yummy_unnamed_device']
@asyncio.coroutine async def test_setup_loads_platforms(hass):
def test_extract_from_service_available_device(hass): """Test the loading of the platforms."""
component_setup = Mock(return_value=True)
platform_setup = Mock(return_value=None)
mock_integration(hass, MockModule('test_component', setup=component_setup))
# mock the dependencies
mock_integration(hass, MockModule('mod2', dependencies=['test_component']))
mock_entity_platform(hass, 'test_domain.mod2',
MockPlatform(platform_setup))
component = EntityComponent(_LOGGER, DOMAIN, hass)
assert not component_setup.called
assert not platform_setup.called
component.setup({
DOMAIN: {
'platform': 'mod2',
}
})
await hass.async_block_till_done()
assert component_setup.called
assert platform_setup.called
async def test_setup_recovers_when_setup_raises(hass):
"""Test the setup if exceptions are happening."""
platform1_setup = Mock(side_effect=Exception('Broken'))
platform2_setup = Mock(return_value=None)
mock_entity_platform(hass, 'test_domain.mod1',
MockPlatform(platform1_setup))
mock_entity_platform(hass, 'test_domain.mod2',
MockPlatform(platform2_setup))
component = EntityComponent(_LOGGER, DOMAIN, hass)
assert not platform1_setup.called
assert not platform2_setup.called
component.setup(OrderedDict([
(DOMAIN, {'platform': 'mod1'}),
("{} 2".format(DOMAIN), {'platform': 'non_exist'}),
("{} 3".format(DOMAIN), {'platform': 'mod2'}),
]))
await hass.async_block_till_done()
assert platform1_setup.called
assert platform2_setup.called
@asynctest.patch('homeassistant.helpers.entity_component.EntityComponent'
'._async_setup_platform', return_value=mock_coro())
@asynctest.patch('homeassistant.setup.async_setup_component',
return_value=mock_coro(True))
async def test_setup_does_discovery(mock_setup_component, mock_setup, hass):
"""Test setup for discovery."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
component.setup({})
discovery.load_platform(hass, DOMAIN, 'platform_test',
{'msg': 'discovery_info'}, {DOMAIN: {}})
await hass.async_block_till_done()
assert mock_setup.called
assert ('platform_test', {}, {'msg': 'discovery_info'}) == \
mock_setup.call_args[0]
@asynctest.patch('homeassistant.helpers.entity_platform.'
'async_track_time_interval')
async def test_set_scan_interval_via_config(mock_track, hass):
"""Test the setting of the scan interval via configuration."""
def platform_setup(hass, config, add_entities, discovery_info=None):
"""Test the platform setup."""
add_entities([MockEntity(should_poll=True)])
mock_entity_platform(hass, 'test_domain.platform',
MockPlatform(platform_setup))
component = EntityComponent(_LOGGER, DOMAIN, hass)
component.setup({
DOMAIN: {
'platform': 'platform',
'scan_interval': timedelta(seconds=30),
}
})
await hass.async_block_till_done()
assert mock_track.called
assert timedelta(seconds=30) == mock_track.call_args[0][2]
async def test_set_entity_namespace_via_config(hass):
"""Test setting an entity namespace."""
def platform_setup(hass, config, add_entities, discovery_info=None):
"""Test the platform setup."""
add_entities([
MockEntity(name='beer'),
MockEntity(name=None),
])
platform = MockPlatform(platform_setup)
mock_entity_platform(hass, 'test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, hass)
component.setup({
DOMAIN: {
'platform': 'platform',
'entity_namespace': 'yummy'
}
})
await hass.async_block_till_done()
assert sorted(hass.states.async_entity_ids()) == \
['test_domain.yummy_beer', 'test_domain.yummy_unnamed_device']
async def test_extract_from_service_available_device(hass):
"""Test the extraction of entity from service and device is available.""" """Test the extraction of entity from service and device is available."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='test_1'), MockEntity(name='test_1'),
MockEntity(name='test_2', available=False), MockEntity(name='test_2', available=False),
MockEntity(name='test_3'), MockEntity(name='test_3'),
@ -209,7 +199,7 @@ def test_extract_from_service_available_device(hass):
assert ['test_domain.test_1', 'test_domain.test_3'] == \ assert ['test_domain.test_1', 'test_domain.test_3'] == \
sorted(ent.entity_id for ent in sorted(ent.entity_id for ent in
(yield from component.async_extract_from_service(call_1))) (await component.async_extract_from_service(call_1)))
call_2 = ha.ServiceCall('test', 'service', data={ call_2 = ha.ServiceCall('test', 'service', data={
'entity_id': ['test_domain.test_3', 'test_domain.test_4'], 'entity_id': ['test_domain.test_3', 'test_domain.test_4'],
@ -217,11 +207,10 @@ def test_extract_from_service_available_device(hass):
assert ['test_domain.test_3'] == \ assert ['test_domain.test_3'] == \
sorted(ent.entity_id for ent in sorted(ent.entity_id for ent in
(yield from component.async_extract_from_service(call_2))) (await component.async_extract_from_service(call_2)))
@asyncio.coroutine async def test_platform_not_ready(hass):
def test_platform_not_ready(hass):
"""Test that we retry when platform not ready.""" """Test that we retry when platform not ready."""
platform1_setup = Mock(side_effect=[PlatformNotReady, PlatformNotReady, platform1_setup = Mock(side_effect=[PlatformNotReady, PlatformNotReady,
None]) None])
@ -231,7 +220,7 @@ def test_platform_not_ready(hass):
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_setup({ await component.async_setup({
DOMAIN: { DOMAIN: {
'platform': 'mod1' 'platform': 'mod1'
} }
@ -245,32 +234,31 @@ def test_platform_not_ready(hass):
with patch('homeassistant.util.dt.utcnow', return_value=utcnow): with patch('homeassistant.util.dt.utcnow', return_value=utcnow):
# Should not trigger attempt 2 # Should not trigger attempt 2
async_fire_time_changed(hass, utcnow + timedelta(seconds=29)) async_fire_time_changed(hass, utcnow + timedelta(seconds=29))
yield from hass.async_block_till_done() await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 1 assert len(platform1_setup.mock_calls) == 1
# Should trigger attempt 2 # Should trigger attempt 2
async_fire_time_changed(hass, utcnow + timedelta(seconds=30)) async_fire_time_changed(hass, utcnow + timedelta(seconds=30))
yield from hass.async_block_till_done() await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 2 assert len(platform1_setup.mock_calls) == 2
assert 'test_domain.mod1' not in hass.config.components assert 'test_domain.mod1' not in hass.config.components
# This should not trigger attempt 3 # This should not trigger attempt 3
async_fire_time_changed(hass, utcnow + timedelta(seconds=59)) async_fire_time_changed(hass, utcnow + timedelta(seconds=59))
yield from hass.async_block_till_done() await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 2 assert len(platform1_setup.mock_calls) == 2
# Trigger attempt 3, which succeeds # Trigger attempt 3, which succeeds
async_fire_time_changed(hass, utcnow + timedelta(seconds=60)) async_fire_time_changed(hass, utcnow + timedelta(seconds=60))
yield from hass.async_block_till_done() await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 3 assert len(platform1_setup.mock_calls) == 3
assert 'test_domain.mod1' in hass.config.components assert 'test_domain.mod1' in hass.config.components
@asyncio.coroutine async def test_extract_from_service_returns_all_if_no_entity_id(hass):
def test_extract_from_service_returns_all_if_no_entity_id(hass):
"""Test the extraction of everything from service.""" """Test the extraction of everything from service."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='test_1'), MockEntity(name='test_1'),
MockEntity(name='test_2'), MockEntity(name='test_2'),
]) ])
@ -279,14 +267,13 @@ def test_extract_from_service_returns_all_if_no_entity_id(hass):
assert ['test_domain.test_1', 'test_domain.test_2'] == \ assert ['test_domain.test_1', 'test_domain.test_2'] == \
sorted(ent.entity_id for ent in sorted(ent.entity_id for ent in
(yield from component.async_extract_from_service(call))) (await component.async_extract_from_service(call)))
@asyncio.coroutine async def test_extract_from_service_filter_out_non_existing_entities(hass):
def test_extract_from_service_filter_out_non_existing_entities(hass):
"""Test the extraction of non existing entities from service.""" """Test the extraction of non existing entities from service."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='test_1'), MockEntity(name='test_1'),
MockEntity(name='test_2'), MockEntity(name='test_2'),
]) ])
@ -297,28 +284,26 @@ def test_extract_from_service_filter_out_non_existing_entities(hass):
assert ['test_domain.test_2'] == \ assert ['test_domain.test_2'] == \
[ent.entity_id for ent [ent.entity_id for ent
in (yield from component.async_extract_from_service(call))] in await component.async_extract_from_service(call)]
@asyncio.coroutine async def test_extract_from_service_no_group_expand(hass):
def test_extract_from_service_no_group_expand(hass):
"""Test not expanding a group.""" """Test not expanding a group."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
test_group = yield from group.Group.async_create_group( test_group = await group.Group.async_create_group(
hass, 'test_group', ['light.Ceiling', 'light.Kitchen']) hass, 'test_group', ['light.Ceiling', 'light.Kitchen'])
yield from component.async_add_entities([test_group]) await component.async_add_entities([test_group])
call = ha.ServiceCall('test', 'service', { call = ha.ServiceCall('test', 'service', {
'entity_id': ['group.test_group'] 'entity_id': ['group.test_group']
}) })
extracted = yield from component.async_extract_from_service( extracted = await component.async_extract_from_service(
call, expand_group=False) call, expand_group=False)
assert extracted == [test_group] assert extracted == [test_group]
@asyncio.coroutine async def test_setup_dependencies_platform(hass):
def test_setup_dependencies_platform(hass):
"""Test we setup the dependencies of a platform. """Test we setup the dependencies of a platform.
We're explictely testing that we process dependencies even if a component We're explictely testing that we process dependencies even if a component
@ -331,7 +316,7 @@ def test_setup_dependencies_platform(hass):
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_setup({ await component.async_setup({
DOMAIN: { DOMAIN: {
'platform': 'test_component', 'platform': 'test_component',
} }
@ -355,7 +340,7 @@ async def test_setup_entry(hass):
assert await component.async_setup_entry(entry) assert await component.async_setup_entry(entry)
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
p_hass, p_entry, p_add_entities = mock_setup_entry.mock_calls[0][1] p_hass, p_entry, _ = mock_setup_entry.mock_calls[0][1]
assert p_hass is hass assert p_hass is hass
assert p_entry is entry assert p_entry is entry
@ -448,7 +433,7 @@ async def test_set_service_race(hass):
await async_setup_component(hass, 'group', {}) await async_setup_component(hass, 'group', {})
component = EntityComponent(_LOGGER, DOMAIN, hass, group_name='yo') component = EntityComponent(_LOGGER, DOMAIN, hass, group_name='yo')
for i in range(2): for _ in range(2):
hass.async_create_task(component.async_add_entities([MockEntity()])) hass.async_create_task(component.async_add_entities([MockEntity()]))
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -1,14 +1,14 @@
"""Tests for the EntityPlatform helper.""" """Tests for the EntityPlatform helper."""
import asyncio import asyncio
import logging import logging
import unittest
from unittest.mock import patch, Mock, MagicMock from unittest.mock import patch, Mock, MagicMock
from datetime import timedelta from datetime import timedelta
import asynctest
import pytest import pytest
from homeassistant.exceptions import PlatformNotReady from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_component import ( from homeassistant.helpers.entity_component import (
EntityComponent, DEFAULT_SCAN_INTERVAL) EntityComponent, DEFAULT_SCAN_INTERVAL)
from homeassistant.helpers import entity_platform, entity_registry from homeassistant.helpers import entity_platform, entity_registry
@ -16,7 +16,7 @@ from homeassistant.helpers import entity_platform, entity_registry
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from tests.common import ( from tests.common import (
get_test_home_assistant, MockPlatform, fire_time_changed, mock_registry, MockPlatform, async_fire_time_changed, mock_registry,
MockEntity, MockEntityPlatform, MockConfigEntry, mock_entity_platform) MockEntity, MockEntityPlatform, MockConfigEntry, mock_entity_platform)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -24,164 +24,158 @@ DOMAIN = "test_domain"
PLATFORM = 'test_platform' PLATFORM = 'test_platform'
class TestHelpersEntityPlatform(unittest.TestCase): async def test_polling_only_updates_entities_it_should_poll(hass):
"""Test homeassistant.helpers.entity_component module.""" """Test the polling of only updated entities."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, timedelta(seconds=20))
def setUp(self): # pylint: disable=invalid-name no_poll_ent = MockEntity(should_poll=False)
"""Initialize a test Home Assistant instance.""" no_poll_ent.async_update = Mock()
self.hass = get_test_home_assistant() poll_ent = MockEntity(should_poll=True)
poll_ent.async_update = Mock()
def tearDown(self): # pylint: disable=invalid-name await component.async_add_entities([no_poll_ent, poll_ent])
"""Clean up the test Home Assistant instance."""
self.hass.stop()
def test_polling_only_updates_entities_it_should_poll(self): no_poll_ent.async_update.reset_mock()
"""Test the polling of only updated entities.""" poll_ent.async_update.reset_mock()
component = EntityComponent(
_LOGGER, DOMAIN, self.hass, timedelta(seconds=20))
no_poll_ent = MockEntity(should_poll=False) async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=20))
no_poll_ent.async_update = Mock() await hass.async_block_till_done()
poll_ent = MockEntity(should_poll=True)
poll_ent.async_update = Mock()
component.add_entities([no_poll_ent, poll_ent]) assert not no_poll_ent.async_update.called
assert poll_ent.async_update.called
no_poll_ent.async_update.reset_mock()
poll_ent.async_update.reset_mock()
fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=20))
self.hass.block_till_done()
assert not no_poll_ent.async_update.called
assert poll_ent.async_update.called
def test_polling_updates_entities_with_exception(self):
"""Test the updated entities that not break with an exception."""
component = EntityComponent(
_LOGGER, DOMAIN, self.hass, timedelta(seconds=20))
update_ok = []
update_err = []
def update_mock():
"""Mock normal update."""
update_ok.append(None)
def update_mock_err():
"""Mock error update."""
update_err.append(None)
raise AssertionError("Fake error update")
ent1 = MockEntity(should_poll=True)
ent1.update = update_mock_err
ent2 = MockEntity(should_poll=True)
ent2.update = update_mock
ent3 = MockEntity(should_poll=True)
ent3.update = update_mock
ent4 = MockEntity(should_poll=True)
ent4.update = update_mock
component.add_entities([ent1, ent2, ent3, ent4])
update_ok.clear()
update_err.clear()
fire_time_changed(self.hass, dt_util.utcnow() + timedelta(seconds=20))
self.hass.block_till_done()
assert len(update_ok) == 3
assert len(update_err) == 1
def test_update_state_adds_entities(self):
"""Test if updating poll entities cause an entity to be added works."""
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
ent1 = MockEntity()
ent2 = MockEntity(should_poll=True)
component.add_entities([ent2])
assert 1 == len(self.hass.states.entity_ids())
ent2.update = lambda *_: component.add_entities([ent1])
fire_time_changed(
self.hass, dt_util.utcnow() + DEFAULT_SCAN_INTERVAL
)
self.hass.block_till_done()
assert 2 == len(self.hass.states.entity_ids())
def test_update_state_adds_entities_with_update_before_add_true(self):
"""Test if call update before add to state machine."""
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
ent = MockEntity()
ent.update = Mock(spec_set=True)
component.add_entities([ent], True)
self.hass.block_till_done()
assert 1 == len(self.hass.states.entity_ids())
assert ent.update.called
def test_update_state_adds_entities_with_update_before_add_false(self):
"""Test if not call update before add to state machine."""
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
ent = MockEntity()
ent.update = Mock(spec_set=True)
component.add_entities([ent], False)
self.hass.block_till_done()
assert 1 == len(self.hass.states.entity_ids())
assert not ent.update.called
@patch('homeassistant.helpers.entity_platform.'
'async_track_time_interval')
def test_set_scan_interval_via_platform(self, mock_track):
"""Test the setting of the scan interval via platform."""
def platform_setup(hass, config, add_entities, discovery_info=None):
"""Test the platform setup."""
add_entities([MockEntity(should_poll=True)])
platform = MockPlatform(platform_setup)
platform.SCAN_INTERVAL = timedelta(seconds=30)
mock_entity_platform(self.hass, 'test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
component.setup({
DOMAIN: {
'platform': 'platform',
}
})
self.hass.block_till_done()
assert mock_track.called
assert timedelta(seconds=30) == mock_track.call_args[0][2]
def test_adding_entities_with_generator_and_thread_callback(self):
"""Test generator in add_entities that calls thread method.
We should make sure we resolve the generator to a list before passing
it into an async context.
"""
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
def create_entity(number):
"""Create entity helper."""
entity = MockEntity()
entity.entity_id = generate_entity_id(DOMAIN + '.{}',
'Number', hass=self.hass)
return entity
component.add_entities(create_entity(i) for i in range(2))
@asyncio.coroutine async def test_polling_updates_entities_with_exception(hass):
def test_platform_warn_slow_setup(hass): """Test the updated entities that not break with an exception."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, timedelta(seconds=20))
update_ok = []
update_err = []
def update_mock():
"""Mock normal update."""
update_ok.append(None)
def update_mock_err():
"""Mock error update."""
update_err.append(None)
raise AssertionError("Fake error update")
ent1 = MockEntity(should_poll=True)
ent1.update = update_mock_err
ent2 = MockEntity(should_poll=True)
ent2.update = update_mock
ent3 = MockEntity(should_poll=True)
ent3.update = update_mock
ent4 = MockEntity(should_poll=True)
ent4.update = update_mock
await component.async_add_entities([ent1, ent2, ent3, ent4])
update_ok.clear()
update_err.clear()
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=20))
await hass.async_block_till_done()
assert len(update_ok) == 3
assert len(update_err) == 1
async def test_update_state_adds_entities(hass):
"""Test if updating poll entities cause an entity to be added works."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
ent1 = MockEntity()
ent2 = MockEntity(should_poll=True)
await component.async_add_entities([ent2])
assert len(hass.states.async_entity_ids()) == 1
ent2.update = lambda *_: component.add_entities([ent1])
async_fire_time_changed(
hass, dt_util.utcnow() + DEFAULT_SCAN_INTERVAL
)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids()) == 2
async def test_update_state_adds_entities_with_update_before_add_true(hass):
"""Test if call update before add to state machine."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
ent = MockEntity()
ent.update = Mock(spec_set=True)
await component.async_add_entities([ent], True)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids()) == 1
assert ent.update.called
async def test_update_state_adds_entities_with_update_before_add_false(hass):
"""Test if not call update before add to state machine."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
ent = MockEntity()
ent.update = Mock(spec_set=True)
await component.async_add_entities([ent], False)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids()) == 1
assert not ent.update.called
@asynctest.patch('homeassistant.helpers.entity_platform.'
'async_track_time_interval')
async def test_set_scan_interval_via_platform(mock_track, hass):
"""Test the setting of the scan interval via platform."""
def platform_setup(hass, config, add_entities, discovery_info=None):
"""Test the platform setup."""
add_entities([MockEntity(should_poll=True)])
platform = MockPlatform(platform_setup)
platform.SCAN_INTERVAL = timedelta(seconds=30)
mock_entity_platform(hass, 'test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, hass)
component.setup({
DOMAIN: {
'platform': 'platform',
}
})
await hass.async_block_till_done()
assert mock_track.called
assert timedelta(seconds=30) == mock_track.call_args[0][2]
async def test_adding_entities_with_generator_and_thread_callback(hass):
"""Test generator in add_entities that calls thread method.
We should make sure we resolve the generator to a list before passing
it into an async context.
"""
component = EntityComponent(_LOGGER, DOMAIN, hass)
def create_entity(number):
"""Create entity helper."""
entity = MockEntity()
entity.entity_id = async_generate_entity_id(DOMAIN + '.{}',
'Number', hass=hass)
return entity
await component.async_add_entities(create_entity(i) for i in range(2))
async def test_platform_warn_slow_setup(hass):
"""Warn we log when platform setup takes a long time.""" """Warn we log when platform setup takes a long time."""
platform = MockPlatform() platform = MockPlatform()
@ -191,7 +185,7 @@ def test_platform_warn_slow_setup(hass):
with patch.object(hass.loop, 'call_later', MagicMock()) \ with patch.object(hass.loop, 'call_later', MagicMock()) \
as mock_call: as mock_call:
yield from component.async_setup({ await component.async_setup({
DOMAIN: { DOMAIN: {
'platform': 'platform', 'platform': 'platform',
} }
@ -208,21 +202,19 @@ def test_platform_warn_slow_setup(hass):
assert mock_call().cancel.called assert mock_call().cancel.called
@asyncio.coroutine async def test_platform_error_slow_setup(hass, caplog):
def test_platform_error_slow_setup(hass, caplog):
"""Don't block startup more than SLOW_SETUP_MAX_WAIT.""" """Don't block startup more than SLOW_SETUP_MAX_WAIT."""
with patch.object(entity_platform, 'SLOW_SETUP_MAX_WAIT', 0): with patch.object(entity_platform, 'SLOW_SETUP_MAX_WAIT', 0):
called = [] called = []
@asyncio.coroutine async def setup_platform(*args):
def setup_platform(*args):
called.append(1) called.append(1)
yield from asyncio.sleep(1, loop=hass.loop) await asyncio.sleep(1, loop=hass.loop)
platform = MockPlatform(async_setup_platform=setup_platform) platform = MockPlatform(async_setup_platform=setup_platform)
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
mock_entity_platform(hass, 'test_domain.test_platform', platform) mock_entity_platform(hass, 'test_domain.test_platform', platform)
yield from component.async_setup({ await component.async_setup({
DOMAIN: { DOMAIN: {
'platform': 'test_platform', 'platform': 'test_platform',
} }
@ -232,23 +224,21 @@ def test_platform_error_slow_setup(hass, caplog):
assert 'test_platform is taking longer than 0 seconds' in caplog.text assert 'test_platform is taking longer than 0 seconds' in caplog.text
@asyncio.coroutine async def test_updated_state_used_for_entity_id(hass):
def test_updated_state_used_for_entity_id(hass):
"""Test that first update results used for entity ID generation.""" """Test that first update results used for entity ID generation."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
class MockEntityNameFetcher(MockEntity): class MockEntityNameFetcher(MockEntity):
"""Mock entity that fetches a friendly name.""" """Mock entity that fetches a friendly name."""
@asyncio.coroutine async def async_update(self):
def async_update(self):
"""Mock update that assigns a name.""" """Mock update that assigns a name."""
self._values['name'] = "Living Room" self._values['name'] = "Living Room"
yield from component.async_add_entities([MockEntityNameFetcher()], True) await component.async_add_entities([MockEntityNameFetcher()], True)
entity_ids = hass.states.async_entity_ids() entity_ids = hass.states.async_entity_ids()
assert 1 == len(entity_ids) assert len(entity_ids) == 1
assert entity_ids[0] == "test_domain.living_room" assert entity_ids[0] == "test_domain.living_room"
@ -374,8 +364,7 @@ async def test_parallel_updates_sync_platform_with_constant(hass):
assert entity.parallel_updates._value == 2 assert entity.parallel_updates._value == 2
@asyncio.coroutine async def test_raise_error_on_update(hass):
def test_raise_error_on_update(hass):
"""Test the add entity if they raise an error on update.""" """Test the add entity if they raise an error on update."""
updates = [] updates = []
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
@ -389,63 +378,58 @@ def test_raise_error_on_update(hass):
entity1.update = _raise entity1.update = _raise
entity2.update = lambda: updates.append(1) entity2.update = lambda: updates.append(1)
yield from component.async_add_entities([entity1, entity2], True) await component.async_add_entities([entity1, entity2], True)
assert len(updates) == 1 assert len(updates) == 1
assert 1 in updates assert 1 in updates
@asyncio.coroutine async def test_async_remove_with_platform(hass):
def test_async_remove_with_platform(hass):
"""Remove an entity from a platform.""" """Remove an entity from a platform."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
entity1 = MockEntity(name='test_1') entity1 = MockEntity(name='test_1')
yield from component.async_add_entities([entity1]) await component.async_add_entities([entity1])
assert len(hass.states.async_entity_ids()) == 1 assert len(hass.states.async_entity_ids()) == 1
yield from entity1.async_remove() await entity1.async_remove()
assert len(hass.states.async_entity_ids()) == 0 assert len(hass.states.async_entity_ids()) == 0
@asyncio.coroutine async def test_not_adding_duplicate_entities_with_unique_id(hass):
def test_not_adding_duplicate_entities_with_unique_id(hass):
"""Test for not adding duplicate entities.""" """Test for not adding duplicate entities."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='test1', unique_id='not_very_unique')]) MockEntity(name='test1', unique_id='not_very_unique')])
assert len(hass.states.async_entity_ids()) == 1 assert len(hass.states.async_entity_ids()) == 1
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='test2', unique_id='not_very_unique')]) MockEntity(name='test2', unique_id='not_very_unique')])
assert len(hass.states.async_entity_ids()) == 1 assert len(hass.states.async_entity_ids()) == 1
@asyncio.coroutine async def test_using_prescribed_entity_id(hass):
def test_using_prescribed_entity_id(hass):
"""Test for using predefined entity ID.""" """Test for using predefined entity ID."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='bla', entity_id='hello.world')]) MockEntity(name='bla', entity_id='hello.world')])
assert 'hello.world' in hass.states.async_entity_ids() assert 'hello.world' in hass.states.async_entity_ids()
@asyncio.coroutine async def test_using_prescribed_entity_id_with_unique_id(hass):
def test_using_prescribed_entity_id_with_unique_id(hass):
"""Test for ammending predefined entity ID because currently exists.""" """Test for ammending predefined entity ID because currently exists."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(entity_id='test_domain.world')]) MockEntity(entity_id='test_domain.world')])
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(entity_id='test_domain.world', unique_id='bla')]) MockEntity(entity_id='test_domain.world', unique_id='bla')])
assert 'test_domain.world_2' in hass.states.async_entity_ids() assert 'test_domain.world_2' in hass.states.async_entity_ids()
@asyncio.coroutine async def test_using_prescribed_entity_id_which_is_registered(hass):
def test_using_prescribed_entity_id_which_is_registered(hass):
"""Test not allowing predefined entity ID that already registered.""" """Test not allowing predefined entity ID that already registered."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
registry = mock_registry(hass) registry = mock_registry(hass)
@ -454,14 +438,13 @@ def test_using_prescribed_entity_id_which_is_registered(hass):
DOMAIN, 'test', '1234', suggested_object_id='world') DOMAIN, 'test', '1234', suggested_object_id='world')
# This entity_id will be rewritten # This entity_id will be rewritten
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(entity_id='test_domain.world')]) MockEntity(entity_id='test_domain.world')])
assert 'test_domain.world_2' in hass.states.async_entity_ids() assert 'test_domain.world_2' in hass.states.async_entity_ids()
@asyncio.coroutine async def test_name_which_conflict_with_registered(hass):
def test_name_which_conflict_with_registered(hass):
"""Test not generating conflicting entity ID based on name.""" """Test not generating conflicting entity ID based on name."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
registry = mock_registry(hass) registry = mock_registry(hass)
@ -470,24 +453,22 @@ def test_name_which_conflict_with_registered(hass):
registry.async_get_or_create( registry.async_get_or_create(
DOMAIN, 'test', '1234', suggested_object_id='world') DOMAIN, 'test', '1234', suggested_object_id='world')
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(name='world')]) MockEntity(name='world')])
assert 'test_domain.world_2' in hass.states.async_entity_ids() assert 'test_domain.world_2' in hass.states.async_entity_ids()
@asyncio.coroutine async def test_entity_with_name_and_entity_id_getting_registered(hass):
def test_entity_with_name_and_entity_id_getting_registered(hass):
"""Ensure that entity ID is used for registration.""" """Ensure that entity ID is used for registration."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(unique_id='1234', name='bla', MockEntity(unique_id='1234', name='bla',
entity_id='test_domain.world')]) entity_id='test_domain.world')])
assert 'test_domain.world' in hass.states.async_entity_ids() assert 'test_domain.world' in hass.states.async_entity_ids()
@asyncio.coroutine async def test_overriding_name_from_registry(hass):
def test_overriding_name_from_registry(hass):
"""Test that we can override a name via the Entity Registry.""" """Test that we can override a name via the Entity Registry."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
mock_registry(hass, { mock_registry(hass, {
@ -499,7 +480,7 @@ def test_overriding_name_from_registry(hass):
name='Overridden' name='Overridden'
) )
}) })
yield from component.async_add_entities([ await component.async_add_entities([
MockEntity(unique_id='1234', name='Device Name')]) MockEntity(unique_id='1234', name='Device Name')])
state = hass.states.get('test_domain.world') state = hass.states.get('test_domain.world')
@ -507,18 +488,16 @@ def test_overriding_name_from_registry(hass):
assert state.name == 'Overridden' assert state.name == 'Overridden'
@asyncio.coroutine async def test_registry_respect_entity_namespace(hass):
def test_registry_respect_entity_namespace(hass):
"""Test that the registry respects entity namespace.""" """Test that the registry respects entity namespace."""
mock_registry(hass) mock_registry(hass)
platform = MockEntityPlatform(hass, entity_namespace='ns') platform = MockEntityPlatform(hass, entity_namespace='ns')
entity = MockEntity(unique_id='1234', name='Device Name') entity = MockEntity(unique_id='1234', name='Device Name')
yield from platform.async_add_entities([entity]) await platform.async_add_entities([entity])
assert entity.entity_id == 'test_domain.ns_device_name' assert entity.entity_id == 'test_domain.ns_device_name'
@asyncio.coroutine async def test_registry_respect_entity_disabled(hass):
def test_registry_respect_entity_disabled(hass):
"""Test that the registry respects entity disabled.""" """Test that the registry respects entity disabled."""
mock_registry(hass, { mock_registry(hass, {
'test_domain.world': entity_registry.RegistryEntry( 'test_domain.world': entity_registry.RegistryEntry(
@ -531,7 +510,7 @@ def test_registry_respect_entity_disabled(hass):
}) })
platform = MockEntityPlatform(hass) platform = MockEntityPlatform(hass)
entity = MockEntity(unique_id='1234') entity = MockEntity(unique_id='1234')
yield from platform.async_add_entities([entity]) await platform.async_add_entities([entity])
assert entity.entity_id is None assert entity.entity_id is None
assert hass.states.async_entity_ids() == [] assert hass.states.async_entity_ids() == []
@ -643,12 +622,11 @@ async def test_reset_cancels_retry_setup(hass):
assert ent_platform._async_cancel_retry_setup is None assert ent_platform._async_cancel_retry_setup is None
@asyncio.coroutine async def test_not_fails_with_adding_empty_entities_(hass):
def test_not_fails_with_adding_empty_entities_(hass):
"""Test for not fails on empty entities list.""" """Test for not fails on empty entities list."""
component = EntityComponent(_LOGGER, DOMAIN, hass) component = EntityComponent(_LOGGER, DOMAIN, hass)
yield from component.async_add_entities([]) await component.async_add_entities([])
assert len(hass.states.async_entity_ids()) == 0 assert len(hass.states.async_entity_ids()) == 0

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,43 @@
"""Test Home Assistant icon util methods.""" """Test Home Assistant icon util methods."""
import unittest
class TestIconUtil(unittest.TestCase): def test_battery_icon():
"""Test icon util methods.""" """Test icon generator for battery sensor."""
from homeassistant.helpers.icon import icon_for_battery_level
def test_battery_icon(self): assert icon_for_battery_level(None, True) == 'mdi:battery-unknown'
"""Test icon generator for battery sensor.""" assert icon_for_battery_level(None, False) == 'mdi:battery-unknown'
from homeassistant.helpers.icon import icon_for_battery_level
assert 'mdi:battery-unknown' == \ assert icon_for_battery_level(5, True) == 'mdi:battery-outline'
icon_for_battery_level(None, True) assert icon_for_battery_level(5, False) == 'mdi:battery-alert'
assert 'mdi:battery-unknown' == \
icon_for_battery_level(None, False)
assert 'mdi:battery-outline' == \ assert icon_for_battery_level(100, True) == 'mdi:battery-charging-100'
icon_for_battery_level(5, True) assert icon_for_battery_level(100, False) == 'mdi:battery'
assert 'mdi:battery-alert' == \
icon_for_battery_level(5, False)
assert 'mdi:battery-charging-100' == \ iconbase = 'mdi:battery'
icon_for_battery_level(100, True) for level in range(0, 100, 5):
assert 'mdi:battery' == \ print('Level: %d. icon: %s, charging: %s'
icon_for_battery_level(100, False) % (level, icon_for_battery_level(level, False),
icon_for_battery_level(level, True)))
iconbase = 'mdi:battery' if level <= 10:
for level in range(0, 100, 5): postfix_charging = '-outline'
print('Level: %d. icon: %s, charging: %s' elif level <= 30:
% (level, icon_for_battery_level(level, False), postfix_charging = '-charging-20'
icon_for_battery_level(level, True))) elif level <= 50:
if level <= 10: postfix_charging = '-charging-40'
postfix_charging = '-outline' elif level <= 70:
elif level <= 30: postfix_charging = '-charging-60'
postfix_charging = '-charging-20' elif level <= 90:
elif level <= 50: postfix_charging = '-charging-80'
postfix_charging = '-charging-40' else:
elif level <= 70: postfix_charging = '-charging-100'
postfix_charging = '-charging-60' if 5 < level < 95:
elif level <= 90: postfix = '-{}'.format(int(round(level / 10 - .01)) * 10)
postfix_charging = '-charging-80' elif level <= 5:
else: postfix = '-alert'
postfix_charging = '-charging-100' else:
if 5 < level < 95: postfix = ''
postfix = '-{}'.format(int(round(level / 10 - .01)) * 10) assert iconbase + postfix == \
elif level <= 5: icon_for_battery_level(level, False)
postfix = '-alert' assert iconbase + postfix_charging == \
else: icon_for_battery_level(level, True)
postfix = ''
assert iconbase + postfix == \
icon_for_battery_level(level, False)
assert iconbase + postfix_charging == \
icon_for_battery_level(level, True)

View File

@ -1,50 +1,35 @@
"""Test component helpers.""" """Test component helpers."""
# pylint: disable=protected-access # pylint: disable=protected-access
from collections import OrderedDict from collections import OrderedDict
import unittest
from homeassistant import helpers from homeassistant import helpers
from tests.common import get_test_home_assistant
def test_extract_domain_configs():
"""Test the extraction of domain configuration."""
config = {
'zone': None,
'zoner': None,
'zone ': None,
'zone Hallo': None,
'zone 100': None,
}
assert set(['zone', 'zone Hallo', 'zone 100']) == \
set(helpers.extract_domain_configs(config, 'zone'))
class TestHelpers(unittest.TestCase): def test_config_per_platform():
"""Tests homeassistant.helpers module.""" """Test config per platform method."""
config = OrderedDict([
('zone', {'platform': 'hello'}),
('zoner', None),
('zone Hallo', [1, {'platform': 'hello 2'}]),
('zone 100', None),
])
# pylint: disable=invalid-name assert [
def setUp(self): ('hello', config['zone']),
"""Init needed objects.""" (None, 1),
self.hass = get_test_home_assistant() ('hello 2', config['zone Hallo'][1]),
] == list(helpers.config_per_platform(config, 'zone'))
# pylint: disable=invalid-name
def tearDown(self):
"""Stop everything that was started."""
self.hass.stop()
def test_extract_domain_configs(self):
"""Test the extraction of domain configuration."""
config = {
'zone': None,
'zoner': None,
'zone ': None,
'zone Hallo': None,
'zone 100': None,
}
assert set(['zone', 'zone Hallo', 'zone 100']) == \
set(helpers.extract_domain_configs(config, 'zone'))
def test_config_per_platform(self):
"""Test config per platform method."""
config = OrderedDict([
('zone', {'platform': 'hello'}),
('zoner', None),
('zone Hallo', [1, {'platform': 'hello 2'}]),
('zone 100', None),
])
assert [
('hello', config['zone']),
(None, 1),
('hello 2', config['zone Hallo'][1]),
] == list(helpers.config_per_platform(config, 'zone'))

View File

@ -1,11 +1,11 @@
"""Tests for the intent helpers.""" """Tests for the intent helpers."""
import unittest
import voluptuous as vol import voluptuous as vol
import pytest
from homeassistant.core import State from homeassistant.core import State
from homeassistant.helpers import (intent, config_validation as cv) from homeassistant.helpers import (intent, config_validation as cv)
import pytest
class MockIntentHandler(intent.IntentHandler): class MockIntentHandler(intent.IntentHandler):
@ -25,23 +25,20 @@ def test_async_match_state():
assert state is state1 assert state is state1
class TestIntentHandler(unittest.TestCase): def test_async_validate_slots():
"""Test the Home Assistant event helpers.""" """Test async_validate_slots of IntentHandler."""
handler1 = MockIntentHandler({
vol.Required('name'): cv.string,
})
def test_async_validate_slots(self): with pytest.raises(vol.error.MultipleInvalid):
"""Test async_validate_slots of IntentHandler.""" handler1.async_validate_slots({})
handler1 = MockIntentHandler({ with pytest.raises(vol.error.MultipleInvalid):
vol.Required('name'): cv.string, handler1.async_validate_slots({'name': 1})
}) with pytest.raises(vol.error.MultipleInvalid):
handler1.async_validate_slots({'name': 'kitchen'})
with pytest.raises(vol.error.MultipleInvalid): handler1.async_validate_slots({'name': {'value': 'kitchen'}})
handler1.async_validate_slots({}) handler1.async_validate_slots({
with pytest.raises(vol.error.MultipleInvalid): 'name': {'value': 'kitchen'},
handler1.async_validate_slots({'name': 1}) 'probability': {'value': '0.5'}
with pytest.raises(vol.error.MultipleInvalid): })
handler1.async_validate_slots({'name': 'kitchen'})
handler1.async_validate_slots({'name': {'value': 'kitchen'}})
handler1.async_validate_slots({
'name': {'value': 'kitchen'},
'probability': {'value': '0.5'}
})

View File

@ -1,58 +1,57 @@
"""Tests Home Assistant location helpers.""" """Tests Home Assistant location helpers."""
import unittest
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
from homeassistant.core import State from homeassistant.core import State
from homeassistant.helpers import location from homeassistant.helpers import location
class TestHelpersLocation(unittest.TestCase): def test_has_location_with_invalid_states():
"""Set up the tests.""" """Set up the tests."""
for state in (None, 1, "hello", object):
def test_has_location_with_invalid_states(self):
"""Set up the tests."""
for state in (None, 1, "hello", object):
assert not location.has_location(state)
def test_has_location_with_states_with_invalid_locations(self):
"""Set up the tests."""
state = State('hello.world', 'invalid', {
ATTR_LATITUDE: 'no number',
ATTR_LONGITUDE: 123.12
})
assert not location.has_location(state) assert not location.has_location(state)
def test_has_location_with_states_with_valid_location(self):
"""Set up the tests."""
state = State('hello.world', 'invalid', {
ATTR_LATITUDE: 123.12,
ATTR_LONGITUDE: 123.12
})
assert location.has_location(state)
def test_closest_with_no_states_with_location(self): def test_has_location_with_states_with_invalid_locations():
"""Set up the tests.""" """Set up the tests."""
state = State('light.test', 'on') state = State('hello.world', 'invalid', {
state2 = State('light.test', 'on', { ATTR_LATITUDE: 'no number',
ATTR_LATITUDE: 'invalid', ATTR_LONGITUDE: 123.12
ATTR_LONGITUDE: 123.45, })
}) assert not location.has_location(state)
state3 = State('light.test', 'on', {
ATTR_LONGITUDE: 123.45,
})
assert \
location.closest(123.45, 123.45, [state, state2, state3]) is None
def test_closest_returns_closest(self): def test_has_location_with_states_with_valid_location():
"""Test .""" """Set up the tests."""
state = State('light.test', 'on', { state = State('hello.world', 'invalid', {
ATTR_LATITUDE: 124.45, ATTR_LATITUDE: 123.12,
ATTR_LONGITUDE: 124.45, ATTR_LONGITUDE: 123.12
}) })
state2 = State('light.test', 'on', { assert location.has_location(state)
ATTR_LATITUDE: 125.45,
ATTR_LONGITUDE: 125.45,
})
assert state == location.closest(123.45, 123.45, [state, state2])
def test_closest_with_no_states_with_location():
"""Set up the tests."""
state = State('light.test', 'on')
state2 = State('light.test', 'on', {
ATTR_LATITUDE: 'invalid',
ATTR_LONGITUDE: 123.45,
})
state3 = State('light.test', 'on', {
ATTR_LONGITUDE: 123.45,
})
assert \
location.closest(123.45, 123.45, [state, state2, state3]) is None
def test_closest_returns_closest():
"""Test ."""
state = State('light.test', 'on', {
ATTR_LATITUDE: 124.45,
ATTR_LONGITUDE: 124.45,
})
state2 = State('light.test', 'on', {
ATTR_LATITUDE: 125.45,
ATTR_LONGITUDE: 125.45,
})
assert state == location.closest(123.45, 123.45, [state, state2])

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,12 @@
"""Test state helpers.""" """Test state helpers."""
import asyncio import asyncio
from datetime import timedelta from datetime import timedelta
import unittest
from unittest.mock import patch from unittest.mock import patch
import pytest
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.setup import async_setup_component
from homeassistant.const import (SERVICE_TURN_ON, SERVICE_TURN_OFF) from homeassistant.const import (SERVICE_TURN_ON, SERVICE_TURN_OFF)
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from homeassistant.helpers import state from homeassistant.helpers import state
from homeassistant.const import ( from homeassistant.const import (
@ -18,8 +17,7 @@ from homeassistant.const import (
from homeassistant.components.sun import (STATE_ABOVE_HORIZON, from homeassistant.components.sun import (STATE_ABOVE_HORIZON,
STATE_BELOW_HORIZON) STATE_BELOW_HORIZON)
from tests.common import get_test_home_assistant, mock_service from tests.common import async_mock_service
import pytest
@asyncio.coroutine @asyncio.coroutine
@ -82,141 +80,134 @@ def test_call_to_component(hass):
context=context) context=context)
class TestStateHelpers(unittest.TestCase): async def test_get_changed_since(hass):
"""Test the Home Assistant event helpers.""" """Test get_changed_since."""
point1 = dt_util.utcnow()
point2 = point1 + timedelta(seconds=5)
point3 = point2 + timedelta(seconds=5)
def setUp(self): # pylint: disable=invalid-name with patch('homeassistant.core.dt_util.utcnow', return_value=point1):
"""Run when tests are started.""" hass.states.async_set('light.test', 'on')
self.hass = get_test_home_assistant() state1 = hass.states.get('light.test')
run_coroutine_threadsafe(async_setup_component(
self.hass, 'homeassistant', {}), self.hass.loop).result()
def tearDown(self): # pylint: disable=invalid-name with patch('homeassistant.core.dt_util.utcnow', return_value=point2):
"""Stop when tests are finished.""" hass.states.async_set('light.test2', 'on')
self.hass.stop() state2 = hass.states.get('light.test2')
def test_get_changed_since(self): with patch('homeassistant.core.dt_util.utcnow', return_value=point3):
"""Test get_changed_since.""" hass.states.async_set('light.test3', 'on')
point1 = dt_util.utcnow() state3 = hass.states.get('light.test3')
point2 = point1 + timedelta(seconds=5)
point3 = point2 + timedelta(seconds=5)
with patch('homeassistant.core.dt_util.utcnow', return_value=point1): assert [state2, state3] == \
self.hass.states.set('light.test', 'on') state.get_changed_since([state1, state2, state3], point2)
state1 = self.hass.states.get('light.test')
with patch('homeassistant.core.dt_util.utcnow', return_value=point2):
self.hass.states.set('light.test2', 'on')
state2 = self.hass.states.get('light.test2')
with patch('homeassistant.core.dt_util.utcnow', return_value=point3): async def test_reproduce_with_no_entity(hass):
self.hass.states.set('light.test3', 'on') """Test reproduce_state with no entity."""
state3 = self.hass.states.get('light.test3') calls = async_mock_service(hass, 'light', SERVICE_TURN_ON)
assert [state2, state3] == \ await state.async_reproduce_state(hass, ha.State('light.test', 'on'))
state.get_changed_since([state1, state2, state3], point2)
def test_reproduce_with_no_entity(self): await hass.async_block_till_done()
"""Test reproduce_state with no entity."""
calls = mock_service(self.hass, 'light', SERVICE_TURN_ON)
state.reproduce_state(self.hass, ha.State('light.test', 'on')) assert len(calls) == 0
assert hass.states.get('light.test') is None
self.hass.block_till_done()
assert len(calls) == 0 async def test_reproduce_turn_on(hass):
assert self.hass.states.get('light.test') is None """Test reproduce_state with SERVICE_TURN_ON."""
calls = async_mock_service(hass, 'light', SERVICE_TURN_ON)
def test_reproduce_turn_on(self): hass.states.async_set('light.test', 'off')
"""Test reproduce_state with SERVICE_TURN_ON."""
calls = mock_service(self.hass, 'light', SERVICE_TURN_ON)
self.hass.states.set('light.test', 'off') await state.async_reproduce_state(hass, ha.State('light.test', 'on'))
state.reproduce_state(self.hass, ha.State('light.test', 'on')) await hass.async_block_till_done()
self.hass.block_till_done() assert len(calls) > 0
last_call = calls[-1]
assert last_call.domain == 'light'
assert SERVICE_TURN_ON == last_call.service
assert ['light.test'] == last_call.data.get('entity_id')
assert len(calls) > 0
last_call = calls[-1]
assert 'light' == last_call.domain
assert SERVICE_TURN_ON == last_call.service
assert ['light.test'] == last_call.data.get('entity_id')
def test_reproduce_turn_off(self): async def test_reproduce_turn_off(hass):
"""Test reproduce_state with SERVICE_TURN_OFF.""" """Test reproduce_state with SERVICE_TURN_OFF."""
calls = mock_service(self.hass, 'light', SERVICE_TURN_OFF) calls = async_mock_service(hass, 'light', SERVICE_TURN_OFF)
self.hass.states.set('light.test', 'on') hass.states.async_set('light.test', 'on')
state.reproduce_state(self.hass, ha.State('light.test', 'off')) await state.async_reproduce_state(hass, ha.State('light.test', 'off'))
self.hass.block_till_done() await hass.async_block_till_done()
assert len(calls) > 0 assert len(calls) > 0
last_call = calls[-1] last_call = calls[-1]
assert 'light' == last_call.domain assert last_call.domain == 'light'
assert SERVICE_TURN_OFF == last_call.service assert SERVICE_TURN_OFF == last_call.service
assert ['light.test'] == last_call.data.get('entity_id') assert ['light.test'] == last_call.data.get('entity_id')
def test_reproduce_complex_data(self):
"""Test reproduce_state with complex service data."""
calls = mock_service(self.hass, 'light', SERVICE_TURN_ON)
self.hass.states.set('light.test', 'off') async def test_reproduce_complex_data(hass):
"""Test reproduce_state with complex service data."""
calls = async_mock_service(hass, 'light', SERVICE_TURN_ON)
complex_data = ['hello', {'11': '22'}] hass.states.async_set('light.test', 'off')
state.reproduce_state(self.hass, ha.State('light.test', 'on', { complex_data = ['hello', {'11': '22'}]
'complex': complex_data
}))
self.hass.block_till_done() await state.async_reproduce_state(hass, ha.State('light.test', 'on', {
'complex': complex_data
}))
assert len(calls) > 0 await hass.async_block_till_done()
last_call = calls[-1]
assert 'light' == last_call.domain
assert SERVICE_TURN_ON == last_call.service
assert complex_data == last_call.data.get('complex')
def test_reproduce_bad_state(self): assert len(calls) > 0
"""Test reproduce_state with bad state.""" last_call = calls[-1]
calls = mock_service(self.hass, 'light', SERVICE_TURN_ON) assert last_call.domain == 'light'
assert SERVICE_TURN_ON == last_call.service
assert complex_data == last_call.data.get('complex')
self.hass.states.set('light.test', 'off')
state.reproduce_state(self.hass, ha.State('light.test', 'bad')) async def test_reproduce_bad_state(hass):
"""Test reproduce_state with bad state."""
calls = async_mock_service(hass, 'light', SERVICE_TURN_ON)
self.hass.block_till_done() hass.states.async_set('light.test', 'off')
assert len(calls) == 0 await state.async_reproduce_state(hass, ha.State('light.test', 'bad'))
assert 'off' == self.hass.states.get('light.test').state
def test_as_number_states(self): await hass.async_block_till_done()
"""Test state_as_number with states."""
zero_states = (STATE_OFF, STATE_CLOSED, STATE_UNLOCKED,
STATE_BELOW_HORIZON, STATE_NOT_HOME)
one_states = (STATE_ON, STATE_OPEN, STATE_LOCKED, STATE_ABOVE_HORIZON,
STATE_HOME)
for _state in zero_states:
assert 0 == state.state_as_number(
ha.State('domain.test', _state, {}))
for _state in one_states:
assert 1 == state.state_as_number(
ha.State('domain.test', _state, {}))
def test_as_number_coercion(self): assert len(calls) == 0
"""Test state_as_number with number.""" assert hass.states.get('light.test').state == 'off'
for _state in ('0', '0.0', 0, 0.0):
assert 0.0 == state.state_as_number(
ha.State('domain.test', _state, {}))
for _state in ('1', '1.0', 1, 1.0):
assert 1.0 == state.state_as_number(
ha.State('domain.test', _state, {}))
def test_as_number_invalid_cases(self):
"""Test state_as_number with invalid cases.""" async def test_as_number_states(hass):
for _state in ('', 'foo', 'foo.bar', None, False, True, object, """Test state_as_number with states."""
object()): zero_states = (STATE_OFF, STATE_CLOSED, STATE_UNLOCKED,
with pytest.raises(ValueError): STATE_BELOW_HORIZON, STATE_NOT_HOME)
state.state_as_number(ha.State('domain.test', _state, {})) one_states = (STATE_ON, STATE_OPEN, STATE_LOCKED, STATE_ABOVE_HORIZON,
STATE_HOME)
for _state in zero_states:
assert state.state_as_number(ha.State('domain.test', _state, {})) == 0
for _state in one_states:
assert state.state_as_number(ha.State('domain.test', _state, {})) == 1
async def test_as_number_coercion(hass):
"""Test state_as_number with number."""
for _state in ('0', '0.0', 0, 0.0):
assert state.state_as_number(
ha.State('domain.test', _state, {})) == 0.0
for _state in ('1', '1.0', 1, 1.0):
assert state.state_as_number(
ha.State('domain.test', _state, {})) == 1.0
async def test_as_number_invalid_cases(hass):
"""Test state_as_number with invalid cases."""
for _state in ('', 'foo', 'foo.bar', None, False, True, object,
object()):
with pytest.raises(ValueError):
state.state_as_number(ha.State('domain.test', _state, {}))

View File

@ -1,6 +1,5 @@
"""The tests for the Sun helpers.""" """The tests for the Sun helpers."""
# pylint: disable=protected-access # pylint: disable=protected-access
import unittest
from unittest.mock import patch from unittest.mock import patch
from datetime import timedelta, datetime from datetime import timedelta, datetime
@ -8,223 +7,214 @@ from homeassistant.const import SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.helpers.sun as sun import homeassistant.helpers.sun as sun
from tests.common import get_test_home_assistant
def test_next_events(hass):
"""Test retrieving next sun events."""
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
from astral import Astral
astral = Astral()
utc_today = utc_now.date()
latitude = hass.config.latitude
longitude = hass.config.longitude
mod = -1
while True:
next_dawn = (astral.dawn_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_dawn > utc_now:
break
mod += 1
mod = -1
while True:
next_dusk = (astral.dusk_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_dusk > utc_now:
break
mod += 1
mod = -1
while True:
next_midnight = (astral.solar_midnight_utc(
utc_today + timedelta(days=mod), longitude))
if next_midnight > utc_now:
break
mod += 1
mod = -1
while True:
next_noon = (astral.solar_noon_utc(
utc_today + timedelta(days=mod), longitude))
if next_noon > utc_now:
break
mod += 1
mod = -1
while True:
next_rising = (astral.sunrise_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_rising > utc_now:
break
mod += 1
mod = -1
while True:
next_setting = (astral.sunset_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_setting > utc_now:
break
mod += 1
with patch('homeassistant.helpers.condition.dt_util.utcnow',
return_value=utc_now):
assert next_dawn == sun.get_astral_event_next(
hass, 'dawn')
assert next_dusk == sun.get_astral_event_next(
hass, 'dusk')
assert next_midnight == sun.get_astral_event_next(
hass, 'solar_midnight')
assert next_noon == sun.get_astral_event_next(
hass, 'solar_noon')
assert next_rising == sun.get_astral_event_next(
hass, SUN_EVENT_SUNRISE)
assert next_setting == sun.get_astral_event_next(
hass, SUN_EVENT_SUNSET)
# pylint: disable=invalid-name def test_date_events(hass):
class TestSun(unittest.TestCase): """Test retrieving next sun events."""
"""Test the sun helpers.""" utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
from astral import Astral
def setUp(self): astral = Astral()
"""Set up things to be run when tests are started.""" utc_today = utc_now.date()
self.hass = get_test_home_assistant()
def tearDown(self): latitude = hass.config.latitude
"""Stop everything that was started.""" longitude = hass.config.longitude
self.hass.stop()
def test_next_events(self): dawn = astral.dawn_utc(utc_today, latitude, longitude)
"""Test retrieving next sun events.""" dusk = astral.dusk_utc(utc_today, latitude, longitude)
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC) midnight = astral.solar_midnight_utc(utc_today, longitude)
from astral import Astral noon = astral.solar_noon_utc(utc_today, longitude)
sunrise = astral.sunrise_utc(utc_today, latitude, longitude)
sunset = astral.sunset_utc(utc_today, latitude, longitude)
astral = Astral() assert dawn == sun.get_astral_event_date(
utc_today = utc_now.date() hass, 'dawn', utc_today)
assert dusk == sun.get_astral_event_date(
hass, 'dusk', utc_today)
assert midnight == sun.get_astral_event_date(
hass, 'solar_midnight', utc_today)
assert noon == sun.get_astral_event_date(
hass, 'solar_noon', utc_today)
assert sunrise == sun.get_astral_event_date(
hass, SUN_EVENT_SUNRISE, utc_today)
assert sunset == sun.get_astral_event_date(
hass, SUN_EVENT_SUNSET, utc_today)
latitude = self.hass.config.latitude
longitude = self.hass.config.longitude
mod = -1 def test_date_events_default_date(hass):
while True: """Test retrieving next sun events."""
next_dawn = (astral.dawn_utc( utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
utc_today + timedelta(days=mod), latitude, longitude)) from astral import Astral
if next_dawn > utc_now:
break
mod += 1
mod = -1 astral = Astral()
while True: utc_today = utc_now.date()
next_dusk = (astral.dusk_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_dusk > utc_now:
break
mod += 1
mod = -1 latitude = hass.config.latitude
while True: longitude = hass.config.longitude
next_midnight = (astral.solar_midnight_utc(
utc_today + timedelta(days=mod), longitude))
if next_midnight > utc_now:
break
mod += 1
mod = -1 dawn = astral.dawn_utc(utc_today, latitude, longitude)
while True: dusk = astral.dusk_utc(utc_today, latitude, longitude)
next_noon = (astral.solar_noon_utc( midnight = astral.solar_midnight_utc(utc_today, longitude)
utc_today + timedelta(days=mod), longitude)) noon = astral.solar_noon_utc(utc_today, longitude)
if next_noon > utc_now: sunrise = astral.sunrise_utc(utc_today, latitude, longitude)
break sunset = astral.sunset_utc(utc_today, latitude, longitude)
mod += 1
mod = -1
while True:
next_rising = (astral.sunrise_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_rising > utc_now:
break
mod += 1
mod = -1
while True:
next_setting = (astral.sunset_utc(
utc_today + timedelta(days=mod), latitude, longitude))
if next_setting > utc_now:
break
mod += 1
with patch('homeassistant.helpers.condition.dt_util.utcnow',
return_value=utc_now):
assert next_dawn == sun.get_astral_event_next(
self.hass, 'dawn')
assert next_dusk == sun.get_astral_event_next(
self.hass, 'dusk')
assert next_midnight == sun.get_astral_event_next(
self.hass, 'solar_midnight')
assert next_noon == sun.get_astral_event_next(
self.hass, 'solar_noon')
assert next_rising == sun.get_astral_event_next(
self.hass, SUN_EVENT_SUNRISE)
assert next_setting == sun.get_astral_event_next(
self.hass, SUN_EVENT_SUNSET)
def test_date_events(self):
"""Test retrieving next sun events."""
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
from astral import Astral
astral = Astral()
utc_today = utc_now.date()
latitude = self.hass.config.latitude
longitude = self.hass.config.longitude
dawn = astral.dawn_utc(utc_today, latitude, longitude)
dusk = astral.dusk_utc(utc_today, latitude, longitude)
midnight = astral.solar_midnight_utc(utc_today, longitude)
noon = astral.solar_noon_utc(utc_today, longitude)
sunrise = astral.sunrise_utc(utc_today, latitude, longitude)
sunset = astral.sunset_utc(utc_today, latitude, longitude)
with patch('homeassistant.util.dt.now', return_value=utc_now):
assert dawn == sun.get_astral_event_date( assert dawn == sun.get_astral_event_date(
self.hass, 'dawn', utc_today) hass, 'dawn', utc_today)
assert dusk == sun.get_astral_event_date( assert dusk == sun.get_astral_event_date(
self.hass, 'dusk', utc_today) hass, 'dusk', utc_today)
assert midnight == sun.get_astral_event_date( assert midnight == sun.get_astral_event_date(
self.hass, 'solar_midnight', utc_today) hass, 'solar_midnight', utc_today)
assert noon == sun.get_astral_event_date( assert noon == sun.get_astral_event_date(
self.hass, 'solar_noon', utc_today) hass, 'solar_noon', utc_today)
assert sunrise == sun.get_astral_event_date( assert sunrise == sun.get_astral_event_date(
self.hass, SUN_EVENT_SUNRISE, utc_today) hass, SUN_EVENT_SUNRISE, utc_today)
assert sunset == sun.get_astral_event_date( assert sunset == sun.get_astral_event_date(
self.hass, SUN_EVENT_SUNSET, utc_today) hass, SUN_EVENT_SUNSET, utc_today)
def test_date_events_default_date(self):
"""Test retrieving next sun events."""
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
from astral import Astral
astral = Astral() def test_date_events_accepts_datetime(hass):
utc_today = utc_now.date() """Test retrieving next sun events."""
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
from astral import Astral
latitude = self.hass.config.latitude astral = Astral()
longitude = self.hass.config.longitude utc_today = utc_now.date()
dawn = astral.dawn_utc(utc_today, latitude, longitude) latitude = hass.config.latitude
dusk = astral.dusk_utc(utc_today, latitude, longitude) longitude = hass.config.longitude
midnight = astral.solar_midnight_utc(utc_today, longitude)
noon = astral.solar_noon_utc(utc_today, longitude)
sunrise = astral.sunrise_utc(utc_today, latitude, longitude)
sunset = astral.sunset_utc(utc_today, latitude, longitude)
with patch('homeassistant.util.dt.now', return_value=utc_now): dawn = astral.dawn_utc(utc_today, latitude, longitude)
assert dawn == sun.get_astral_event_date( dusk = astral.dusk_utc(utc_today, latitude, longitude)
self.hass, 'dawn', utc_today) midnight = astral.solar_midnight_utc(utc_today, longitude)
assert dusk == sun.get_astral_event_date( noon = astral.solar_noon_utc(utc_today, longitude)
self.hass, 'dusk', utc_today) sunrise = astral.sunrise_utc(utc_today, latitude, longitude)
assert midnight == sun.get_astral_event_date( sunset = astral.sunset_utc(utc_today, latitude, longitude)
self.hass, 'solar_midnight', utc_today)
assert noon == sun.get_astral_event_date(
self.hass, 'solar_noon', utc_today)
assert sunrise == sun.get_astral_event_date(
self.hass, SUN_EVENT_SUNRISE, utc_today)
assert sunset == sun.get_astral_event_date(
self.hass, SUN_EVENT_SUNSET, utc_today)
def test_date_events_accepts_datetime(self): assert dawn == sun.get_astral_event_date(
"""Test retrieving next sun events.""" hass, 'dawn', utc_now)
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC) assert dusk == sun.get_astral_event_date(
from astral import Astral hass, 'dusk', utc_now)
assert midnight == sun.get_astral_event_date(
hass, 'solar_midnight', utc_now)
assert noon == sun.get_astral_event_date(
hass, 'solar_noon', utc_now)
assert sunrise == sun.get_astral_event_date(
hass, SUN_EVENT_SUNRISE, utc_now)
assert sunset == sun.get_astral_event_date(
hass, SUN_EVENT_SUNSET, utc_now)
astral = Astral()
utc_today = utc_now.date()
latitude = self.hass.config.latitude def test_is_up(hass):
longitude = self.hass.config.longitude """Test retrieving next sun events."""
utc_now = datetime(2016, 11, 1, 12, 0, 0, tzinfo=dt_util.UTC)
with patch('homeassistant.helpers.condition.dt_util.utcnow',
return_value=utc_now):
assert not sun.is_up(hass)
dawn = astral.dawn_utc(utc_today, latitude, longitude) utc_now = datetime(2016, 11, 1, 18, 0, 0, tzinfo=dt_util.UTC)
dusk = astral.dusk_utc(utc_today, latitude, longitude) with patch('homeassistant.helpers.condition.dt_util.utcnow',
midnight = astral.solar_midnight_utc(utc_today, longitude) return_value=utc_now):
noon = astral.solar_noon_utc(utc_today, longitude) assert sun.is_up(hass)
sunrise = astral.sunrise_utc(utc_today, latitude, longitude)
sunset = astral.sunset_utc(utc_today, latitude, longitude)
assert dawn == sun.get_astral_event_date(
self.hass, 'dawn', utc_now)
assert dusk == sun.get_astral_event_date(
self.hass, 'dusk', utc_now)
assert midnight == sun.get_astral_event_date(
self.hass, 'solar_midnight', utc_now)
assert noon == sun.get_astral_event_date(
self.hass, 'solar_noon', utc_now)
assert sunrise == sun.get_astral_event_date(
self.hass, SUN_EVENT_SUNRISE, utc_now)
assert sunset == sun.get_astral_event_date(
self.hass, SUN_EVENT_SUNSET, utc_now)
def test_is_up(self): def test_norway_in_june(hass):
"""Test retrieving next sun events.""" """Test location in Norway where the sun doesn't set in summer."""
utc_now = datetime(2016, 11, 1, 12, 0, 0, tzinfo=dt_util.UTC) hass.config.latitude = 69.6
with patch('homeassistant.helpers.condition.dt_util.utcnow', hass.config.longitude = 18.8
return_value=utc_now):
assert not sun.is_up(self.hass)
utc_now = datetime(2016, 11, 1, 18, 0, 0, tzinfo=dt_util.UTC) june = datetime(2016, 6, 1, tzinfo=dt_util.UTC)
with patch('homeassistant.helpers.condition.dt_util.utcnow',
return_value=utc_now):
assert sun.is_up(self.hass)
def test_norway_in_june(self): print(sun.get_astral_event_date(hass, SUN_EVENT_SUNRISE,
"""Test location in Norway where the sun doesn't set in summer.""" datetime(2017, 7, 25)))
self.hass.config.latitude = 69.6 print(sun.get_astral_event_date(hass, SUN_EVENT_SUNSET,
self.hass.config.longitude = 18.8 datetime(2017, 7, 25)))
june = datetime(2016, 6, 1, tzinfo=dt_util.UTC) print(sun.get_astral_event_date(hass, SUN_EVENT_SUNRISE,
datetime(2017, 7, 26)))
print(sun.get_astral_event_date(hass, SUN_EVENT_SUNSET,
datetime(2017, 7, 26)))
print(sun.get_astral_event_date(self.hass, SUN_EVENT_SUNRISE, assert sun.get_astral_event_next(hass, SUN_EVENT_SUNRISE, june) \
datetime(2017, 7, 25))) == datetime(2016, 7, 25, 23, 23, 39, tzinfo=dt_util.UTC)
print(sun.get_astral_event_date(self.hass, SUN_EVENT_SUNSET, assert sun.get_astral_event_next(hass, SUN_EVENT_SUNSET, june) \
datetime(2017, 7, 25))) == datetime(2016, 7, 26, 22, 19, 1, tzinfo=dt_util.UTC)
assert sun.get_astral_event_date(hass, SUN_EVENT_SUNRISE, june) \
print(sun.get_astral_event_date(self.hass, SUN_EVENT_SUNRISE, is None
datetime(2017, 7, 26))) assert sun.get_astral_event_date(hass, SUN_EVENT_SUNSET, june) \
print(sun.get_astral_event_date(self.hass, SUN_EVENT_SUNSET, is None
datetime(2017, 7, 26)))
assert sun.get_astral_event_next(self.hass, SUN_EVENT_SUNRISE, june) \
== datetime(2016, 7, 25, 23, 23, 39, tzinfo=dt_util.UTC)
assert sun.get_astral_event_next(self.hass, SUN_EVENT_SUNSET, june) \
== datetime(2016, 7, 26, 22, 19, 1, tzinfo=dt_util.UTC)
assert sun.get_astral_event_date(self.hass, SUN_EVENT_SUNRISE, june) \
is None
assert sun.get_astral_event_date(self.hass, SUN_EVENT_SUNSET, june) \
is None

View File

@ -1,50 +1,34 @@
"""Tests Home Assistant temperature helpers.""" """Tests Home Assistant temperature helpers."""
import unittest import pytest
from tests.common import get_test_home_assistant
from homeassistant.const import ( from homeassistant.const import (
TEMP_CELSIUS, PRECISION_WHOLE, TEMP_FAHRENHEIT, PRECISION_HALVES, TEMP_CELSIUS, PRECISION_WHOLE, TEMP_FAHRENHEIT, PRECISION_HALVES,
PRECISION_TENTHS) PRECISION_TENTHS)
from homeassistant.helpers.temperature import display_temp from homeassistant.helpers.temperature import display_temp
from homeassistant.util.unit_system import METRIC_SYSTEM
import pytest
TEMP = 24.636626 TEMP = 24.636626
class TestHelpersTemperature(unittest.TestCase): def test_temperature_not_a_number(hass):
"""Set up the temperature tests.""" """Test that temperature is a number."""
temp = "Temperature"
with pytest.raises(Exception) as exception:
display_temp(hass, temp, TEMP_CELSIUS, PRECISION_HALVES)
def setUp(self): assert "Temperature is not a number: {}".format(temp) \
"""Set up the tests.""" in str(exception)
self.hass = get_test_home_assistant()
self.hass.config.unit_system = METRIC_SYSTEM
def tearDown(self):
"""Stop down stuff we started."""
self.hass.stop()
def test_temperature_not_a_number(self): def test_celsius_halves(hass):
"""Test that temperature is a number.""" """Test temperature to celsius rounding to halves."""
temp = "Temperature" assert display_temp(hass, TEMP, TEMP_CELSIUS, PRECISION_HALVES) == 24.5
with pytest.raises(Exception) as exception:
display_temp(self.hass, temp, TEMP_CELSIUS, PRECISION_HALVES)
assert "Temperature is not a number: {}".format(temp) \
in str(exception)
def test_celsius_halves(self): def test_celsius_tenths(hass):
"""Test temperature to celsius rounding to halves.""" """Test temperature to celsius rounding to tenths."""
assert 24.5 == display_temp( assert display_temp(hass, TEMP, TEMP_CELSIUS, PRECISION_TENTHS) == 24.6
self.hass, TEMP, TEMP_CELSIUS, PRECISION_HALVES)
def test_celsius_tenths(self):
"""Test temperature to celsius rounding to tenths."""
assert 24.6 == display_temp(
self.hass, TEMP, TEMP_CELSIUS, PRECISION_TENTHS)
def test_fahrenheit_wholes(self): def test_fahrenheit_wholes(hass):
"""Test temperature to fahrenheit rounding to wholes.""" """Test temperature to fahrenheit rounding to wholes."""
assert -4 == display_temp( assert display_temp(hass, TEMP, TEMP_FAHRENHEIT, PRECISION_WHOLE) == -4
self.hass, TEMP, TEMP_FAHRENHEIT, PRECISION_WHOLE)

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,6 @@
"""Test check_config script.""" """Test check_config script."""
import asyncio
import logging import logging
import os # noqa: F401 pylint: disable=unused-import import os # noqa: F401 pylint: disable=unused-import
import unittest
from unittest.mock import patch from unittest.mock import patch
import homeassistant.scripts.check_config as check_config import homeassistant.scripts.check_config as check_config
@ -36,149 +34,138 @@ def normalize_yaml_files(check_dict):
for key in sorted(check_dict['yaml_files'].keys())] for key in sorted(check_dict['yaml_files'].keys())]
# pylint: disable=unsubscriptable-object # pylint: disable=no-self-use,invalid-name
class TestCheckConfig(unittest.TestCase): @patch('os.path.isfile', return_value=True)
"""Tests for the homeassistant.scripts.check_config module.""" def test_bad_core_config(isfile_patch, loop):
"""Test a bad core config setup."""
files = {
YAML_CONFIG_FILE: BAD_CORE_CONFIG,
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res['except'].keys() == {'homeassistant'}
assert res['except']['homeassistant'][1] == {'unit_system': 'bad'}
def setUp(self):
"""Prepare the test."""
# Somewhere in the tests our event loop gets killed,
# this ensures we have one.
try:
asyncio.get_event_loop()
except RuntimeError:
asyncio.set_event_loop(asyncio.new_event_loop())
# Will allow seeing full diff @patch('os.path.isfile', return_value=True)
self.maxDiff = None # pylint: disable=invalid-name def test_config_platform_valid(isfile_patch, loop):
"""Test a valid platform setup."""
files = {
YAML_CONFIG_FILE: BASE_CONFIG + 'light:\n platform: demo',
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res['components'].keys() == {'homeassistant', 'light'}
assert res['components']['light'] == [{'platform': 'demo'}]
assert res['except'] == {}
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
# pylint: disable=no-self-use,invalid-name
@patch('os.path.isfile', return_value=True) @patch('os.path.isfile', return_value=True)
def test_bad_core_config(self, isfile_patch): def test_component_platform_not_found(isfile_patch, loop):
"""Test a bad core config setup.""" """Test errors if component or platform not found."""
files = { # Make sure they don't exist
YAML_CONFIG_FILE: BAD_CORE_CONFIG, files = {
YAML_CONFIG_FILE: BASE_CONFIG + 'beer:',
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res['components'].keys() == {'homeassistant'}
assert res['except'] == {
check_config.ERROR_STR: ['Integration not found: beer']}
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
files = {
YAML_CONFIG_FILE: BASE_CONFIG + 'light:\n platform: beer',
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res['components'].keys() == {'homeassistant', 'light'}
assert res['components']['light'] == []
assert res['except'] == {
check_config.ERROR_STR: [
'Integration beer not found when trying to verify its '
'light platform.',
]}
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
@patch('os.path.isfile', return_value=True)
def test_secrets(isfile_patch, loop):
"""Test secrets config checking method."""
secrets_path = get_test_config_dir('secrets.yaml')
files = {
get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG + (
'http:\n'
' api_password: !secret http_pw'),
secrets_path: (
'logger: debug\n'
'http_pw: abc123'),
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir(), True)
assert res['except'] == {}
assert res['components'].keys() == {'homeassistant', 'http'}
assert res['components']['http'] == {
'api_password': 'abc123',
'cors_allowed_origins': [],
'ip_ban_enabled': True,
'login_attempts_threshold': -1,
'server_host': '0.0.0.0',
'server_port': 8123,
'trusted_networks': [],
'ssl_profile': 'modern',
} }
with patch_yaml_files(files): assert res['secret_cache'] == {secrets_path: {'http_pw': 'abc123'}}
res = check_config.check(get_test_config_dir()) assert res['secrets'] == {'http_pw': 'abc123'}
assert res['except'].keys() == {'homeassistant'} assert normalize_yaml_files(res) == [
assert res['except']['homeassistant'][1] == {'unit_system': 'bad'} '.../configuration.yaml', '.../secrets.yaml']
@patch('os.path.isfile', return_value=True)
def test_config_platform_valid(self, isfile_patch):
"""Test a valid platform setup."""
files = {
YAML_CONFIG_FILE: BASE_CONFIG + 'light:\n platform: demo',
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res['components'].keys() == {'homeassistant', 'light'}
assert res['components']['light'] == [{'platform': 'demo'}]
assert res['except'] == {}
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
@patch('os.path.isfile', return_value=True) @patch('os.path.isfile', return_value=True)
def test_component_platform_not_found(self, isfile_patch): def test_package_invalid(isfile_patch, loop):
"""Test errors if component or platform not found.""" """Test a valid platform setup."""
# Make sure they don't exist files = {
files = { YAML_CONFIG_FILE: BASE_CONFIG + (
YAML_CONFIG_FILE: BASE_CONFIG + 'beer:', ' packages:\n'
} ' p1:\n'
with patch_yaml_files(files): ' group: ["a"]'),
res = check_config.check(get_test_config_dir()) }
assert res['components'].keys() == {'homeassistant'} with patch_yaml_files(files):
assert res['except'] == { res = check_config.check(get_test_config_dir())
check_config.ERROR_STR: ['Integration not found: beer']}
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
files = { assert res['except'].keys() == {'homeassistant.packages.p1.group'}
YAML_CONFIG_FILE: BASE_CONFIG + 'light:\n platform: beer', assert res['except']['homeassistant.packages.p1.group'][1] == \
} {'group': ['a']}
with patch_yaml_files(files): assert len(res['except']) == 1
res = check_config.check(get_test_config_dir()) assert res['components'].keys() == {'homeassistant'}
assert res['components'].keys() == {'homeassistant', 'light'} assert len(res['components']) == 1
assert res['components']['light'] == [] assert res['secret_cache'] == {}
assert res['except'] == { assert res['secrets'] == {}
check_config.ERROR_STR: [ assert len(res['yaml_files']) == 1
'Integration beer not found when trying to verify its '
'light platform.',
]}
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
@patch('os.path.isfile', return_value=True)
def test_secrets(self, isfile_patch):
"""Test secrets config checking method."""
secrets_path = get_test_config_dir('secrets.yaml')
files = { def test_bootstrap_error(loop):
get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG + ( """Test a valid platform setup."""
'http:\n' files = {
' api_password: !secret http_pw'), YAML_CONFIG_FILE: BASE_CONFIG + 'automation: !include no.yaml',
secrets_path: ( }
'logger: debug\n' with patch_yaml_files(files):
'http_pw: abc123'), res = check_config.check(get_test_config_dir(YAML_CONFIG_FILE))
} err = res['except'].pop(check_config.ERROR_STR)
assert len(err) == 1
with patch_yaml_files(files): assert res['except'] == {}
assert res['components'] == {} # No components, load failed
res = check_config.check(get_test_config_dir(), True) assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert res['except'] == {} assert res['yaml_files'] == {}
assert res['components'].keys() == {'homeassistant', 'http'}
assert res['components']['http'] == {
'api_password': 'abc123',
'cors_allowed_origins': [],
'ip_ban_enabled': True,
'login_attempts_threshold': -1,
'server_host': '0.0.0.0',
'server_port': 8123,
'trusted_networks': [],
'ssl_profile': 'modern',
}
assert res['secret_cache'] == {secrets_path: {'http_pw': 'abc123'}}
assert res['secrets'] == {'http_pw': 'abc123'}
assert normalize_yaml_files(res) == [
'.../configuration.yaml', '.../secrets.yaml']
@patch('os.path.isfile', return_value=True)
def test_package_invalid(self, isfile_patch):
"""Test a valid platform setup."""
files = {
YAML_CONFIG_FILE: BASE_CONFIG + (
' packages:\n'
' p1:\n'
' group: ["a"]'),
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res['except'].keys() == {'homeassistant.packages.p1.group'}
assert res['except']['homeassistant.packages.p1.group'][1] == \
{'group': ['a']}
assert len(res['except']) == 1
assert res['components'].keys() == {'homeassistant'}
assert len(res['components']) == 1
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert len(res['yaml_files']) == 1
def test_bootstrap_error(self):
"""Test a valid platform setup."""
files = {
YAML_CONFIG_FILE: BASE_CONFIG + 'automation: !include no.yaml',
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir(YAML_CONFIG_FILE))
err = res['except'].pop(check_config.ERROR_STR)
assert len(err) == 1
assert res['except'] == {}
assert res['components'] == {} # No components, load failed
assert res['secret_cache'] == {}
assert res['secrets'] == {}
assert res['yaml_files'] == {}

View File

@ -1,19 +1,15 @@
"""Test script init.""" """Test script init."""
import unittest
from unittest.mock import patch from unittest.mock import patch
import homeassistant.scripts as scripts import homeassistant.scripts as scripts
class TestScripts(unittest.TestCase): @patch('homeassistant.scripts.get_default_config_dir',
"""Tests homeassistant.scripts module.""" return_value='/default')
def test_config_per_platform(mock_def):
@patch('homeassistant.scripts.get_default_config_dir', """Test config per platform method."""
return_value='/default') assert scripts.get_default_config_dir() == '/default'
def test_config_per_platform(self, mock_def): assert scripts.extract_config_dir() == '/default'
"""Test config per platform method.""" assert scripts.extract_config_dir(['']) == '/default'
self.assertEqual(scripts.get_default_config_dir(), '/default') assert scripts.extract_config_dir(['-c', '/arg']) == '/arg'
self.assertEqual(scripts.extract_config_dir(), '/default') assert scripts.extract_config_dir(['--config', '/a']) == '/a'
self.assertEqual(scripts.extract_config_dir(['']), '/default')
self.assertEqual(scripts.extract_config_dir(['-c', '/arg']), '/arg')
self.assertEqual(scripts.extract_config_dir(['--config', '/a']), '/a')

View File

@ -2,7 +2,6 @@
# pylint: disable=protected-access # pylint: disable=protected-access
import asyncio import asyncio
import os import os
import unittest
import unittest.mock as mock import unittest.mock as mock
from collections import OrderedDict from collections import OrderedDict
from ipaddress import ip_network from ipaddress import ip_network
@ -23,7 +22,6 @@ from homeassistant.const import (
CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES) CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES)
from homeassistant.util import location as location_util, dt as dt_util from homeassistant.util import location as location_util, dt as dt_util
from homeassistant.util.yaml import SECRET_YAML from homeassistant.util.yaml import SECRET_YAML
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.components.config.group import ( from homeassistant.components.config.group import (
CONFIG_PATH as GROUP_CONFIG_PATH) CONFIG_PATH as GROUP_CONFIG_PATH)
@ -36,7 +34,7 @@ from homeassistant.components.config.customize import (
import homeassistant.scripts.check_config as check_config import homeassistant.scripts.check_config as check_config
from tests.common import ( from tests.common import (
get_test_config_dir, get_test_home_assistant, patch_yaml_files) get_test_config_dir, patch_yaml_files)
CONFIG_DIR = get_test_config_dir() CONFIG_DIR = get_test_config_dir()
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE) YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
@ -55,512 +53,508 @@ def create_file(path):
pass pass
class TestConfig(unittest.TestCase): def teardown():
"""Test the configutils.""" """Clean up."""
dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE
# pylint: disable=invalid-name if os.path.isfile(YAML_PATH):
def setUp(self): os.remove(YAML_PATH)
"""Initialize a test Home Assistant instance."""
self.hass = get_test_home_assistant()
# pylint: disable=invalid-name if os.path.isfile(SECRET_PATH):
def tearDown(self): os.remove(SECRET_PATH)
"""Clean up."""
dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE
if os.path.isfile(YAML_PATH): if os.path.isfile(VERSION_PATH):
os.remove(YAML_PATH) os.remove(VERSION_PATH)
if os.path.isfile(SECRET_PATH): if os.path.isfile(GROUP_PATH):
os.remove(SECRET_PATH) os.remove(GROUP_PATH)
if os.path.isfile(VERSION_PATH): if os.path.isfile(AUTOMATIONS_PATH):
os.remove(VERSION_PATH) os.remove(AUTOMATIONS_PATH)
if os.path.isfile(GROUP_PATH): if os.path.isfile(SCRIPTS_PATH):
os.remove(GROUP_PATH) os.remove(SCRIPTS_PATH)
if os.path.isfile(AUTOMATIONS_PATH): if os.path.isfile(CUSTOMIZE_PATH):
os.remove(AUTOMATIONS_PATH) os.remove(CUSTOMIZE_PATH)
if os.path.isfile(SCRIPTS_PATH):
os.remove(SCRIPTS_PATH)
if os.path.isfile(CUSTOMIZE_PATH): def test_create_default_config():
os.remove(CUSTOMIZE_PATH) """Test creation of default config."""
config_util.create_default_config(CONFIG_DIR, False)
self.hass.stop() assert os.path.isfile(YAML_PATH)
assert os.path.isfile(SECRET_PATH)
assert os.path.isfile(VERSION_PATH)
assert os.path.isfile(GROUP_PATH)
assert os.path.isfile(AUTOMATIONS_PATH)
assert os.path.isfile(CUSTOMIZE_PATH)
# pylint: disable=no-self-use
def test_create_default_config(self):
"""Test creation of default config."""
config_util.create_default_config(CONFIG_DIR, False)
assert os.path.isfile(YAML_PATH) def test_find_config_file_yaml():
assert os.path.isfile(SECRET_PATH) """Test if it finds a YAML config file."""
assert os.path.isfile(VERSION_PATH) create_file(YAML_PATH)
assert os.path.isfile(GROUP_PATH)
assert os.path.isfile(AUTOMATIONS_PATH)
assert os.path.isfile(CUSTOMIZE_PATH)
def test_find_config_file_yaml(self): assert YAML_PATH == config_util.find_config_file(CONFIG_DIR)
"""Test if it finds a YAML config file."""
create_file(YAML_PATH)
assert YAML_PATH == config_util.find_config_file(CONFIG_DIR)
@mock.patch('builtins.print') @mock.patch('builtins.print')
def test_ensure_config_exists_creates_config(self, mock_print): def test_ensure_config_exists_creates_config(mock_print):
"""Test that calling ensure_config_exists. """Test that calling ensure_config_exists.
If not creates a new config file. If not creates a new config file.
""" """
config_util.ensure_config_exists(CONFIG_DIR, False) config_util.ensure_config_exists(CONFIG_DIR, False)
assert os.path.isfile(YAML_PATH) assert os.path.isfile(YAML_PATH)
assert mock_print.called assert mock_print.called
def test_ensure_config_exists_uses_existing_config(self):
"""Test that calling ensure_config_exists uses existing config."""
create_file(YAML_PATH)
config_util.ensure_config_exists(CONFIG_DIR, False)
with open(YAML_PATH) as f: def test_ensure_config_exists_uses_existing_config():
content = f.read() """Test that calling ensure_config_exists uses existing config."""
create_file(YAML_PATH)
config_util.ensure_config_exists(CONFIG_DIR, False)
# File created with create_file are empty with open(YAML_PATH) as f:
assert '' == content content = f.read()
def test_load_yaml_config_converts_empty_files_to_dict(self): # File created with create_file are empty
"""Test that loading an empty file returns an empty dict.""" assert content == ''
create_file(YAML_PATH)
assert isinstance(config_util.load_yaml_config_file(YAML_PATH), dict)
def test_load_yaml_config_raises_error_if_not_dict(self): def test_load_yaml_config_converts_empty_files_to_dict():
"""Test error raised when YAML file is not a dict.""" """Test that loading an empty file returns an empty dict."""
with open(YAML_PATH, 'w') as f: create_file(YAML_PATH)
f.write('5')
with pytest.raises(HomeAssistantError): assert isinstance(config_util.load_yaml_config_file(YAML_PATH), dict)
config_util.load_yaml_config_file(YAML_PATH)
def test_load_yaml_config_raises_error_if_malformed_yaml(self):
"""Test error raised if invalid YAML."""
with open(YAML_PATH, 'w') as f:
f.write(':')
with pytest.raises(HomeAssistantError): def test_load_yaml_config_raises_error_if_not_dict():
config_util.load_yaml_config_file(YAML_PATH) """Test error raised when YAML file is not a dict."""
with open(YAML_PATH, 'w') as f:
f.write('5')
def test_load_yaml_config_raises_error_if_unsafe_yaml(self): with pytest.raises(HomeAssistantError):
"""Test error raised if unsafe YAML.""" config_util.load_yaml_config_file(YAML_PATH)
with open(YAML_PATH, 'w') as f:
f.write('hello: !!python/object/apply:os.system')
with pytest.raises(HomeAssistantError):
config_util.load_yaml_config_file(YAML_PATH)
def test_load_yaml_config_preserves_key_order(self): def test_load_yaml_config_raises_error_if_malformed_yaml():
"""Test removal of library.""" """Test error raised if invalid YAML."""
with open(YAML_PATH, 'w') as f: with open(YAML_PATH, 'w') as f:
f.write('hello: 2\n') f.write(':')
f.write('world: 1\n')
assert [('hello', 2), ('world', 1)] == \ with pytest.raises(HomeAssistantError):
list(config_util.load_yaml_config_file(YAML_PATH).items()) config_util.load_yaml_config_file(YAML_PATH)
@mock.patch('homeassistant.util.location.detect_location_info',
return_value=location_util.LocationInfo(
'0.0.0.0', 'US', 'United States', 'CA', 'California',
'San Diego', '92122', 'America/Los_Angeles', 32.8594,
-117.2073, True))
@mock.patch('homeassistant.util.location.elevation', return_value=101)
@mock.patch('builtins.print')
def test_create_default_config_detect_location(self, mock_detect,
mock_elev, mock_print):
"""Test that detect location sets the correct config keys."""
config_util.ensure_config_exists(CONFIG_DIR)
config = config_util.load_yaml_config_file(YAML_PATH) def test_load_yaml_config_raises_error_if_unsafe_yaml():
"""Test error raised if unsafe YAML."""
with open(YAML_PATH, 'w') as f:
f.write('hello: !!python/object/apply:os.system')
assert DOMAIN in config with pytest.raises(HomeAssistantError):
config_util.load_yaml_config_file(YAML_PATH)
ha_conf = config[DOMAIN]
expected_values = { def test_load_yaml_config_preserves_key_order():
CONF_LATITUDE: 32.8594, """Test removal of library."""
CONF_LONGITUDE: -117.2073, with open(YAML_PATH, 'w') as f:
CONF_ELEVATION: 101, f.write('hello: 2\n')
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, f.write('world: 1\n')
CONF_NAME: 'Home',
CONF_TIME_ZONE: 'America/Los_Angeles',
CONF_CUSTOMIZE: OrderedDict(),
}
assert expected_values == ha_conf assert [('hello', 2), ('world', 1)] == \
assert mock_print.called list(config_util.load_yaml_config_file(YAML_PATH).items())
@mock.patch('builtins.print')
def test_create_default_config_returns_none_if_write_error(self,
mock_print):
"""Test the writing of a default configuration.
Non existing folder returns None. @mock.patch('homeassistant.util.location.detect_location_info',
""" return_value=location_util.LocationInfo(
assert config_util.create_default_config( '0.0.0.0', 'US', 'United States', 'CA', 'California',
os.path.join(CONFIG_DIR, 'non_existing_dir/'), False) is None 'San Diego', '92122', 'America/Los_Angeles', 32.8594,
assert mock_print.called -117.2073, True))
@mock.patch('homeassistant.util.location.elevation', return_value=101)
@mock.patch('builtins.print')
def test_create_default_config_detect_location(mock_detect,
mock_elev, mock_print):
"""Test that detect location sets the correct config keys."""
config_util.ensure_config_exists(CONFIG_DIR)
# pylint: disable=no-self-use config = config_util.load_yaml_config_file(YAML_PATH)
def test_core_config_schema(self):
"""Test core config schema."""
for value in (
{CONF_UNIT_SYSTEM: 'K'},
{'time_zone': 'non-exist'},
{'latitude': '91'},
{'longitude': -181},
{'customize': 'bla'},
{'customize': {'light.sensor': 100}},
{'customize': {'entity_id': []}},
):
with pytest.raises(MultipleInvalid):
config_util.CORE_CONFIG_SCHEMA(value)
config_util.CORE_CONFIG_SCHEMA({ assert DOMAIN in config
'name': 'Test name',
'latitude': '-23.45', ha_conf = config[DOMAIN]
'longitude': '123.45',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, expected_values = {
'customize': { CONF_LATITUDE: 32.8594,
'sensor.temperature': { CONF_LONGITUDE: -117.2073,
'hidden': True, CONF_ELEVATION: 101,
}, CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
CONF_NAME: 'Home',
CONF_TIME_ZONE: 'America/Los_Angeles',
CONF_CUSTOMIZE: OrderedDict(),
}
assert expected_values == ha_conf
assert mock_print.called
@mock.patch('builtins.print')
def test_create_default_config_returns_none_if_write_error(mock_print):
"""Test the writing of a default configuration.
Non existing folder returns None.
"""
assert config_util.create_default_config(
os.path.join(CONFIG_DIR, 'non_existing_dir/'), False) is None
assert mock_print.called
def test_core_config_schema():
"""Test core config schema."""
for value in (
{CONF_UNIT_SYSTEM: 'K'},
{'time_zone': 'non-exist'},
{'latitude': '91'},
{'longitude': -181},
{'customize': 'bla'},
{'customize': {'light.sensor': 100}},
{'customize': {'entity_id': []}},
):
with pytest.raises(MultipleInvalid):
config_util.CORE_CONFIG_SCHEMA(value)
config_util.CORE_CONFIG_SCHEMA({
'name': 'Test name',
'latitude': '-23.45',
'longitude': '123.45',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
'customize': {
'sensor.temperature': {
'hidden': True,
}, },
},
})
def test_customize_dict_schema():
"""Test basic customize config validation."""
values = (
{ATTR_FRIENDLY_NAME: None},
{ATTR_HIDDEN: '2'},
{ATTR_ASSUMED_STATE: '2'},
)
for val in values:
print(val)
with pytest.raises(MultipleInvalid):
config_util.CUSTOMIZE_DICT_SCHEMA(val)
assert config_util.CUSTOMIZE_DICT_SCHEMA({
ATTR_FRIENDLY_NAME: 2,
ATTR_HIDDEN: '1',
ATTR_ASSUMED_STATE: '0',
}) == {
ATTR_FRIENDLY_NAME: '2',
ATTR_HIDDEN: True,
ATTR_ASSUMED_STATE: False
}
def test_customize_glob_is_ordered():
"""Test that customize_glob preserves order."""
conf = config_util.CORE_CONFIG_SCHEMA(
{'customize_glob': OrderedDict()})
assert isinstance(conf['customize_glob'], OrderedDict)
async def _compute_state(hass, config):
await config_util.async_process_ha_core_config(hass, config)
entity = Entity()
entity.entity_id = 'test.test'
entity.hass = hass
entity.schedule_update_ha_state()
await hass.async_block_till_done()
return hass.states.get('test.test')
async def test_entity_customization(hass):
"""Test entity customization through configuration."""
config = {CONF_LATITUDE: 50,
CONF_LONGITUDE: 50,
CONF_NAME: 'Test',
CONF_CUSTOMIZE: {'test.test': {'hidden': True}}}
state = await _compute_state(hass, config)
assert state.attributes['hidden']
@mock.patch('homeassistant.config.shutil')
@mock.patch('homeassistant.config.os')
def test_remove_lib_on_upgrade(mock_os, mock_shutil, hass):
"""Test removal of library on upgrade from before 0.50."""
ha_version = '0.49.0'
mock_os.path.isdir = mock.Mock(return_value=True)
mock_open = mock.mock_open()
with mock.patch('homeassistant.config.open', mock_open, create=True):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
hass.config.path = mock.Mock()
config_util.process_ha_config_upgrade(hass)
hass_path = hass.config.path.return_value
assert mock_os.path.isdir.call_count == 1
assert mock_os.path.isdir.call_args == mock.call(hass_path)
assert mock_shutil.rmtree.call_count == 1
assert mock_shutil.rmtree.call_args == mock.call(hass_path)
def test_process_config_upgrade(hass):
"""Test update of version on upgrade."""
ha_version = '0.92.0'
mock_open = mock.mock_open()
with mock.patch('homeassistant.config.open', mock_open, create=True), \
mock.patch.object(config_util, '__version__', '0.91.0'):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
config_util.process_ha_config_upgrade(hass)
assert opened_file.write.call_count == 1
assert opened_file.write.call_args == mock.call('0.91.0')
def test_config_upgrade_same_version(hass):
"""Test no update of version on no upgrade."""
ha_version = __version__
mock_open = mock.mock_open()
with mock.patch('homeassistant.config.open', mock_open, create=True):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
config_util.process_ha_config_upgrade(hass)
assert opened_file.write.call_count == 0
@mock.patch('homeassistant.config.find_config_file', mock.Mock())
def test_config_upgrade_no_file(hass):
"""Test update of version on upgrade, with no version file."""
mock_open = mock.mock_open()
mock_open.side_effect = [FileNotFoundError(),
mock.DEFAULT,
mock.DEFAULT]
with mock.patch('homeassistant.config.open', mock_open, create=True):
opened_file = mock_open.return_value
# pylint: disable=no-member
config_util.process_ha_config_upgrade(hass)
assert opened_file.write.call_count == 1
assert opened_file.write.call_args == mock.call(__version__)
@mock.patch('homeassistant.config.shutil')
@mock.patch('homeassistant.config.os')
@mock.patch('homeassistant.config.find_config_file', mock.Mock())
def test_migrate_file_on_upgrade(mock_os, mock_shutil, hass):
"""Test migrate of config files on upgrade."""
ha_version = '0.7.0'
mock_os.path.isdir = mock.Mock(return_value=True)
mock_open = mock.mock_open()
def _mock_isfile(filename):
return True
with mock.patch('homeassistant.config.open', mock_open, create=True), \
mock.patch('homeassistant.config.os.path.isfile', _mock_isfile):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
hass.config.path = mock.Mock()
config_util.process_ha_config_upgrade(hass)
assert mock_os.rename.call_count == 1
@mock.patch('homeassistant.config.shutil')
@mock.patch('homeassistant.config.os')
@mock.patch('homeassistant.config.find_config_file', mock.Mock())
def test_migrate_no_file_on_upgrade(mock_os, mock_shutil, hass):
"""Test not migrating config files on upgrade."""
ha_version = '0.7.0'
mock_os.path.isdir = mock.Mock(return_value=True)
mock_open = mock.mock_open()
def _mock_isfile(filename):
return False
with mock.patch('homeassistant.config.open', mock_open, create=True), \
mock.patch('homeassistant.config.os.path.isfile', _mock_isfile):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
hass.config.path = mock.Mock()
config_util.process_ha_config_upgrade(hass)
assert mock_os.rename.call_count == 0
async def test_loading_configuration(hass):
"""Test loading core config onto hass object."""
hass.config = mock.Mock()
await config_util.async_process_ha_core_config(hass, {
'latitude': 60,
'longitude': 50,
'elevation': 25,
'name': 'Huis',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL,
'time_zone': 'America/New_York',
'whitelist_external_dirs': '/tmp',
})
assert hass.config.latitude == 60
assert hass.config.longitude == 50
assert hass.config.elevation == 25
assert hass.config.location_name == 'Huis'
assert hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL
assert hass.config.time_zone.zone == 'America/New_York'
assert len(hass.config.whitelist_external_dirs) == 2
assert '/tmp' in hass.config.whitelist_external_dirs
async def test_loading_configuration_temperature_unit(hass):
"""Test backward compatibility when loading core config."""
hass.config = mock.Mock()
await config_util.async_process_ha_core_config(hass, {
'latitude': 60,
'longitude': 50,
'elevation': 25,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'America/New_York',
})
assert hass.config.latitude == 60
assert hass.config.longitude == 50
assert hass.config.elevation == 25
assert hass.config.location_name == 'Huis'
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
assert hass.config.time_zone.zone == 'America/New_York'
async def test_loading_configuration_from_packages(hass):
"""Test loading packages config onto hass object config."""
hass.config = mock.Mock()
await config_util.async_process_ha_core_config(hass, {
'latitude': 39,
'longitude': -1,
'elevation': 500,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'Europe/Madrid',
'packages': {
'package_1': {'wake_on_lan': None},
'package_2': {'light': {'platform': 'hue'},
'media_extractor': None,
'sun': None}},
})
# Empty packages not allowed
with pytest.raises(MultipleInvalid):
await config_util.async_process_ha_core_config(hass, {
'latitude': 39,
'longitude': -1,
'elevation': 500,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'Europe/Madrid',
'packages': {'empty_package': None},
}) })
def test_customize_dict_schema(self):
"""Test basic customize config validation."""
values = (
{ATTR_FRIENDLY_NAME: None},
{ATTR_HIDDEN: '2'},
{ATTR_ASSUMED_STATE: '2'},
)
for val in values: @asynctest.mock.patch('homeassistant.util.location.detect_location_info',
print(val) autospec=True, return_value=location_util.LocationInfo(
with pytest.raises(MultipleInvalid): '0.0.0.0', 'US', 'United States', 'CA', 'California',
config_util.CUSTOMIZE_DICT_SCHEMA(val) 'San Diego', '92122', 'America/Los_Angeles', 32.8594,
-117.2073, True))
@asynctest.mock.patch('homeassistant.util.location.elevation',
autospec=True, return_value=101)
async def test_discovering_configuration(mock_detect, mock_elevation, hass):
"""Test auto discovery for missing core configs."""
hass.config.latitude = None
hass.config.longitude = None
hass.config.elevation = None
hass.config.location_name = None
hass.config.time_zone = None
assert config_util.CUSTOMIZE_DICT_SCHEMA({ await config_util.async_process_ha_core_config(hass, {})
ATTR_FRIENDLY_NAME: 2,
ATTR_HIDDEN: '1',
ATTR_ASSUMED_STATE: '0',
}) == {
ATTR_FRIENDLY_NAME: '2',
ATTR_HIDDEN: True,
ATTR_ASSUMED_STATE: False
}
def test_customize_glob_is_ordered(self): assert hass.config.latitude == 32.8594
"""Test that customize_glob preserves order.""" assert hass.config.longitude == -117.2073
conf = config_util.CORE_CONFIG_SCHEMA( assert hass.config.elevation == 101
{'customize_glob': OrderedDict()}) assert hass.config.location_name == 'San Diego'
assert isinstance(conf['customize_glob'], OrderedDict) assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
assert hass.config.units.is_metric
assert hass.config.time_zone.zone == 'America/Los_Angeles'
def _compute_state(self, config):
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(self.hass, config),
self.hass.loop).result()
entity = Entity() @asynctest.mock.patch('homeassistant.util.location.detect_location_info',
entity.entity_id = 'test.test' autospec=True, return_value=None)
entity.hass = self.hass @asynctest.mock.patch('homeassistant.util.location.elevation', return_value=0)
entity.schedule_update_ha_state() async def test_discovering_configuration_auto_detect_fails(mock_detect,
mock_elevation,
hass):
"""Test config remains unchanged if discovery fails."""
hass.config = Config()
hass.config.config_dir = "/test/config"
self.hass.block_till_done() await config_util.async_process_ha_core_config(hass, {})
return self.hass.states.get('test.test') blankConfig = Config()
assert hass.config.latitude == blankConfig.latitude
assert hass.config.longitude == blankConfig.longitude
assert hass.config.elevation == blankConfig.elevation
assert hass.config.location_name == blankConfig.location_name
assert hass.config.units == blankConfig.units
assert hass.config.time_zone == blankConfig.time_zone
assert len(hass.config.whitelist_external_dirs) == 1
assert "/test/config/www" in hass.config.whitelist_external_dirs
def test_entity_customization(self):
"""Test entity customization through configuration."""
config = {CONF_LATITUDE: 50,
CONF_LONGITUDE: 50,
CONF_NAME: 'Test',
CONF_CUSTOMIZE: {'test.test': {'hidden': True}}}
state = self._compute_state(config) @asynctest.mock.patch(
'homeassistant.scripts.check_config.check_ha_config_file')
async def test_check_ha_config_file_correct(mock_check, hass):
"""Check that restart propagates to stop."""
mock_check.return_value = check_config.HomeAssistantConfig()
assert await config_util.async_check_ha_config_file(hass) is None
assert state.attributes['hidden']
@mock.patch('homeassistant.config.shutil') @asynctest.mock.patch(
@mock.patch('homeassistant.config.os') 'homeassistant.scripts.check_config.check_ha_config_file')
def test_remove_lib_on_upgrade(self, mock_os, mock_shutil): async def test_check_ha_config_file_wrong(mock_check, hass):
"""Test removal of library on upgrade from before 0.50.""" """Check that restart with a bad config doesn't propagate to stop."""
ha_version = '0.49.0' mock_check.return_value = check_config.HomeAssistantConfig()
mock_os.path.isdir = mock.Mock(return_value=True) mock_check.return_value.add_error("bad")
mock_open = mock.mock_open()
with mock.patch('homeassistant.config.open', mock_open, create=True):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
self.hass.config.path = mock.Mock()
config_util.process_ha_config_upgrade(self.hass)
hass_path = self.hass.config.path.return_value
assert mock_os.path.isdir.call_count == 1 assert await config_util.async_check_ha_config_file(hass) == 'bad'
assert mock_os.path.isdir.call_args == mock.call(hass_path)
assert mock_shutil.rmtree.call_count == 1
assert mock_shutil.rmtree.call_args == mock.call(hass_path)
def test_process_config_upgrade(self):
"""Test update of version on upgrade."""
ha_version = '0.92.0'
mock_open = mock.mock_open()
with mock.patch('homeassistant.config.open', mock_open, create=True), \
mock.patch.object(config_util, '__version__', '0.91.0'):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
config_util.process_ha_config_upgrade(self.hass)
assert opened_file.write.call_count == 1
assert opened_file.write.call_args == mock.call('0.91.0')
def test_config_upgrade_same_version(self):
"""Test no update of version on no upgrade."""
ha_version = __version__
mock_open = mock.mock_open()
with mock.patch('homeassistant.config.open', mock_open, create=True):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
config_util.process_ha_config_upgrade(self.hass)
assert opened_file.write.call_count == 0
@mock.patch('homeassistant.config.find_config_file', mock.Mock())
def test_config_upgrade_no_file(self):
"""Test update of version on upgrade, with no version file."""
mock_open = mock.mock_open()
mock_open.side_effect = [FileNotFoundError(),
mock.DEFAULT,
mock.DEFAULT]
with mock.patch('homeassistant.config.open', mock_open, create=True):
opened_file = mock_open.return_value
# pylint: disable=no-member
config_util.process_ha_config_upgrade(self.hass)
assert opened_file.write.call_count == 1
assert opened_file.write.call_args == mock.call(__version__)
@mock.patch('homeassistant.config.shutil')
@mock.patch('homeassistant.config.os')
@mock.patch('homeassistant.config.find_config_file', mock.Mock())
def test_migrate_file_on_upgrade(self, mock_os, mock_shutil):
"""Test migrate of config files on upgrade."""
ha_version = '0.7.0'
mock_os.path.isdir = mock.Mock(return_value=True)
mock_open = mock.mock_open()
def _mock_isfile(filename):
return True
with mock.patch('homeassistant.config.open', mock_open, create=True), \
mock.patch(
'homeassistant.config.os.path.isfile', _mock_isfile):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
self.hass.config.path = mock.Mock()
config_util.process_ha_config_upgrade(self.hass)
assert mock_os.rename.call_count == 1
@mock.patch('homeassistant.config.shutil')
@mock.patch('homeassistant.config.os')
@mock.patch('homeassistant.config.find_config_file', mock.Mock())
def test_migrate_no_file_on_upgrade(self, mock_os, mock_shutil):
"""Test not migrating config files on upgrade."""
ha_version = '0.7.0'
mock_os.path.isdir = mock.Mock(return_value=True)
mock_open = mock.mock_open()
def _mock_isfile(filename):
return False
with mock.patch('homeassistant.config.open', mock_open, create=True), \
mock.patch(
'homeassistant.config.os.path.isfile', _mock_isfile):
opened_file = mock_open.return_value
# pylint: disable=no-member
opened_file.readline.return_value = ha_version
self.hass.config.path = mock.Mock()
config_util.process_ha_config_upgrade(self.hass)
assert mock_os.rename.call_count == 0
def test_loading_configuration(self):
"""Test loading core config onto hass object."""
self.hass.config = mock.Mock()
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(self.hass, {
'latitude': 60,
'longitude': 50,
'elevation': 25,
'name': 'Huis',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL,
'time_zone': 'America/New_York',
'whitelist_external_dirs': '/tmp',
}), self.hass.loop).result()
assert self.hass.config.latitude == 60
assert self.hass.config.longitude == 50
assert self.hass.config.elevation == 25
assert self.hass.config.location_name == 'Huis'
assert self.hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL
assert self.hass.config.time_zone.zone == 'America/New_York'
assert len(self.hass.config.whitelist_external_dirs) == 2
assert '/tmp' in self.hass.config.whitelist_external_dirs
def test_loading_configuration_temperature_unit(self):
"""Test backward compatibility when loading core config."""
self.hass.config = mock.Mock()
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(self.hass, {
'latitude': 60,
'longitude': 50,
'elevation': 25,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'America/New_York',
}), self.hass.loop).result()
assert self.hass.config.latitude == 60
assert self.hass.config.longitude == 50
assert self.hass.config.elevation == 25
assert self.hass.config.location_name == 'Huis'
assert self.hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
assert self.hass.config.time_zone.zone == 'America/New_York'
def test_loading_configuration_from_packages(self):
"""Test loading packages config onto hass object config."""
self.hass.config = mock.Mock()
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(self.hass, {
'latitude': 39,
'longitude': -1,
'elevation': 500,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'Europe/Madrid',
'packages': {
'package_1': {'wake_on_lan': None},
'package_2': {'light': {'platform': 'hue'},
'media_extractor': None,
'sun': None}},
}), self.hass.loop).result()
# Empty packages not allowed
with pytest.raises(MultipleInvalid):
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(self.hass, {
'latitude': 39,
'longitude': -1,
'elevation': 500,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'Europe/Madrid',
'packages': {'empty_package': None},
}), self.hass.loop).result()
@mock.patch('homeassistant.util.location.detect_location_info',
autospec=True, return_value=location_util.LocationInfo(
'0.0.0.0', 'US', 'United States', 'CA', 'California',
'San Diego', '92122', 'America/Los_Angeles', 32.8594,
-117.2073, True))
@mock.patch('homeassistant.util.location.elevation',
autospec=True, return_value=101)
def test_discovering_configuration(self, mock_detect, mock_elevation):
"""Test auto discovery for missing core configs."""
self.hass.config.latitude = None
self.hass.config.longitude = None
self.hass.config.elevation = None
self.hass.config.location_name = None
self.hass.config.time_zone = None
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(
self.hass, {}), self.hass.loop
).result()
assert self.hass.config.latitude == 32.8594
assert self.hass.config.longitude == -117.2073
assert self.hass.config.elevation == 101
assert self.hass.config.location_name == 'San Diego'
assert self.hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
assert self.hass.config.units.is_metric
assert self.hass.config.time_zone.zone == 'America/Los_Angeles'
@mock.patch('homeassistant.util.location.detect_location_info',
autospec=True, return_value=None)
@mock.patch('homeassistant.util.location.elevation', return_value=0)
def test_discovering_configuration_auto_detect_fails(self, mock_detect,
mock_elevation):
"""Test config remains unchanged if discovery fails."""
self.hass.config = Config()
self.hass.config.config_dir = "/test/config"
run_coroutine_threadsafe(
config_util.async_process_ha_core_config(
self.hass, {}), self.hass.loop
).result()
blankConfig = Config()
assert self.hass.config.latitude == blankConfig.latitude
assert self.hass.config.longitude == blankConfig.longitude
assert self.hass.config.elevation == blankConfig.elevation
assert self.hass.config.location_name == blankConfig.location_name
assert self.hass.config.units == blankConfig.units
assert self.hass.config.time_zone == blankConfig.time_zone
assert len(self.hass.config.whitelist_external_dirs) == 1
assert "/test/config/www" in self.hass.config.whitelist_external_dirs
@asynctest.mock.patch(
'homeassistant.scripts.check_config.check_ha_config_file')
def test_check_ha_config_file_correct(self, mock_check):
"""Check that restart propagates to stop."""
mock_check.return_value = check_config.HomeAssistantConfig()
assert run_coroutine_threadsafe(
config_util.async_check_ha_config_file(self.hass),
self.hass.loop
).result() is None
@asynctest.mock.patch(
'homeassistant.scripts.check_config.check_ha_config_file')
def test_check_ha_config_file_wrong(self, mock_check):
"""Check that restart with a bad config doesn't propagate to stop."""
mock_check.return_value = check_config.HomeAssistantConfig()
mock_check.return_value.add_error("bad")
assert run_coroutine_threadsafe(
config_util.async_check_ha_config_file(self.hass),
self.hass.loop
).result() == 'bad'
@asynctest.mock.patch('homeassistant.config.os.path.isfile', @asynctest.mock.patch('homeassistant.config.os.path.isfile',

View File

@ -1,10 +1,9 @@
"""Test Home Assistant color util methods.""" """Test Home Assistant color util methods."""
import unittest
import homeassistant.util.color as color_util
import pytest import pytest
import voluptuous as vol import voluptuous as vol
import homeassistant.util.color as color_util
GAMUT = color_util.GamutType(color_util.XYPoint(0.704, 0.296), GAMUT = color_util.GamutType(color_util.XYPoint(0.704, 0.296),
color_util.XYPoint(0.2151, 0.7106), color_util.XYPoint(0.2151, 0.7106),
color_util.XYPoint(0.138, 0.08)) color_util.XYPoint(0.138, 0.08))
@ -22,408 +21,340 @@ GAMUT_INVALID_4 = color_util.GamutType(color_util.XYPoint(0.1, 0.1),
color_util.XYPoint(0.7, 0.7)) color_util.XYPoint(0.7, 0.7))
class TestColorUtil(unittest.TestCase): # pylint: disable=invalid-name
"""Test color util methods.""" def test_color_RGB_to_xy_brightness():
"""Test color_RGB_to_xy_brightness."""
assert color_util.color_RGB_to_xy_brightness(0, 0, 0) == (0, 0, 0)
assert color_util.color_RGB_to_xy_brightness(255, 255, 255) == \
(0.323, 0.329, 255)
# pylint: disable=invalid-name assert color_util.color_RGB_to_xy_brightness(0, 0, 255) == \
def test_color_RGB_to_xy_brightness(self): (0.136, 0.04, 12)
"""Test color_RGB_to_xy_brightness."""
assert (0, 0, 0) == \
color_util.color_RGB_to_xy_brightness(0, 0, 0)
assert (0.323, 0.329, 255) == \
color_util.color_RGB_to_xy_brightness(255, 255, 255)
assert (0.136, 0.04, 12) == \ assert color_util.color_RGB_to_xy_brightness(0, 255, 0) == \
color_util.color_RGB_to_xy_brightness(0, 0, 255) (0.172, 0.747, 170)
assert (0.172, 0.747, 170) == \ assert color_util.color_RGB_to_xy_brightness(255, 0, 0) == \
color_util.color_RGB_to_xy_brightness(0, 255, 0) (0.701, 0.299, 72)
assert (0.701, 0.299, 72) == \ assert color_util.color_RGB_to_xy_brightness(128, 0, 0) == \
color_util.color_RGB_to_xy_brightness(255, 0, 0) (0.701, 0.299, 16)
assert (0.701, 0.299, 16) == \ assert color_util.color_RGB_to_xy_brightness(255, 0, 0, GAMUT) == \
color_util.color_RGB_to_xy_brightness(128, 0, 0) (0.7, 0.299, 72)
assert (0.7, 0.299, 72) == \ assert color_util.color_RGB_to_xy_brightness(0, 255, 0, GAMUT) == \
color_util.color_RGB_to_xy_brightness(255, 0, 0, GAMUT) (0.215, 0.711, 170)
assert (0.215, 0.711, 170) == \ assert color_util.color_RGB_to_xy_brightness(0, 0, 255, GAMUT) == \
color_util.color_RGB_to_xy_brightness(0, 255, 0, GAMUT) (0.138, 0.08, 12)
assert (0.138, 0.08, 12) == \
color_util.color_RGB_to_xy_brightness(0, 0, 255, GAMUT)
def test_color_RGB_to_xy(self): def test_color_RGB_to_xy():
"""Test color_RGB_to_xy.""" """Test color_RGB_to_xy."""
assert (0, 0) == \ assert color_util.color_RGB_to_xy(0, 0, 0) == (0, 0)
color_util.color_RGB_to_xy(0, 0, 0) assert color_util.color_RGB_to_xy(255, 255, 255) == (0.323, 0.329)
assert (0.323, 0.329) == \
color_util.color_RGB_to_xy(255, 255, 255)
assert (0.136, 0.04) == \ assert color_util.color_RGB_to_xy(0, 0, 255) == (0.136, 0.04)
color_util.color_RGB_to_xy(0, 0, 255)
assert (0.172, 0.747) == \ assert color_util.color_RGB_to_xy(0, 255, 0) == (0.172, 0.747)
color_util.color_RGB_to_xy(0, 255, 0)
assert (0.701, 0.299) == \ assert color_util.color_RGB_to_xy(255, 0, 0) == (0.701, 0.299)
color_util.color_RGB_to_xy(255, 0, 0)
assert (0.701, 0.299) == \ assert color_util.color_RGB_to_xy(128, 0, 0) == (0.701, 0.299)
color_util.color_RGB_to_xy(128, 0, 0)
assert (0.138, 0.08) == \ assert color_util.color_RGB_to_xy(0, 0, 255, GAMUT) == (0.138, 0.08)
color_util.color_RGB_to_xy(0, 0, 255, GAMUT)
assert (0.215, 0.711) == \ assert color_util.color_RGB_to_xy(0, 255, 0, GAMUT) == (0.215, 0.711)
color_util.color_RGB_to_xy(0, 255, 0, GAMUT)
assert (0.7, 0.299) == \ assert color_util.color_RGB_to_xy(255, 0, 0, GAMUT) == (0.7, 0.299)
color_util.color_RGB_to_xy(255, 0, 0, GAMUT)
def test_color_xy_brightness_to_RGB(self):
"""Test color_xy_brightness_to_RGB."""
assert (0, 0, 0) == \
color_util.color_xy_brightness_to_RGB(1, 1, 0)
assert (194, 186, 169) == \ def test_color_xy_brightness_to_RGB():
color_util.color_xy_brightness_to_RGB(.35, .35, 128) """Test color_xy_brightness_to_RGB."""
assert color_util.color_xy_brightness_to_RGB(1, 1, 0) == (0, 0, 0)
assert (255, 243, 222) == \ assert color_util.color_xy_brightness_to_RGB(.35, .35, 128) == \
color_util.color_xy_brightness_to_RGB(.35, .35, 255) (194, 186, 169)
assert (255, 0, 60) == \ assert color_util.color_xy_brightness_to_RGB(.35, .35, 255) == \
color_util.color_xy_brightness_to_RGB(1, 0, 255) (255, 243, 222)
assert (0, 255, 0) == \ assert color_util.color_xy_brightness_to_RGB(1, 0, 255) == (255, 0, 60)
color_util.color_xy_brightness_to_RGB(0, 1, 255)
assert (0, 63, 255) == \ assert color_util.color_xy_brightness_to_RGB(0, 1, 255) == (0, 255, 0)
color_util.color_xy_brightness_to_RGB(0, 0, 255)
assert (255, 0, 3) == \ assert color_util.color_xy_brightness_to_RGB(0, 0, 255) == (0, 63, 255)
color_util.color_xy_brightness_to_RGB(1, 0, 255, GAMUT)
assert (82, 255, 0) == \ assert color_util.color_xy_brightness_to_RGB(1, 0, 255, GAMUT) == \
color_util.color_xy_brightness_to_RGB(0, 1, 255, GAMUT) (255, 0, 3)
assert (9, 85, 255) == \ assert color_util.color_xy_brightness_to_RGB(0, 1, 255, GAMUT) == \
color_util.color_xy_brightness_to_RGB(0, 0, 255, GAMUT) (82, 255, 0)
def test_color_xy_to_RGB(self): assert color_util.color_xy_brightness_to_RGB(0, 0, 255, GAMUT) == \
"""Test color_xy_to_RGB.""" (9, 85, 255)
assert (255, 243, 222) == \
color_util.color_xy_to_RGB(.35, .35)
assert (255, 0, 60) == \
color_util.color_xy_to_RGB(1, 0)
assert (0, 255, 0) == \ def test_color_xy_to_RGB():
color_util.color_xy_to_RGB(0, 1) """Test color_xy_to_RGB."""
assert color_util.color_xy_to_RGB(.35, .35) == (255, 243, 222)
assert (0, 63, 255) == \ assert color_util.color_xy_to_RGB(1, 0) == (255, 0, 60)
color_util.color_xy_to_RGB(0, 0)
assert (255, 0, 3) == \ assert color_util.color_xy_to_RGB(0, 1) == (0, 255, 0)
color_util.color_xy_to_RGB(1, 0, GAMUT)
assert (82, 255, 0) == \ assert color_util.color_xy_to_RGB(0, 0) == (0, 63, 255)
color_util.color_xy_to_RGB(0, 1, GAMUT)
assert (9, 85, 255) == \ assert color_util.color_xy_to_RGB(1, 0, GAMUT) == (255, 0, 3)
color_util.color_xy_to_RGB(0, 0, GAMUT)
def test_color_RGB_to_hsv(self): assert color_util.color_xy_to_RGB(0, 1, GAMUT) == (82, 255, 0)
"""Test color_RGB_to_hsv."""
assert (0, 0, 0) == \
color_util.color_RGB_to_hsv(0, 0, 0)
assert (0, 0, 100) == \ assert color_util.color_xy_to_RGB(0, 0, GAMUT) == (9, 85, 255)
color_util.color_RGB_to_hsv(255, 255, 255)
assert (240, 100, 100) == \
color_util.color_RGB_to_hsv(0, 0, 255)
assert (120, 100, 100) == \ def test_color_RGB_to_hsv():
color_util.color_RGB_to_hsv(0, 255, 0) """Test color_RGB_to_hsv."""
assert color_util.color_RGB_to_hsv(0, 0, 0) == (0, 0, 0)
assert (0, 100, 100) == \ assert color_util.color_RGB_to_hsv(255, 255, 255) == (0, 0, 100)
color_util.color_RGB_to_hsv(255, 0, 0)
def test_color_hsv_to_RGB(self): assert color_util.color_RGB_to_hsv(0, 0, 255) == (240, 100, 100)
"""Test color_hsv_to_RGB."""
assert (0, 0, 0) == \
color_util.color_hsv_to_RGB(0, 0, 0)
assert (255, 255, 255) == \ assert color_util.color_RGB_to_hsv(0, 255, 0) == (120, 100, 100)
color_util.color_hsv_to_RGB(0, 0, 100)
assert (0, 0, 255) == \ assert color_util.color_RGB_to_hsv(255, 0, 0) == (0, 100, 100)
color_util.color_hsv_to_RGB(240, 100, 100)
assert (0, 255, 0) == \
color_util.color_hsv_to_RGB(120, 100, 100)
assert (255, 0, 0) == \ def test_color_hsv_to_RGB():
color_util.color_hsv_to_RGB(0, 100, 100) """Test color_hsv_to_RGB."""
assert color_util.color_hsv_to_RGB(0, 0, 0) == (0, 0, 0)
def test_color_hsb_to_RGB(self): assert color_util.color_hsv_to_RGB(0, 0, 100) == (255, 255, 255)
"""Test color_hsb_to_RGB."""
assert (0, 0, 0) == \
color_util.color_hsb_to_RGB(0, 0, 0)
assert (255, 255, 255) == \ assert color_util.color_hsv_to_RGB(240, 100, 100) == (0, 0, 255)
color_util.color_hsb_to_RGB(0, 0, 1.0)
assert (0, 0, 255) == \ assert color_util.color_hsv_to_RGB(120, 100, 100) == (0, 255, 0)
color_util.color_hsb_to_RGB(240, 1.0, 1.0)
assert (0, 255, 0) == \ assert color_util.color_hsv_to_RGB(0, 100, 100) == (255, 0, 0)
color_util.color_hsb_to_RGB(120, 1.0, 1.0)
assert (255, 0, 0) == \
color_util.color_hsb_to_RGB(0, 1.0, 1.0)
def test_color_xy_to_hs(self): def test_color_hsb_to_RGB():
"""Test color_xy_to_hs.""" """Test color_hsb_to_RGB."""
assert (47.294, 100) == \ assert color_util.color_hsb_to_RGB(0, 0, 0) == (0, 0, 0)
color_util.color_xy_to_hs(1, 1)
assert (38.182, 12.941) == \ assert color_util.color_hsb_to_RGB(0, 0, 1.0) == (255, 255, 255)
color_util.color_xy_to_hs(.35, .35)
assert (345.882, 100) == \ assert color_util.color_hsb_to_RGB(240, 1.0, 1.0) == (0, 0, 255)
color_util.color_xy_to_hs(1, 0)
assert (120, 100) == \ assert color_util.color_hsb_to_RGB(120, 1.0, 1.0) == (0, 255, 0)
color_util.color_xy_to_hs(0, 1)
assert (225.176, 100) == \ assert color_util.color_hsb_to_RGB(0, 1.0, 1.0) == (255, 0, 0)
color_util.color_xy_to_hs(0, 0)
assert (359.294, 100) == \
color_util.color_xy_to_hs(1, 0, GAMUT)
assert (100.706, 100) == \ def test_color_xy_to_hs():
color_util.color_xy_to_hs(0, 1, GAMUT) """Test color_xy_to_hs."""
assert color_util.color_xy_to_hs(1, 1) == (47.294, 100)
assert (221.463, 96.471) == \ assert color_util.color_xy_to_hs(.35, .35) == (38.182, 12.941)
color_util.color_xy_to_hs(0, 0, GAMUT)
def test_color_hs_to_xy(self): assert color_util.color_xy_to_hs(1, 0) == (345.882, 100)
"""Test color_hs_to_xy."""
assert (0.151, 0.343) == \
color_util.color_hs_to_xy(180, 100)
assert (0.356, 0.321) == \ assert color_util.color_xy_to_hs(0, 1) == (120, 100)
color_util.color_hs_to_xy(350, 12.5)
assert (0.229, 0.474) == \ assert color_util.color_xy_to_hs(0, 0) == (225.176, 100)
color_util.color_hs_to_xy(140, 50)
assert (0.474, 0.317) == \ assert color_util.color_xy_to_hs(1, 0, GAMUT) == (359.294, 100)
color_util.color_hs_to_xy(0, 40)
assert (0.323, 0.329) == \ assert color_util.color_xy_to_hs(0, 1, GAMUT) == (100.706, 100)
color_util.color_hs_to_xy(360, 0)
assert (0.7, 0.299) == \ assert color_util.color_xy_to_hs(0, 0, GAMUT) == (221.463, 96.471)
color_util.color_hs_to_xy(0, 100, GAMUT)
assert (0.215, 0.711) == \
color_util.color_hs_to_xy(120, 100, GAMUT)
assert (0.17, 0.34) == \ def test_color_hs_to_xy():
color_util.color_hs_to_xy(180, 100, GAMUT) """Test color_hs_to_xy."""
assert color_util.color_hs_to_xy(180, 100) == (0.151, 0.343)
assert (0.138, 0.08) == \ assert color_util.color_hs_to_xy(350, 12.5) == (0.356, 0.321)
color_util.color_hs_to_xy(240, 100, GAMUT)
assert (0.7, 0.299) == \ assert color_util.color_hs_to_xy(140, 50) == (0.229, 0.474)
color_util.color_hs_to_xy(360, 100, GAMUT)
def test_rgb_hex_to_rgb_list(self): assert color_util.color_hs_to_xy(0, 40) == (0.474, 0.317)
"""Test rgb_hex_to_rgb_list."""
assert [255, 255, 255] == \
color_util.rgb_hex_to_rgb_list('ffffff')
assert [0, 0, 0] == \ assert color_util.color_hs_to_xy(360, 0) == (0.323, 0.329)
color_util.rgb_hex_to_rgb_list('000000')
assert [255, 255, 255, 255] == \ assert color_util.color_hs_to_xy(0, 100, GAMUT) == (0.7, 0.299)
color_util.rgb_hex_to_rgb_list('ffffffff')
assert [0, 0, 0, 0] == \ assert color_util.color_hs_to_xy(120, 100, GAMUT) == (0.215, 0.711)
color_util.rgb_hex_to_rgb_list('00000000')
assert [51, 153, 255] == \ assert color_util.color_hs_to_xy(180, 100, GAMUT) == (0.17, 0.34)
color_util.rgb_hex_to_rgb_list('3399ff')
assert [51, 153, 255, 0] == \ assert color_util.color_hs_to_xy(240, 100, GAMUT) == (0.138, 0.08)
color_util.rgb_hex_to_rgb_list('3399ff00')
def test_color_name_to_rgb_valid_name(self): assert color_util.color_hs_to_xy(360, 100, GAMUT) == (0.7, 0.299)
"""Test color_name_to_rgb."""
assert (255, 0, 0) == \
color_util.color_name_to_rgb('red')
assert (0, 0, 255) == \
color_util.color_name_to_rgb('blue')
assert (0, 128, 0) == \ def test_rgb_hex_to_rgb_list():
color_util.color_name_to_rgb('green') """Test rgb_hex_to_rgb_list."""
assert [255, 255, 255] == \
color_util.rgb_hex_to_rgb_list('ffffff')
# spaces in the name assert [0, 0, 0] == \
assert (72, 61, 139) == \ color_util.rgb_hex_to_rgb_list('000000')
color_util.color_name_to_rgb('dark slate blue')
# spaces removed from name assert [255, 255, 255, 255] == \
assert (72, 61, 139) == \ color_util.rgb_hex_to_rgb_list('ffffffff')
color_util.color_name_to_rgb('darkslateblue')
assert (72, 61, 139) == \
color_util.color_name_to_rgb('dark slateblue')
assert (72, 61, 139) == \
color_util.color_name_to_rgb('darkslate blue')
def test_color_name_to_rgb_unknown_name_raises_value_error(self): assert [0, 0, 0, 0] == \
"""Test color_name_to_rgb.""" color_util.rgb_hex_to_rgb_list('00000000')
with pytest.raises(ValueError):
color_util.color_name_to_rgb('not a color')
def test_color_rgb_to_rgbw(self): assert [51, 153, 255] == \
"""Test color_rgb_to_rgbw.""" color_util.rgb_hex_to_rgb_list('3399ff')
assert (0, 0, 0, 0) == \
color_util.color_rgb_to_rgbw(0, 0, 0)
assert (0, 0, 0, 255) == \ assert [51, 153, 255, 0] == \
color_util.color_rgb_to_rgbw(255, 255, 255) color_util.rgb_hex_to_rgb_list('3399ff00')
assert (255, 0, 0, 0) == \
color_util.color_rgb_to_rgbw(255, 0, 0)
assert (0, 255, 0, 0) == \ def test_color_name_to_rgb_valid_name():
color_util.color_rgb_to_rgbw(0, 255, 0) """Test color_name_to_rgb."""
assert color_util.color_name_to_rgb('red') == (255, 0, 0)
assert (0, 0, 255, 0) == \ assert color_util.color_name_to_rgb('blue') == (0, 0, 255)
color_util.color_rgb_to_rgbw(0, 0, 255)
assert (255, 127, 0, 0) == \ assert color_util.color_name_to_rgb('green') == (0, 128, 0)
color_util.color_rgb_to_rgbw(255, 127, 0)
assert (255, 0, 0, 253) == \ # spaces in the name
color_util.color_rgb_to_rgbw(255, 127, 127) assert color_util.color_name_to_rgb('dark slate blue') == (72, 61, 139)
assert (0, 0, 0, 127) == \ # spaces removed from name
color_util.color_rgb_to_rgbw(127, 127, 127) assert color_util.color_name_to_rgb('darkslateblue') == (72, 61, 139)
assert color_util.color_name_to_rgb('dark slateblue') == (72, 61, 139)
assert color_util.color_name_to_rgb('darkslate blue') == (72, 61, 139)
def test_color_rgbw_to_rgb(self):
"""Test color_rgbw_to_rgb."""
assert (0, 0, 0) == \
color_util.color_rgbw_to_rgb(0, 0, 0, 0)
assert (255, 255, 255) == \ def test_color_name_to_rgb_unknown_name_raises_value_error():
color_util.color_rgbw_to_rgb(0, 0, 0, 255) """Test color_name_to_rgb."""
with pytest.raises(ValueError):
color_util.color_name_to_rgb('not a color')
assert (255, 0, 0) == \
color_util.color_rgbw_to_rgb(255, 0, 0, 0)
assert (0, 255, 0) == \ def test_color_rgb_to_rgbw():
color_util.color_rgbw_to_rgb(0, 255, 0, 0) """Test color_rgb_to_rgbw."""
assert color_util.color_rgb_to_rgbw(0, 0, 0) == (0, 0, 0, 0)
assert (0, 0, 255) == \ assert color_util.color_rgb_to_rgbw(255, 255, 255) == (0, 0, 0, 255)
color_util.color_rgbw_to_rgb(0, 0, 255, 0)
assert (255, 127, 0) == \ assert color_util.color_rgb_to_rgbw(255, 0, 0) == (255, 0, 0, 0)
color_util.color_rgbw_to_rgb(255, 127, 0, 0)
assert (255, 127, 127) == \ assert color_util.color_rgb_to_rgbw(0, 255, 0) == (0, 255, 0, 0)
color_util.color_rgbw_to_rgb(255, 0, 0, 253)
assert (127, 127, 127) == \ assert color_util.color_rgb_to_rgbw(0, 0, 255) == (0, 0, 255, 0)
color_util.color_rgbw_to_rgb(0, 0, 0, 127)
def test_color_rgb_to_hex(self): assert color_util.color_rgb_to_rgbw(255, 127, 0) == (255, 127, 0, 0)
"""Test color_rgb_to_hex."""
assert color_util.color_rgb_to_hex(255, 255, 255) == 'ffffff'
assert color_util.color_rgb_to_hex(0, 0, 0) == '000000'
assert color_util.color_rgb_to_hex(51, 153, 255) == '3399ff'
assert color_util.color_rgb_to_hex(255, 67.9204190, 0) == 'ff4400'
def test_gamut(self): assert color_util.color_rgb_to_rgbw(255, 127, 127) == (255, 0, 0, 253)
"""Test gamut functions."""
assert color_util.check_valid_gamut(GAMUT)
assert not color_util.check_valid_gamut(GAMUT_INVALID_1)
assert not color_util.check_valid_gamut(GAMUT_INVALID_2)
assert not color_util.check_valid_gamut(GAMUT_INVALID_3)
assert not color_util.check_valid_gamut(GAMUT_INVALID_4)
assert color_util.color_rgb_to_rgbw(127, 127, 127) == (0, 0, 0, 127)
class ColorTemperatureMiredToKelvinTests(unittest.TestCase):
"""Test color_temperature_mired_to_kelvin."""
def test_should_return_25000_kelvin_when_input_is_40_mired(self): def test_color_rgbw_to_rgb():
"""Function should return 25000K if given 40 mired.""" """Test color_rgbw_to_rgb."""
kelvin = color_util.color_temperature_mired_to_kelvin(40) assert color_util.color_rgbw_to_rgb(0, 0, 0, 0) == (0, 0, 0)
assert 25000 == kelvin
def test_should_return_5000_kelvin_when_input_is_200_mired(self): assert color_util.color_rgbw_to_rgb(0, 0, 0, 255) == (255, 255, 255)
"""Function should return 5000K if given 200 mired."""
kelvin = color_util.color_temperature_mired_to_kelvin(200)
assert 5000 == kelvin
class ColorTemperatureKelvinToMiredTests(unittest.TestCase):
"""Test color_temperature_kelvin_to_mired."""
def test_should_return_40_mired_when_input_is_25000_kelvin(self):
"""Function should return 40 mired when given 25000 Kelvin."""
mired = color_util.color_temperature_kelvin_to_mired(25000)
assert 40 == mired
def test_should_return_200_mired_when_input_is_5000_kelvin(self):
"""Function should return 200 mired when given 5000 Kelvin."""
mired = color_util.color_temperature_kelvin_to_mired(5000)
assert 200 == mired
assert color_util.color_rgbw_to_rgb(255, 0, 0, 0) == (255, 0, 0)
class ColorTemperatureToRGB(unittest.TestCase): assert color_util.color_rgbw_to_rgb(0, 255, 0, 0) == (0, 255, 0)
"""Test color_temperature_to_rgb."""
def test_returns_same_value_for_any_two_temperatures_below_1000(self): assert color_util.color_rgbw_to_rgb(0, 0, 255, 0) == (0, 0, 255)
"""Function should return same value for 999 Kelvin and 0 Kelvin."""
rgb_1 = color_util.color_temperature_to_rgb(999)
rgb_2 = color_util.color_temperature_to_rgb(0)
assert rgb_1 == rgb_2
def test_returns_same_value_for_any_two_temperatures_above_40000(self): assert color_util.color_rgbw_to_rgb(255, 127, 0, 0) == (255, 127, 0)
"""Function should return same value for 40001K and 999999K."""
rgb_1 = color_util.color_temperature_to_rgb(40001)
rgb_2 = color_util.color_temperature_to_rgb(999999)
assert rgb_1 == rgb_2
def test_should_return_pure_white_at_6600(self): assert color_util.color_rgbw_to_rgb(255, 0, 0, 253) == (255, 127, 127)
"""
Function should return red=255, blue=255, green=255 when given 6600K.
6600K is considered "pure white" light. assert color_util.color_rgbw_to_rgb(0, 0, 0, 127) == (127, 127, 127)
This is just a rough estimate because the formula itself is a "best
guess" approach.
""" def test_color_rgb_to_hex():
rgb = color_util.color_temperature_to_rgb(6600) """Test color_rgb_to_hex."""
assert (255, 255, 255) == rgb assert color_util.color_rgb_to_hex(255, 255, 255) == 'ffffff'
assert color_util.color_rgb_to_hex(0, 0, 0) == '000000'
def test_color_above_6600_should_have_more_blue_than_red_or_green(self): assert color_util.color_rgb_to_hex(51, 153, 255) == '3399ff'
"""Function should return a higher blue value for blue-ish light.""" assert color_util.color_rgb_to_hex(255, 67.9204190, 0) == 'ff4400'
rgb = color_util.color_temperature_to_rgb(6700)
assert rgb[2] > rgb[1]
assert rgb[2] > rgb[0] def test_gamut():
"""Test gamut functions."""
def test_color_below_6600_should_have_more_red_than_blue_or_green(self): assert color_util.check_valid_gamut(GAMUT)
"""Function should return a higher red value for red-ish light.""" assert not color_util.check_valid_gamut(GAMUT_INVALID_1)
rgb = color_util.color_temperature_to_rgb(6500) assert not color_util.check_valid_gamut(GAMUT_INVALID_2)
assert rgb[0] > rgb[1] assert not color_util.check_valid_gamut(GAMUT_INVALID_3)
assert rgb[0] > rgb[2] assert not color_util.check_valid_gamut(GAMUT_INVALID_4)
def test_should_return_25000_kelvin_when_input_is_40_mired():
"""Function should return 25000K if given 40 mired."""
kelvin = color_util.color_temperature_mired_to_kelvin(40)
assert kelvin == 25000
def test_should_return_5000_kelvin_when_input_is_200_mired():
"""Function should return 5000K if given 200 mired."""
kelvin = color_util.color_temperature_mired_to_kelvin(200)
assert kelvin == 5000
def test_should_return_40_mired_when_input_is_25000_kelvin():
"""Function should return 40 mired when given 25000 Kelvin."""
mired = color_util.color_temperature_kelvin_to_mired(25000)
assert mired == 40
def test_should_return_200_mired_when_input_is_5000_kelvin():
"""Function should return 200 mired when given 5000 Kelvin."""
mired = color_util.color_temperature_kelvin_to_mired(5000)
assert mired == 200
def test_returns_same_value_for_any_two_temperatures_below_1000():
"""Function should return same value for 999 Kelvin and 0 Kelvin."""
rgb_1 = color_util.color_temperature_to_rgb(999)
rgb_2 = color_util.color_temperature_to_rgb(0)
assert rgb_1 == rgb_2
def test_returns_same_value_for_any_two_temperatures_above_40000():
"""Function should return same value for 40001K and 999999K."""
rgb_1 = color_util.color_temperature_to_rgb(40001)
rgb_2 = color_util.color_temperature_to_rgb(999999)
assert rgb_1 == rgb_2
def test_should_return_pure_white_at_6600():
"""
Function should return red=255, blue=255, green=255 when given 6600K.
6600K is considered "pure white" light.
This is just a rough estimate because the formula itself is a "best
guess" approach.
"""
rgb = color_util.color_temperature_to_rgb(6600)
assert (255, 255, 255) == rgb
def test_color_above_6600_should_have_more_blue_than_red_or_green():
"""Function should return a higher blue value for blue-ish light."""
rgb = color_util.color_temperature_to_rgb(6700)
assert rgb[2] > rgb[1]
assert rgb[2] > rgb[0]
def test_color_below_6600_should_have_more_red_than_blue_or_green():
"""Function should return a higher red value for red-ish light."""
rgb = color_util.color_temperature_to_rgb(6500)
assert rgb[0] > rgb[1]
assert rgb[0] > rgb[2]
def test_get_color_in_voluptuous(): def test_get_color_in_voluptuous():

View File

@ -1,79 +1,68 @@
"""Test homeassistant distance utility functions.""" """Test homeassistant distance utility functions."""
import unittest import pytest
import homeassistant.util.distance as distance_util import homeassistant.util.distance as distance_util
from homeassistant.const import (LENGTH_KILOMETERS, LENGTH_METERS, LENGTH_FEET, from homeassistant.const import (LENGTH_KILOMETERS, LENGTH_METERS, LENGTH_FEET,
LENGTH_MILES) LENGTH_MILES)
import pytest
INVALID_SYMBOL = 'bob' INVALID_SYMBOL = 'bob'
VALID_SYMBOL = LENGTH_KILOMETERS VALID_SYMBOL = LENGTH_KILOMETERS
class TestDistanceUtil(unittest.TestCase): def test_convert_same_unit():
"""Test the distance utility functions.""" """Test conversion from any unit to same unit."""
assert distance_util.convert(5, LENGTH_KILOMETERS, LENGTH_KILOMETERS) == 5
assert distance_util.convert(2, LENGTH_METERS, LENGTH_METERS) == 2
assert distance_util.convert(10, LENGTH_MILES, LENGTH_MILES) == 10
assert distance_util.convert(9, LENGTH_FEET, LENGTH_FEET) == 9
def test_convert_same_unit(self):
"""Test conversion from any unit to same unit."""
assert 5 == distance_util.convert(5, LENGTH_KILOMETERS,
LENGTH_KILOMETERS)
assert 2 == distance_util.convert(2, LENGTH_METERS,
LENGTH_METERS)
assert 10 == distance_util.convert(10, LENGTH_MILES, LENGTH_MILES)
assert 9 == distance_util.convert(9, LENGTH_FEET, LENGTH_FEET)
def test_convert_invalid_unit(self): def test_convert_invalid_unit():
"""Test exception is thrown for invalid units.""" """Test exception is thrown for invalid units."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
distance_util.convert(5, INVALID_SYMBOL, distance_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL)
VALID_SYMBOL)
with pytest.raises(ValueError): with pytest.raises(ValueError):
distance_util.convert(5, VALID_SYMBOL, distance_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL)
INVALID_SYMBOL)
def test_convert_nonnumeric_value(self):
"""Test exception is thrown for nonnumeric type."""
with pytest.raises(TypeError):
distance_util.convert('a', LENGTH_KILOMETERS, LENGTH_METERS)
def test_convert_from_miles(self): def test_convert_nonnumeric_value():
"""Test conversion from miles to other units.""" """Test exception is thrown for nonnumeric type."""
miles = 5 with pytest.raises(TypeError):
assert distance_util.convert( distance_util.convert('a', LENGTH_KILOMETERS, LENGTH_METERS)
miles, LENGTH_MILES, LENGTH_KILOMETERS
) == 8.04672
assert distance_util.convert(miles, LENGTH_MILES, LENGTH_METERS) == \
8046.72
assert distance_util.convert(miles, LENGTH_MILES, LENGTH_FEET) == \
26400.0008448
def test_convert_from_feet(self):
"""Test conversion from feet to other units."""
feet = 5000
assert distance_util.convert(feet, LENGTH_FEET, LENGTH_KILOMETERS) == \
1.524
assert distance_util.convert(feet, LENGTH_FEET, LENGTH_METERS) == \
1524
assert distance_util.convert(feet, LENGTH_FEET, LENGTH_MILES) == \
0.9469694040000001
def test_convert_from_kilometers(self): def test_convert_from_miles():
"""Test conversion from kilometers to other units.""" """Test conversion from miles to other units."""
km = 5 miles = 5
assert distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_FEET) == \ assert distance_util.convert(miles, LENGTH_MILES, LENGTH_KILOMETERS) == \
16404.2 8.04672
assert distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_METERS) == \ assert distance_util.convert(miles, LENGTH_MILES, LENGTH_METERS) == 8046.72
5000 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_FEET) == \
assert distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_MILES) == \ 26400.0008448
3.106855
def test_convert_from_meters(self):
"""Test conversion from meters to other units.""" def test_convert_from_feet():
m = 5000 """Test conversion from feet to other units."""
assert distance_util.convert(m, LENGTH_METERS, LENGTH_FEET) == \ feet = 5000
16404.2 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_KILOMETERS) == 1.524
assert distance_util.convert(m, LENGTH_METERS, LENGTH_KILOMETERS) == \ assert distance_util.convert(feet, LENGTH_FEET, LENGTH_METERS) == 1524
5 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_MILES) == \
assert distance_util.convert(m, LENGTH_METERS, LENGTH_MILES) == \ 0.9469694040000001
3.106855
def test_convert_from_kilometers():
"""Test conversion from kilometers to other units."""
km = 5
assert distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_FEET) == 16404.2
assert distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_METERS) == 5000
assert distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_MILES) == \
3.106855
def test_convert_from_meters():
"""Test conversion from meters to other units."""
m = 5000
assert distance_util.convert(m, LENGTH_METERS, LENGTH_FEET) == 16404.2
assert distance_util.convert(m, LENGTH_METERS, LENGTH_KILOMETERS) == 5
assert distance_util.convert(m, LENGTH_METERS, LENGTH_MILES) == 3.106855

View File

@ -1,253 +1,266 @@
"""Test Home Assistant date util methods.""" """Test Home Assistant date util methods."""
import unittest
from datetime import datetime, timedelta from datetime import datetime, timedelta
import homeassistant.util.dt as dt_util
import pytest import pytest
import homeassistant.util.dt as dt_util
DEFAULT_TIME_ZONE = dt_util.DEFAULT_TIME_ZONE
TEST_TIME_ZONE = 'America/Los_Angeles' TEST_TIME_ZONE = 'America/Los_Angeles'
class TestDateUtil(unittest.TestCase): def teardown():
"""Test util date methods.""" """Stop everything that was started."""
dt_util.set_default_time_zone(DEFAULT_TIME_ZONE)
def setUp(self):
"""Set up the tests."""
self.orig_default_time_zone = dt_util.DEFAULT_TIME_ZONE
def tearDown(self): def test_get_time_zone_retrieves_valid_time_zone():
"""Stop everything that was started.""" """Test getting a time zone."""
dt_util.set_default_time_zone(self.orig_default_time_zone) time_zone = dt_util.get_time_zone(TEST_TIME_ZONE)
def test_get_time_zone_retrieves_valid_time_zone(self): assert time_zone is not None
"""Test getting a time zone.""" assert TEST_TIME_ZONE == time_zone.zone
time_zone = dt_util.get_time_zone(TEST_TIME_ZONE)
assert time_zone is not None
assert TEST_TIME_ZONE == time_zone.zone
def test_get_time_zone_returns_none_for_garbage_time_zone(self): def test_get_time_zone_returns_none_for_garbage_time_zone():
"""Test getting a non existing time zone.""" """Test getting a non existing time zone."""
time_zone = dt_util.get_time_zone("Non existing time zone") time_zone = dt_util.get_time_zone("Non existing time zone")
assert time_zone is None assert time_zone is None
def test_set_default_time_zone(self):
"""Test setting default time zone."""
time_zone = dt_util.get_time_zone(TEST_TIME_ZONE)
dt_util.set_default_time_zone(time_zone) def test_set_default_time_zone():
"""Test setting default time zone."""
time_zone = dt_util.get_time_zone(TEST_TIME_ZONE)
# We cannot compare the timezones directly because of DST dt_util.set_default_time_zone(time_zone)
assert time_zone.zone == dt_util.now().tzinfo.zone
def test_utcnow(self): # We cannot compare the timezones directly because of DST
"""Test the UTC now method.""" assert time_zone.zone == dt_util.now().tzinfo.zone
assert abs(dt_util.utcnow().replace(tzinfo=None)-datetime.utcnow()) < \
timedelta(seconds=1)
def test_now(self):
"""Test the now method."""
dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE))
assert abs( def test_utcnow():
dt_util.as_utc(dt_util.now()).replace( """Test the UTC now method."""
tzinfo=None assert abs(dt_util.utcnow().replace(tzinfo=None)-datetime.utcnow()) < \
) - datetime.utcnow() timedelta(seconds=1)
) < timedelta(seconds=1)
def test_as_utc_with_naive_object(self):
"""Test the now method."""
utcnow = datetime.utcnow()
assert utcnow == dt_util.as_utc(utcnow).replace(tzinfo=None) def test_now():
"""Test the now method."""
dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE))
def test_as_utc_with_utc_object(self): assert abs(
"""Test UTC time with UTC object.""" dt_util.as_utc(dt_util.now()).replace(
utcnow = dt_util.utcnow() tzinfo=None
) - datetime.utcnow()
) < timedelta(seconds=1)
assert utcnow == dt_util.as_utc(utcnow)
def test_as_utc_with_local_object(self): def test_as_utc_with_naive_object():
"""Test the UTC time with local object.""" """Test the now method."""
dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE)) utcnow = datetime.utcnow()
localnow = dt_util.now()
utcnow = dt_util.as_utc(localnow)
assert localnow == utcnow assert utcnow == dt_util.as_utc(utcnow).replace(tzinfo=None)
assert localnow.tzinfo != utcnow.tzinfo
def test_as_local_with_naive_object(self):
"""Test local time with native object."""
now = dt_util.now()
assert abs(now-dt_util.as_local(datetime.utcnow())) < \
timedelta(seconds=1)
def test_as_local_with_local_object(self): def test_as_utc_with_utc_object():
"""Test local with local object.""" """Test UTC time with UTC object."""
now = dt_util.now() utcnow = dt_util.utcnow()
assert now == now
def test_as_local_with_utc_object(self): assert utcnow == dt_util.as_utc(utcnow)
"""Test local time with UTC object."""
dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE))
utcnow = dt_util.utcnow()
localnow = dt_util.as_local(utcnow)
assert localnow == utcnow def test_as_utc_with_local_object():
assert localnow.tzinfo != utcnow.tzinfo """Test the UTC time with local object."""
dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE))
localnow = dt_util.now()
utcnow = dt_util.as_utc(localnow)
def test_utc_from_timestamp(self): assert localnow == utcnow
"""Test utc_from_timestamp method.""" assert localnow.tzinfo != utcnow.tzinfo
assert datetime(1986, 7, 9, tzinfo=dt_util.UTC) == \
dt_util.utc_from_timestamp(521251200)
def test_as_timestamp(self):
"""Test as_timestamp method."""
ts = 1462401234
utc_dt = dt_util.utc_from_timestamp(ts)
assert ts == dt_util.as_timestamp(utc_dt)
utc_iso = utc_dt.isoformat()
assert ts == dt_util.as_timestamp(utc_iso)
# confirm the ability to handle a string passed in def test_as_local_with_naive_object():
delta = dt_util.as_timestamp("2016-01-01 12:12:12") """Test local time with native object."""
delta -= dt_util.as_timestamp("2016-01-01 12:12:11") now = dt_util.now()
assert 1 == delta assert abs(now-dt_util.as_local(datetime.utcnow())) < \
timedelta(seconds=1)
def test_parse_datetime_converts_correctly(self):
"""Test parse_datetime converts strings."""
assert \
datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) == \
dt_util.parse_datetime("1986-07-09T12:00:00Z")
utcnow = dt_util.utcnow() def test_as_local_with_local_object():
"""Test local with local object."""
now = dt_util.now()
assert now == now
assert utcnow == dt_util.parse_datetime(utcnow.isoformat())
def test_parse_datetime_returns_none_for_incorrect_format(self): def test_as_local_with_utc_object():
"""Test parse_datetime returns None if incorrect format.""" """Test local time with UTC object."""
assert dt_util.parse_datetime("not a datetime string") is None dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE))
def test_get_age(self): utcnow = dt_util.utcnow()
"""Test get_age.""" localnow = dt_util.as_local(utcnow)
diff = dt_util.now() - timedelta(seconds=0)
assert dt_util.get_age(diff) == "0 seconds"
diff = dt_util.now() - timedelta(seconds=1) assert localnow == utcnow
assert dt_util.get_age(diff) == "1 second" assert localnow.tzinfo != utcnow.tzinfo
diff = dt_util.now() - timedelta(seconds=30)
assert dt_util.get_age(diff) == "30 seconds"
diff = dt_util.now() - timedelta(minutes=5) def test_utc_from_timestamp():
assert dt_util.get_age(diff) == "5 minutes" """Test utc_from_timestamp method."""
assert datetime(1986, 7, 9, tzinfo=dt_util.UTC) == \
dt_util.utc_from_timestamp(521251200)
diff = dt_util.now() - timedelta(minutes=1)
assert dt_util.get_age(diff) == "1 minute"
diff = dt_util.now() - timedelta(minutes=300) def test_as_timestamp():
assert dt_util.get_age(diff) == "5 hours" """Test as_timestamp method."""
ts = 1462401234
utc_dt = dt_util.utc_from_timestamp(ts)
assert ts == dt_util.as_timestamp(utc_dt)
utc_iso = utc_dt.isoformat()
assert ts == dt_util.as_timestamp(utc_iso)
diff = dt_util.now() - timedelta(minutes=320) # confirm the ability to handle a string passed in
assert dt_util.get_age(diff) == "5 hours" delta = dt_util.as_timestamp("2016-01-01 12:12:12")
delta -= dt_util.as_timestamp("2016-01-01 12:12:11")
assert delta == 1
diff = dt_util.now() - timedelta(minutes=2*60*24)
assert dt_util.get_age(diff) == "2 days"
diff = dt_util.now() - timedelta(minutes=32*60*24) def test_parse_datetime_converts_correctly():
assert dt_util.get_age(diff) == "1 month" """Test parse_datetime converts strings."""
assert \
datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) == \
dt_util.parse_datetime("1986-07-09T12:00:00Z")
diff = dt_util.now() - timedelta(minutes=365*60*24) utcnow = dt_util.utcnow()
assert dt_util.get_age(diff) == "1 year"
def test_parse_time_expression(self): assert utcnow == dt_util.parse_datetime(utcnow.isoformat())
"""Test parse_time_expression."""
assert [x for x in range(60)] == \
dt_util.parse_time_expression('*', 0, 59)
assert [x for x in range(60)] == \
dt_util.parse_time_expression(None, 0, 59)
assert [x for x in range(0, 60, 5)] == \
dt_util.parse_time_expression('/5', 0, 59)
assert [1, 2, 3] == \ def test_parse_datetime_returns_none_for_incorrect_format():
dt_util.parse_time_expression([2, 1, 3], 0, 59) """Test parse_datetime returns None if incorrect format."""
assert dt_util.parse_datetime("not a datetime string") is None
assert [x for x in range(24)] == \
dt_util.parse_time_expression('*', 0, 23)
assert [42] == \ def test_get_age():
dt_util.parse_time_expression(42, 0, 59) """Test get_age."""
diff = dt_util.now() - timedelta(seconds=0)
assert dt_util.get_age(diff) == "0 seconds"
with pytest.raises(ValueError): diff = dt_util.now() - timedelta(seconds=1)
dt_util.parse_time_expression(61, 0, 60) assert dt_util.get_age(diff) == "1 second"
def test_find_next_time_expression_time_basic(self): diff = dt_util.now() - timedelta(seconds=30)
"""Test basic stuff for find_next_time_expression_time.""" assert dt_util.get_age(diff) == "30 seconds"
def find(dt, hour, minute, second):
"""Call test_find_next_time_expression_time."""
seconds = dt_util.parse_time_expression(second, 0, 59)
minutes = dt_util.parse_time_expression(minute, 0, 59)
hours = dt_util.parse_time_expression(hour, 0, 23)
return dt_util.find_next_time_expression_time( diff = dt_util.now() - timedelta(minutes=5)
dt, seconds, minutes, hours) assert dt_util.get_age(diff) == "5 minutes"
assert datetime(2018, 10, 7, 10, 30, 0) == \ diff = dt_util.now() - timedelta(minutes=1)
find(datetime(2018, 10, 7, 10, 20, 0), '*', '/30', 0) assert dt_util.get_age(diff) == "1 minute"
assert datetime(2018, 10, 7, 10, 30, 0) == \ diff = dt_util.now() - timedelta(minutes=300)
find(datetime(2018, 10, 7, 10, 30, 0), '*', '/30', 0) assert dt_util.get_age(diff) == "5 hours"
assert datetime(2018, 10, 7, 12, 30, 30) == \ diff = dt_util.now() - timedelta(minutes=320)
find(datetime(2018, 10, 7, 10, 30, 0), '/3', '/30', [30, 45]) assert dt_util.get_age(diff) == "5 hours"
assert datetime(2018, 10, 8, 5, 0, 0) == \ diff = dt_util.now() - timedelta(minutes=2*60*24)
find(datetime(2018, 10, 7, 10, 30, 0), 5, 0, 0) assert dt_util.get_age(diff) == "2 days"
def test_find_next_time_expression_time_dst(self): diff = dt_util.now() - timedelta(minutes=32*60*24)
"""Test daylight saving time for find_next_time_expression_time.""" assert dt_util.get_age(diff) == "1 month"
tz = dt_util.get_time_zone('Europe/Vienna')
dt_util.set_default_time_zone(tz)
def find(dt, hour, minute, second): diff = dt_util.now() - timedelta(minutes=365*60*24)
"""Call test_find_next_time_expression_time.""" assert dt_util.get_age(diff) == "1 year"
seconds = dt_util.parse_time_expression(second, 0, 59)
minutes = dt_util.parse_time_expression(minute, 0, 59)
hours = dt_util.parse_time_expression(hour, 0, 23)
return dt_util.find_next_time_expression_time(
dt, seconds, minutes, hours)
# Entering DST, clocks are rolled forward def test_parse_time_expression():
assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == \ """Test parse_time_expression."""
find(tz.localize(datetime(2018, 3, 25, 1, 50, 0)), 2, 30, 0) assert [x for x in range(60)] == \
dt_util.parse_time_expression('*', 0, 59)
assert [x for x in range(60)] == \
dt_util.parse_time_expression(None, 0, 59)
assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == \ assert [x for x in range(0, 60, 5)] == \
find(tz.localize(datetime(2018, 3, 25, 3, 50, 0)), 2, 30, 0) dt_util.parse_time_expression('/5', 0, 59)
assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == \ assert [1, 2, 3] == \
find(tz.localize(datetime(2018, 3, 26, 1, 50, 0)), 2, 30, 0) dt_util.parse_time_expression([2, 1, 3], 0, 59)
# Leaving DST, clocks are rolled back assert [x for x in range(24)] == \
assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=False) == \ dt_util.parse_time_expression('*', 0, 23)
find(tz.localize(datetime(2018, 10, 28, 2, 5, 0), is_dst=False),
2, 30, 0)
assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=False) == \ assert [42] == \
find(tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=True), dt_util.parse_time_expression(42, 0, 59)
2, 30, 0)
assert tz.localize(datetime(2018, 10, 28, 4, 30, 0), is_dst=False) == \ with pytest.raises(ValueError):
find(tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=True), dt_util.parse_time_expression(61, 0, 60)
4, 30, 0)
assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=True) == \
find(tz.localize(datetime(2018, 10, 28, 2, 5, 0), is_dst=True),
2, 30, 0)
assert tz.localize(datetime(2018, 10, 29, 2, 30, 0)) == \ def test_find_next_time_expression_time_basic():
find(tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=False), """Test basic stuff for find_next_time_expression_time."""
2, 30, 0) def find(dt, hour, minute, second):
"""Call test_find_next_time_expression_time."""
seconds = dt_util.parse_time_expression(second, 0, 59)
minutes = dt_util.parse_time_expression(minute, 0, 59)
hours = dt_util.parse_time_expression(hour, 0, 23)
return dt_util.find_next_time_expression_time(
dt, seconds, minutes, hours)
assert datetime(2018, 10, 7, 10, 30, 0) == \
find(datetime(2018, 10, 7, 10, 20, 0), '*', '/30', 0)
assert datetime(2018, 10, 7, 10, 30, 0) == \
find(datetime(2018, 10, 7, 10, 30, 0), '*', '/30', 0)
assert datetime(2018, 10, 7, 12, 30, 30) == \
find(datetime(2018, 10, 7, 10, 30, 0), '/3', '/30', [30, 45])
assert datetime(2018, 10, 8, 5, 0, 0) == \
find(datetime(2018, 10, 7, 10, 30, 0), 5, 0, 0)
def test_find_next_time_expression_time_dst():
"""Test daylight saving time for find_next_time_expression_time."""
tz = dt_util.get_time_zone('Europe/Vienna')
dt_util.set_default_time_zone(tz)
def find(dt, hour, minute, second):
"""Call test_find_next_time_expression_time."""
seconds = dt_util.parse_time_expression(second, 0, 59)
minutes = dt_util.parse_time_expression(minute, 0, 59)
hours = dt_util.parse_time_expression(hour, 0, 23)
return dt_util.find_next_time_expression_time(
dt, seconds, minutes, hours)
# Entering DST, clocks are rolled forward
assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == \
find(tz.localize(datetime(2018, 3, 25, 1, 50, 0)), 2, 30, 0)
assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == \
find(tz.localize(datetime(2018, 3, 25, 3, 50, 0)), 2, 30, 0)
assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == \
find(tz.localize(datetime(2018, 3, 26, 1, 50, 0)), 2, 30, 0)
# Leaving DST, clocks are rolled back
assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=False) == \
find(tz.localize(datetime(2018, 10, 28, 2, 5, 0), is_dst=False),
2, 30, 0)
assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=False) == \
find(tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=True),
2, 30, 0)
assert tz.localize(datetime(2018, 10, 28, 4, 30, 0), is_dst=False) == \
find(tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=True),
4, 30, 0)
assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=True) == \
find(tz.localize(datetime(2018, 10, 28, 2, 5, 0), is_dst=True),
2, 30, 0)
assert tz.localize(datetime(2018, 10, 29, 2, 30, 0)) == \
find(tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=False),
2, 30, 0)

View File

@ -1,223 +1,228 @@
"""Test Home Assistant util methods.""" """Test Home Assistant util methods."""
import unittest
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from datetime import datetime, timedelta from datetime import datetime, timedelta
from homeassistant import util
import homeassistant.util.dt as dt_util
import pytest import pytest
from homeassistant import util
import homeassistant.util.dt as dt_util
class TestUtil(unittest.TestCase):
"""Test util methods."""
def test_sanitize_filename(self): def test_sanitize_filename():
"""Test sanitize_filename.""" """Test sanitize_filename."""
assert "test" == util.sanitize_filename("test") assert util.sanitize_filename("test") == 'test'
assert "test" == util.sanitize_filename("/test") assert util.sanitize_filename("/test") == 'test'
assert "test" == util.sanitize_filename("..test") assert util.sanitize_filename("..test") == 'test'
assert "test" == util.sanitize_filename("\\test") assert util.sanitize_filename("\\test") == 'test'
assert "test" == util.sanitize_filename("\\../test") assert util.sanitize_filename("\\../test") == 'test'
def test_sanitize_path(self):
"""Test sanitize_path."""
assert "test/path" == util.sanitize_path("test/path")
assert "test/path" == util.sanitize_path("~test/path")
assert "//test/path" == util.sanitize_path("~/../test/path")
def test_slugify(self): def test_sanitize_path():
"""Test slugify.""" """Test sanitize_path."""
assert "t_est" == util.slugify("T-!@#$!#@$!$est") assert util.sanitize_path("test/path") == 'test/path'
assert "test_more" == util.slugify("Test More") assert util.sanitize_path("~test/path") == 'test/path'
assert "test_more" == util.slugify("Test_(More)") assert util.sanitize_path("~/../test/path") == '//test/path'
assert "test_more" == util.slugify("Tèst_Mörê")
assert "b8_27_eb_00_00_00" == util.slugify("B8:27:EB:00:00:00")
assert "test_com" == util.slugify("test.com")
assert "greg_phone_exp_wayp1" == \
util.slugify("greg_phone - exp_wayp1")
assert "we_are_we_are_a_test_calendar" == \
util.slugify("We are, we are, a... Test Calendar")
assert "test_aouss_aou" == util.slugify("Tèst_äöüß_ÄÖÜ")
assert "ying_shi_ma" == util.slugify("影師嗎")
assert "keihuonto" == util.slugify("けいふぉんと")
def test_repr_helper(self):
"""Test repr_helper."""
assert "A" == util.repr_helper("A")
assert "5" == util.repr_helper(5)
assert "True" == util.repr_helper(True)
assert "test=1" == util.repr_helper({"test": 1})
assert "1986-07-09T12:00:00+00:00" == \
util.repr_helper(datetime(1986, 7, 9, 12, 0, 0))
def test_convert(self): def test_slugify():
"""Test convert.""" """Test slugify."""
assert 5 == util.convert("5", int) assert util.slugify("T-!@#$!#@$!$est") == 't_est'
assert 5.0 == util.convert("5", float) assert util.slugify("Test More") == 'test_more'
assert util.convert("True", bool) is True assert util.slugify("Test_(More)") == 'test_more'
assert 1 == util.convert("NOT A NUMBER", int, 1) assert util.slugify("Tèst_Mörê") == 'test_more'
assert 1 == util.convert(None, int, 1) assert util.slugify("B8:27:EB:00:00:00") == 'b8_27_eb_00_00_00'
assert 1 == util.convert(object, int, 1) assert util.slugify("test.com") == 'test_com'
assert util.slugify("greg_phone - exp_wayp1") == 'greg_phone_exp_wayp1'
assert util.slugify("We are, we are, a... Test Calendar") == \
'we_are_we_are_a_test_calendar'
assert util.slugify("Tèst_äöüß_ÄÖÜ") == 'test_aouss_aou'
assert util.slugify("影師嗎") == 'ying_shi_ma'
assert util.slugify("けいふぉんと") == 'keihuonto'
def test_ensure_unique_string(self):
"""Test ensure_unique_string."""
assert "Beer_3" == \
util.ensure_unique_string("Beer", ["Beer", "Beer_2"])
assert "Beer" == \
util.ensure_unique_string("Beer", ["Wine", "Soda"])
def test_ordered_enum(self): def test_repr_helper():
"""Test the ordered enum class.""" """Test repr_helper."""
class TestEnum(util.OrderedEnum): assert util.repr_helper("A") == 'A'
"""Test enum that can be ordered.""" assert util.repr_helper(5) == '5'
assert util.repr_helper(True) == 'True'
assert util.repr_helper({"test": 1}) == 'test=1'
assert util.repr_helper(datetime(1986, 7, 9, 12, 0, 0)) == \
'1986-07-09T12:00:00+00:00'
FIRST = 1
SECOND = 2
THIRD = 3
assert TestEnum.SECOND >= TestEnum.FIRST def test_convert():
assert TestEnum.SECOND >= TestEnum.SECOND """Test convert."""
assert not (TestEnum.SECOND >= TestEnum.THIRD) assert util.convert("5", int) == 5
assert util.convert("5", float) == 5.0
assert util.convert("True", bool) is True
assert util.convert("NOT A NUMBER", int, 1) == 1
assert util.convert(None, int, 1) == 1
assert util.convert(object, int, 1) == 1
assert TestEnum.SECOND > TestEnum.FIRST
assert not (TestEnum.SECOND > TestEnum.SECOND)
assert not (TestEnum.SECOND > TestEnum.THIRD)
assert not (TestEnum.SECOND <= TestEnum.FIRST) def test_ensure_unique_string():
assert TestEnum.SECOND <= TestEnum.SECOND """Test ensure_unique_string."""
assert TestEnum.SECOND <= TestEnum.THIRD assert util.ensure_unique_string("Beer", ["Beer", "Beer_2"]) == 'Beer_3'
assert util.ensure_unique_string("Beer", ["Wine", "Soda"]) == 'Beer'
assert not (TestEnum.SECOND < TestEnum.FIRST)
assert not (TestEnum.SECOND < TestEnum.SECOND)
assert TestEnum.SECOND < TestEnum.THIRD
# Python will raise a TypeError if the <, <=, >, >= methods def test_ordered_enum():
# raise a NotImplemented error. """Test the ordered enum class."""
with pytest.raises(TypeError): class TestEnum(util.OrderedEnum):
TestEnum.FIRST < 1 """Test enum that can be ordered."""
with pytest.raises(TypeError): FIRST = 1
TestEnum.FIRST <= 1 SECOND = 2
THIRD = 3
with pytest.raises(TypeError): assert TestEnum.SECOND >= TestEnum.FIRST
TestEnum.FIRST > 1 assert TestEnum.SECOND >= TestEnum.SECOND
assert TestEnum.SECOND < TestEnum.THIRD
with pytest.raises(TypeError): assert TestEnum.SECOND > TestEnum.FIRST
TestEnum.FIRST >= 1 assert TestEnum.SECOND <= TestEnum.SECOND
assert TestEnum.SECOND <= TestEnum.THIRD
def test_throttle(self): assert TestEnum.SECOND > TestEnum.FIRST
"""Test the add cooldown decorator.""" assert TestEnum.SECOND <= TestEnum.SECOND
calls1 = [] assert TestEnum.SECOND <= TestEnum.THIRD
calls2 = []
@util.Throttle(timedelta(seconds=4)) assert TestEnum.SECOND >= TestEnum.FIRST
def test_throttle1(): assert TestEnum.SECOND >= TestEnum.SECOND
calls1.append(1) assert TestEnum.SECOND < TestEnum.THIRD
@util.Throttle(timedelta(seconds=4), timedelta(seconds=2)) # Python will raise a TypeError if the <, <=, >, >= methods
def test_throttle2(): # raise a NotImplemented error.
calls2.append(1) with pytest.raises(TypeError):
TestEnum.FIRST < 1
now = dt_util.utcnow() with pytest.raises(TypeError):
plus3 = now + timedelta(seconds=3) TestEnum.FIRST <= 1
plus5 = plus3 + timedelta(seconds=2)
# Call first time and ensure methods got called with pytest.raises(TypeError):
TestEnum.FIRST > 1
with pytest.raises(TypeError):
TestEnum.FIRST >= 1
def test_throttle():
"""Test the add cooldown decorator."""
calls1 = []
calls2 = []
@util.Throttle(timedelta(seconds=4))
def test_throttle1():
calls1.append(1)
@util.Throttle(timedelta(seconds=4), timedelta(seconds=2))
def test_throttle2():
calls2.append(1)
now = dt_util.utcnow()
plus3 = now + timedelta(seconds=3)
plus5 = plus3 + timedelta(seconds=2)
# Call first time and ensure methods got called
test_throttle1()
test_throttle2()
assert len(calls1) == 1
assert len(calls2) == 1
# Call second time. Methods should not get called
test_throttle1()
test_throttle2()
assert len(calls1) == 1
assert len(calls2) == 1
# Call again, overriding throttle, only first one should fire
test_throttle1(no_throttle=True)
test_throttle2(no_throttle=True)
assert len(calls1) == 2
assert len(calls2) == 1
with patch('homeassistant.util.utcnow', return_value=plus3):
test_throttle1() test_throttle1()
test_throttle2() test_throttle2()
assert 1 == len(calls1) assert len(calls1) == 2
assert 1 == len(calls2) assert len(calls2) == 1
# Call second time. Methods should not get called with patch('homeassistant.util.utcnow', return_value=plus5):
test_throttle1() test_throttle1()
test_throttle2() test_throttle2()
assert 1 == len(calls1) assert len(calls1) == 3
assert 1 == len(calls2) assert len(calls2) == 2
# Call again, overriding throttle, only first one should fire
test_throttle1(no_throttle=True)
test_throttle2(no_throttle=True)
assert 2 == len(calls1) def test_throttle_per_instance():
assert 1 == len(calls2) """Test that the throttle method is done per instance of a class."""
class Tester:
"""A tester class for the throttle."""
with patch('homeassistant.util.utcnow', return_value=plus3): @util.Throttle(timedelta(seconds=1))
test_throttle1() def hello(self):
test_throttle2() """Test the throttle."""
return True
assert 2 == len(calls1) assert Tester().hello()
assert 1 == len(calls2) assert Tester().hello()
with patch('homeassistant.util.utcnow', return_value=plus5):
test_throttle1()
test_throttle2()
assert 3 == len(calls1) def test_throttle_on_method():
assert 2 == len(calls2) """Test that throttle works when wrapping a method."""
class Tester:
"""A tester class for the throttle."""
def test_throttle_per_instance(self): def hello(self):
"""Test that the throttle method is done per instance of a class.""" """Test the throttle."""
class Tester: return True
"""A tester class for the throttle."""
@util.Throttle(timedelta(seconds=1)) tester = Tester()
def hello(self): throttled = util.Throttle(timedelta(seconds=1))(tester.hello)
"""Test the throttle."""
return True
assert Tester().hello() assert throttled()
assert Tester().hello() assert throttled() is None
def test_throttle_on_method(self):
"""Test that throttle works when wrapping a method."""
class Tester:
"""A tester class for the throttle."""
def hello(self): def test_throttle_on_two_method():
"""Test the throttle.""" """Test that throttle works when wrapping two methods."""
return True class Tester:
"""A test class for the throttle."""
tester = Tester() @util.Throttle(timedelta(seconds=1))
throttled = util.Throttle(timedelta(seconds=1))(tester.hello) def hello(self):
"""Test the throttle."""
return True
assert throttled() @util.Throttle(timedelta(seconds=1))
assert throttled() is None def goodbye(self):
"""Test the throttle."""
return True
def test_throttle_on_two_method(self): tester = Tester()
"""Test that throttle works when wrapping two methods."""
class Tester:
"""A test class for the throttle."""
@util.Throttle(timedelta(seconds=1)) assert tester.hello()
def hello(self): assert tester.goodbye()
"""Test the throttle."""
return True
@util.Throttle(timedelta(seconds=1))
def goodbye(self):
"""Test the throttle."""
return True
tester = Tester() @patch.object(util, 'random')
def test_get_random_string(mock_random):
"""Test get random string."""
results = ['A', 'B', 'C']
assert tester.hello() def mock_choice(choices):
assert tester.goodbye() return results.pop(0)
@patch.object(util, 'random') generator = MagicMock()
def test_get_random_string(self, mock_random): generator.choice.side_effect = mock_choice
"""Test get random string.""" mock_random.SystemRandom.return_value = generator
results = ['A', 'B', 'C']
def mock_choice(choices): assert util.get_random_string(length=3) == 'ABC'
return results.pop(0)
generator = MagicMock()
generator.choice.side_effect = mock_choice
mock_random.SystemRandom.return_value = generator
assert util.get_random_string(length=3) == 'ABC'
async def test_throttle_async(): async def test_throttle_async():

View File

@ -2,15 +2,16 @@
from json import JSONEncoder from json import JSONEncoder
import os import os
import unittest import unittest
from unittest.mock import Mock
import sys import sys
from tempfile import mkdtemp from tempfile import mkdtemp
import pytest
from homeassistant.util.json import ( from homeassistant.util.json import (
SerializationError, load_json, save_json) SerializationError, load_json, save_json)
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
import pytest
from unittest.mock import Mock
# Test data that can be saved as JSON # Test data that can be saved as JSON
TEST_JSON_A = {"a": 1, "B": "two"} TEST_JSON_A = {"a": 1, "B": "two"}
@ -19,75 +20,82 @@ TEST_JSON_B = {"a": "one", "B": 2}
TEST_BAD_OBJECT = {("A",): 1} TEST_BAD_OBJECT = {("A",): 1}
# Test data that can not be loaded as JSON # Test data that can not be loaded as JSON
TEST_BAD_SERIALIED = "THIS IS NOT JSON\n" TEST_BAD_SERIALIED = "THIS IS NOT JSON\n"
TMP_DIR = None
class TestJSON(unittest.TestCase): def setup():
"""Test util.json save and load.""" """Set up for tests."""
global TMP_DIR
TMP_DIR = mkdtemp()
def setUp(self):
"""Set up for tests."""
self.tmp_dir = mkdtemp()
def tearDown(self): def teardown():
"""Clean up after tests.""" """Clean up after tests."""
for fname in os.listdir(self.tmp_dir): for fname in os.listdir(TMP_DIR):
os.remove(os.path.join(self.tmp_dir, fname)) os.remove(os.path.join(TMP_DIR, fname))
os.rmdir(self.tmp_dir) os.rmdir(TMP_DIR)
def _path_for(self, leaf_name):
return os.path.join(self.tmp_dir, leaf_name+".json")
def test_save_and_load(self): def _path_for(leaf_name):
"""Test saving and loading back.""" return os.path.join(TMP_DIR, leaf_name+".json")
fname = self._path_for("test1")
save_json(fname, TEST_JSON_A)
data = load_json(fname)
assert data == TEST_JSON_A
# Skipped on Windows
@unittest.skipIf(sys.platform.startswith('win'),
"private permissions not supported on Windows")
def test_save_and_load_private(self):
"""Test we can load private files and that they are protected."""
fname = self._path_for("test2")
save_json(fname, TEST_JSON_A, private=True)
data = load_json(fname)
assert data == TEST_JSON_A
stats = os.stat(fname)
assert stats.st_mode & 0o77 == 0
def test_overwrite_and_reload(self): def test_save_and_load():
"""Test that we can overwrite an existing file and read back.""" """Test saving and loading back."""
fname = self._path_for("test3") fname = _path_for("test1")
save_json(fname, TEST_JSON_A) save_json(fname, TEST_JSON_A)
save_json(fname, TEST_JSON_B) data = load_json(fname)
data = load_json(fname) assert data == TEST_JSON_A
assert data == TEST_JSON_B
def test_save_bad_data(self):
"""Test error from trying to save unserialisable data."""
fname = self._path_for("test4")
with pytest.raises(SerializationError):
save_json(fname, TEST_BAD_OBJECT)
def test_load_bad_data(self): # Skipped on Windows
"""Test error from trying to load unserialisable data.""" @unittest.skipIf(sys.platform.startswith('win'),
fname = self._path_for("test5") "private permissions not supported on Windows")
with open(fname, "w") as fh: def test_save_and_load_private():
fh.write(TEST_BAD_SERIALIED) """Test we can load private files and that they are protected."""
with pytest.raises(HomeAssistantError): fname = _path_for("test2")
load_json(fname) save_json(fname, TEST_JSON_A, private=True)
data = load_json(fname)
assert data == TEST_JSON_A
stats = os.stat(fname)
assert stats.st_mode & 0o77 == 0
def test_custom_encoder(self):
"""Test serializing with a custom encoder."""
class MockJSONEncoder(JSONEncoder):
"""Mock JSON encoder."""
def default(self, o): def test_overwrite_and_reload():
"""Mock JSON encode method.""" """Test that we can overwrite an existing file and read back."""
return "9" fname = _path_for("test3")
save_json(fname, TEST_JSON_A)
save_json(fname, TEST_JSON_B)
data = load_json(fname)
assert data == TEST_JSON_B
fname = self._path_for("test6")
save_json(fname, Mock(), encoder=MockJSONEncoder) def test_save_bad_data():
data = load_json(fname) """Test error from trying to save unserialisable data."""
self.assertEqual(data, "9") fname = _path_for("test4")
with pytest.raises(SerializationError):
save_json(fname, TEST_BAD_OBJECT)
def test_load_bad_data():
"""Test error from trying to load unserialisable data."""
fname = _path_for("test5")
with open(fname, "w") as fh:
fh.write(TEST_BAD_SERIALIED)
with pytest.raises(HomeAssistantError):
load_json(fname)
def test_custom_encoder():
"""Test serializing with a custom encoder."""
class MockJSONEncoder(JSONEncoder):
"""Mock JSON encoder."""
def default(self, o):
"""Mock JSON encode method."""
return "9"
fname = _path_for("test6")
save_json(fname, Mock(), encoder=MockJSONEncoder)
data = load_json(fname)
assert data == "9"

View File

@ -1,5 +1,4 @@
"""Test homeassistant pressure utility functions.""" """Test homeassistant pressure utility functions."""
import unittest
import pytest import pytest
from homeassistant.const import (PRESSURE_PA, PRESSURE_HPA, PRESSURE_MBAR, from homeassistant.const import (PRESSURE_PA, PRESSURE_HPA, PRESSURE_MBAR,
@ -10,57 +9,50 @@ INVALID_SYMBOL = 'bob'
VALID_SYMBOL = PRESSURE_PA VALID_SYMBOL = PRESSURE_PA
class TestPressureUtil(unittest.TestCase): def test_convert_same_unit():
"""Test the pressure utility functions.""" """Test conversion from any unit to same unit."""
assert pressure_util.convert(2, PRESSURE_PA, PRESSURE_PA) == 2
assert pressure_util.convert(3, PRESSURE_HPA, PRESSURE_HPA) == 3
assert pressure_util.convert(4, PRESSURE_MBAR, PRESSURE_MBAR) == 4
assert pressure_util.convert(5, PRESSURE_INHG, PRESSURE_INHG) == 5
def test_convert_same_unit(self):
"""Test conversion from any unit to same unit."""
assert pressure_util.convert(2, PRESSURE_PA, PRESSURE_PA) == 2
assert pressure_util.convert(3, PRESSURE_HPA, PRESSURE_HPA) == 3
assert pressure_util.convert(4, PRESSURE_MBAR, PRESSURE_MBAR) == 4
assert pressure_util.convert(5, PRESSURE_INHG, PRESSURE_INHG) == 5
def test_convert_invalid_unit(self): def test_convert_invalid_unit():
"""Test exception is thrown for invalid units.""" """Test exception is thrown for invalid units."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
pressure_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL) pressure_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL)
with pytest.raises(ValueError): with pytest.raises(ValueError):
pressure_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL) pressure_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL)
def test_convert_nonnumeric_value(self):
"""Test exception is thrown for nonnumeric type."""
with pytest.raises(TypeError):
pressure_util.convert('a', PRESSURE_HPA, PRESSURE_INHG)
def test_convert_from_hpascals(self): def test_convert_nonnumeric_value():
"""Test conversion from hPA to other units.""" """Test exception is thrown for nonnumeric type."""
hpascals = 1000 with pytest.raises(TypeError):
self.assertAlmostEqual( pressure_util.convert('a', PRESSURE_HPA, PRESSURE_INHG)
pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_PSI),
14.5037743897)
self.assertAlmostEqual(
pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_INHG),
29.5299801647)
self.assertAlmostEqual(
pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_PA),
100000)
self.assertAlmostEqual(
pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_MBAR),
1000)
def test_convert_from_inhg(self):
"""Test conversion from inHg to other units.""" def test_convert_from_hpascals():
inhg = 30 """Test conversion from hPA to other units."""
self.assertAlmostEqual( hpascals = 1000
pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_PSI), assert pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_PSI) == \
14.7346266155) pytest.approx(14.5037743897)
self.assertAlmostEqual( assert pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_INHG) == \
pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_HPA), pytest.approx(29.5299801647)
1015.9167) assert pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_PA) == \
self.assertAlmostEqual( pytest.approx(100000)
pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_PA), assert pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_MBAR) == \
101591.67) pytest.approx(1000)
self.assertAlmostEqual(
pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_MBAR),
1015.9167) def test_convert_from_inhg():
"""Test conversion from inHg to other units."""
inhg = 30
assert pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_PSI) == \
pytest.approx(14.7346266155)
assert pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_HPA) == \
pytest.approx(1015.9167)
assert pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_PA) == \
pytest.approx(101591.67)
assert pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_MBAR) == \
pytest.approx(1015.9167)

View File

@ -1,6 +1,5 @@
"""Test Home Assistant ruamel.yaml loader.""" """Test Home Assistant ruamel.yaml loader."""
import os import os
import unittest
from tempfile import mkdtemp from tempfile import mkdtemp
import pytest import pytest
@ -114,45 +113,51 @@ views:
cards: !include cards.yaml cards: !include cards.yaml
""" """
TMP_DIR = None
class TestYAML(unittest.TestCase):
"""Test lovelace.yaml save and load."""
def setUp(self): def setup():
"""Set up for tests.""" """Set up for tests."""
self.tmp_dir = mkdtemp() global TMP_DIR
self.yaml = YAML(typ='rt') TMP_DIR = mkdtemp()
def tearDown(self):
"""Clean up after tests."""
for fname in os.listdir(self.tmp_dir):
os.remove(os.path.join(self.tmp_dir, fname))
os.rmdir(self.tmp_dir)
def _path_for(self, leaf_name): def teardown():
return os.path.join(self.tmp_dir, leaf_name+".yaml") """Clean up after tests."""
for fname in os.listdir(TMP_DIR):
os.remove(os.path.join(TMP_DIR, fname))
os.rmdir(TMP_DIR)
def test_save_and_load(self):
"""Test saving and loading back."""
fname = self._path_for("test1")
open(fname, "w+").close()
util_yaml.save_yaml(fname, self.yaml.load(TEST_YAML_A))
data = util_yaml.load_yaml(fname, True)
assert data == self.yaml.load(TEST_YAML_A)
def test_overwrite_and_reload(self): def _path_for(leaf_name):
"""Test that we can overwrite an existing file and read back.""" return os.path.join(TMP_DIR, leaf_name+".yaml")
fname = self._path_for("test2")
open(fname, "w+").close()
util_yaml.save_yaml(fname, self.yaml.load(TEST_YAML_A))
util_yaml.save_yaml(fname, self.yaml.load(TEST_YAML_B))
data = util_yaml.load_yaml(fname, True)
assert data == self.yaml.load(TEST_YAML_B)
def test_load_bad_data(self):
"""Test error from trying to load unserialisable data.""" def test_save_and_load():
fname = self._path_for("test3") """Test saving and loading back."""
with open(fname, "w") as fh: yaml = YAML(typ='rt')
fh.write(TEST_BAD_YAML) fname = _path_for("test1")
with pytest.raises(HomeAssistantError): open(fname, "w+").close()
util_yaml.load_yaml(fname, True) util_yaml.save_yaml(fname, yaml.load(TEST_YAML_A))
data = util_yaml.load_yaml(fname, True)
assert data == yaml.load(TEST_YAML_A)
def test_overwrite_and_reload():
"""Test that we can overwrite an existing file and read back."""
yaml = YAML(typ='rt')
fname = _path_for("test2")
open(fname, "w+").close()
util_yaml.save_yaml(fname, yaml.load(TEST_YAML_A))
util_yaml.save_yaml(fname, yaml.load(TEST_YAML_B))
data = util_yaml.load_yaml(fname, True)
assert data == yaml.load(TEST_YAML_B)
def test_load_bad_data():
"""Test error from trying to load unserialisable data."""
fname = _path_for("test3")
with open(fname, "w") as fh:
fh.write(TEST_BAD_YAML)
with pytest.raises(HomeAssistantError):
util_yaml.load_yaml(fname, True)

View File

@ -1,5 +1,5 @@
"""Test the unit system helper.""" """Test the unit system helper."""
import unittest import pytest
from homeassistant.util.unit_system import ( from homeassistant.util.unit_system import (
UnitSystem, UnitSystem,
@ -19,141 +19,138 @@ from homeassistant.const import (
TEMPERATURE, TEMPERATURE,
VOLUME VOLUME
) )
import pytest
SYSTEM_NAME = 'TEST' SYSTEM_NAME = 'TEST'
INVALID_UNIT = 'INVALID' INVALID_UNIT = 'INVALID'
class TestUnitSystem(unittest.TestCase): def test_invalid_units():
"""Test the unit system helper.""" """Test errors are raised when invalid units are passed in."""
with pytest.raises(ValueError):
UnitSystem(SYSTEM_NAME, INVALID_UNIT, LENGTH_METERS, VOLUME_LITERS,
MASS_GRAMS, PRESSURE_PA)
def test_invalid_units(self): with pytest.raises(ValueError):
"""Test errors are raised when invalid units are passed in.""" UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, INVALID_UNIT, VOLUME_LITERS,
with pytest.raises(ValueError): MASS_GRAMS, PRESSURE_PA)
UnitSystem(SYSTEM_NAME, INVALID_UNIT, LENGTH_METERS, VOLUME_LITERS,
MASS_GRAMS, PRESSURE_PA)
with pytest.raises(ValueError): with pytest.raises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, INVALID_UNIT, VOLUME_LITERS, UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, INVALID_UNIT,
MASS_GRAMS, PRESSURE_PA) MASS_GRAMS, PRESSURE_PA)
with pytest.raises(ValueError): with pytest.raises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, INVALID_UNIT, UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS,
MASS_GRAMS, PRESSURE_PA) INVALID_UNIT, PRESSURE_PA)
with pytest.raises(ValueError): with pytest.raises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS, UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS,
INVALID_UNIT, PRESSURE_PA) MASS_GRAMS, INVALID_UNIT)
with pytest.raises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS,
MASS_GRAMS, INVALID_UNIT)
def test_invalid_value(self): def test_invalid_value():
"""Test no conversion happens if value is non-numeric.""" """Test no conversion happens if value is non-numeric."""
with pytest.raises(TypeError): with pytest.raises(TypeError):
METRIC_SYSTEM.length('25a', LENGTH_KILOMETERS) METRIC_SYSTEM.length('25a', LENGTH_KILOMETERS)
with pytest.raises(TypeError): with pytest.raises(TypeError):
METRIC_SYSTEM.temperature('50K', TEMP_CELSIUS) METRIC_SYSTEM.temperature('50K', TEMP_CELSIUS)
with pytest.raises(TypeError): with pytest.raises(TypeError):
METRIC_SYSTEM.volume('50L', VOLUME_LITERS) METRIC_SYSTEM.volume('50L', VOLUME_LITERS)
with pytest.raises(TypeError): with pytest.raises(TypeError):
METRIC_SYSTEM.pressure('50Pa', PRESSURE_PA) METRIC_SYSTEM.pressure('50Pa', PRESSURE_PA)
def test_as_dict(self):
"""Test that the as_dict() method returns the expected dictionary."""
expected = {
LENGTH: LENGTH_KILOMETERS,
TEMPERATURE: TEMP_CELSIUS,
VOLUME: VOLUME_LITERS,
MASS: MASS_GRAMS,
PRESSURE: PRESSURE_PA
}
assert expected == METRIC_SYSTEM.as_dict() def test_as_dict():
"""Test that the as_dict() method returns the expected dictionary."""
expected = {
LENGTH: LENGTH_KILOMETERS,
TEMPERATURE: TEMP_CELSIUS,
VOLUME: VOLUME_LITERS,
MASS: MASS_GRAMS,
PRESSURE: PRESSURE_PA
}
def test_temperature_same_unit(self): assert expected == METRIC_SYSTEM.as_dict()
"""Test no conversion happens if to unit is same as from unit."""
assert 5 == \
METRIC_SYSTEM.temperature(5,
METRIC_SYSTEM.temperature_unit)
def test_temperature_unknown_unit(self):
"""Test no conversion happens if unknown unit."""
with pytest.raises(ValueError):
METRIC_SYSTEM.temperature(5, 'K')
def test_temperature_to_metric(self): def test_temperature_same_unit():
"""Test temperature conversion to metric system.""" """Test no conversion happens if to unit is same as from unit."""
assert 25 == \ assert METRIC_SYSTEM.temperature(5, METRIC_SYSTEM.temperature_unit) == 5
METRIC_SYSTEM.temperature(25, METRIC_SYSTEM.temperature_unit)
assert 26.7 == \
round(METRIC_SYSTEM.temperature(
80, IMPERIAL_SYSTEM.temperature_unit), 1)
def test_temperature_to_imperial(self):
"""Test temperature conversion to imperial system."""
assert 77 == \
IMPERIAL_SYSTEM.temperature(77, IMPERIAL_SYSTEM.temperature_unit)
assert 77 == \
IMPERIAL_SYSTEM.temperature(25, METRIC_SYSTEM.temperature_unit)
def test_length_unknown_unit(self): def test_temperature_unknown_unit():
"""Test length conversion with unknown from unit.""" """Test no conversion happens if unknown unit."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
METRIC_SYSTEM.length(5, 'fr') METRIC_SYSTEM.temperature(5, 'K')
def test_length_to_metric(self):
"""Test length conversion to metric system."""
assert 100 == \
METRIC_SYSTEM.length(100, METRIC_SYSTEM.length_unit)
assert 8.04672 == \
METRIC_SYSTEM.length(5, IMPERIAL_SYSTEM.length_unit)
def test_length_to_imperial(self): def test_temperature_to_metric():
"""Test length conversion to imperial system.""" """Test temperature conversion to metric system."""
assert 100 == \ assert METRIC_SYSTEM.temperature(25, METRIC_SYSTEM.temperature_unit) == 25
IMPERIAL_SYSTEM.length(100, assert round(METRIC_SYSTEM.temperature(
IMPERIAL_SYSTEM.length_unit) 80, IMPERIAL_SYSTEM.temperature_unit), 1) == 26.7
assert 3.106855 == \
IMPERIAL_SYSTEM.length(5, METRIC_SYSTEM.length_unit)
def test_pressure_same_unit(self):
"""Test no conversion happens if to unit is same as from unit."""
assert 5 == \
METRIC_SYSTEM.pressure(5, METRIC_SYSTEM.pressure_unit)
def test_pressure_unknown_unit(self): def test_temperature_to_imperial():
"""Test no conversion happens if unknown unit.""" """Test temperature conversion to imperial system."""
with pytest.raises(ValueError): assert IMPERIAL_SYSTEM.temperature(
METRIC_SYSTEM.pressure(5, 'K') 77, IMPERIAL_SYSTEM.temperature_unit) == 77
assert IMPERIAL_SYSTEM.temperature(
25, METRIC_SYSTEM.temperature_unit) == 77
def test_pressure_to_metric(self):
"""Test pressure conversion to metric system."""
assert 25 == \
METRIC_SYSTEM.pressure(25, METRIC_SYSTEM.pressure_unit)
self.assertAlmostEqual(
METRIC_SYSTEM.pressure(14.7, IMPERIAL_SYSTEM.pressure_unit),
101352.932, places=1)
def test_pressure_to_imperial(self): def test_length_unknown_unit():
"""Test pressure conversion to imperial system.""" """Test length conversion with unknown from unit."""
assert 77 == \ with pytest.raises(ValueError):
IMPERIAL_SYSTEM.pressure(77, IMPERIAL_SYSTEM.pressure_unit) METRIC_SYSTEM.length(5, 'fr')
self.assertAlmostEqual(
IMPERIAL_SYSTEM.pressure(101352.932, METRIC_SYSTEM.pressure_unit),
14.7, places=4)
def test_properties(self):
"""Test the unit properties are returned as expected."""
assert LENGTH_KILOMETERS == METRIC_SYSTEM.length_unit
assert TEMP_CELSIUS == METRIC_SYSTEM.temperature_unit
assert MASS_GRAMS == METRIC_SYSTEM.mass_unit
assert VOLUME_LITERS == METRIC_SYSTEM.volume_unit
assert PRESSURE_PA == METRIC_SYSTEM.pressure_unit
def test_is_metric(self): def test_length_to_metric():
"""Test the is metric flag.""" """Test length conversion to metric system."""
assert METRIC_SYSTEM.is_metric assert METRIC_SYSTEM.length(100, METRIC_SYSTEM.length_unit) == 100
assert not IMPERIAL_SYSTEM.is_metric assert METRIC_SYSTEM.length(5, IMPERIAL_SYSTEM.length_unit) == 8.04672
def test_length_to_imperial():
"""Test length conversion to imperial system."""
assert IMPERIAL_SYSTEM.length(100, IMPERIAL_SYSTEM.length_unit) == 100
assert IMPERIAL_SYSTEM.length(5, METRIC_SYSTEM.length_unit) == 3.106855
def test_pressure_same_unit():
"""Test no conversion happens if to unit is same as from unit."""
assert METRIC_SYSTEM.pressure(5, METRIC_SYSTEM.pressure_unit) == 5
def test_pressure_unknown_unit():
"""Test no conversion happens if unknown unit."""
with pytest.raises(ValueError):
METRIC_SYSTEM.pressure(5, 'K')
def test_pressure_to_metric():
"""Test pressure conversion to metric system."""
assert METRIC_SYSTEM.pressure(25, METRIC_SYSTEM.pressure_unit) == 25
assert METRIC_SYSTEM.pressure(14.7, IMPERIAL_SYSTEM.pressure_unit) == \
pytest.approx(101352.932, abs=1e-1)
def test_pressure_to_imperial():
"""Test pressure conversion to imperial system."""
assert IMPERIAL_SYSTEM.pressure(77, IMPERIAL_SYSTEM.pressure_unit) == 77
assert IMPERIAL_SYSTEM.pressure(
101352.932, METRIC_SYSTEM.pressure_unit) == \
pytest.approx(14.7, abs=1e-4)
def test_properties():
"""Test the unit properties are returned as expected."""
assert LENGTH_KILOMETERS == METRIC_SYSTEM.length_unit
assert TEMP_CELSIUS == METRIC_SYSTEM.temperature_unit
assert MASS_GRAMS == METRIC_SYSTEM.mass_unit
assert VOLUME_LITERS == METRIC_SYSTEM.volume_unit
assert PRESSURE_PA == METRIC_SYSTEM.pressure_unit
def test_is_metric():
"""Test the is metric flag."""
assert METRIC_SYSTEM.is_metric
assert not IMPERIAL_SYSTEM.is_metric

View File

@ -1,49 +1,45 @@
"""Test homeassistant volume utility functions.""" """Test homeassistant volume utility functions."""
import unittest import pytest
import homeassistant.util.volume as volume_util import homeassistant.util.volume as volume_util
from homeassistant.const import (VOLUME_LITERS, VOLUME_MILLILITERS, from homeassistant.const import (VOLUME_LITERS, VOLUME_MILLILITERS,
VOLUME_GALLONS, VOLUME_FLUID_OUNCE) VOLUME_GALLONS, VOLUME_FLUID_OUNCE)
import pytest
INVALID_SYMBOL = 'bob' INVALID_SYMBOL = 'bob'
VALID_SYMBOL = VOLUME_LITERS VALID_SYMBOL = VOLUME_LITERS
class TestVolumeUtil(unittest.TestCase): def test_convert_same_unit():
"""Test the volume utility functions.""" """Test conversion from any unit to same unit."""
assert volume_util.convert(2, VOLUME_LITERS, VOLUME_LITERS) == 2
assert volume_util.convert(3, VOLUME_MILLILITERS, VOLUME_MILLILITERS) == 3
assert volume_util.convert(4, VOLUME_GALLONS, VOLUME_GALLONS) == 4
assert volume_util.convert(5, VOLUME_FLUID_OUNCE, VOLUME_FLUID_OUNCE) == 5
def test_convert_same_unit(self):
"""Test conversion from any unit to same unit."""
assert 2 == volume_util.convert(2, VOLUME_LITERS, VOLUME_LITERS)
assert 3 == volume_util.convert(3, VOLUME_MILLILITERS,
VOLUME_MILLILITERS)
assert 4 == volume_util.convert(4, VOLUME_GALLONS,
VOLUME_GALLONS)
assert 5 == volume_util.convert(5, VOLUME_FLUID_OUNCE,
VOLUME_FLUID_OUNCE)
def test_convert_invalid_unit(self): def test_convert_invalid_unit():
"""Test exception is thrown for invalid units.""" """Test exception is thrown for invalid units."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
volume_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL) volume_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL)
with pytest.raises(ValueError): with pytest.raises(ValueError):
volume_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL) volume_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL)
def test_convert_nonnumeric_value(self):
"""Test exception is thrown for nonnumeric type."""
with pytest.raises(TypeError):
volume_util.convert('a', VOLUME_GALLONS, VOLUME_LITERS)
def test_convert_from_liters(self): def test_convert_nonnumeric_value():
"""Test conversion from liters to other units.""" """Test exception is thrown for nonnumeric type."""
liters = 5 with pytest.raises(TypeError):
assert volume_util.convert(liters, VOLUME_LITERS, volume_util.convert('a', VOLUME_GALLONS, VOLUME_LITERS)
VOLUME_GALLONS) == 1.321
def test_convert_from_gallons(self):
"""Test conversion from gallons to other units.""" def test_convert_from_liters():
gallons = 5 """Test conversion from liters to other units."""
assert volume_util.convert(gallons, VOLUME_GALLONS, liters = 5
VOLUME_LITERS) == 18.925 assert volume_util.convert(liters, VOLUME_LITERS, VOLUME_GALLONS) == 1.321
def test_convert_from_gallons():
"""Test conversion from gallons to other units."""
gallons = 5
assert volume_util.convert(gallons, VOLUME_GALLONS,
VOLUME_LITERS) == 18.925

View File

@ -21,256 +21,269 @@ def mock_credstash():
yield mock_credstash yield mock_credstash
class TestYaml(unittest.TestCase): def test_simple_list():
"""Test util.yaml loader.""" """Test simple list."""
conf = "config:\n - simple\n - list"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc['config'] == ["simple", "list"]
# pylint: disable=no-self-use, invalid-name
def test_simple_list(self): def test_simple_dict():
"""Test simple list.""" """Test simple dict."""
conf = "config:\n - simple\n - list" conf = "key: value"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc['key'] == 'value'
def test_unhashable_key():
"""Test an unhasable key."""
files = {YAML_CONFIG_FILE: 'message:\n {{ states.state }}'}
with pytest.raises(HomeAssistantError), \
patch_yaml_files(files):
load_yaml_config_file(YAML_CONFIG_FILE)
def test_no_key():
"""Test item without a key."""
files = {YAML_CONFIG_FILE: 'a: a\nnokeyhere'}
with pytest.raises(HomeAssistantError), \
patch_yaml_files(files):
yaml.load_yaml(YAML_CONFIG_FILE)
def test_environment_variable():
"""Test config file with environment variable."""
os.environ["PASSWORD"] = "secret_password"
conf = "password: !env_var PASSWORD"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc['password'] == "secret_password"
del os.environ["PASSWORD"]
def test_environment_variable_default():
"""Test config file with default value for environment variable."""
conf = "password: !env_var PASSWORD secret_password"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc['password'] == "secret_password"
def test_invalid_environment_variable():
"""Test config file with no environment variable sat."""
conf = "password: !env_var PASSWORD"
with pytest.raises(HomeAssistantError):
with io.StringIO(conf) as file:
yaml.yaml.safe_load(file)
def test_include_yaml():
"""Test include yaml."""
with patch_yaml_files({'test.yaml': 'value'}):
conf = 'key: !include test.yaml'
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert doc['config'] == ["simple", "list"] assert doc["key"] == "value"
def test_simple_dict(self): with patch_yaml_files({'test.yaml': None}):
"""Test simple dict.""" conf = 'key: !include test.yaml'
conf = "key: value"
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert doc['key'] == 'value' assert doc["key"] == {}
def test_unhashable_key(self):
"""Test an unhasable key."""
files = {YAML_CONFIG_FILE: 'message:\n {{ states.state }}'}
with pytest.raises(HomeAssistantError), \
patch_yaml_files(files):
load_yaml_config_file(YAML_CONFIG_FILE)
def test_no_key(self): @patch('homeassistant.util.yaml.os.walk')
"""Test item without a key.""" def test_include_dir_list(mock_walk):
files = {YAML_CONFIG_FILE: 'a: a\nnokeyhere'} """Test include dir list yaml."""
with pytest.raises(HomeAssistantError), \ mock_walk.return_value = [
patch_yaml_files(files): ['/tmp', [], ['two.yaml', 'one.yaml']],
yaml.load_yaml(YAML_CONFIG_FILE) ]
def test_environment_variable(self): with patch_yaml_files({
"""Test config file with environment variable."""
os.environ["PASSWORD"] = "secret_password"
conf = "password: !env_var PASSWORD"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc['password'] == "secret_password"
del os.environ["PASSWORD"]
def test_environment_variable_default(self):
"""Test config file with default value for environment variable."""
conf = "password: !env_var PASSWORD secret_password"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc['password'] == "secret_password"
def test_invalid_environment_variable(self):
"""Test config file with no environment variable sat."""
conf = "password: !env_var PASSWORD"
with pytest.raises(HomeAssistantError):
with io.StringIO(conf) as file:
yaml.yaml.safe_load(file)
def test_include_yaml(self):
"""Test include yaml."""
with patch_yaml_files({'test.yaml': 'value'}):
conf = 'key: !include test.yaml'
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc["key"] == "value"
with patch_yaml_files({'test.yaml': None}):
conf = 'key: !include test.yaml'
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc["key"] == {}
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_list(self, mock_walk):
"""Test include dir list yaml."""
mock_walk.return_value = [
['/tmp', [], ['two.yaml', 'one.yaml']],
]
with patch_yaml_files({
'/tmp/one.yaml': 'one', '/tmp/one.yaml': 'one',
'/tmp/two.yaml': 'two', '/tmp/two.yaml': 'two',
}): }):
conf = "key: !include_dir_list /tmp" conf = "key: !include_dir_list /tmp"
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert doc["key"] == sorted(["one", "two"]) assert doc["key"] == sorted(["one", "two"])
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_list_recursive(self, mock_walk):
"""Test include dir recursive list yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['zero.yaml']],
['/tmp/tmp2', [], ['one.yaml', 'two.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({ @patch('homeassistant.util.yaml.os.walk')
def test_include_dir_list_recursive(mock_walk):
"""Test include dir recursive list yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['zero.yaml']],
['/tmp/tmp2', [], ['one.yaml', 'two.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({
'/tmp/zero.yaml': 'zero', '/tmp/zero.yaml': 'zero',
'/tmp/tmp2/one.yaml': 'one', '/tmp/tmp2/one.yaml': 'one',
'/tmp/tmp2/two.yaml': 'two' '/tmp/tmp2/two.yaml': 'two'
}): }):
conf = "key: !include_dir_list /tmp" conf = "key: !include_dir_list /tmp"
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
assert '.ignore' in mock_walk.return_value[0][1], \ assert '.ignore' in mock_walk.return_value[0][1], \
"Expecting .ignore in here" "Expecting .ignore in here"
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert 'tmp2' in mock_walk.return_value[0][1] assert 'tmp2' in mock_walk.return_value[0][1]
assert '.ignore' not in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1]
assert sorted(doc["key"]) == sorted(["zero", "one", "two"]) assert sorted(doc["key"]) == sorted(["zero", "one", "two"])
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_named(self, mock_walk):
"""Test include dir named yaml."""
mock_walk.return_value = [
['/tmp', [], ['first.yaml', 'second.yaml', 'secrets.yaml']]
]
with patch_yaml_files({ @patch('homeassistant.util.yaml.os.walk')
def test_include_dir_named(mock_walk):
"""Test include dir named yaml."""
mock_walk.return_value = [
['/tmp', [], ['first.yaml', 'second.yaml', 'secrets.yaml']]
]
with patch_yaml_files({
'/tmp/first.yaml': 'one', '/tmp/first.yaml': 'one',
'/tmp/second.yaml': 'two' '/tmp/second.yaml': 'two'
}): }):
conf = "key: !include_dir_named /tmp" conf = "key: !include_dir_named /tmp"
correct = {'first': 'one', 'second': 'two'} correct = {'first': 'one', 'second': 'two'}
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert doc["key"] == correct assert doc["key"] == correct
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_named_recursive(self, mock_walk):
"""Test include dir named yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['first.yaml']],
['/tmp/tmp2', [], ['second.yaml', 'third.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({ @patch('homeassistant.util.yaml.os.walk')
def test_include_dir_named_recursive(mock_walk):
"""Test include dir named yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['first.yaml']],
['/tmp/tmp2', [], ['second.yaml', 'third.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({
'/tmp/first.yaml': 'one', '/tmp/first.yaml': 'one',
'/tmp/tmp2/second.yaml': 'two', '/tmp/tmp2/second.yaml': 'two',
'/tmp/tmp2/third.yaml': 'three' '/tmp/tmp2/third.yaml': 'three'
}): }):
conf = "key: !include_dir_named /tmp" conf = "key: !include_dir_named /tmp"
correct = {'first': 'one', 'second': 'two', 'third': 'three'} correct = {'first': 'one', 'second': 'two', 'third': 'three'}
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
assert '.ignore' in mock_walk.return_value[0][1], \ assert '.ignore' in mock_walk.return_value[0][1], \
"Expecting .ignore in here" "Expecting .ignore in here"
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert 'tmp2' in mock_walk.return_value[0][1] assert 'tmp2' in mock_walk.return_value[0][1]
assert '.ignore' not in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1]
assert doc["key"] == correct assert doc["key"] == correct
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_merge_list(self, mock_walk):
"""Test include dir merge list yaml."""
mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]]
with patch_yaml_files({ @patch('homeassistant.util.yaml.os.walk')
def test_include_dir_merge_list(mock_walk):
"""Test include dir merge list yaml."""
mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]]
with patch_yaml_files({
'/tmp/first.yaml': '- one', '/tmp/first.yaml': '- one',
'/tmp/second.yaml': '- two\n- three' '/tmp/second.yaml': '- two\n- three'
}): }):
conf = "key: !include_dir_merge_list /tmp" conf = "key: !include_dir_merge_list /tmp"
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert sorted(doc["key"]) == sorted(["one", "two", "three"]) assert sorted(doc["key"]) == sorted(["one", "two", "three"])
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_merge_list_recursive(self, mock_walk):
"""Test include dir merge list yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['first.yaml']],
['/tmp/tmp2', [], ['second.yaml', 'third.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({ @patch('homeassistant.util.yaml.os.walk')
def test_include_dir_merge_list_recursive(mock_walk):
"""Test include dir merge list yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['first.yaml']],
['/tmp/tmp2', [], ['second.yaml', 'third.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({
'/tmp/first.yaml': '- one', '/tmp/first.yaml': '- one',
'/tmp/tmp2/second.yaml': '- two', '/tmp/tmp2/second.yaml': '- two',
'/tmp/tmp2/third.yaml': '- three\n- four' '/tmp/tmp2/third.yaml': '- three\n- four'
}): }):
conf = "key: !include_dir_merge_list /tmp" conf = "key: !include_dir_merge_list /tmp"
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
assert '.ignore' in mock_walk.return_value[0][1], \ assert '.ignore' in mock_walk.return_value[0][1], \
"Expecting .ignore in here" "Expecting .ignore in here"
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert 'tmp2' in mock_walk.return_value[0][1] assert 'tmp2' in mock_walk.return_value[0][1]
assert '.ignore' not in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1]
assert sorted(doc["key"]) == sorted(["one", "two", assert sorted(doc["key"]) == sorted(["one", "two",
"three", "four"]) "three", "four"])
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_merge_named(self, mock_walk):
"""Test include dir merge named yaml."""
mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]]
files = { @patch('homeassistant.util.yaml.os.walk')
'/tmp/first.yaml': 'key1: one', def test_include_dir_merge_named(mock_walk):
'/tmp/second.yaml': 'key2: two\nkey3: three', """Test include dir merge named yaml."""
} mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]]
with patch_yaml_files(files): files = {
conf = "key: !include_dir_merge_named /tmp" '/tmp/first.yaml': 'key1: one',
with io.StringIO(conf) as file: '/tmp/second.yaml': 'key2: two\nkey3: three',
doc = yaml.yaml.safe_load(file) }
assert doc["key"] == {
"key1": "one",
"key2": "two",
"key3": "three"
}
@patch('homeassistant.util.yaml.os.walk') with patch_yaml_files(files):
def test_include_dir_merge_named_recursive(self, mock_walk): conf = "key: !include_dir_merge_named /tmp"
"""Test include dir merge named yaml.""" with io.StringIO(conf) as file:
mock_walk.return_value = [ doc = yaml.yaml.safe_load(file)
['/tmp', ['tmp2', '.ignore', 'ignore'], ['first.yaml']], assert doc["key"] == {
['/tmp/tmp2', [], ['second.yaml', 'third.yaml']], "key1": "one",
['/tmp/ignore', [], ['.ignore.yaml']] "key2": "two",
] "key3": "three"
}
with patch_yaml_files({
@patch('homeassistant.util.yaml.os.walk')
def test_include_dir_merge_named_recursive(mock_walk):
"""Test include dir merge named yaml."""
mock_walk.return_value = [
['/tmp', ['tmp2', '.ignore', 'ignore'], ['first.yaml']],
['/tmp/tmp2', [], ['second.yaml', 'third.yaml']],
['/tmp/ignore', [], ['.ignore.yaml']]
]
with patch_yaml_files({
'/tmp/first.yaml': 'key1: one', '/tmp/first.yaml': 'key1: one',
'/tmp/tmp2/second.yaml': 'key2: two', '/tmp/tmp2/second.yaml': 'key2: two',
'/tmp/tmp2/third.yaml': 'key3: three\nkey4: four' '/tmp/tmp2/third.yaml': 'key3: three\nkey4: four'
}): }):
conf = "key: !include_dir_merge_named /tmp" conf = "key: !include_dir_merge_named /tmp"
with io.StringIO(conf) as file: with io.StringIO(conf) as file:
assert '.ignore' in mock_walk.return_value[0][1], \ assert '.ignore' in mock_walk.return_value[0][1], \
"Expecting .ignore in here" "Expecting .ignore in here"
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert 'tmp2' in mock_walk.return_value[0][1] assert 'tmp2' in mock_walk.return_value[0][1]
assert '.ignore' not in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1]
assert doc["key"] == { assert doc["key"] == {
"key1": "one", "key1": "one",
"key2": "two", "key2": "two",
"key3": "three", "key3": "three",
"key4": "four" "key4": "four"
} }
@patch('homeassistant.util.yaml.open', create=True)
def test_load_yaml_encoding_error(self, mock_open):
"""Test raising a UnicodeDecodeError."""
mock_open.side_effect = UnicodeDecodeError('', b'', 1, 0, '')
with pytest.raises(HomeAssistantError):
yaml.load_yaml('test')
def test_dump(self): @patch('homeassistant.util.yaml.open', create=True)
"""The that the dump method returns empty None values.""" def test_load_yaml_encoding_error(mock_open):
assert yaml.dump({'a': None, 'b': 'b'}) == 'a:\nb: b\n' """Test raising a UnicodeDecodeError."""
mock_open.side_effect = UnicodeDecodeError('', b'', 1, 0, '')
with pytest.raises(HomeAssistantError):
yaml.load_yaml('test')
def test_dump_unicode(self):
"""The that the dump method returns empty None values.""" def test_dump():
assert yaml.dump({'a': None, 'b': 'привет'}) == 'a:\nb: привет\n' """The that the dump method returns empty None values."""
assert yaml.dump({'a': None, 'b': 'b'}) == 'a:\nb: b\n'
def test_dump_unicode():
"""The that the dump method returns empty None values."""
assert yaml.dump({'a': None, 'b': 'привет'}) == 'a:\nb: привет\n'
FILES = {} FILES = {}
@ -415,9 +428,9 @@ class TestSecrets(unittest.TestCase):
def test_secrets_are_not_dict(self): def test_secrets_are_not_dict(self):
"""Did secrets handle non-dict file.""" """Did secrets handle non-dict file."""
FILES[self._secret_path] = ( FILES[self._secret_path] = (
'- http_pw: pwhttp\n' '- http_pw: pwhttp\n'
' comp1_un: un1\n' ' comp1_un: un1\n'
' comp1_pw: pw1\n') ' comp1_pw: pw1\n')
yaml.clear_secret_cache() yaml.clear_secret_cache()
with pytest.raises(HomeAssistantError): with pytest.raises(HomeAssistantError):
load_yaml(self._yaml_path, load_yaml(self._yaml_path,