Further integration load cleanups (#23104)

* Further integration load cleanups

* Fix tests

* Unflake MQTT vacuum command test
This commit is contained in:
Paulus Schoutsen 2019-04-14 19:07:05 -07:00 committed by GitHub
parent 930f75220c
commit d722f4d64a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 160 additions and 237 deletions

View file

@ -290,23 +290,27 @@ class ConfigEntry:
self._async_cancel_retry_setup = None
async def async_setup(
self, hass: HomeAssistant, *, component=None, tries=0) -> None:
self, hass: HomeAssistant, *,
integration: Optional[loader.Integration] = None, tries=0) -> None:
"""Set up an entry."""
if component is None:
component = getattr(hass.components, self.domain)
if integration is None:
integration = await loader.async_get_integration(hass, self.domain)
component = integration.get_component()
# Perform migration
if component.DOMAIN == self.domain:
if integration.domain == self.domain:
if not await self.async_migrate(hass):
self.state = ENTRY_STATE_MIGRATION_ERROR
return
try:
result = await component.async_setup_entry(hass, self)
result = await component.async_setup_entry( # type: ignore
hass, self)
if not isinstance(result, bool):
_LOGGER.error('%s.async_setup_entry did not return boolean',
component.DOMAIN)
integration.domain)
result = False
except ConfigEntryNotReady:
self.state = ENTRY_STATE_SETUP_RETRY
@ -319,18 +323,19 @@ class ConfigEntry:
async def setup_again(now):
"""Run setup again."""
self._async_cancel_retry_setup = None
await self.async_setup(hass, component=component, tries=tries)
await self.async_setup(
hass, integration=integration, tries=tries)
self._async_cancel_retry_setup = \
hass.helpers.event.async_call_later(wait_time, setup_again)
return
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error setting up entry %s for %s',
self.title, component.DOMAIN)
self.title, integration.domain)
result = False
# Only store setup result as state if it was not forwarded.
if self.domain != component.DOMAIN:
if self.domain != integration.domain:
return
if result:
@ -338,15 +343,17 @@ class ConfigEntry:
else:
self.state = ENTRY_STATE_SETUP_ERROR
async def async_unload(self, hass, *, component=None) -> bool:
async def async_unload(self, hass, *, integration=None) -> bool:
"""Unload an entry.
Returns if unload is possible and was successful.
"""
if component is None:
component = getattr(hass.components, self.domain)
if integration is None:
integration = await loader.async_get_integration(hass, self.domain)
if component.DOMAIN == self.domain:
component = integration.get_component()
if integration.domain == self.domain:
if self.state in UNRECOVERABLE_STATES:
return False
@ -361,7 +368,7 @@ class ConfigEntry:
supports_unload = hasattr(component, 'async_unload_entry')
if not supports_unload:
if component.DOMAIN == self.domain:
if integration.domain == self.domain:
self.state = ENTRY_STATE_FAILED_UNLOAD
return False
@ -371,27 +378,29 @@ class ConfigEntry:
assert isinstance(result, bool)
# Only adjust state if we unloaded the component
if result and component.DOMAIN == self.domain:
if result and integration.domain == self.domain:
self.state = ENTRY_STATE_NOT_LOADED
return result
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error unloading entry %s for %s',
self.title, component.DOMAIN)
if component.DOMAIN == self.domain:
self.title, integration.domain)
if integration.domain == self.domain:
self.state = ENTRY_STATE_FAILED_UNLOAD
return False
async def async_remove(self, hass: HomeAssistant) -> None:
"""Invoke remove callback on component."""
component = getattr(hass.components, self.domain)
integration = await loader.async_get_integration(hass, self.domain)
component = integration.get_component()
if not hasattr(component, 'async_remove_entry'):
return
try:
await component.async_remove_entry(hass, self)
await component.async_remove_entry( # type: ignore
hass, self)
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error calling entry remove callback %s for %s',
self.title, component.DOMAIN)
self.title, integration.domain)
async def async_migrate(self, hass: HomeAssistant) -> bool:
"""Migrate an entry.
@ -624,7 +633,7 @@ class ConfigEntries:
self._async_schedule_save()
async def async_forward_entry_setup(self, entry, component):
async def async_forward_entry_setup(self, entry, domain):
"""Forward the setup of an entry to a different component.
By default an entry is setup with the component it belongs to. If that
@ -635,24 +644,26 @@ class ConfigEntries:
setup of a component, because it can cause a deadlock.
"""
# Setup Component if not set up yet
if component not in self.hass.config.components:
if domain not in self.hass.config.components:
result = await async_setup_component(
self.hass, component, self._hass_config)
self.hass, domain, self._hass_config)
if not result:
return False
await entry.async_setup(
self.hass, component=getattr(self.hass.components, component))
integration = await loader.async_get_integration(self.hass, domain)
async def async_forward_entry_unload(self, entry, component):
await entry.async_setup(self.hass, integration=integration)
async def async_forward_entry_unload(self, entry, domain):
"""Forward the unloading of an entry to a different component."""
# It was never loaded.
if component not in self.hass.config.components:
if domain not in self.hass.config.components:
return True
return await entry.async_unload(
self.hass, component=getattr(self.hass.components, component))
integration = await loader.async_get_integration(self.hass, domain)
return await entry.async_unload(self.hass, integration=integration)
async def _async_finish_flow(self, flow, result):
"""Finish a config flow and add an entry."""
@ -688,10 +699,10 @@ class ConfigEntries:
Handler key is the domain of the component that we want to set up.
"""
integration = await loader.async_get_integration(
self.hass, handler_key)
if integration is None:
try:
integration = await loader.async_get_integration(
self.hass, handler_key)
except loader.IntegrationNotFound:
raise data_entry_flow.UnknownHandler
handler = HANDLERS.get(handler_key)

