Bootstrap / Component setup async (#6264)

* Bootstrap / Entiy setup async

* Cleanup add_job stuff / return task/future object

* Address paulus comments / part 1

* fix install pip

* Cleanup bootstrap / move config stuff to config.py

* Make demo async

* Further bootstrap improvement

* Address Martin's comments

* Fix initial tests

* Fix final tests

* Fix bug with prepare loader

* Remove no longer needed things

* Log error when invalid config

* More cleanup

* Cleanups platform events & fix lint

* Use a non blocking add_entities callback for platform

* Fix Autoamtion is setup befor entity is ready

* Better automation fix

* Address paulus comments

* Typo

* fix lint

* rename functions

* fix tests

* fix test

* change exceptions

* fix spell
This commit is contained in:
Pascal Vizeli 2017-03-01 05:33:19 +01:00 committed by Paulus Schoutsen
parent 383b0914b3
commit 41f558b181
109 changed files with 764 additions and 848 deletions

View file

@ -7,12 +7,10 @@ import threading
import logging
import voluptuous as vol
import pytest
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_START
import homeassistant.config as config_util
from homeassistant.exceptions import HomeAssistantError
from homeassistant import bootstrap, loader
import homeassistant.util.dt as dt_util
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
@ -79,23 +77,8 @@ class TestBootstrap:
patch_yaml_files(files, True):
self.hass = bootstrap.from_config_file('config.yaml')
components.add('group')
assert components == self.hass.config.components
def test_handle_setup_circular_dependency(self):
"""Test the setup of circular dependencies."""
loader.set_component('comp_b', MockModule('comp_b', ['comp_a']))
def setup_a(hass, config):
"""Setup the another component."""
bootstrap.setup_component(hass, 'comp_b')
return True
loader.set_component('comp_a', MockModule('comp_a', setup=setup_a))
bootstrap.setup_component(self.hass, 'comp_a')
assert set(['comp_a']) == self.hass.config.components
def test_validate_component_config(self):
"""Test validating component configuration."""
config_schema = vol.Schema({
@ -109,16 +92,22 @@ class TestBootstrap:
with assert_setup_component(0):
assert not bootstrap.setup_component(self.hass, 'comp_conf', {})
self.hass.data.pop(bootstrap.DATA_SETUP)
with assert_setup_component(0):
assert not bootstrap.setup_component(self.hass, 'comp_conf', {
'comp_conf': None
})
self.hass.data.pop(bootstrap.DATA_SETUP)
with assert_setup_component(0):
assert not bootstrap.setup_component(self.hass, 'comp_conf', {
'comp_conf': {}
})
self.hass.data.pop(bootstrap.DATA_SETUP)
with assert_setup_component(0):
assert not bootstrap.setup_component(self.hass, 'comp_conf', {
'comp_conf': {
@ -127,6 +116,8 @@ class TestBootstrap:
}
})
self.hass.data.pop(bootstrap.DATA_SETUP)
with assert_setup_component(1):
assert bootstrap.setup_component(self.hass, 'comp_conf', {
'comp_conf': {
@ -154,6 +145,7 @@ class TestBootstrap:
}
})
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('platform_conf')
with assert_setup_component(1):
@ -167,6 +159,7 @@ class TestBootstrap:
}
})
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('platform_conf')
with assert_setup_component(0):
@ -177,6 +170,7 @@ class TestBootstrap:
}
})
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('platform_conf')
with assert_setup_component(1):
@ -187,6 +181,7 @@ class TestBootstrap:
}
})
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('platform_conf')
with assert_setup_component(1):
@ -197,6 +192,7 @@ class TestBootstrap:
}]
})
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('platform_conf')
# Any falsey platform config will be ignored (None, {}, etc)
@ -244,22 +240,27 @@ class TestBootstrap:
def test_component_not_setup_twice_if_loaded_during_other_setup(self):
"""Test component setup while waiting for lock is not setup twice."""
loader.set_component('comp', MockModule('comp'))
result = []
@asyncio.coroutine
def async_setup(hass, config):
"""Tracking Setup."""
result.append(1)
loader.set_component(
'comp', MockModule('comp', async_setup=async_setup))
def setup_component():
"""Setup the component."""
result.append(bootstrap.setup_component(self.hass, 'comp'))
bootstrap.setup_component(self.hass, 'comp')
thread = threading.Thread(target=setup_component)
thread.start()
self.hass.config.components.add('comp')
bootstrap.setup_component(self.hass, 'comp')
thread.join()
assert len(result) == 1
assert result[0]
def test_component_not_setup_missing_dependencies(self):
"""Test we do not setup a component if not all dependencies loaded."""
@ -269,8 +270,9 @@ class TestBootstrap:
assert not bootstrap.setup_component(self.hass, 'comp', {})
assert 'comp' not in self.hass.config.components
loader.set_component('non_existing', MockModule('non_existing'))
self.hass.data.pop(bootstrap.DATA_SETUP)
loader.set_component('non_existing', MockModule('non_existing'))
assert bootstrap.setup_component(self.hass, 'comp', {})
def test_component_failing_setup(self):
@ -349,6 +351,7 @@ class TestBootstrap:
})
assert mock_setup.call_count == 0
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('switch')
with assert_setup_component(0):
@ -361,6 +364,7 @@ class TestBootstrap:
})
assert mock_setup.call_count == 0
self.hass.data.pop(bootstrap.DATA_SETUP)
self.hass.config.components.remove('switch')
with assert_setup_component(1):
@ -382,6 +386,7 @@ class TestBootstrap:
assert loader.get_component('disabled_component') is None
assert 'disabled_component' not in self.hass.config.components
self.hass.data.pop(bootstrap.DATA_SETUP)
loader.set_component(
'disabled_component',
MockModule('disabled_component', setup=lambda hass, config: False))
@ -390,6 +395,7 @@ class TestBootstrap:
assert loader.get_component('disabled_component') is not None
assert 'disabled_component' not in self.hass.config.components
self.hass.data.pop(bootstrap.DATA_SETUP)
loader.set_component(
'disabled_component',
MockModule('disabled_component', setup=lambda hass, config: True))
@ -435,35 +441,16 @@ class TestBootstrap:
self.hass.bus.listen_once(EVENT_HOMEASSISTANT_START, track_start)
self.hass.loop.run_until_complete = \
lambda _: self.hass.block_till_done()
bootstrap.from_config_dict({'test_component1': None}, self.hass)
self.hass.add_job(bootstrap.async_setup_component(
self.hass, 'test_component1', {}))
self.hass.block_till_done()
self.hass.start()
assert call_order == [1, 1, 2]
@asyncio.coroutine
def test_component_cannot_depend_config(hass):
"""Test config is not allowed to be a dependency."""
loader.set_component(
'test_component1',
MockModule('test_component1', dependencies=['config']))
with pytest.raises(HomeAssistantError):
yield from bootstrap.async_from_config_dict(
{'test_component1': None}, hass)
@asyncio.coroutine
def test_platform_cannot_depend_config():
"""Test config is not allowed to be a dependency."""
loader.set_component(
'test_component1.test',
MockPlatform('whatever', dependencies=['config']))
with pytest.raises(HomeAssistantError):
yield from bootstrap.async_prepare_setup_platform(
mock.MagicMock(), {}, 'test_component1', 'test')
result = yield from bootstrap._async_process_dependencies(
hass, None, 'test', ['config'])
assert not result