View file

@ -22,8 +22,6 @@ from typing import (
Dict
)
from homeassistant.const import PLATFORM_FORMAT
# Typing imports that create a circular dependency
# pylint: disable=using-constant-test,unused-import
if TYPE_CHECKING:
@ -38,7 +36,7 @@ DEPENDENCY_BLACKLIST = {'config'}
_LOGGER = logging.getLogger(__name__)
DATA_KEY = 'components'
DATA_COMPONENTS = 'components'
DATA_INTEGRATIONS = 'integrations'
PACKAGE_CUSTOM_COMPONENTS = 'custom_components'
PACKAGE_BUILTIN = 'homeassistant.components'
@ -117,14 +115,14 @@ class Integration:
def get_component(self) -> ModuleType:
"""Return the component."""
cache = self.hass.data.setdefault(DATA_KEY, {})
cache = self.hass.data.setdefault(DATA_COMPONENTS, {})
if self.domain not in cache:
cache[self.domain] = importlib.import_module(self.pkg_path)
return cache[self.domain] # type: ignore
def get_platform(self, platform_name: str) -> ModuleType:
"""Return a platform for an integration."""
cache = self.hass.data.setdefault(DATA_KEY, {})
cache = self.hass.data.setdefault(DATA_COMPONENTS, {})
full_name = "{}.{}".format(self.domain, platform_name)
if full_name not in cache:
cache[full_name] = importlib.import_module(
@ -212,68 +210,6 @@ class CircularDependency(LoaderError):
self.to_domain = to_domain
def set_component(hass, # type: HomeAssistant
comp_name: str, component: Optional[ModuleType]) -> None:
"""Set a component in the cache.
Async friendly.
"""
cache = hass.data.setdefault(DATA_KEY, {})
cache[comp_name] = component
def get_platform(hass, # type: HomeAssistant
domain: str, platform_name: str) -> Optional[ModuleType]:
"""Try to load specified platform.
Example invocation: get_platform(hass, 'light', 'hue')
Async friendly.
"""
# If the platform has a component, we will limit the platform loading path
# to be the same source (custom/built-in).
component = _load_file(hass, platform_name, LOOKUP_PATHS)
# Until we have moved all platforms under their component/own folder, it
# can be that the component is None.
if component is not None:
base_paths = [component.__name__.rsplit('.', 1)[0]]
else:
base_paths = LOOKUP_PATHS
platform = _load_file(
hass, PLATFORM_FORMAT.format(domain=domain, platform=platform_name),
base_paths)
if platform is not None:
return platform
# Legacy platform check for custom: custom_components/light/hue.py
# Only check if the component was also in custom components.
if component is None or base_paths[0] == PACKAGE_CUSTOM_COMPONENTS:
platform = _load_file(
hass,
PLATFORM_FORMAT.format(domain=platform_name, platform=domain),
[PACKAGE_CUSTOM_COMPONENTS]
)
if platform is None:
if component is None:
extra = ""
else:
extra = " Search path was limited to path of component: {}".format(
base_paths[0])
_LOGGER.error("Unable to find platform %s.%s", platform_name, extra)
return None
_LOGGER.error(
"Integrations need to be in their own folder. Change %s/%s.py to "
"%s/%s.py. This will stop working soon.",
domain, platform_name, platform_name, domain)
return platform
def get_component(hass, # type: HomeAssistant
comp_or_platform: str) -> Optional[ModuleType]:
"""Try to load specified component.
@ -298,15 +234,15 @@ def _load_file(hass, # type: HomeAssistant
Async friendly.
"""
try:
return hass.data[DATA_KEY][comp_or_platform] # type: ignore
return hass.data[DATA_COMPONENTS][comp_or_platform] # type: ignore
except KeyError:
pass
cache = hass.data.get(DATA_KEY)
cache = hass.data.get(DATA_COMPONENTS)
if cache is None:
if not _async_mount_config_dir(hass):
return None
cache = hass.data[DATA_KEY] = {}
cache = hass.data[DATA_COMPONENTS] = {}
for path in ('{}.{}'.format(base, comp_or_platform)
for base in base_paths):
@ -392,9 +328,18 @@ class Components:
def __getattr__(self, comp_name: str) -> ModuleWrapper:
"""Fetch a component."""
component = get_component(self._hass, comp_name)
# Test integration cache
integration = self._hass.data.get(DATA_INTEGRATIONS, {}).get(comp_name)
if integration:
component = integration.get_component()
else:
# Fallback to importing old-school
component = _load_file(self._hass, comp_name, LOOKUP_PATHS)
if component is None:
raise ImportError('Unable to load {}'.format(comp_name))
wrapped = ModuleWrapper(self._hass, component)
setattr(self, comp_name, wrapped)
return wrapped

View file

@ -168,12 +168,11 @@ async def _async_setup_component(hass: core.HomeAssistant,
if result is not True:
log_error("Component {!r} did not return boolean if setup was "
"successful. Disabling component.".format(domain))
loader.set_component(hass, domain, None)
return False
if hass.config_entries:
for entry in hass.config_entries.async_entries(domain):
await entry.async_setup(hass, component=component)
await entry.async_setup(hass, integration=integration)
hass.config.components.add(component.DOMAIN) # type: ignore

View file

@ -4,6 +4,7 @@ import functools as ft
import json
import logging
import os
import uuid
import sys
import threading
@ -607,7 +608,7 @@ class MockConfigEntry(config_entries.ConfigEntry):
connection_class=config_entries.CONN_CLASS_UNKNOWN):
"""Initialize a mock config entry."""
kwargs = {
'entry_id': entry_id or 'mock-id',
'entry_id': entry_id or uuid.uuid4().hex,
'domain': domain,
'data': data or {},
'options': options,
@ -911,7 +912,7 @@ def mock_integration(hass, module):
hass.data.setdefault(
loader.DATA_INTEGRATIONS, {}
)[module.DOMAIN] = integration
hass.data.setdefault(loader.DATA_KEY, {})[module.DOMAIN] = module
hass.data.setdefault(loader.DATA_COMPONENTS, {})[module.DOMAIN] = module
def mock_entity_platform(hass, platform_path, module):
@ -921,8 +922,8 @@ def mock_entity_platform(hass, platform_path, module):
hue.light.
"""
domain, platform_name = platform_path.split('.')
integration_cache = hass.data.setdefault(loader.DATA_KEY, {})
module_cache = hass.data.setdefault(loader.DATA_KEY, {})
integration_cache = hass.data.setdefault(loader.DATA_COMPONENTS, {})
module_cache = hass.data.setdefault(loader.DATA_COMPONENTS, {})
if platform_name not in integration_cache:
mock_integration(hass, MockModule(platform_name))

View file

@ -12,15 +12,15 @@ from homeassistant.config_entries import HANDLERS
from homeassistant.core import callback
from homeassistant.setup import async_setup_component
from homeassistant.components.config import config_entries
from homeassistant.loader import set_component
from tests.common import MockConfigEntry, MockModule, mock_coro_func
from tests.common import (
MockConfigEntry, MockModule, mock_coro_func, mock_integration)
@pytest.fixture(autouse=True)
def mock_test_component(hass):
"""Ensure a component called 'test' exists."""
set_component(hass, 'test', MockModule('test'))
mock_integration(hass, MockModule('test'))
@pytest.fixture
@ -244,8 +244,8 @@ def test_abort(hass, client):
@asyncio.coroutine
def test_create_account(hass, client):
"""Test a flow that creates an account."""
set_component(
hass, 'test',
mock_integration(
hass,
MockModule('test', async_setup_entry=mock_coro_func(True)))
class TestFlow(core_ce.ConfigFlow):
@ -283,8 +283,8 @@ def test_create_account(hass, client):
@asyncio.coroutine
def test_two_step_flow(hass, client):
"""Test we can finish a two step flow."""
set_component(
hass, 'test',
mock_integration(
hass,
MockModule('test', async_setup_entry=mock_coro_func(True)))
class TestFlow(core_ce.ConfigFlow):
@ -349,8 +349,8 @@ def test_two_step_flow(hass, client):
async def test_continue_flow_unauth(hass, client, hass_admin_user):
"""Test we can't finish a two step flow."""
set_component(
hass, 'test',
mock_integration(
hass,
MockModule('test', async_setup_entry=mock_coro_func(True)))
class TestFlow(core_ce.ConfigFlow):
@ -562,8 +562,8 @@ async def test_options_flow(hass, client):
async def test_two_step_options_flow(hass, client):
"""Test we can finish a two step options flow."""
set_component(
hass, 'test',
mock_integration(
hass,
MockModule('test', async_setup_entry=mock_coro_func(True)))
class TestFlow(core_ce.ConfigFlow):

View file

@ -136,9 +136,10 @@ async def test_all_commands(hass, mock_publish):
entity_id='vacuum.mqtttest')
await hass.async_block_till_done()
await hass.async_block_till_done()
mock_publish.async_publish.assert_called_once_with(
'vacuum/send_command', '{"command": "44 FE 93", "key": "value"}',
0, False)
assert json.loads(mock_publish.async_publish.mock_calls[-1][1][1]) == {
"command": "44 FE 93",
"key": "value"
}
async def test_status(hass, mock_publish):

View file

@ -7,7 +7,7 @@ from homeassistant import core, loader
from homeassistant.components import switch
from homeassistant.const import STATE_ON, STATE_OFF, CONF_PLATFORM
from tests.common import get_test_home_assistant
from tests.common import get_test_home_assistant, mock_entity_platform
from tests.components.switch import common
@ -80,7 +80,7 @@ class TestSwitch(unittest.TestCase):
test_platform = loader.get_component(self.hass, 'test.switch')
test_platform.init(True)
loader.set_component(self.hass, 'switch.test2', test_platform)
mock_entity_platform(self.hass, 'switch.test2', test_platform)
test_platform.init(False)
assert setup_component(

View file

@ -3,9 +3,10 @@ from unittest.mock import patch, Mock
import pytest
from homeassistant import config_entries, data_entry_flow, loader, setup
from homeassistant import config_entries, data_entry_flow, setup
from homeassistant.helpers import config_entry_flow
from tests.common import MockConfigEntry, MockModule, mock_coro
from tests.common import (
MockConfigEntry, MockModule, mock_coro, mock_integration)
@pytest.fixture
@ -101,7 +102,7 @@ async def test_discovery_confirmation(hass, discovery_flow_conf):
async def test_multiple_discoveries(hass, discovery_flow_conf):
"""Test we only create one instance for multiple discoveries."""
loader.set_component(hass, 'test', MockModule('test'))
mock_integration(hass, MockModule('test'))
result = await hass.config_entries.flow.async_init(
'test', context={'source': config_entries.SOURCE_DISCOVERY}, data={})
@ -115,7 +116,7 @@ async def test_multiple_discoveries(hass, discovery_flow_conf):
async def test_only_one_in_progress(hass, discovery_flow_conf):
"""Test a user initialized one will finish and cancel discovered one."""
loader.set_component(hass, 'test', MockModule('test'))
mock_integration(hass, MockModule('test'))
# Discovery starts flow
result = await hass.config_entries.flow.async_init(
@ -202,7 +203,7 @@ async def test_webhook_create_cloudhook(hass, webhook_flow_conf):
async_setup_entry = Mock(return_value=mock_coro(True))
async_unload_entry = Mock(return_value=mock_coro(True))
loader.set_component(hass, 'test_single', MockModule(
mock_integration(hass, MockModule(
'test_single',
async_setup_entry=async_setup_entry,
async_unload_entry=async_unload_entry,

View file

@ -3,13 +3,14 @@ from unittest.mock import patch
import pytest
from homeassistant import loader, setup
from homeassistant import setup
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import discovery
from tests.common import (
get_test_home_assistant, MockModule, MockPlatform, mock_coro)
get_test_home_assistant, MockModule, MockPlatform, mock_coro,
mock_integration, mock_entity_platform)
class TestHelpersDiscovery:
@ -128,17 +129,17 @@ class TestHelpersDiscovery:
"""Set up mock platform."""
platform_calls.append('disc' if discovery_info else 'component')
loader.set_component(
self.hass, 'test_component',
mock_integration(
self.hass,
MockModule('test_component', setup=component_setup))
# dependencies are only set in component level
# since we are using manifest to hold them
loader.set_component(
self.hass, 'test_circular',
mock_integration(
self.hass,
MockModule('test_circular', dependencies=['test_component']))
loader.set_component(
self.hass, 'test_circular.switch',
mock_entity_platform(
self.hass, 'switch.test_circular',
MockPlatform(setup_platform))
setup.setup_component(self.hass, 'test_component', {
@ -180,12 +181,12 @@ class TestHelpersDiscovery:
component_calls.append(1)
return True
loader.set_component(
self.hass, 'test_component1',
mock_integration(
self.hass,
MockModule('test_component1', setup=component1_setup))
loader.set_component(
self.hass, 'test_component2',
mock_integration(
self.hass,
MockModule('test_component2', setup=component2_setup))
@callback

View file

@ -10,7 +10,6 @@ from datetime import timedelta
import pytest
import homeassistant.core as ha
import homeassistant.loader as loader
from homeassistant.exceptions import PlatformNotReady
from homeassistant.components import group
from homeassistant.helpers.entity_component import EntityComponent
@ -226,9 +225,8 @@ def test_platform_not_ready(hass):
"""Test that we retry when platform not ready."""
platform1_setup = Mock(side_effect=[PlatformNotReady, PlatformNotReady,
None])
loader.set_component(hass, 'mod1',
MockModule('mod1'))
loader.set_component(hass, 'mod1.test_domain',
mock_integration(hass, MockModule('mod1'))
mock_entity_platform(hass, 'test_domain.mod1',
MockPlatform(platform1_setup))
component = EntityComponent(_LOGGER, DOMAIN, hass)
@ -326,12 +324,10 @@ def test_setup_dependencies_platform(hass):
We're explictely testing that we process dependencies even if a component
with the same name has already been loaded.
"""
loader.set_component(hass, 'test_component',
MockModule('test_component',
dependencies=['test_component2']))
loader.set_component(hass, 'test_component2',
MockModule('test_component2'))
loader.set_component(hass, 'test_component.test_domain', MockPlatform())
mock_integration(hass, MockModule('test_component',
dependencies=['test_component2']))
mock_integration(hass, MockModule('test_component2'))
mock_entity_platform(hass, 'test_domain.test_component', MockPlatform())
component = EntityComponent(_LOGGER, DOMAIN, hass)

View file

@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch
import pytest
from homeassistant import config_entries, loader, data_entry_flow
from homeassistant import config_entries, data_entry_flow
from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.setup import async_setup_component
@ -49,8 +49,8 @@ async def test_call_setup_entry(hass):
mock_setup_entry = MagicMock(return_value=mock_coro(True))
mock_migrate_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry,
async_migrate_entry=mock_migrate_entry))
@ -70,8 +70,8 @@ async def test_call_async_migrate_entry(hass):
mock_migrate_entry = MagicMock(return_value=mock_coro(True))
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry,
async_migrate_entry=mock_migrate_entry))
@ -91,8 +91,8 @@ async def test_call_async_migrate_entry_failure_false(hass):
mock_migrate_entry = MagicMock(return_value=mock_coro(False))
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry,
async_migrate_entry=mock_migrate_entry))
@ -113,8 +113,8 @@ async def test_call_async_migrate_entry_failure_exception(hass):
return_value=mock_coro(exception=Exception))
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry,
async_migrate_entry=mock_migrate_entry))
@ -135,8 +135,8 @@ async def test_call_async_migrate_entry_failure_not_bool(hass):
return_value=mock_coro())
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry,
async_migrate_entry=mock_migrate_entry))
@ -155,8 +155,8 @@ async def test_call_async_migrate_entry_failure_not_supported(hass):
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry))
result = await async_setup_component(hass, 'comp', {})
@ -265,7 +265,7 @@ async def test_remove_entry_handles_callback_error(hass, manager):
mock_unload_entry = MagicMock(return_value=mock_coro(True))
mock_remove_entry = MagicMock(
side_effect=lambda *args, **kwargs: mock_coro())
loader.set_component(hass, 'test', MockModule(
mock_integration(hass, MockModule(
'test',
async_setup_entry=mock_setup_entry,
async_unload_entry=mock_unload_entry,
@ -304,13 +304,12 @@ def test_remove_entry_raises(hass, manager):
"""Mock unload entry function."""
raise Exception("BROKEN")
loader.set_component(
hass, 'test',
MockModule('comp', async_unload_entry=mock_unload_entry))
mock_integration(hass, MockModule(
'comp', async_unload_entry=mock_unload_entry))
MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager)
MockConfigEntry(
domain='test',
domain='comp',
entry_id='test2',
state=config_entries.ENTRY_STATE_LOADED
).add_to_manager(manager)
@ -330,15 +329,14 @@ def test_remove_entry_raises(hass, manager):
@asyncio.coroutine
def test_remove_entry_if_not_loaded(hass, manager):
"""Test that we can remove an entry."""
"""Test that we can remove an entry that is not loaded."""
mock_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'test',
MockModule('comp', async_unload_entry=mock_unload_entry))
mock_integration(hass, MockModule(
'comp', async_unload_entry=mock_unload_entry))
MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager)
MockConfigEntry(domain='test', entry_id='test2').add_to_manager(manager)
MockConfigEntry(domain='comp', entry_id='test2').add_to_manager(manager)
MockConfigEntry(domain='test', entry_id='test3').add_to_manager(manager)
assert [item.entry_id for item in manager.async_entries()] == \
@ -352,7 +350,7 @@ def test_remove_entry_if_not_loaded(hass, manager):
assert [item.entry_id for item in manager.async_entries()] == \
['test1', 'test3']
assert len(mock_unload_entry.mock_calls) == 1
assert len(mock_unload_entry.mock_calls) == 0
@asyncio.coroutine
@ -360,8 +358,8 @@ def test_add_entry_calls_setup_entry(hass, manager):
"""Test we call setup_config_entry."""
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'comp',
mock_integration(
hass,
MockModule('comp', async_setup_entry=mock_setup_entry))
class TestFlow(config_entries.ConfigFlow):
@ -416,9 +414,8 @@ def test_domains_gets_uniques(manager):
async def test_saving_and_loading(hass):
"""Test that we're saving and loading correctly."""
loader.set_component(
hass, 'test',
MockModule('test', async_setup_entry=lambda *args: mock_coro(True)))
mock_integration(hass, MockModule(
'test', async_setup_entry=lambda *args: mock_coro(True)))
class TestFlow(config_entries.ConfigFlow):
VERSION = 5
@ -480,13 +477,13 @@ async def test_forward_entry_sets_up_component(hass):
entry = MockConfigEntry(domain='original')
mock_original_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'original',
mock_integration(
hass,
MockModule('original', async_setup_entry=mock_original_setup_entry))
mock_forwarded_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(
hass, 'forwarded',
mock_integration(
hass,
MockModule('forwarded', async_setup_entry=mock_forwarded_setup_entry))
await hass.config_entries.async_forward_entry_setup(entry, 'forwarded')
@ -500,7 +497,7 @@ async def test_forward_entry_does_not_setup_entry_if_setup_fails(hass):
mock_setup = MagicMock(return_value=mock_coro(False))
mock_setup_entry = MagicMock()
hass, loader.set_component(hass, 'forwarded', MockModule(
mock_integration(hass, MockModule(
'forwarded',
async_setup=mock_setup,
async_setup_entry=mock_setup_entry,
@ -513,7 +510,7 @@ async def test_forward_entry_does_not_setup_entry_if_setup_fails(hass):
async def test_discovery_notification(hass):
"""Test that we create/dismiss a notification when source is discovery."""
loader.set_component(hass, 'test', MockModule('test'))
mock_integration(hass, MockModule('test'))
await async_setup_component(hass, 'persistent_notification', {})
class TestFlow(config_entries.ConfigFlow):
@ -550,7 +547,7 @@ async def test_discovery_notification(hass):
async def test_discovery_notification_not_created(hass):
"""Test that we not create a notification when discovery is aborted."""
loader.set_component(hass, 'test', MockModule('test'))
mock_integration(hass, MockModule('test'))
await async_setup_component(hass, 'persistent_notification', {})
class TestFlow(config_entries.ConfigFlow):
@ -630,8 +627,8 @@ async def test_setup_raise_not_ready(hass, caplog):
entry = MockConfigEntry(domain='test')
mock_setup_entry = MagicMock(side_effect=ConfigEntryNotReady)
loader.set_component(
hass, 'test', MockModule('test', async_setup_entry=mock_setup_entry))
mock_integration(
hass, MockModule('test', async_setup_entry=mock_setup_entry))
with patch('homeassistant.helpers.event.async_call_later') as mock_call:
await entry.async_setup(hass)
@ -656,8 +653,8 @@ async def test_setup_retrying_during_unload(hass):
entry = MockConfigEntry(domain='test')
mock_setup_entry = MagicMock(side_effect=ConfigEntryNotReady)
loader.set_component(
hass, 'test', MockModule('test', async_setup_entry=mock_setup_entry))
mock_integration(
hass, MockModule('test', async_setup_entry=mock_setup_entry))
with patch('homeassistant.helpers.event.async_call_later') as mock_call:
await entry.async_setup(hass)
@ -718,7 +715,7 @@ async def test_entry_setup_succeed(hass, manager):
mock_setup = MagicMock(return_value=mock_coro(True))
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_setup=mock_setup,
async_setup_entry=mock_setup_entry
@ -748,7 +745,7 @@ async def test_entry_setup_invalid_state(hass, manager, state):
mock_setup = MagicMock(return_value=mock_coro(True))
mock_setup_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_setup=mock_setup,
async_setup_entry=mock_setup_entry
@ -772,7 +769,7 @@ async def test_entry_unload_succeed(hass, manager):
async_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_unload_entry=async_unload_entry
))
@ -797,7 +794,7 @@ async def test_entry_unload_failed_to_load(hass, manager, state):
async_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_unload_entry=async_unload_entry
))
@ -821,7 +818,7 @@ async def test_entry_unload_invalid_state(hass, manager, state):
async_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_unload_entry=async_unload_entry
))
@ -845,7 +842,7 @@ async def test_entry_reload_succeed(hass, manager):
async_setup_entry = MagicMock(return_value=mock_coro(True))
async_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_setup=async_setup,
async_setup_entry=async_setup_entry,
@ -876,7 +873,7 @@ async def test_entry_reload_not_loaded(hass, manager, state):
async_setup_entry = MagicMock(return_value=mock_coro(True))
async_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_setup=async_setup,
async_setup_entry=async_setup_entry,
@ -906,7 +903,7 @@ async def test_entry_reload_error(hass, manager, state):
async_setup_entry = MagicMock(return_value=mock_coro(True))
async_unload_entry = MagicMock(return_value=mock_coro(True))
loader.set_component(hass, 'comp', MockModule(
mock_integration(hass, MockModule(
'comp',
async_setup=async_setup,
async_setup_entry=async_setup_entry,

View file

@ -8,14 +8,6 @@ from homeassistant.components.hue import light as hue_light
from tests.common import MockModule, async_mock_service, mock_integration
def test_set_component(hass):
"""Test if set_component works."""
comp = object()
loader.set_component(hass, 'switch.test_set', comp)
assert loader.get_component(hass, 'switch.test_set') is comp
def test_get_component(hass):
"""Test if get_component works."""
assert http == loader.get_component(hass, 'http')
@ -119,27 +111,6 @@ async def test_log_warning_custom_component(hass, caplog):
assert 'You are using a custom component for test.light' in caplog.text
async def test_get_platform(hass, caplog):
"""Test get_platform."""
# Test we prefer embedded over normal platforms."""
embedded_platform = loader.get_platform(hass, 'switch', 'test_embedded')
assert embedded_platform.__name__ == \
'custom_components.test_embedded.switch'
caplog.clear()
legacy_platform = loader.get_platform(hass, 'switch', 'test_legacy')
assert legacy_platform.__name__ == 'custom_components.switch.test_legacy'
assert 'Integrations need to be in their own folder.' in caplog.text
async def test_get_platform_enforces_component_path(hass, caplog):
"""Test that existence of a component limits lookup path of platforms."""
assert loader.get_platform(hass, 'comp_path_test', 'hue') is None
assert ('Search path was limited to path of component: '
'homeassistant.components') in caplog.text
async def test_get_integration(hass):
"""Test resolving integration."""
integration = await loader.async_get_integration(hass, 'hue')

View file

@ -2,13 +2,14 @@
import os
from unittest.mock import patch, call
from homeassistant import loader, setup
from homeassistant import setup
from homeassistant.requirements import (
CONSTRAINT_FILE, PackageLoadable, async_process_requirements)
import pkg_resources
from tests.common import get_test_home_assistant, MockModule, mock_coro
from tests.common import (
get_test_home_assistant, MockModule, mock_coro, mock_integration)
RESOURCE_DIR = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', 'resources'))
@ -43,8 +44,8 @@ class TestRequirements:
mock_venv.return_value = True
mock_dirname.return_value = 'ha_package_path'
self.hass.config.skip_pip = False
loader.set_component(
self.hass, 'comp',
mock_integration(
self.hass,
MockModule('comp', requirements=['package==0.0.1']))
assert setup.setup_component(self.hass, 'comp', {})
assert 'comp' in self.hass.config.components
@ -60,8 +61,8 @@ class TestRequirements:
"""Test requirement installed in deps directory."""
mock_dirname.return_value = 'ha_package_path'
self.hass.config.skip_pip = False
loader.set_component(
self.hass, 'comp',
mock_integration(
self.hass,
MockModule('comp', requirements=['package==0.0.1']))
assert setup.setup_component(self.hass, 'comp', {})
assert 'comp' in self.hass.config.components

View file

@ -494,7 +494,6 @@ class TestSetup:
MockModule('disabled_component', setup=lambda hass, config: None))
assert not setup.setup_component(self.hass, 'disabled_component', {})
assert loader.get_component(self.hass, 'disabled_component') is None
assert 'disabled_component' not in self.hass.config.components
self.hass.data.pop(setup.DATA_SETUP)