diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index ffef2fbc99d..26dc2b977c6 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -1,11 +1,10 @@ """Provides methods to bootstrap a home assistant instance.""" - +import asyncio import logging import logging.handlers import os import sys from collections import defaultdict -from threading import RLock from types import ModuleType from typing import Any, Optional, Dict @@ -19,6 +18,8 @@ import homeassistant.config as conf_util import homeassistant.core as core import homeassistant.loader as loader import homeassistant.util.package as pkg_util +from homeassistant.util.async import ( + run_coroutine_threadsafe, run_callback_threadsafe) from homeassistant.util.yaml import clear_secret_cache from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT from homeassistant.exceptions import HomeAssistantError @@ -26,7 +27,6 @@ from homeassistant.helpers import ( event_decorators, service, config_per_platform, extract_domain_configs) _LOGGER = logging.getLogger(__name__) -_SETUP_LOCK = RLock() _CURRENT_SETUP = [] ATTR_COMPONENT = 'component' @@ -39,11 +39,23 @@ _PERSISTENT_VALIDATION = set() def setup_component(hass: core.HomeAssistant, domain: str, config: Optional[Dict]=None) -> bool: """Setup a component and all its dependencies.""" + return run_coroutine_threadsafe( + async_setup_component(hass, domain, config), loop=hass.loop).result() + + +@asyncio.coroutine +def async_setup_component(hass: core.HomeAssistant, domain: str, + config: Optional[Dict]=None) -> bool: + """Setup a component and all its dependencies. + + This method need to run in a executor. + """ if domain in hass.config.components: _LOGGER.debug('Component %s already set up.', domain) return True - _ensure_loader_prepared(hass) + if not loader.PREPARED: + yield from hass.loop.run_in_executor(None, loader.prepare, hass) if config is None: config = defaultdict(dict) @@ -55,7 +67,8 @@ def setup_component(hass: core.HomeAssistant, domain: str, return False for component in components: - if not _setup_component(hass, component, config): + res = yield from _async_setup_component(hass, component, config) + if not res: _LOGGER.error('Component %s failed to setup', component) return False @@ -64,7 +77,11 @@ def setup_component(hass: core.HomeAssistant, domain: str, def _handle_requirements(hass: core.HomeAssistant, component, name: str) -> bool: - """Install the requirements for a component.""" + """Install the requirements for a component. + + Asyncio don't support file operation jet. + This method need to run in a executor. + """ if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'): return True @@ -77,65 +94,82 @@ def _handle_requirements(hass: core.HomeAssistant, component, return True -def _setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: - """Setup a component for Home Assistant.""" +@asyncio.coroutine +def _async_setup_component(hass: core.HomeAssistant, + domain: str, config) -> bool: + """Setup a component for Home Assistant. + + This method is a coroutine. + """ # pylint: disable=too-many-return-statements,too-many-branches # pylint: disable=too-many-statements if domain in hass.config.components: return True - with _SETUP_LOCK: - # It might have been loaded while waiting for lock - if domain in hass.config.components: - return True + if domain in _CURRENT_SETUP: + _LOGGER.error('Attempt made to setup %s during setup of %s', + domain, domain) + return False - if domain in _CURRENT_SETUP: - _LOGGER.error('Attempt made to setup %s during setup of %s', - domain, domain) + config = yield from async_prepare_setup_component(hass, config, domain) + + if config is None: + return False + + component = loader.get_component(domain) + _CURRENT_SETUP.append(domain) + + try: + if hasattr(component, 'async_setup'): + result = yield from component.async_setup(hass, config) + else: + result = yield from hass.loop.run_in_executor( + None, component.setup, hass, config) + + if result is False: + _LOGGER.error('component %s failed to initialize', domain) return False - - config = prepare_setup_component(hass, config, domain) - - if config is None: + elif result is not True: + _LOGGER.error('component %s did not return boolean if setup ' + 'was successful. Disabling component.', domain) + loader.set_component(domain, None) return False + except Exception: # pylint: disable=broad-except + _LOGGER.exception('Error during setup of component %s', domain) + return False + finally: + _CURRENT_SETUP.remove(domain) - component = loader.get_component(domain) - _CURRENT_SETUP.append(domain) + hass.config.components.append(component.DOMAIN) - try: - result = component.setup(hass, config) - if result is False: - _LOGGER.error('component %s failed to initialize', domain) - return False - elif result is not True: - _LOGGER.error('component %s did not return boolean if setup ' - 'was successful. Disabling component.', domain) - loader.set_component(domain, None) - return False - except Exception: # pylint: disable=broad-except - _LOGGER.exception('Error during setup of component %s', domain) - return False - finally: - _CURRENT_SETUP.remove(domain) + # Assumption: if a component does not depend on groups + # it communicates with devices + if 'group' not in getattr(component, 'DEPENDENCIES', []) and \ + hass.pool.worker_count <= 10: + hass.pool.add_worker() - hass.config.components.append(component.DOMAIN) + hass.bus.async_fire( + EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN} + ) - # Assumption: if a component does not depend on groups - # it communicates with devices - if 'group' not in getattr(component, 'DEPENDENCIES', []) and \ - hass.pool.worker_count <= 10: - hass.pool.add_worker() - - hass.bus.fire( - EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN} - ) - - return True + return True def prepare_setup_component(hass: core.HomeAssistant, config: dict, domain: str): """Prepare setup of a component and return processed config.""" + return run_coroutine_threadsafe( + async_prepare_setup_component(hass, config, domain), loop=hass.loop + ).result() + + +@asyncio.coroutine +def async_prepare_setup_component(hass: core.HomeAssistant, config: dict, + domain: str): + """Prepare setup of a component and return processed config. + + This method is a coroutine. + """ # pylint: disable=too-many-return-statements component = loader.get_component(domain) missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) @@ -151,7 +185,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict, try: config = component.CONFIG_SCHEMA(config) except vol.Invalid as ex: - log_exception(ex, domain, config, hass) + async_log_exception(ex, domain, config, hass) return None elif hasattr(component, 'PLATFORM_SCHEMA'): @@ -161,7 +195,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict, try: p_validated = component.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: - log_exception(ex, domain, config, hass) + async_log_exception(ex, domain, config, hass) continue # Not all platform components follow same pattern for platforms @@ -171,8 +205,8 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict, platforms.append(p_validated) continue - platform = prepare_setup_platform(hass, config, domain, - p_name) + platform = yield from async_prepare_setup_platform( + hass, config, domain, p_name) if platform is None: continue @@ -180,10 +214,11 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict, # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: + # pylint: disable=no-member p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: - log_exception(ex, '{}.{}'.format(domain, p_name), - p_validated, hass) + async_log_exception(ex, '{}.{}'.format(domain, p_name), + p_validated, hass) continue platforms.append(p_validated) @@ -195,7 +230,9 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict, if key not in filter_keys} config[domain] = platforms - if not _handle_requirements(hass, component, domain): + res = yield from hass.loop.run_in_executor( + None, _handle_requirements, hass, component, domain) + if not res: return None return config @@ -204,7 +241,22 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict, def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str, platform_name: str) -> Optional[ModuleType]: """Load a platform and makes sure dependencies are setup.""" - _ensure_loader_prepared(hass) + return run_coroutine_threadsafe( + async_prepare_setup_platform(hass, config, domain, platform_name), + loop=hass.loop + ).result() + + +@asyncio.coroutine +def async_prepare_setup_platform(hass: core.HomeAssistant, config, domain: str, + platform_name: str) \ + -> Optional[ModuleType]: + """Load a platform and makes sure dependencies are setup. + + This method is a coroutine. + """ + if not loader.PREPARED: + yield from hass.loop.run_in_executor(None, loader.prepare, hass) platform_path = PLATFORM_FORMAT.format(domain, platform_name) @@ -218,7 +270,7 @@ def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str, message = ('Unable to find the following platforms: ' + ', '.join(list(_PERSISTENT_PLATFORMS)) + '(please check your configuration)') - persistent_notification.create( + persistent_notification.async_create( hass, message, 'Invalid platforms', 'platform_errors') return None @@ -228,14 +280,17 @@ def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str, # Load dependencies for component in getattr(platform, 'DEPENDENCIES', []): - if not setup_component(hass, component, config): + res = yield from async_setup_component(hass, component, config) + if not res: _LOGGER.error( 'Unable to prepare setup for platform %s because ' 'dependency %s could not be initialized', platform_path, component) return None - if not _handle_requirements(hass, platform, platform_path): + res = yield from hass.loop.run_in_executor( + None, _handle_requirements, hass, platform, platform_path) + if not res: return None return platform @@ -261,15 +316,50 @@ def from_config_dict(config: Dict[str, Any], hass.config.config_dir = config_dir mount_local_lib_path(config_dir) + @asyncio.coroutine + def _async_init_from_config_dict(future): + try: + re_hass = yield from async_from_config_dict( + config, hass, config_dir, enable_log, verbose, skip_pip, + log_rotate_days) + future.set_result(re_hass) + # pylint: disable=broad-except + except Exception as exc: + future.set_exception(exc) + + # run task + future = asyncio.Future() + asyncio.Task(_async_init_from_config_dict(future), loop=hass.loop) + hass.loop.run_until_complete(future) + + return future.result() + + +@asyncio.coroutine +# pylint: disable=too-many-branches, too-many-statements, too-many-arguments +def async_from_config_dict(config: Dict[str, Any], + hass: core.HomeAssistant, + config_dir: Optional[str]=None, + enable_log: bool=True, + verbose: bool=False, + skip_pip: bool=False, + log_rotate_days: Any=None) \ + -> Optional[core.HomeAssistant]: + """Try to configure Home Assistant from a config dict. + + Dynamically loads required components and its dependencies. + This method is a coroutine. + """ core_config = config.get(core.DOMAIN, {}) try: - conf_util.process_ha_core_config(hass, core_config) + yield from conf_util.async_process_ha_core_config(hass, core_config) except vol.Invalid as ex: - log_exception(ex, 'homeassistant', core_config, hass) + async_log_exception(ex, 'homeassistant', core_config, hass) return None - conf_util.process_ha_config_upgrade(hass) + yield from hass.loop.run_in_executor( + None, conf_util.process_ha_config_upgrade, hass) if enable_log: enable_logging(hass, verbose, log_rotate_days) @@ -279,7 +369,8 @@ def from_config_dict(config: Dict[str, Any], _LOGGER.warning('Skipping pip installation of required modules. ' 'This may cause issues.') - _ensure_loader_prepared(hass) + if not loader.PREPARED: + yield from hass.loop.run_in_executor(None, loader.prepare, hass) # Make a copy because we are mutating it. # Convert it to defaultdict so components can always have config dict @@ -291,29 +382,25 @@ def from_config_dict(config: Dict[str, Any], components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) - # Setup in a thread to avoid blocking - def component_setup(): - """Set up a component.""" - if not core_components.setup(hass, config): - _LOGGER.error('Home Assistant core failed to initialize. ' - 'Further initialization aborted.') - return hass + # setup components + # pylint: disable=not-an-iterable + res = yield from core_components.async_setup(hass, config) + if not res: + _LOGGER.error('Home Assistant core failed to initialize. ' + 'Further initialization aborted.') + return hass - persistent_notification.setup(hass, config) + yield from persistent_notification.async_setup(hass, config) - _LOGGER.info('Home Assistant core initialized') + _LOGGER.info('Home Assistant core initialized') - # Give event decorators access to HASS - event_decorators.HASS = hass - service.HASS = hass + # Give event decorators access to HASS + event_decorators.HASS = hass + service.HASS = hass - # Setup the components - for domain in loader.load_order_components(components): - _setup_component(hass, domain, config) - - hass.loop.run_until_complete( - hass.loop.run_in_executor(None, component_setup) - ) + # Setup the components + for domain in loader.load_order_components(components): + yield from _async_setup_component(hass, domain, config) return hass @@ -331,27 +418,62 @@ def from_config_file(config_path: str, if hass is None: hass = core.HomeAssistant() + @asyncio.coroutine + def _async_init_from_config_file(future): + try: + re_hass = yield from async_from_config_file( + config_path, hass, verbose, skip_pip, log_rotate_days) + future.set_result(re_hass) + # pylint: disable=broad-except + except Exception as exc: + future.set_exception(exc) + + # run task + future = asyncio.Future() + asyncio.Task(_async_init_from_config_file(future), loop=hass.loop) + hass.loop.run_until_complete(future) + + return future.result() + + +@asyncio.coroutine +def async_from_config_file(config_path: str, + hass: core.HomeAssistant, + verbose: bool=False, + skip_pip: bool=True, + log_rotate_days: Any=None): + """Read the configuration file and try to start all the functionality. + + Will add functionality to 'hass' parameter. + This method is a coroutine. + """ # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir - mount_local_lib_path(config_dir) + yield from hass.loop.run_in_executor( + None, mount_local_lib_path, config_dir) enable_logging(hass, verbose, log_rotate_days) try: - config_dict = conf_util.load_yaml_config_file(config_path) + config_dict = yield from hass.loop.run_in_executor( + None, conf_util.load_yaml_config_file, config_path) except HomeAssistantError: return None finally: clear_secret_cache() - return from_config_dict(config_dict, hass, enable_log=False, - skip_pip=skip_pip) + hass = yield from async_from_config_dict( + config_dict, hass, enable_log=False, skip_pip=skip_pip) + return hass def enable_logging(hass: core.HomeAssistant, verbose: bool=False, log_rotate_days=None) -> None: - """Setup the logging.""" + """Setup the logging. + + Async friendly. + """ logging.basicConfig(level=logging.INFO) fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) " "[%(name)s] %(message)s%(reset)s") @@ -407,44 +529,50 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False, 'Unable to setup error log %s (access denied)', err_log_path) -def _ensure_loader_prepared(hass: core.HomeAssistant) -> None: - """Ensure Home Assistant loader is prepared.""" - if not loader.PREPARED: - loader.prepare(hass) - - -def log_exception(ex, domain, config, hass=None): +def log_exception(ex, domain, config, hass): """Generate log exception for config validation.""" + run_callback_threadsafe( + hass.loop, async_log_exception, ex, domain, config, hass).result() + + +@core.callback +def async_log_exception(ex, domain, config, hass): + """Generate log exception for config validation. + + Need to run in a async loop. + """ message = 'Invalid config for [{}]: '.format(domain) - if hass is not None: - _PERSISTENT_VALIDATION.add(domain) - message = ('The following platforms contain invalid configuration: ' + - ', '.join(list(_PERSISTENT_VALIDATION)) + - ' (please check your configuration)') - persistent_notification.create( - hass, message, 'Invalid config', 'invalid_config') + _PERSISTENT_VALIDATION.add(domain) + message = ('The following platforms contain invalid configuration: ' + + ', '.join(list(_PERSISTENT_VALIDATION)) + + ' (please check your configuration). ') + persistent_notification.async_create( + hass, message, 'Invalid config', 'invalid_config') if 'extra keys not allowed' in ex.error_message: message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\ .format(ex.path[-1], domain, domain, - '->'.join('%s' % m for m in ex.path)) + '->'.join(str(m) for m in ex.path)) else: message += '{}.'.format(humanize_error(config, ex)) domain_config = config.get(domain, config) - message += " (See {}:{})".format( + message += " (See {}:{}). ".format( getattr(domain_config, '__config_file__', '?'), getattr(domain_config, '__line__', '?')) if domain != 'homeassistant': - message += (' Please check the docs at ' + message += ('Please check the docs at ' 'https://home-assistant.io/components/{}/'.format(domain)) _LOGGER.error(message) def mount_local_lib_path(config_dir: str) -> str: - """Add local library to Python Path.""" + """Add local library to Python Path. + + Async friendly. + """ deps_dir = os.path.join(config_dir, 'deps') if deps_dir not in sys.path: sys.path.insert(0, os.path.join(config_dir, 'deps')) diff --git a/homeassistant/components/__init__.py b/homeassistant/components/__init__.py index 7d025bac765..81450c726f1 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -7,6 +7,7 @@ Component design guidelines: format ".". - Each component should publish services only under its own domain. """ +import asyncio import itertools as it import logging @@ -79,8 +80,10 @@ def reload_core_config(hass): hass.services.call(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup general services related to Home Assistant.""" + @asyncio.coroutine def handle_turn_service(service): """Method to handle calls to homeassistant.turn_on/off.""" entity_ids = extract_entity_ids(hass, service) @@ -96,6 +99,8 @@ def setup(hass, config): by_domain = it.groupby(sorted(entity_ids), lambda item: ha.split_entity_id(item)[0]) + tasks = [] + for domain, ent_ids in by_domain: # We want to block for all calls and only return when all calls # have been processed. If a service does not exist it causes a 10 @@ -111,27 +116,34 @@ def setup(hass, config): # ent_ids is a generator, convert it to a list. data[ATTR_ENTITY_ID] = list(ent_ids) - hass.services.call(domain, service.service, data, blocking) + tasks.append(hass.services.async_call( + domain, service.service, data, blocking)) - hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service) - hass.services.register(ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service) - hass.services.register(ha.DOMAIN, SERVICE_TOGGLE, handle_turn_service) + yield from asyncio.gather(*tasks, loop=hass.loop) + hass.services.async_register( + ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_TOGGLE, handle_turn_service) + + @asyncio.coroutine def handle_reload_config(call): """Service handler for reloading core config.""" from homeassistant.exceptions import HomeAssistantError from homeassistant import config as conf_util try: - path = conf_util.find_config_file(hass.config.config_dir) - conf = conf_util.load_yaml_config_file(path) + conf = yield from conf_util.async_hass_config_yaml(hass) except HomeAssistantError as err: _LOGGER.error(err) return - conf_util.process_ha_core_config(hass, conf.get(ha.DOMAIN) or {}) + yield from conf_util.async_process_ha_core_config( + hass, conf.get(ha.DOMAIN) or {}) - hass.services.register(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, - handle_reload_config) + hass.services.async_register( + ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, handle_reload_config) return True diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 244887ca10a..df0026a45ab 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -11,7 +11,6 @@ import os import voluptuous as vol -from homeassistant.core import callback from homeassistant.bootstrap import prepare_setup_platform from homeassistant import config as conf_util from homeassistant.const import ( @@ -25,7 +24,6 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.loader import get_platform from homeassistant.util.dt import utcnow import homeassistant.helpers.config_validation as cv -from homeassistant.util.async import run_coroutine_threadsafe DOMAIN = 'automation' ENTITY_ID_FORMAT = DOMAIN + '.{}' @@ -144,42 +142,50 @@ def reload(hass): hass.services.call(DOMAIN, SERVICE_RELOAD) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup the automation.""" component = EntityComponent(_LOGGER, DOMAIN, hass, group_name=GROUP_NAME_ALL_AUTOMATIONS) - success = run_coroutine_threadsafe( - _async_process_config(hass, config, component), hass.loop).result() + success = yield from _async_process_config(hass, config, component) if not success: return False - descriptions = conf_util.load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) + descriptions = yield from hass.loop.run_in_executor( + None, conf_util.load_yaml_config_file, os.path.join( + os.path.dirname(__file__), 'services.yaml') + ) - @callback + @asyncio.coroutine def trigger_service_handler(service_call): """Handle automation triggers.""" + tasks = [] for entity in component.async_extract_from_service(service_call): - hass.loop.create_task(entity.async_trigger( + tasks.append(entity.async_trigger( service_call.data.get(ATTR_VARIABLES), True)) + yield from asyncio.gather(*tasks, loop=hass.loop) - @callback + @asyncio.coroutine def turn_onoff_service_handler(service_call): """Handle automation turn on/off service calls.""" + tasks = [] method = 'async_{}'.format(service_call.service) for entity in component.async_extract_from_service(service_call): - hass.loop.create_task(getattr(entity, method)()) + tasks.append(getattr(entity, method)()) + yield from asyncio.gather(*tasks, loop=hass.loop) - @callback + @asyncio.coroutine def toggle_service_handler(service_call): """Handle automation toggle service calls.""" + tasks = [] for entity in component.async_extract_from_service(service_call): if entity.is_on: - hass.loop.create_task(entity.async_turn_off()) + tasks.append(entity.async_turn_off()) else: - hass.loop.create_task(entity.async_turn_on()) + tasks.append(entity.async_turn_on()) + yield from asyncio.gather(*tasks, loop=hass.loop) @asyncio.coroutine def reload_service_handler(service_call): @@ -187,24 +193,24 @@ def setup(hass, config): conf = yield from component.async_prepare_reload() if conf is None: return - hass.loop.create_task(_async_process_config(hass, conf, component)) + yield from _async_process_config(hass, conf, component) - hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service_handler, - descriptions.get(SERVICE_TRIGGER), - schema=TRIGGER_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_TRIGGER, trigger_service_handler, + descriptions.get(SERVICE_TRIGGER), schema=TRIGGER_SERVICE_SCHEMA) - hass.services.register(DOMAIN, SERVICE_RELOAD, reload_service_handler, - descriptions.get(SERVICE_RELOAD), - schema=RELOAD_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_RELOAD, reload_service_handler, + descriptions.get(SERVICE_RELOAD), schema=RELOAD_SERVICE_SCHEMA) - hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service_handler, - descriptions.get(SERVICE_TOGGLE), - schema=SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_TOGGLE, toggle_service_handler, + descriptions.get(SERVICE_TOGGLE), schema=SERVICE_SCHEMA) for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF): - hass.services.register(DOMAIN, service, turn_onoff_service_handler, - descriptions.get(service), - schema=SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, service, turn_onoff_service_handler, + descriptions.get(service), schema=SERVICE_SCHEMA) return True diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 12735940916..aefc220c809 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -114,7 +114,7 @@ def setup(hass: HomeAssistantType, config: ConfigType): try: conf = config.get(DOMAIN, []) except vol.Invalid as ex: - log_exception(ex, DOMAIN, config) + log_exception(ex, DOMAIN, config, hass) return False else: conf = conf[0] if len(conf) > 0 else {} @@ -431,7 +431,7 @@ def load_config(path: str, hass: HomeAssistantType, consider_home: timedelta): device = dev_schema(device) device['dev_id'] = cv.slugify(dev_id) except vol.Invalid as exp: - log_exception(exp, dev_id, devices) + log_exception(exp, dev_id, devices, hass) else: result.append(Device(hass, **device)) return result diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index 915254bd618..59683247d5b 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -144,15 +144,17 @@ def get_entity_ids(hass, entity_id, domain_filter=None): if ent_id.startswith(domain_filter)] -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup all groups found definded in the configuration.""" component = EntityComponent(_LOGGER, DOMAIN, hass) - run_coroutine_threadsafe( - _async_process_config(hass, config, component), hass.loop).result() + yield from _async_process_config(hass, config, component) - descriptions = conf_util.load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) + descriptions = yield from hass.loop.run_in_executor( + None, conf_util.load_yaml_config_file, os.path.join( + os.path.dirname(__file__), 'services.yaml') + ) @asyncio.coroutine def reload_service_handler(service_call): @@ -162,9 +164,9 @@ def setup(hass, config): return hass.loop.create_task(_async_process_config(hass, conf, component)) - hass.services.register(DOMAIN, SERVICE_RELOAD, reload_service_handler, - descriptions[DOMAIN][SERVICE_RELOAD], - schema=RELOAD_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_RELOAD, reload_service_handler, + descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA) return True diff --git a/homeassistant/components/persistent_notification.py b/homeassistant/components/persistent_notification.py index d27389b51f9..5e91aef4d9f 100644 --- a/homeassistant/components/persistent_notification.py +++ b/homeassistant/components/persistent_notification.py @@ -10,12 +10,13 @@ import logging import voluptuous as vol +from homeassistant.core import callback from homeassistant.exceptions import TemplateError from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity import generate_entity_id +from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.util import slugify from homeassistant.config import load_yaml_config_file -from homeassistant.util.async import run_coroutine_threadsafe +from homeassistant.util.async import run_callback_threadsafe DOMAIN = 'persistent_notification' ENTITY_ID_FORMAT = DOMAIN + '.{}' @@ -38,12 +39,12 @@ _LOGGER = logging.getLogger(__name__) def create(hass, message, title=None, notification_id=None): """Generate a notification.""" - run_coroutine_threadsafe( - async_create(hass, message, title, notification_id), hass.loop + run_callback_threadsafe( + hass.loop, async_create, hass, message, title, notification_id ).result() -@asyncio.coroutine +@callback def async_create(hass, message, title=None, notification_id=None): """Generate a notification.""" data = { @@ -54,11 +55,14 @@ def async_create(hass, message, title=None, notification_id=None): ] if value is not None } - yield from hass.services.async_call(DOMAIN, SERVICE_CREATE, data) + hass.loop.create_task( + hass.services.async_call(DOMAIN, SERVICE_CREATE, data)) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup the persistent notification component.""" + @callback def create_service(call): """Handle a create notification service call.""" title = call.data.get(ATTR_TITLE) @@ -68,13 +72,13 @@ def setup(hass, config): if notification_id is not None: entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id)) else: - entity_id = generate_entity_id(ENTITY_ID_FORMAT, DEFAULT_OBJECT_ID, - hass=hass) + entity_id = async_generate_entity_id( + ENTITY_ID_FORMAT, DEFAULT_OBJECT_ID, hass=hass) attr = {} if title is not None: try: title.hass = hass - title = title.render() + title = title.async_render() except TemplateError as ex: _LOGGER.error('Error rendering title %s: %s', title, ex) title = title.template @@ -83,17 +87,19 @@ def setup(hass, config): try: message.hass = hass - message = message.render() + message = message.async_render() except TemplateError as ex: _LOGGER.error('Error rendering message %s: %s', message, ex) message = message.template - hass.states.set(entity_id, message, attr) + hass.states.async_set(entity_id, message, attr) - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) - hass.services.register(DOMAIN, SERVICE_CREATE, create_service, - descriptions[DOMAIN][SERVICE_CREATE], - SCHEMA_SERVICE_CREATE) + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, os.path.join( + os.path.dirname(__file__), 'services.yaml') + ) + hass.services.async_register(DOMAIN, SERVICE_CREATE, create_service, + descriptions[DOMAIN][SERVICE_CREATE], + SCHEMA_SERVICE_CREATE) return True diff --git a/homeassistant/config.py b/homeassistant/config.py index 712740419d0..0e2192b3152 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -1,4 +1,5 @@ """Module to help with parsing and generating configuration files.""" +import asyncio import logging import os import shutil @@ -180,6 +181,24 @@ def create_default_config(config_dir, detect_location=True): return None +@asyncio.coroutine +def async_hass_config_yaml(hass): + """Load YAML from hass config File. + + This function allow component inside asyncio loop to reload his config by + self. + + This method is a coroutine. + """ + def _load_hass_yaml_config(): + path = find_config_file(hass.config.config_dir) + conf = load_yaml_config_file(path) + return conf + + conf = yield from hass.loop.run_in_executor(None, _load_hass_yaml_config) + return conf + + def find_config_file(config_dir): """Look in given directory for supported configuration files.""" config_path = os.path.join(config_dir, YAML_CONFIG_FILE) @@ -201,7 +220,11 @@ def load_yaml_config_file(config_path): def process_ha_config_upgrade(hass): - """Upgrade config if necessary.""" + """Upgrade config if necessary. + + Asyncio don't support file operation jet. + This method need to run in a executor. + """ version_path = hass.config.path(VERSION_FILE) try: @@ -225,8 +248,12 @@ def process_ha_config_upgrade(hass): outp.write(__version__) -def process_ha_core_config(hass, config): - """Process the [homeassistant] section from the config.""" +@asyncio.coroutine +def async_process_ha_core_config(hass, config): + """Process the [homeassistant] section from the config. + + This method is a coroutine. + """ # pylint: disable=too-many-branches config = CORE_CONFIG_SCHEMA(config) hac = hass.config @@ -282,7 +309,8 @@ def process_ha_core_config(hass, config): # If we miss some of the needed values, auto detect them if None in (hac.latitude, hac.longitude, hac.units, hac.time_zone): - info = loc_util.detect_location_info() + info = yield from hass.loop.run_in_executor( + None, loc_util.detect_location_info) if info is None: _LOGGER.error('Could not detect location information') @@ -307,7 +335,8 @@ def process_ha_core_config(hass, config): if hac.elevation is None and hac.latitude is not None and \ hac.longitude is not None: - elevation = loc_util.elevation(hac.latitude, hac.longitude) + elevation = yield from hass.loop.run_in_executor( + None, loc_util.elevation, hac.latitude, hac.longitude) hac.elevation = elevation discovered.append(('elevation', elevation)) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 08f93b3697b..27f180a72ca 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -56,7 +56,10 @@ def async_generate_entity_id(entity_id_format: str, name: Optional[str], def set_customize(customize: Dict[str, Any]) -> None: - """Overwrite all current customize settings.""" + """Overwrite all current customize settings. + + Async friendly. + """ global _OVERWRITE _OVERWRITE = {key.lower(): val for key, val in customize.items()} diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 2576970065f..ed450bb1a14 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -2,8 +2,8 @@ import asyncio from homeassistant import config as conf_util -from homeassistant.bootstrap import (prepare_setup_platform, - prepare_setup_component) +from homeassistant.bootstrap import ( + async_prepare_setup_platform, async_prepare_setup_component) from homeassistant.const import ( ATTR_ENTITY_ID, CONF_SCAN_INTERVAL, CONF_ENTITY_NAMESPACE, DEVICE_DEFAULT_NAME) @@ -118,10 +118,8 @@ class EntityComponent(object): This method must be run in the event loop. """ - platform = yield from self.hass.loop.run_in_executor( - None, prepare_setup_platform, self.hass, self.config, self.domain, - platform_type - ) + platform = yield from async_prepare_setup_platform( + self.hass, self.config, self.domain, platform_type) if platform is None: return @@ -238,20 +236,8 @@ class EntityComponent(object): def prepare_reload(self): """Prepare reloading this entity component.""" - try: - path = conf_util.find_config_file(self.hass.config.config_dir) - conf = conf_util.load_yaml_config_file(path) - except HomeAssistantError as err: - self.logger.error(err) - return None - - conf = prepare_setup_component(self.hass, conf, self.domain) - - if conf is None: - return None - - self.reset() - return conf + return run_coroutine_threadsafe( + self.async_prepare_reload(), loop=self.hass.loop).result() @asyncio.coroutine def async_prepare_reload(self): @@ -259,9 +245,20 @@ class EntityComponent(object): This method must be run in the event loop. """ - conf = yield from self.hass.loop.run_in_executor( - None, self.prepare_reload - ) + try: + conf = yield from \ + conf_util.async_hass_config_yaml(self.hass) + except HomeAssistantError as err: + self.logger.error(err) + return None + + conf = yield from async_prepare_setup_component( + self.hass, conf, self.domain) + + if conf is None: + return None + + yield from self.async_reset() return conf diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 591e7d229a3..a19d835543d 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -40,7 +40,11 @@ _LOGGER = logging.getLogger(__name__) def prepare(hass: 'HomeAssistant'): - """Prepare the loading of components.""" + """Prepare the loading of components. + + Asyncio don't support file operation jet. + This method need to run in a executor. + """ global PREPARED # pylint: disable=global-statement # Load the built-in components @@ -81,14 +85,20 @@ def prepare(hass: 'HomeAssistant'): def set_component(comp_name: str, component: ModuleType) -> None: - """Set a component in the cache.""" + """Set a component in the cache. + + Async friendly. + """ _check_prepared() _COMPONENT_CACHE[comp_name] = component def get_platform(domain: str, platform: str) -> Optional[ModuleType]: - """Try to load specified platform.""" + """Try to load specified platform. + + Async friendly. + """ return get_component(PLATFORM_FORMAT.format(domain, platform)) @@ -97,6 +107,8 @@ def get_component(comp_name) -> Optional[ModuleType]: Looks in config dir first, then built-in components. Only returns it if also found to be valid. + + Async friendly. """ if comp_name in _COMPONENT_CACHE: return _COMPONENT_CACHE[comp_name] @@ -166,6 +178,8 @@ def load_order_components(components: Sequence[str]) -> OrderedSet: - Will ensure that all components that do not directly depend on the group component will be loaded before the group component. - returns an OrderedSet load order. + + Async friendly. """ _check_prepared() @@ -192,13 +206,18 @@ def load_order_component(comp_name: str) -> OrderedSet: Raises HomeAssistantError if a circular dependency is detected. Returns an empty list if component could not be loaded. + + Async friendly. """ return _load_order_component(comp_name, OrderedSet(), set()) def _load_order_component(comp_name: str, load_order: OrderedSet, loading: Set) -> OrderedSet: - """Recursive function to get load order of components.""" + """Recursive function to get load order of components. + + Async friendly. + """ component = get_component(comp_name) # If None it does not exist, error already thrown by get_component. @@ -235,7 +254,10 @@ def _load_order_component(comp_name: str, load_order: OrderedSet, def _check_prepared() -> None: - """Issue a warning if loader.prepare() has never been called.""" + """Issue a warning if loader.prepare() has never been called. + + Async friendly. + """ if not PREPARED: _LOGGER.warning(( "You did not call loader.prepare() yet. " diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index f8b6fc6e69b..736498cf935 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -26,8 +26,8 @@ MOCKS = { 'load*': ("homeassistant.config.load_yaml", yaml.load_yaml), 'get': ("homeassistant.loader.get_component", loader.get_component), 'secrets': ("homeassistant.util.yaml._secret_yaml", yaml._secret_yaml), - 'except': ("homeassistant.bootstrap.log_exception", - bootstrap.log_exception) + 'except': ("homeassistant.bootstrap.async_log_exception", + bootstrap.async_log_exception) } SILENCE = ( 'homeassistant.bootstrap.clear_secret_cache', @@ -185,9 +185,15 @@ def check(config_path): # Test if platform/component and overwrite setup if '.' in comp_name: module.setup_platform = mock_setup + + if hasattr(module, 'async_setup_platform'): + del module.async_setup_platform else: module.setup = mock_setup + if hasattr(module, 'async_setup'): + del module.async_setup + return module def mock_secrets(ldr, node): # pylint: disable=unused-variable diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index 282ddf9bb8c..828281aa897 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -23,7 +23,10 @@ DATETIME_RE = re.compile( def set_default_time_zone(time_zone: dt.tzinfo) -> None: - """Set a default time zone to be used when none is specified.""" + """Set a default time zone to be used when none is specified. + + Async friendly. + """ global DEFAULT_TIME_ZONE # pylint: disable=global-statement # NOTE: Remove in the future in favour of typing @@ -33,7 +36,10 @@ def set_default_time_zone(time_zone: dt.tzinfo) -> None: def get_time_zone(time_zone_str: str) -> Optional[dt.tzinfo]: - """Get time zone from string. Return None if unable to determine.""" + """Get time zone from string. Return None if unable to determine. + + Async friendly. + """ try: return pytz.timezone(time_zone_str) except pytz.exceptions.UnknownTimeZoneError: diff --git a/homeassistant/util/yaml.py b/homeassistant/util/yaml.py index 6b1bc2227c8..4a73ac7c8dd 100644 --- a/homeassistant/util/yaml.py +++ b/homeassistant/util/yaml.py @@ -49,7 +49,10 @@ def load_yaml(fname: str) -> Union[List, Dict]: def clear_secret_cache() -> None: - """Clear the secret cache.""" + """Clear the secret cache. + + Async friendly. + """ __SECRET_CACHE.clear() diff --git a/tests/common.py b/tests/common.py index b73af5fc4c5..8896a97881b 100644 --- a/tests/common.py +++ b/tests/common.py @@ -10,7 +10,8 @@ import threading from contextlib import contextmanager from homeassistant import core as ha, loader -from homeassistant.bootstrap import setup_component, prepare_setup_component +from homeassistant.bootstrap import ( + setup_component, async_prepare_setup_component) from homeassistant.helpers.entity import ToggleEntity from homeassistant.util.unit_system import METRIC_SYSTEM import homeassistant.util.dt as date_util @@ -234,6 +235,7 @@ class MockModule(object): self.DOMAIN = domain self.DEPENDENCIES = dependencies or [] self.REQUIREMENTS = requirements or [] + self._setup = setup if config_schema is not None: self.CONFIG_SCHEMA = config_schema @@ -241,11 +243,11 @@ class MockModule(object): if platform_schema is not None: self.PLATFORM_SCHEMA = platform_schema - # Setup a mock setup if none given. - if setup is None: - self.setup = lambda hass, config: True - else: - self.setup = setup + def setup(self, hass, config): + """Setup the component.""" + if self._setup is not None: + return self._setup(hass, config) + return True class MockPlatform(object): @@ -366,16 +368,19 @@ def assert_setup_component(count, domain=None): """ config = {} + @asyncio.coroutine def mock_psc(hass, config_input, domain): """Mock the prepare_setup_component to capture config.""" - res = prepare_setup_component(hass, config_input, domain) + res = yield from async_prepare_setup_component( + hass, config_input, domain) config[domain] = None if res is None else res.get(domain) _LOGGER.debug('Configuration for %s, Validated: %s, Original %s', domain, config[domain], config_input.get(domain)) return res assert isinstance(config, dict) - with patch('homeassistant.bootstrap.prepare_setup_component', mock_psc): + with patch('homeassistant.bootstrap.async_prepare_setup_component', + mock_psc): yield config if domain is None: diff --git a/tests/components/automation/test_event.py b/tests/components/automation/test_event.py index 33fc5bf117a..2ab62833eda 100644 --- a/tests/components/automation/test_event.py +++ b/tests/components/automation/test_event.py @@ -1,7 +1,7 @@ """The tests for the Event automation.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.automation as automation from tests.common import get_test_home_assistant @@ -28,7 +28,7 @@ class TestAutomationEvent(unittest.TestCase): def test_if_fires_on_event(self): """Test the firing of events.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -53,7 +53,7 @@ class TestAutomationEvent(unittest.TestCase): def test_if_fires_on_event_with_data(self): """Test the firing of events with data.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -73,7 +73,7 @@ class TestAutomationEvent(unittest.TestCase): def test_if_not_fires_if_event_data_not_matches(self): """Test firing of event if no match.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index ec128f77756..2956be98b00 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -379,21 +379,22 @@ class TestAutomation(unittest.TestCase): self.hass.block_till_done() assert automation.is_on(self.hass, entity_id) - @patch('homeassistant.config.load_yaml_config_file', return_value={ - automation.DOMAIN: { - 'alias': 'bye', - 'trigger': { - 'platform': 'event', - 'event_type': 'test_event2', - }, - 'action': { - 'service': 'test.automation', - 'data_template': { - 'event': '{{ trigger.event.event_type }}' + @patch('homeassistant.config.load_yaml_config_file', autospec=True, + return_value={ + automation.DOMAIN: { + 'alias': 'bye', + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event2', + }, + 'action': { + 'service': 'test.automation', + 'data_template': { + 'event': '{{ trigger.event.event_type }}' + } + } } - } - } - }) + }) def test_reload_config_service(self, mock_load_yaml): """Test the reload config service.""" assert setup_component(self.hass, automation.DOMAIN, { @@ -443,9 +444,8 @@ class TestAutomation(unittest.TestCase): assert len(self.calls) == 2 assert self.calls[1].data.get('event') == 'test_event2' - @patch('homeassistant.config.load_yaml_config_file', return_value={ - automation.DOMAIN: 'not valid', - }) + @patch('homeassistant.config.load_yaml_config_file', autospec=True, + return_value={automation.DOMAIN: 'not valid'}) def test_reload_config_when_invalid_config(self, mock_load_yaml): """Test the reload config service handling invalid config.""" with assert_setup_component(1): diff --git a/tests/components/automation/test_mqtt.py b/tests/components/automation/test_mqtt.py index aade8b7dad8..b7da76fda20 100644 --- a/tests/components/automation/test_mqtt.py +++ b/tests/components/automation/test_mqtt.py @@ -1,7 +1,7 @@ """The tests for the MQTT automation.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.automation as automation from tests.common import ( mock_mqtt_component, fire_mqtt_message, get_test_home_assistant) @@ -28,7 +28,7 @@ class TestAutomationMQTT(unittest.TestCase): def test_if_fires_on_topic_match(self): """Test if message is fired on topic match.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'mqtt', @@ -58,7 +58,7 @@ class TestAutomationMQTT(unittest.TestCase): def test_if_fires_on_topic_and_payload_match(self): """Test if message is fired on topic and payload match.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'mqtt', @@ -77,7 +77,7 @@ class TestAutomationMQTT(unittest.TestCase): def test_if_not_fires_on_topic_but_no_payload_match(self): """Test if message is not fired on topic but no payload.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'mqtt', diff --git a/tests/components/automation/test_numeric_state.py b/tests/components/automation/test_numeric_state.py index fecd8474763..fa2d237ee00 100644 --- a/tests/components/automation/test_numeric_state.py +++ b/tests/components/automation/test_numeric_state.py @@ -1,7 +1,7 @@ """The tests for numeric state automation.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.automation as automation from tests.common import get_test_home_assistant @@ -28,7 +28,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_fires_on_entity_change_below(self): """"Test the firing with changed entity.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -58,7 +58,7 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.states.set('test.entity', 11) self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -81,7 +81,7 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.states.set('test.entity', 9) self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -101,7 +101,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_fires_on_entity_change_above(self): """"Test the firing with changed entity.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -124,7 +124,7 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.states.set('test.entity', 9) self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -148,7 +148,7 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.states.set('test.entity', 11) self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -168,7 +168,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_fires_on_entity_change_below_range(self): """"Test the firing with changed entity.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -188,7 +188,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_fires_on_entity_change_below_above_range(self): """"Test the firing with changed entity.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -211,7 +211,7 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.states.set('test.entity', 11) self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -235,7 +235,7 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.states.set('test.entity', 11) self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -256,7 +256,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_not_fires_if_entity_not_match(self): """"Test if not fired with non matching entity.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -275,7 +275,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_fires_on_entity_change_below_with_attribute(self): """"Test attributes change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -294,7 +294,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_not_fires_on_entity_change_not_below_with_attribute(self): """"Test attributes.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -313,7 +313,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_fires_on_attribute_change_with_attribute_below(self): """"Test attributes change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -333,7 +333,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_not_fires_on_attribute_change_with_attribute_not_below(self): """"Test attributes change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -353,7 +353,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_not_fires_on_entity_change_with_attribute_below(self): """"Test attributes change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -373,7 +373,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_if_not_fires_on_entity_change_with_not_attribute_below(self): """"Test attributes change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -393,7 +393,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_fires_on_attr_change_with_attribute_below_and_multiple_attr(self): """"Test attributes change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -414,7 +414,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_template_list(self): """"Test template list.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -436,7 +436,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_template_string(self): """"Test template string.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -469,7 +469,7 @@ class TestAutomationNumericState(unittest.TestCase): def test_not_fires_on_attr_change_with_attr_not_below_multiple_attr(self): """"Test if not fired changed attributes.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'numeric_state', @@ -492,7 +492,7 @@ class TestAutomationNumericState(unittest.TestCase): """"Test if action.""" entity_id = 'domain.test_entity' test_state = 10 - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', diff --git a/tests/components/automation/test_state.py b/tests/components/automation/test_state.py index d6fe56453ee..06c127ca6b7 100644 --- a/tests/components/automation/test_state.py +++ b/tests/components/automation/test_state.py @@ -3,11 +3,12 @@ import unittest from datetime import timedelta from unittest.mock import patch -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.util.dt as dt_util import homeassistant.components.automation as automation -from tests.common import fire_time_changed, get_test_home_assistant +from tests.common import ( + fire_time_changed, get_test_home_assistant, assert_setup_component) class TestAutomationState(unittest.TestCase): @@ -34,7 +35,7 @@ class TestAutomationState(unittest.TestCase): self.hass.states.set('test.entity', 'hello') self.hass.block_till_done() - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -67,7 +68,7 @@ class TestAutomationState(unittest.TestCase): def test_if_fires_on_entity_change_with_from_filter(self): """Test for firing on entity change with filter.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -86,7 +87,7 @@ class TestAutomationState(unittest.TestCase): def test_if_fires_on_entity_change_with_to_filter(self): """Test for firing on entity change with no filter.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -105,7 +106,7 @@ class TestAutomationState(unittest.TestCase): def test_if_fires_on_entity_change_with_state_filter(self): """Test for firing on entity change with state filter.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -124,7 +125,7 @@ class TestAutomationState(unittest.TestCase): def test_if_fires_on_entity_change_with_both_filters(self): """Test for firing if both filters are a non match.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -144,7 +145,7 @@ class TestAutomationState(unittest.TestCase): def test_if_not_fires_if_to_filter_not_match(self): """Test for not firing if to filter is not a match.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -166,7 +167,7 @@ class TestAutomationState(unittest.TestCase): """Test for not firing if from filter is not a match.""" self.hass.states.set('test.entity', 'bye') - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -186,7 +187,7 @@ class TestAutomationState(unittest.TestCase): def test_if_not_fires_if_entity_not_match(self): """Test for not firing if entity is not matching.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -206,7 +207,7 @@ class TestAutomationState(unittest.TestCase): """Test for to action.""" entity_id = 'domain.test_entity' test_state = 'new_state' - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -237,68 +238,72 @@ class TestAutomationState(unittest.TestCase): def test_if_fails_setup_if_to_boolean_value(self): """Test for setup failure for boolean to.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': True, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': True, + }, + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) def test_if_fails_setup_if_from_boolean_value(self): """Test for setup failure for boolean from.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'from': True, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'from': True, + }, + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) def test_if_fails_setup_bad_for(self): """Test for setup failure for bad for.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'to': 'world', - 'for': { - 'invalid': 5 + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + 'for': { + 'invalid': 5 + }, }, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) def test_if_fails_setup_for_without_to(self): """Test for setup failures for missing to.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'for': { - 'seconds': 5 + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'for': { + 'seconds': 5 + }, }, - }, - 'action': { - 'service': 'homeassistant.turn_on', - } - }}) + 'action': { + 'service': 'homeassistant.turn_on', + } + }}) def test_if_not_fires_on_entity_change_with_for(self): """Test for not firing on entity change with for.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -324,7 +329,7 @@ class TestAutomationState(unittest.TestCase): def test_if_fires_on_entity_change_with_for(self): """Test for firing on entity change with for.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'state', @@ -353,7 +358,7 @@ class TestAutomationState(unittest.TestCase): with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: mock_utcnow.return_value = point1 self.hass.states.set('test.entity', 'on') - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -384,32 +389,34 @@ class TestAutomationState(unittest.TestCase): def test_if_fails_setup_for_without_time(self): """Test for setup failure if no time is provided.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'event', - 'event_type': 'bla' - }, - 'condition': { - 'platform': 'state', - 'entity_id': 'test.entity', - 'state': 'on', - 'for': {}, - }, - 'action': {'service': 'test.automation'}, - }}) + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'bla' + }, + 'condition': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'state': 'on', + 'for': {}, + }, + 'action': {'service': 'test.automation'}, + }}) def test_if_fails_setup_for_without_entity(self): """Test for setup failure if no entity is provided.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': {'event_type': 'bla'}, - 'condition': { - 'platform': 'state', - 'state': 'on', - 'for': { - 'seconds': 5 + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': {'event_type': 'bla'}, + 'condition': { + 'platform': 'state', + 'state': 'on', + 'for': { + 'seconds': 5 + }, }, - }, - 'action': {'service': 'test.automation'}, - }}) + 'action': {'service': 'test.automation'}, + }}) diff --git a/tests/components/automation/test_sun.py b/tests/components/automation/test_sun.py index 815c540eb3e..ca3d1618013 100644 --- a/tests/components/automation/test_sun.py +++ b/tests/components/automation/test_sun.py @@ -3,7 +3,7 @@ from datetime import datetime import unittest from unittest.mock import patch -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import sun import homeassistant.components.automation as automation import homeassistant.util.dt as dt_util @@ -42,7 +42,7 @@ class TestAutomationSun(unittest.TestCase): with patch('homeassistant.util.dt.utcnow', return_value=now): - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'sun', @@ -81,7 +81,7 @@ class TestAutomationSun(unittest.TestCase): with patch('homeassistant.util.dt.utcnow', return_value=now): - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'sun', @@ -108,7 +108,7 @@ class TestAutomationSun(unittest.TestCase): with patch('homeassistant.util.dt.utcnow', return_value=now): - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'sun', @@ -142,7 +142,7 @@ class TestAutomationSun(unittest.TestCase): with patch('homeassistant.util.dt.utcnow', return_value=now): - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'sun', @@ -165,7 +165,7 @@ class TestAutomationSun(unittest.TestCase): sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z', }) - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -201,7 +201,7 @@ class TestAutomationSun(unittest.TestCase): sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z', }) - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -237,7 +237,7 @@ class TestAutomationSun(unittest.TestCase): sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z', }) - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -274,7 +274,7 @@ class TestAutomationSun(unittest.TestCase): sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z', }) - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -312,7 +312,7 @@ class TestAutomationSun(unittest.TestCase): sun.STATE_ATTR_NEXT_SETTING: '2015-09-16T15:00:00Z', }) - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -358,7 +358,7 @@ class TestAutomationSun(unittest.TestCase): sun.STATE_ATTR_NEXT_SETTING: '2015-09-16T17:30:00Z', }) - _setup_component(self.hass, automation.DOMAIN, { + setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', diff --git a/tests/components/automation/test_template.py b/tests/components/automation/test_template.py index 1334608ecdb..fcd1a48983a 100644 --- a/tests/components/automation/test_template.py +++ b/tests/components/automation/test_template.py @@ -1,10 +1,10 @@ """The tests for the Template automation.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.automation as automation -from tests.common import get_test_home_assistant +from tests.common import get_test_home_assistant, assert_setup_component class TestAutomationTemplate(unittest.TestCase): @@ -29,7 +29,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_bool(self): """Test for firing on boolean change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -54,7 +54,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_str(self): """Test for firing on change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -72,7 +72,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_str_crazy(self): """Test for firing on change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -90,7 +90,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_not_fires_on_change_bool(self): """Test for not firing on boolean change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -108,7 +108,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_not_fires_on_change_str(self): """Test for not firing on string change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -126,7 +126,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_not_fires_on_change_str_crazy(self): """Test for not firing on string change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -144,7 +144,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_no_change(self): """Test for firing on no change.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -165,7 +165,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_two_change(self): """Test for firing on two changes.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -189,7 +189,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_with_template(self): """Test for firing on change with template.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -207,7 +207,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_not_fires_on_change_with_template(self): """Test for not firing on change with template.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -228,7 +228,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_with_template_advanced(self): """Test for firing on change with template advanced.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -262,7 +262,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_no_change_with_template_advanced(self): """Test for firing on no change with template advanced.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -290,7 +290,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_with_template_2(self): """Test for firing on change with template.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', @@ -332,7 +332,7 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_action(self): """Test for firing if action.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -365,21 +365,22 @@ class TestAutomationTemplate(unittest.TestCase): def test_if_fires_on_change_with_bad_template(self): """Test for firing on change with bad template.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'template', - 'value_template': '{{ ', - }, - 'action': { - 'service': 'test.automation' + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'template', + 'value_template': '{{ ', + }, + 'action': { + 'service': 'test.automation' + } } - } - }) + }) def test_if_fires_on_change_with_bad_template_2(self): """Test for firing on change with bad template.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'template', diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index afdab681460..dba100aa345 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -3,11 +3,12 @@ from datetime import timedelta import unittest from unittest.mock import patch -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.util.dt as dt_util import homeassistant.components.automation as automation -from tests.common import fire_time_changed, get_test_home_assistant +from tests.common import ( + fire_time_changed, get_test_home_assistant, assert_setup_component) class TestAutomationTime(unittest.TestCase): @@ -30,7 +31,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_when_hour_matches(self): """Test for firing if hour is matching.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -55,7 +56,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_when_minute_matches(self): """Test for firing if minutes are matching.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -74,7 +75,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_when_second_matches(self): """Test for firing if seconds are matching.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -93,7 +94,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_when_all_matches(self): """Test for firing if everything matches.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -115,7 +116,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_periodic_seconds(self): """Test for firing periodically every second.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -135,7 +136,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_periodic_minutes(self): """Test for firing periodically every minute.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -155,7 +156,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_periodic_hours(self): """Test for firing periodically every hour.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -175,7 +176,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_fires_using_after(self): """Test for firing after.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'time', @@ -200,16 +201,17 @@ class TestAutomationTime(unittest.TestCase): def test_if_not_working_if_no_values_in_conf_provided(self): """Test for failure if no configuration.""" - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - }, - 'action': { - 'service': 'test.automation' + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + }, + 'action': { + 'service': 'test.automation' + } } - } - }) + }) fire_time_changed(self.hass, dt_util.utcnow().replace( hour=5, minute=0, second=0)) @@ -222,18 +224,19 @@ class TestAutomationTime(unittest.TestCase): This should break the before rule. """ - assert not _setup_component(self.hass, automation.DOMAIN, { - automation.DOMAIN: { - 'trigger': { - 'platform': 'time', - 'after': 3605, - # Total seconds. Hour = 3600 second - }, - 'action': { - 'service': 'test.automation' + with assert_setup_component(0): + assert not setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'time', + 'after': 3605, + # Total seconds. Hour = 3600 second + }, + 'action': { + 'service': 'test.automation' + } } - } - }) + }) fire_time_changed(self.hass, dt_util.utcnow().replace( hour=1, minute=0, second=5)) @@ -243,7 +246,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_action_before(self): """Test for if action before.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -278,7 +281,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_action_after(self): """Test for if action after.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -313,7 +316,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_action_one_weekday(self): """Test for if action with one weekday.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', @@ -349,7 +352,7 @@ class TestAutomationTime(unittest.TestCase): def test_if_action_list_weekday(self): """Test for action with a list of weekdays.""" - assert _setup_component(self.hass, automation.DOMAIN, { + assert setup_component(self.hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { 'platform': 'event', diff --git a/tests/components/binary_sensor/test_mqtt.py b/tests/components/binary_sensor/test_mqtt.py index ada4a9b4224..3bff4420a66 100644 --- a/tests/components/binary_sensor/test_mqtt.py +++ b/tests/components/binary_sensor/test_mqtt.py @@ -1,7 +1,7 @@ """The tests for the MQTT binary sensor platform.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.binary_sensor as binary_sensor from tests.common import mock_mqtt_component, fire_mqtt_message from homeassistant.const import (STATE_OFF, STATE_ON) @@ -24,7 +24,7 @@ class TestSensorMQTT(unittest.TestCase): def test_setting_sensor_value_via_mqtt_message(self): """Test the setting of the value via MQTT.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, binary_sensor.DOMAIN, { + assert setup_component(self.hass, binary_sensor.DOMAIN, { binary_sensor.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -50,7 +50,7 @@ class TestSensorMQTT(unittest.TestCase): def test_valid_sensor_class(self): """Test the setting of a valid sensor class.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, binary_sensor.DOMAIN, { + assert setup_component(self.hass, binary_sensor.DOMAIN, { binary_sensor.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -65,7 +65,7 @@ class TestSensorMQTT(unittest.TestCase): def test_invalid_sensor_class(self): """Test the setting of an invalid sensor class.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, binary_sensor.DOMAIN, { + assert setup_component(self.hass, binary_sensor.DOMAIN, { binary_sensor.DOMAIN: { 'platform': 'mqtt', 'name': 'test', diff --git a/tests/components/binary_sensor/test_nx584.py b/tests/components/binary_sensor/test_nx584.py index 71efd1ff1b2..49147279711 100644 --- a/tests/components/binary_sensor/test_nx584.py +++ b/tests/components/binary_sensor/test_nx584.py @@ -8,6 +8,8 @@ from nx584 import client as nx584_client from homeassistant.components.binary_sensor import nx584 from homeassistant.bootstrap import setup_component +from tests.common import get_test_home_assistant + class StopMe(Exception): """Stop helper.""" @@ -20,6 +22,7 @@ class TestNX584SensorSetup(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() self._mock_client = mock.patch.object(nx584_client, 'Client') self._mock_client.start() @@ -35,6 +38,7 @@ class TestNX584SensorSetup(unittest.TestCase): def tearDown(self): """Stop everything that was started.""" + self.hass.stop() self._mock_client.stop() @mock.patch('homeassistant.components.binary_sensor.nx584.NX584Watcher') @@ -42,14 +46,13 @@ class TestNX584SensorSetup(unittest.TestCase): def test_setup_defaults(self, mock_nx, mock_watcher): """Test the setup with no configuration.""" add_devices = mock.MagicMock() - hass = mock.MagicMock() config = { 'host': nx584.DEFAULT_HOST, 'port': nx584.DEFAULT_PORT, 'exclude_zones': [], 'zone_types': {}, } - self.assertTrue(nx584.setup_platform(hass, config, add_devices)) + self.assertTrue(nx584.setup_platform(self.hass, config, add_devices)) mock_nx.assert_has_calls( [mock.call(zone, 'opening') for zone in self.fake_zones]) self.assertTrue(add_devices.called) @@ -69,8 +72,7 @@ class TestNX584SensorSetup(unittest.TestCase): 'zone_types': {3: 'motion'}, } add_devices = mock.MagicMock() - hass = mock.MagicMock() - self.assertTrue(nx584.setup_platform(hass, config, add_devices)) + self.assertTrue(nx584.setup_platform(self.hass, config, add_devices)) mock_nx.assert_has_calls([ mock.call(self.fake_zones[0], 'opening'), mock.call(self.fake_zones[2], 'motion'), @@ -84,9 +86,8 @@ class TestNX584SensorSetup(unittest.TestCase): def _test_assert_graceful_fail(self, config): """Test the failing.""" - hass = add_devices = mock.MagicMock() - self.assertFalse(setup_component(hass, 'binary_sensor.nx584', config)) - self.assertFalse(add_devices.called) + self.assertFalse(setup_component( + self.hass, 'binary_sensor.nx584', config)) def test_setup_bad_config(self): """Test the setup with bad configuration.""" @@ -113,8 +114,8 @@ class TestNX584SensorSetup(unittest.TestCase): def test_setup_no_zones(self): """Test the setup with no zones.""" nx584_client.Client.return_value.list_zones.return_value = [] - hass = add_devices = mock.MagicMock() - self.assertTrue(nx584.setup_platform(hass, {}, add_devices)) + add_devices = mock.MagicMock() + self.assertTrue(nx584.setup_platform(self.hass, {}, add_devices)) self.assertFalse(add_devices.called) diff --git a/tests/components/cover/test_rfxtrx.py b/tests/components/cover/test_rfxtrx.py index 80d49dface4..85ff26145ed 100644 --- a/tests/components/cover/test_rfxtrx.py +++ b/tests/components/cover/test_rfxtrx.py @@ -3,7 +3,7 @@ import unittest import pytest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import rfxtrx as rfxtrx_core from tests.common import get_test_home_assistant @@ -28,7 +28,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_valid_config(self): """Test configuration.""" - self.assertTrue(_setup_component(self.hass, 'cover', { + self.assertTrue(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -39,7 +39,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_invalid_config_capital_letters(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'cover', { + self.assertFalse(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -51,7 +51,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_invalid_config_extra_key(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'cover', { + self.assertFalse(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': True, 'invalid_key': 'afda', @@ -64,7 +64,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_invalid_config_capital_packetid(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'cover', { + self.assertFalse(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -76,7 +76,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_invalid_config_missing_packetid(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'cover', { + self.assertFalse(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -87,14 +87,14 @@ class TestCoverRfxtrx(unittest.TestCase): def test_default_config(self): """Test with 0 cover.""" - self.assertTrue(_setup_component(self.hass, 'cover', { + self.assertTrue(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'devices': {}}})) self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES)) def test_one_cover(self): """Test with 1 cover.""" - self.assertTrue(_setup_component(self.hass, 'cover', { + self.assertTrue(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'devices': {'0b1400cd0213c7f210010f51': { @@ -117,7 +117,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_several_covers(self): """Test with 3 covers.""" - self.assertTrue(_setup_component(self.hass, 'cover', { + self.assertTrue(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'signal_repetitions': 3, 'devices': @@ -145,7 +145,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_discover_covers(self): """Test with discovery of covers.""" - self.assertTrue(_setup_component(self.hass, 'cover', { + self.assertTrue(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': {}}})) @@ -183,7 +183,7 @@ class TestCoverRfxtrx(unittest.TestCase): def test_discover_cover_noautoadd(self): """Test with discovery of cover when auto add is False.""" - self.assertTrue(_setup_component(self.hass, 'cover', { + self.assertTrue(setup_component(self.hass, 'cover', { 'cover': {'platform': 'rfxtrx', 'automatic_add': False, 'devices': {}}})) diff --git a/tests/components/device_tracker/test_mqtt.py b/tests/components/device_tracker/test_mqtt.py index e2af75ed76b..4eebf46e632 100644 --- a/tests/components/device_tracker/test_mqtt.py +++ b/tests/components/device_tracker/test_mqtt.py @@ -4,7 +4,7 @@ from unittest.mock import patch import logging import os -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import device_tracker from homeassistant.const import CONF_PLATFORM @@ -42,7 +42,7 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase): dev_id = 'paulus' topic = '/location/paulus' self.hass.config.components = ['mqtt', 'zone'] - assert _setup_component(self.hass, device_tracker.DOMAIN, { + assert setup_component(self.hass, device_tracker.DOMAIN, { device_tracker.DOMAIN: { CONF_PLATFORM: 'mqtt', 'devices': {dev_id: topic} @@ -58,7 +58,7 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase): location = 'work' self.hass.config.components = ['mqtt', 'zone'] - assert _setup_component(self.hass, device_tracker.DOMAIN, { + assert setup_component(self.hass, device_tracker.DOMAIN, { device_tracker.DOMAIN: { CONF_PLATFORM: 'mqtt', 'devices': {dev_id: topic} diff --git a/tests/components/light/test_mqtt_json.py b/tests/components/light/test_mqtt_json.py index 6fc4a00097d..fc9ade7d6ac 100755 --- a/tests/components/light/test_mqtt_json.py +++ b/tests/components/light/test_mqtt_json.py @@ -30,7 +30,7 @@ light: import json import unittest -from homeassistant.bootstrap import _setup_component, setup_component +from homeassistant.bootstrap import setup_component from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ASSUMED_STATE import homeassistant.components.light as light from tests.common import ( @@ -67,7 +67,7 @@ class TestLightMQTTJSON(unittest.TestCase): # pylint: disable=invalid-name """Test if there is no color and brightness if they aren't defined.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, light.DOMAIN, { + assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', 'name': 'test', @@ -93,7 +93,7 @@ class TestLightMQTTJSON(unittest.TestCase): # pylint: disable=invalid-name """Test the controlling of the state via topic.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, light.DOMAIN, { + assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', 'name': 'test', @@ -153,7 +153,7 @@ class TestLightMQTTJSON(unittest.TestCase): # pylint: disable=invalid-name """Test the sending of command in optimistic mode.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, light.DOMAIN, { + assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', 'name': 'test', @@ -209,7 +209,7 @@ class TestLightMQTTJSON(unittest.TestCase): # pylint: disable=invalid-name """Test for flash length being sent when included.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, light.DOMAIN, { + assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', 'name': 'test', @@ -251,7 +251,7 @@ class TestLightMQTTJSON(unittest.TestCase): def test_transition(self): """Test for transition time being sent when included.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, light.DOMAIN, { + assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', 'name': 'test', @@ -293,7 +293,7 @@ class TestLightMQTTJSON(unittest.TestCase): # pylint: disable=invalid-name """Test that invalid color/brightness values are ignored.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, light.DOMAIN, { + assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', 'name': 'test', diff --git a/tests/components/light/test_rfxtrx.py b/tests/components/light/test_rfxtrx.py index a9c2b2d8bcb..c87e562c4ff 100644 --- a/tests/components/light/test_rfxtrx.py +++ b/tests/components/light/test_rfxtrx.py @@ -3,7 +3,7 @@ import unittest import pytest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import rfxtrx as rfxtrx_core from tests.common import get_test_home_assistant @@ -28,7 +28,7 @@ class TestLightRfxtrx(unittest.TestCase): def test_valid_config(self): """Test configuration.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -36,7 +36,7 @@ class TestLightRfxtrx(unittest.TestCase): 'name': 'Test', rfxtrx_core.ATTR_FIREEVENT: True}}}})) - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -47,7 +47,7 @@ class TestLightRfxtrx(unittest.TestCase): def test_invalid_config(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'light', { + self.assertFalse(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'automatic_add': True, 'invalid_key': 'afda', @@ -59,14 +59,14 @@ class TestLightRfxtrx(unittest.TestCase): def test_default_config(self): """Test with 0 switches.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'devices': {}}})) self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES)) def test_old_config(self): """Test with 1 light.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'devices': {'123efab1': { @@ -110,7 +110,7 @@ class TestLightRfxtrx(unittest.TestCase): def test_one_light(self): """Test with 1 light.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'devices': {'0b1100cd0213c7f210010f51': { @@ -179,7 +179,7 @@ class TestLightRfxtrx(unittest.TestCase): def test_several_lights(self): """Test with 3 lights.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'signal_repetitions': 3, 'devices': @@ -212,7 +212,7 @@ class TestLightRfxtrx(unittest.TestCase): def test_discover_light(self): """Test with discovery of lights.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': {}}})) @@ -265,7 +265,7 @@ class TestLightRfxtrx(unittest.TestCase): def test_discover_light_noautoadd(self): """Test with discover of light when auto add is False.""" - self.assertTrue(_setup_component(self.hass, 'light', { + self.assertTrue(setup_component(self.hass, 'light', { 'light': {'platform': 'rfxtrx', 'automatic_add': False, 'devices': {}}})) diff --git a/tests/components/lock/test_mqtt.py b/tests/components/lock/test_mqtt.py index c51d2736b73..0c85360fc00 100644 --- a/tests/components/lock/test_mqtt.py +++ b/tests/components/lock/test_mqtt.py @@ -1,7 +1,7 @@ """The tests for the MQTT lock platform.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED, ATTR_ASSUMED_STATE) import homeassistant.components.lock as lock @@ -24,7 +24,7 @@ class TestLockMQTT(unittest.TestCase): def test_controlling_state_via_topic(self): """Test the controlling state via topic.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, lock.DOMAIN, { + assert setup_component(self.hass, lock.DOMAIN, { lock.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -54,7 +54,7 @@ class TestLockMQTT(unittest.TestCase): def test_sending_mqtt_commands_and_optimistic(self): """Test the sending MQTT commands in optimistic mode.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, lock.DOMAIN, { + assert setup_component(self.hass, lock.DOMAIN, { lock.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -88,7 +88,7 @@ class TestLockMQTT(unittest.TestCase): def test_controlling_state_via_topic_and_json_message(self): """Test the controlling state via topic and JSON message.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, lock.DOMAIN, { + assert setup_component(self.hass, lock.DOMAIN, { lock.DOMAIN: { 'platform': 'mqtt', 'name': 'test', diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index cfa0766c8ed..5b65df9e1da 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -6,7 +6,7 @@ import socket import voluptuous as vol -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.mqtt as mqtt from homeassistant.const import ( EVENT_CALL_SERVICE, ATTR_DOMAIN, ATTR_SERVICE, EVENT_HOMEASSISTANT_START, @@ -52,7 +52,7 @@ class TestMQTT(unittest.TestCase): with mock.patch('homeassistant.components.mqtt.MQTT', side_effect=socket.error()): self.hass.config.components = [] - assert not _setup_component(self.hass, mqtt.DOMAIN, { + assert not setup_component(self.hass, mqtt.DOMAIN, { mqtt.DOMAIN: { mqtt.CONF_BROKER: 'test-broker', } @@ -62,7 +62,7 @@ class TestMQTT(unittest.TestCase): """Test for setup failure if connection to broker is missing.""" with mock.patch('paho.mqtt.client.Client'): self.hass.config.components = [] - assert _setup_component(self.hass, mqtt.DOMAIN, { + assert setup_component(self.hass, mqtt.DOMAIN, { mqtt.DOMAIN: { mqtt.CONF_BROKER: 'test-broker', mqtt.CONF_PROTOCOL: 3.1, @@ -222,7 +222,7 @@ class TestMQTTCallbacks(unittest.TestCase): with mock.patch('paho.mqtt.client.Client'): self.hass.config.components = [] - assert _setup_component(self.hass, mqtt.DOMAIN, { + assert setup_component(self.hass, mqtt.DOMAIN, { mqtt.DOMAIN: { mqtt.CONF_BROKER: 'mock-broker', } diff --git a/tests/components/mqtt/test_server.py b/tests/components/mqtt/test_server.py index eb7dabb28b3..7b0963da23c 100644 --- a/tests/components/mqtt/test_server.py +++ b/tests/components/mqtt/test_server.py @@ -1,7 +1,7 @@ """The tests for the MQTT component embedded server.""" from unittest.mock import Mock, MagicMock, patch -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.mqtt as mqtt from tests.common import get_test_home_assistant @@ -29,7 +29,7 @@ class TestMQTT: password = 'super_secret' self.hass.config.api = MagicMock(api_password=password) - assert _setup_component(self.hass, mqtt.DOMAIN, {}) + assert setup_component(self.hass, mqtt.DOMAIN, {}) assert mock_mqtt.called assert mock_mqtt.mock_calls[0][1][5] == 'homeassistant' assert mock_mqtt.mock_calls[0][1][6] == password @@ -38,7 +38,7 @@ class TestMQTT: self.hass.config.components = ['http'] self.hass.config.api = MagicMock(api_password=None) - assert _setup_component(self.hass, mqtt.DOMAIN, {}) + assert setup_component(self.hass, mqtt.DOMAIN, {}) assert mock_mqtt.called assert mock_mqtt.mock_calls[0][1][5] is None assert mock_mqtt.mock_calls[0][1][6] is None @@ -54,6 +54,7 @@ class TestMQTT: mock_gather.side_effect = BrokerException self.hass.config.api = MagicMock(api_password=None) - assert not _setup_component(self.hass, mqtt.DOMAIN, { + + assert not setup_component(self.hass, mqtt.DOMAIN, { mqtt.DOMAIN: {mqtt.CONF_EMBEDDED: {}} }) diff --git a/tests/components/notify/test_file.py b/tests/components/notify/test_file.py index f63d16a5711..08407b20a58 100644 --- a/tests/components/notify/test_file.py +++ b/tests/components/notify/test_file.py @@ -8,9 +8,8 @@ import homeassistant.components.notify as notify from homeassistant.components.notify import ( ATTR_TITLE_DEFAULT) import homeassistant.util.dt as dt_util -from homeassistant.bootstrap import _setup_component -from tests.common import get_test_home_assistant +from tests.common import get_test_home_assistant, assert_setup_component class TestNotifyFile(unittest.TestCase): @@ -26,12 +25,13 @@ class TestNotifyFile(unittest.TestCase): def test_bad_config(self): """Test set up the platform with bad/missing config.""" - self.assertFalse(_setup_component(self.hass, notify.DOMAIN, { - 'notify': { - 'name': 'test', - 'platform': 'file', - }, - })) + with assert_setup_component(0): + assert not setup_component(self.hass, notify.DOMAIN, { + 'notify': { + 'name': 'test', + 'platform': 'file', + }, + }) @patch('homeassistant.components.notify.file.os.stat') @patch('homeassistant.util.dt.utcnow') diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index c261e5eedbd..2df88b7a6e4 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -6,7 +6,7 @@ import unittest from homeassistant.const import MATCH_ALL from homeassistant.components import recorder -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from tests.common import get_test_home_assistant @@ -17,7 +17,7 @@ class TestRecorder(unittest.TestCase): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() db_uri = 'sqlite://' # In memory DB - _setup_component(self.hass, recorder.DOMAIN, { + setup_component(self.hass, recorder.DOMAIN, { recorder.DOMAIN: {recorder.CONF_DB_URL: db_uri}}) self.hass.start() recorder._verify_instance() diff --git a/tests/components/sensor/test_mqtt.py b/tests/components/sensor/test_mqtt.py index 2ef341d9e21..cac02d6bcd2 100644 --- a/tests/components/sensor/test_mqtt.py +++ b/tests/components/sensor/test_mqtt.py @@ -1,7 +1,7 @@ """The tests for the MQTT sensor platform.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.components.sensor as sensor from tests.common import mock_mqtt_component, fire_mqtt_message @@ -23,7 +23,7 @@ class TestSensorMQTT(unittest.TestCase): def test_setting_sensor_value_via_mqtt_message(self): """Test the setting of the value via MQTT.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, sensor.DOMAIN, { + assert setup_component(self.hass, sensor.DOMAIN, { sensor.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -43,7 +43,7 @@ class TestSensorMQTT(unittest.TestCase): def test_setting_sensor_value_via_mqtt_json_message(self): """Test the setting of the value via MQTT with JSON playload.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, sensor.DOMAIN, { + assert setup_component(self.hass, sensor.DOMAIN, { sensor.DOMAIN: { 'platform': 'mqtt', 'name': 'test', diff --git a/tests/components/sensor/test_rfxtrx.py b/tests/components/sensor/test_rfxtrx.py index cfe5a95605d..1de6cf19419 100644 --- a/tests/components/sensor/test_rfxtrx.py +++ b/tests/components/sensor/test_rfxtrx.py @@ -3,7 +3,7 @@ import unittest import pytest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import rfxtrx as rfxtrx_core from homeassistant.const import TEMP_CELSIUS @@ -29,7 +29,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_default_config(self): """Test with 0 sensor.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'devices': {}}})) @@ -37,7 +37,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_old_config_sensor(self): """Test with 1 sensor.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'devices': {'sensor_0502': { @@ -53,7 +53,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_one_sensor(self): """Test with 1 sensor.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'devices': {'0a52080705020095220269': { @@ -68,7 +68,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_one_sensor_no_datatype(self): """Test with 1 sensor.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'devices': {'0a52080705020095220269': { @@ -88,7 +88,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_several_sensors(self): """Test with 3 sensors.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'devices': {'0a52080705020095220269': { @@ -124,7 +124,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_discover_sensor(self): """Test with discovery of sensor.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': {}}})) @@ -182,7 +182,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_discover_sensor_noautoadd(self): """Test with discover of sensor when auto add is False.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'automatic_add': False, 'devices': {}}})) @@ -209,7 +209,7 @@ class TestSensorRfxtrx(unittest.TestCase): def test_update_of_sensors(self): """Test with 3 sensors.""" - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'devices': {'0a52080705020095220269': { diff --git a/tests/components/sensor/test_yr.py b/tests/components/sensor/test_yr.py index 0f7162c079e..7df47a99688 100644 --- a/tests/components/sensor/test_yr.py +++ b/tests/components/sensor/test_yr.py @@ -2,7 +2,7 @@ from datetime import datetime from unittest.mock import patch -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component import homeassistant.util.dt as dt_util from tests.common import get_test_home_assistant, load_fixture @@ -28,7 +28,7 @@ class TestSensorYr: with patch('homeassistant.components.sensor.yr.dt_util.utcnow', return_value=now): - assert _setup_component(self.hass, 'sensor', { + assert setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'yr', 'elevation': 0}}) @@ -46,7 +46,7 @@ class TestSensorYr: with patch('homeassistant.components.sensor.yr.dt_util.utcnow', return_value=now): - assert _setup_component(self.hass, 'sensor', { + assert setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'yr', 'elevation': 0, 'monitored_conditions': [ diff --git a/tests/components/switch/test_mqtt.py b/tests/components/switch/test_mqtt.py index 6d7314a0895..f39f4d11ec5 100644 --- a/tests/components/switch/test_mqtt.py +++ b/tests/components/switch/test_mqtt.py @@ -1,7 +1,7 @@ """The tests for the MQTT switch platform.""" import unittest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ASSUMED_STATE import homeassistant.components.switch as switch from tests.common import ( @@ -23,7 +23,7 @@ class TestSensorMQTT(unittest.TestCase): def test_controlling_state_via_topic(self): """Test the controlling state via topic.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, switch.DOMAIN, { + assert setup_component(self.hass, switch.DOMAIN, { switch.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -53,7 +53,7 @@ class TestSensorMQTT(unittest.TestCase): def test_sending_mqtt_commands_and_optimistic(self): """Test the sending MQTT commands in optimistic mode.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, switch.DOMAIN, { + assert setup_component(self.hass, switch.DOMAIN, { switch.DOMAIN: { 'platform': 'mqtt', 'name': 'test', @@ -87,7 +87,7 @@ class TestSensorMQTT(unittest.TestCase): def test_controlling_state_via_topic_and_json_message(self): """Test the controlling state via topic and JSON message.""" self.hass.config.components = ['mqtt'] - assert _setup_component(self.hass, switch.DOMAIN, { + assert setup_component(self.hass, switch.DOMAIN, { switch.DOMAIN: { 'platform': 'mqtt', 'name': 'test', diff --git a/tests/components/switch/test_rfxtrx.py b/tests/components/switch/test_rfxtrx.py index 4caf7b3405d..b45342336e3 100644 --- a/tests/components/switch/test_rfxtrx.py +++ b/tests/components/switch/test_rfxtrx.py @@ -3,7 +3,7 @@ import unittest import pytest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import rfxtrx as rfxtrx_core from tests.common import get_test_home_assistant @@ -28,7 +28,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_valid_config(self): """Test configuration.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -39,7 +39,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_valid_config_int_device_id(self): """Test configuration.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -49,7 +49,7 @@ class TestSwitchRfxtrx(unittest.TestCase): }}})) def test_invalid_config1(self): - self.assertFalse(_setup_component(self.hass, 'switch', { + self.assertFalse(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -61,7 +61,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_invalid_config2(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'switch', { + self.assertFalse(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'invalid_key': 'afda', @@ -73,7 +73,7 @@ class TestSwitchRfxtrx(unittest.TestCase): }}})) def test_invalid_config3(self): - self.assertFalse(_setup_component(self.hass, 'switch', { + self.assertFalse(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -85,7 +85,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_invalid_config4(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'switch', { + self.assertFalse(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -96,7 +96,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_default_config(self): """Test with 0 switches.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'devices': {}}})) @@ -104,7 +104,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_old_config(self): """Test with 1 switch.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'devices': {'123efab1': { @@ -132,7 +132,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_one_switch(self): """Test with 1 switch.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'devices': {'0b1100cd0213c7f210010f51': { @@ -170,7 +170,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_several_switches(self): """Test with 3 switches.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'signal_repetitions': 3, 'devices': @@ -203,7 +203,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_discover_switch(self): """Test with discovery of switches.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': {}}})) @@ -253,7 +253,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def test_discover_switch_noautoadd(self): """Test with discovery of switch when auto add is False.""" - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': False, 'devices': {}}})) diff --git a/tests/components/test_conversation.py b/tests/components/test_conversation.py index 26ace315a37..6235fafc495 100644 --- a/tests/components/test_conversation.py +++ b/tests/components/test_conversation.py @@ -7,8 +7,9 @@ from homeassistant.bootstrap import setup_component import homeassistant.components as core_components from homeassistant.components import conversation from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.util.async import run_coroutine_threadsafe -from tests.common import get_test_home_assistant +from tests.common import get_test_home_assistant, assert_setup_component class TestConversation(unittest.TestCase): @@ -19,9 +20,13 @@ class TestConversation(unittest.TestCase): self.ent_id = 'light.kitchen_lights' self.hass = get_test_home_assistant(3) self.hass.states.set(self.ent_id, 'on') - self.assertTrue(core_components.setup(self.hass, {})) - self.assertTrue(setup_component(self.hass, conversation.DOMAIN, { - conversation.DOMAIN: {}})) + self.assertTrue(run_coroutine_threadsafe( + core_components.async_setup(self.hass, {}), self.hass.loop + ).result()) + with assert_setup_component(0): + self.assertTrue(setup_component(self.hass, conversation.DOMAIN, { + conversation.DOMAIN: {} + })) def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" diff --git a/tests/components/test_emulated_hue.py b/tests/components/test_emulated_hue.py index fb55551bdf3..e280ba827ea 100755 --- a/tests/components/test_emulated_hue.py +++ b/tests/components/test_emulated_hue.py @@ -1,425 +1,429 @@ -"""The tests for the emulated Hue component.""" -import time -import json -import threading -import asyncio - -import unittest -import requests - -from homeassistant import bootstrap, const, core -import homeassistant.components as core_components -from homeassistant.components import emulated_hue, http, light -from homeassistant.const import STATE_ON, STATE_OFF -from homeassistant.components.emulated_hue import ( - HUE_API_STATE_ON, HUE_API_STATE_BRI) - -from tests.common import get_test_instance_port, get_test_home_assistant - -HTTP_SERVER_PORT = get_test_instance_port() -BRIDGE_SERVER_PORT = get_test_instance_port() - -BRIDGE_URL_BASE = "http://127.0.0.1:{}".format(BRIDGE_SERVER_PORT) + "{}" -JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON} - - -def setup_hass_instance(emulated_hue_config): - """Setup the Home Assistant instance to test.""" - hass = get_test_home_assistant() - - # We need to do this to get access to homeassistant/turn_(on,off) - core_components.setup(hass, {core.DOMAIN: {}}) - - bootstrap.setup_component( - hass, http.DOMAIN, - {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) - - bootstrap.setup_component(hass, emulated_hue.DOMAIN, emulated_hue_config) - - return hass - - -def start_hass_instance(hass): - """Start the Home Assistant instance to test.""" - hass.start() - time.sleep(0.05) - - -class TestEmulatedHue(unittest.TestCase): - """Test the emulated Hue component.""" - - hass = None - - @classmethod - def setUpClass(cls): - """Setup the class.""" - cls.hass = setup_hass_instance({ - emulated_hue.DOMAIN: { - emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT - }}) - - start_hass_instance(cls.hass) - - @classmethod - def tearDownClass(cls): - """Stop the class.""" - cls.hass.stop() - - def test_description_xml(self): - """Test the description.""" - import xml.etree.ElementTree as ET - - result = requests.get( - BRIDGE_URL_BASE.format('/description.xml'), timeout=5) - - self.assertEqual(result.status_code, 200) - self.assertTrue('text/xml' in result.headers['content-type']) - - # Make sure the XML is parsable - try: - ET.fromstring(result.text) - except: - self.fail('description.xml is not valid XML!') - - def test_create_username(self): - """Test the creation of an username.""" - request_json = {'devicetype': 'my_device'} - - result = requests.post( - BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), - timeout=5) - - self.assertEqual(result.status_code, 200) - self.assertTrue('application/json' in result.headers['content-type']) - - resp_json = result.json() - success_json = resp_json[0] - - self.assertTrue('success' in success_json) - self.assertTrue('username' in success_json['success']) - - def test_valid_username_request(self): - """Test request with a valid username.""" - request_json = {'invalid_key': 'my_device'} - - result = requests.post( - BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), - timeout=5) - - self.assertEqual(result.status_code, 400) - - -class TestEmulatedHueExposedByDefault(unittest.TestCase): - """Test class for emulated hue component.""" - - @classmethod - def setUpClass(cls): - """Setup the class.""" - cls.hass = setup_hass_instance({ - emulated_hue.DOMAIN: { - emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, - emulated_hue.CONF_EXPOSE_BY_DEFAULT: True - } - }) - - bootstrap.setup_component(cls.hass, light.DOMAIN, { - 'light': [ - { - 'platform': 'demo', - } - ] - }) - - start_hass_instance(cls.hass) - - # Kitchen light is explicitly excluded from being exposed - kitchen_light_entity = cls.hass.states.get('light.kitchen_lights') - attrs = dict(kitchen_light_entity.attributes) - attrs[emulated_hue.ATTR_EMULATED_HUE] = False - cls.hass.states.set( - kitchen_light_entity.entity_id, kitchen_light_entity.state, - attributes=attrs) - - @classmethod - def tearDownClass(cls): - """Stop the class.""" - cls.hass.stop() - - def test_discover_lights(self): - """Test the discovery of lights.""" - result = requests.get( - BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) - - self.assertEqual(result.status_code, 200) - self.assertTrue('application/json' in result.headers['content-type']) - - result_json = result.json() - - # Make sure the lights we added to the config are there - self.assertTrue('light.ceiling_lights' in result_json) - self.assertTrue('light.bed_light' in result_json) - self.assertTrue('light.kitchen_lights' not in result_json) - - def test_get_light_state(self): - """Test the getting of light state.""" - # Turn office light on and set to 127 brightness - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_ON, - { - const.ATTR_ENTITY_ID: 'light.ceiling_lights', - light.ATTR_BRIGHTNESS: 127 - }, - blocking=True) - - office_json = self.perform_get_light_state('light.ceiling_lights', 200) - - self.assertEqual(office_json['state'][HUE_API_STATE_ON], True) - self.assertEqual(office_json['state'][HUE_API_STATE_BRI], 127) - - # Turn bedroom light off - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - { - const.ATTR_ENTITY_ID: 'light.bed_light' - }, - blocking=True) - - bedroom_json = self.perform_get_light_state('light.bed_light', 200) - - self.assertEqual(bedroom_json['state'][HUE_API_STATE_ON], False) - self.assertEqual(bedroom_json['state'][HUE_API_STATE_BRI], 0) - - # Make sure kitchen light isn't accessible - kitchen_url = '/api/username/lights/{}'.format('light.kitchen_lights') - kitchen_result = requests.get( - BRIDGE_URL_BASE.format(kitchen_url), timeout=5) - - self.assertEqual(kitchen_result.status_code, 404) - - def test_put_light_state(self): - """Test the seeting of light states.""" - self.perform_put_test_on_ceiling_lights() - - # Turn the bedroom light on first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_ON, - {const.ATTR_ENTITY_ID: 'light.bed_light', - light.ATTR_BRIGHTNESS: 153}, - blocking=True) - - bed_light = self.hass.states.get('light.bed_light') - self.assertEqual(bed_light.state, STATE_ON) - self.assertEqual(bed_light.attributes[light.ATTR_BRIGHTNESS], 153) - - # Go through the API to turn it off - bedroom_result = self.perform_put_light_state( - 'light.bed_light', False) - - bedroom_result_json = bedroom_result.json() - - self.assertEqual(bedroom_result.status_code, 200) - self.assertTrue( - 'application/json' in bedroom_result.headers['content-type']) - - self.assertEqual(len(bedroom_result_json), 1) - - # Check to make sure the state changed - bed_light = self.hass.states.get('light.bed_light') - self.assertEqual(bed_light.state, STATE_OFF) - - # Make sure we can't change the kitchen light state - kitchen_result = self.perform_put_light_state( - 'light.kitchen_light', True) - self.assertEqual(kitchen_result.status_code, 404) - - def test_put_with_form_urlencoded_content_type(self): - """Test the form with urlencoded content.""" - # Needed for Alexa - self.perform_put_test_on_ceiling_lights( - 'application/x-www-form-urlencoded') - - # Make sure we fail gracefully when we can't parse the data - data = {'key1': 'value1', 'key2': 'value2'} - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - "light.ceiling_lights")), data=data) - - self.assertEqual(result.status_code, 400) - - def test_entity_not_found(self): - """Test for entity which are not found.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format("not.existant_entity")), - timeout=5) - - self.assertEqual(result.status_code, 404) - - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format("non.existant_entity")), - timeout=5) - - self.assertEqual(result.status_code, 404) - - def test_allowed_methods(self): - """Test the allowed methods.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - "light.ceiling_lights"))) - - self.assertEqual(result.status_code, 405) - - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format("light.ceiling_lights")), - data={'key1': 'value1'}) - - self.assertEqual(result.status_code, 405) - - result = requests.put( - BRIDGE_URL_BASE.format('/api/username/lights'), - data={'key1': 'value1'}) - - self.assertEqual(result.status_code, 405) - - def test_proper_put_state_request(self): - """Test the request to set the state.""" - # Test proper on value parsing - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - "light.ceiling_lights")), - data=json.dumps({HUE_API_STATE_ON: 1234})) - - self.assertEqual(result.status_code, 400) - - # Test proper brightness value parsing - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - "light.ceiling_lights")), data=json.dumps({ - HUE_API_STATE_ON: True, - HUE_API_STATE_BRI: 'Hello world!' - })) - - self.assertEqual(result.status_code, 400) - - def perform_put_test_on_ceiling_lights(self, - content_type='application/json'): - """Test the setting of a light.""" - # Turn the office light off first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, - blocking=True) - - ceiling_lights = self.hass.states.get('light.ceiling_lights') - self.assertEqual(ceiling_lights.state, STATE_OFF) - - # Go through the API to turn it on - office_result = self.perform_put_light_state( - 'light.ceiling_lights', True, 56, content_type) - - office_result_json = office_result.json() - - self.assertEqual(office_result.status_code, 200) - self.assertTrue( - 'application/json' in office_result.headers['content-type']) - - self.assertEqual(len(office_result_json), 2) - - # Check to make sure the state changed - ceiling_lights = self.hass.states.get('light.ceiling_lights') - self.assertEqual(ceiling_lights.state, STATE_ON) - self.assertEqual(ceiling_lights.attributes[light.ATTR_BRIGHTNESS], 56) - - def perform_get_light_state(self, entity_id, expected_status): - """Test the gettting of a light state.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format(entity_id)), timeout=5) - - self.assertEqual(result.status_code, expected_status) - - if expected_status == 200: - self.assertTrue( - 'application/json' in result.headers['content-type']) - - return result.json() - - return None - - def perform_put_light_state(self, entity_id, is_on, brightness=None, - content_type='application/json'): - """Test the setting of a light state.""" - url = BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format(entity_id)) - - req_headers = {'Content-Type': content_type} - - data = {HUE_API_STATE_ON: is_on} - - if brightness is not None: - data[HUE_API_STATE_BRI] = brightness - - result = requests.put( - url, data=json.dumps(data), timeout=5, headers=req_headers) - return result - - -class MQTTBroker(object): - """Encapsulates an embedded MQTT broker.""" - - def __init__(self, host, port): - """Initialize a new instance.""" - from hbmqtt.broker import Broker - - self._loop = asyncio.new_event_loop() - - hbmqtt_config = { - 'listeners': { - 'default': { - 'max-connections': 50000, - 'type': 'tcp', - 'bind': '{}:{}'.format(host, port) - } - }, - 'auth': { - 'plugins': ['auth.anonymous'], - 'allow-anonymous': True - } - } - - self._broker = Broker(config=hbmqtt_config, loop=self._loop) - - self._thread = threading.Thread(target=self._run_loop) - self._started_ev = threading.Event() - - def start(self): - """Start the broker.""" - self._thread.start() - self._started_ev.wait() - - def stop(self): - """Stop the broker.""" - self._loop.call_soon_threadsafe(asyncio.async, self._broker.shutdown()) - self._loop.call_soon_threadsafe(self._loop.stop) - self._thread.join() - - def _run_loop(self): - """Run the loop.""" - asyncio.set_event_loop(self._loop) - self._loop.run_until_complete(self._broker_coroutine()) - - self._started_ev.set() - - self._loop.run_forever() - self._loop.close() - - @asyncio.coroutine - def _broker_coroutine(self): - """The Broker coroutine.""" - yield from self._broker.start() +"""The tests for the emulated Hue component.""" +import time +import json +import threading +import asyncio + +import unittest +import requests + +from homeassistant import bootstrap, const, core +import homeassistant.components as core_components +from homeassistant.components import emulated_hue, http, light +from homeassistant.const import STATE_ON, STATE_OFF +from homeassistant.components.emulated_hue import ( + HUE_API_STATE_ON, HUE_API_STATE_BRI) +from homeassistant.util.async import run_coroutine_threadsafe + +from tests.common import get_test_instance_port, get_test_home_assistant + +HTTP_SERVER_PORT = get_test_instance_port() +BRIDGE_SERVER_PORT = get_test_instance_port() + +BRIDGE_URL_BASE = "http://127.0.0.1:{}".format(BRIDGE_SERVER_PORT) + "{}" +JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON} + + +def setup_hass_instance(emulated_hue_config): + """Setup the Home Assistant instance to test.""" + hass = get_test_home_assistant() + + # We need to do this to get access to homeassistant/turn_(on,off) + run_coroutine_threadsafe( + core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop + ).result() + + bootstrap.setup_component( + hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) + + bootstrap.setup_component(hass, emulated_hue.DOMAIN, emulated_hue_config) + + return hass + + +def start_hass_instance(hass): + """Start the Home Assistant instance to test.""" + hass.start() + time.sleep(0.05) + + +class TestEmulatedHue(unittest.TestCase): + """Test the emulated Hue component.""" + + hass = None + + @classmethod + def setUpClass(cls): + """Setup the class.""" + cls.hass = setup_hass_instance({ + emulated_hue.DOMAIN: { + emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT + }}) + + start_hass_instance(cls.hass) + + @classmethod + def tearDownClass(cls): + """Stop the class.""" + cls.hass.stop() + + def test_description_xml(self): + """Test the description.""" + import xml.etree.ElementTree as ET + + result = requests.get( + BRIDGE_URL_BASE.format('/description.xml'), timeout=5) + + self.assertEqual(result.status_code, 200) + self.assertTrue('text/xml' in result.headers['content-type']) + + # Make sure the XML is parsable + try: + ET.fromstring(result.text) + except: + self.fail('description.xml is not valid XML!') + + def test_create_username(self): + """Test the creation of an username.""" + request_json = {'devicetype': 'my_device'} + + result = requests.post( + BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), + timeout=5) + + self.assertEqual(result.status_code, 200) + self.assertTrue('application/json' in result.headers['content-type']) + + resp_json = result.json() + success_json = resp_json[0] + + self.assertTrue('success' in success_json) + self.assertTrue('username' in success_json['success']) + + def test_valid_username_request(self): + """Test request with a valid username.""" + request_json = {'invalid_key': 'my_device'} + + result = requests.post( + BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), + timeout=5) + + self.assertEqual(result.status_code, 400) + + +class TestEmulatedHueExposedByDefault(unittest.TestCase): + """Test class for emulated hue component.""" + + @classmethod + def setUpClass(cls): + """Setup the class.""" + cls.hass = setup_hass_instance({ + emulated_hue.DOMAIN: { + emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, + emulated_hue.CONF_EXPOSE_BY_DEFAULT: True + } + }) + + bootstrap.setup_component(cls.hass, light.DOMAIN, { + 'light': [ + { + 'platform': 'demo', + } + ] + }) + + start_hass_instance(cls.hass) + + # Kitchen light is explicitly excluded from being exposed + kitchen_light_entity = cls.hass.states.get('light.kitchen_lights') + attrs = dict(kitchen_light_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE] = False + cls.hass.states.set( + kitchen_light_entity.entity_id, kitchen_light_entity.state, + attributes=attrs) + + @classmethod + def tearDownClass(cls): + """Stop the class.""" + cls.hass.stop() + + def test_discover_lights(self): + """Test the discovery of lights.""" + result = requests.get( + BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) + + self.assertEqual(result.status_code, 200) + self.assertTrue('application/json' in result.headers['content-type']) + + result_json = result.json() + + # Make sure the lights we added to the config are there + self.assertTrue('light.ceiling_lights' in result_json) + self.assertTrue('light.bed_light' in result_json) + self.assertTrue('light.kitchen_lights' not in result_json) + + def test_get_light_state(self): + """Test the getting of light state.""" + # Turn office light on and set to 127 brightness + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_ON, + { + const.ATTR_ENTITY_ID: 'light.ceiling_lights', + light.ATTR_BRIGHTNESS: 127 + }, + blocking=True) + + office_json = self.perform_get_light_state('light.ceiling_lights', 200) + + self.assertEqual(office_json['state'][HUE_API_STATE_ON], True) + self.assertEqual(office_json['state'][HUE_API_STATE_BRI], 127) + + # Turn bedroom light off + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_OFF, + { + const.ATTR_ENTITY_ID: 'light.bed_light' + }, + blocking=True) + + bedroom_json = self.perform_get_light_state('light.bed_light', 200) + + self.assertEqual(bedroom_json['state'][HUE_API_STATE_ON], False) + self.assertEqual(bedroom_json['state'][HUE_API_STATE_BRI], 0) + + # Make sure kitchen light isn't accessible + kitchen_url = '/api/username/lights/{}'.format('light.kitchen_lights') + kitchen_result = requests.get( + BRIDGE_URL_BASE.format(kitchen_url), timeout=5) + + self.assertEqual(kitchen_result.status_code, 404) + + def test_put_light_state(self): + """Test the seeting of light states.""" + self.perform_put_test_on_ceiling_lights() + + # Turn the bedroom light on first + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_ON, + {const.ATTR_ENTITY_ID: 'light.bed_light', + light.ATTR_BRIGHTNESS: 153}, + blocking=True) + + bed_light = self.hass.states.get('light.bed_light') + self.assertEqual(bed_light.state, STATE_ON) + self.assertEqual(bed_light.attributes[light.ATTR_BRIGHTNESS], 153) + + # Go through the API to turn it off + bedroom_result = self.perform_put_light_state( + 'light.bed_light', False) + + bedroom_result_json = bedroom_result.json() + + self.assertEqual(bedroom_result.status_code, 200) + self.assertTrue( + 'application/json' in bedroom_result.headers['content-type']) + + self.assertEqual(len(bedroom_result_json), 1) + + # Check to make sure the state changed + bed_light = self.hass.states.get('light.bed_light') + self.assertEqual(bed_light.state, STATE_OFF) + + # Make sure we can't change the kitchen light state + kitchen_result = self.perform_put_light_state( + 'light.kitchen_light', True) + self.assertEqual(kitchen_result.status_code, 404) + + def test_put_with_form_urlencoded_content_type(self): + """Test the form with urlencoded content.""" + # Needed for Alexa + self.perform_put_test_on_ceiling_lights( + 'application/x-www-form-urlencoded') + + # Make sure we fail gracefully when we can't parse the data + data = {'key1': 'value1', 'key2': 'value2'} + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + "light.ceiling_lights")), data=data) + + self.assertEqual(result.status_code, 400) + + def test_entity_not_found(self): + """Test for entity which are not found.""" + result = requests.get( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}'.format("not.existant_entity")), + timeout=5) + + self.assertEqual(result.status_code, 404) + + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format("non.existant_entity")), + timeout=5) + + self.assertEqual(result.status_code, 404) + + def test_allowed_methods(self): + """Test the allowed methods.""" + result = requests.get( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + "light.ceiling_lights"))) + + self.assertEqual(result.status_code, 405) + + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}'.format("light.ceiling_lights")), + data={'key1': 'value1'}) + + self.assertEqual(result.status_code, 405) + + result = requests.put( + BRIDGE_URL_BASE.format('/api/username/lights'), + data={'key1': 'value1'}) + + self.assertEqual(result.status_code, 405) + + def test_proper_put_state_request(self): + """Test the request to set the state.""" + # Test proper on value parsing + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + "light.ceiling_lights")), + data=json.dumps({HUE_API_STATE_ON: 1234})) + + self.assertEqual(result.status_code, 400) + + # Test proper brightness value parsing + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + "light.ceiling_lights")), data=json.dumps({ + HUE_API_STATE_ON: True, + HUE_API_STATE_BRI: 'Hello world!' + })) + + self.assertEqual(result.status_code, 400) + + def perform_put_test_on_ceiling_lights(self, + content_type='application/json'): + """Test the setting of a light.""" + # Turn the office light off first + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, + blocking=True) + + ceiling_lights = self.hass.states.get('light.ceiling_lights') + self.assertEqual(ceiling_lights.state, STATE_OFF) + + # Go through the API to turn it on + office_result = self.perform_put_light_state( + 'light.ceiling_lights', True, 56, content_type) + + office_result_json = office_result.json() + + self.assertEqual(office_result.status_code, 200) + self.assertTrue( + 'application/json' in office_result.headers['content-type']) + + self.assertEqual(len(office_result_json), 2) + + # Check to make sure the state changed + ceiling_lights = self.hass.states.get('light.ceiling_lights') + self.assertEqual(ceiling_lights.state, STATE_ON) + self.assertEqual(ceiling_lights.attributes[light.ATTR_BRIGHTNESS], 56) + + def perform_get_light_state(self, entity_id, expected_status): + """Test the gettting of a light state.""" + result = requests.get( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}'.format(entity_id)), timeout=5) + + self.assertEqual(result.status_code, expected_status) + + if expected_status == 200: + self.assertTrue( + 'application/json' in result.headers['content-type']) + + return result.json() + + return None + + def perform_put_light_state(self, entity_id, is_on, brightness=None, + content_type='application/json'): + """Test the setting of a light state.""" + url = BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format(entity_id)) + + req_headers = {'Content-Type': content_type} + + data = {HUE_API_STATE_ON: is_on} + + if brightness is not None: + data[HUE_API_STATE_BRI] = brightness + + result = requests.put( + url, data=json.dumps(data), timeout=5, headers=req_headers) + + return result + + +class MQTTBroker(object): + """Encapsulates an embedded MQTT broker.""" + + def __init__(self, host, port): + """Initialize a new instance.""" + from hbmqtt.broker import Broker + + self._loop = asyncio.new_event_loop() + + hbmqtt_config = { + 'listeners': { + 'default': { + 'max-connections': 50000, + 'type': 'tcp', + 'bind': '{}:{}'.format(host, port) + } + }, + 'auth': { + 'plugins': ['auth.anonymous'], + 'allow-anonymous': True + } + } + + self._broker = Broker(config=hbmqtt_config, loop=self._loop) + + self._thread = threading.Thread(target=self._run_loop) + self._started_ev = threading.Event() + + def start(self): + """Start the broker.""" + self._thread.start() + self._started_ev.wait() + + def stop(self): + """Stop the broker.""" + self._loop.call_soon_threadsafe(asyncio.async, self._broker.shutdown()) + self._loop.call_soon_threadsafe(self._loop.stop) + self._thread.join() + + def _run_loop(self): + """Run the loop.""" + asyncio.set_event_loop(self._loop) + self._loop.run_until_complete(self._broker_coroutine()) + + self._started_ev.set() + + self._loop.run_forever() + self._loop.close() + + @asyncio.coroutine + def _broker_coroutine(self): + """The Broker coroutine.""" + yield from self._broker.start() diff --git a/tests/components/test_influxdb.py b/tests/components/test_influxdb.py index 060fdf01dca..b0517ec2f53 100644 --- a/tests/components/test_influxdb.py +++ b/tests/components/test_influxdb.py @@ -1,7 +1,6 @@ """The tests for the InfluxDB component.""" import unittest from unittest import mock -from unittest.mock import patch import influxdb as influx_client @@ -9,6 +8,8 @@ from homeassistant.bootstrap import setup_component import homeassistant.components.influxdb as influxdb from homeassistant.const import EVENT_STATE_CHANGED, STATE_OFF, STATE_ON +from tests.common import get_test_home_assistant + @mock.patch('influxdb.InfluxDBClient') class TestInfluxDB(unittest.TestCase): @@ -16,9 +17,13 @@ class TestInfluxDB(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = mock.MagicMock() - self.hass.pool.worker_count = 2 + self.hass = get_test_home_assistant(2) self.handler_method = None + self.hass.bus.listen = mock.Mock() + + def tearDown(self): + """Clear data.""" + self.hass.stop() def test_setup_config_full(self, mock_client): """Test the setup with full configuration.""" @@ -61,8 +66,6 @@ class TestInfluxDB(unittest.TestCase): assert setup_component(self.hass, influxdb.DOMAIN, config) - @patch('homeassistant.components.persistent_notification.create', - mock.MagicMock()) def test_setup_missing_password(self, mock_client): """Test the setup with existing username and missing password.""" config = { diff --git a/tests/components/test_init.py b/tests/components/test_init.py index 44a60ee986f..0bc105e3ad1 100644 --- a/tests/components/test_init.py +++ b/tests/components/test_init.py @@ -1,5 +1,6 @@ """The testd for Core components.""" # pylint: disable=protected-access,too-many-public-methods +import asyncio import unittest from unittest.mock import patch, Mock @@ -11,6 +12,7 @@ from homeassistant.const import ( STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE) import homeassistant.components as comps from homeassistant.helpers import entity +from homeassistant.util.async import run_coroutine_threadsafe from tests.common import ( get_test_home_assistant, mock_service, patch_yaml_files) @@ -22,7 +24,9 @@ class TestComponentsCore(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() - self.assertTrue(comps.setup(self.hass, {})) + self.assertTrue(run_coroutine_threadsafe( + comps.async_setup(self.hass, {}), self.hass.loop + ).result()) self.hass.states.set('light.Bowl', STATE_ON) self.hass.states.set('light.Ceiling', STATE_OFF) @@ -66,6 +70,7 @@ class TestComponentsCore(unittest.TestCase): self.hass.block_till_done() self.assertEqual(1, len(calls)) + @asyncio.coroutine @patch('homeassistant.core.ServiceRegistry.call') def test_turn_on_to_not_block_for_domains_without_service(self, mock_call): """Test if turn_on is blocking domain with no service.""" @@ -78,7 +83,7 @@ class TestComponentsCore(unittest.TestCase): 'entity_id': ['light.test', 'sensor.bla', 'light.bla'] }) service = self.hass.services._services['homeassistant']['turn_on'] - service.func(service_call) + yield from service.func(service_call) self.assertEqual(2, mock_call.call_count) self.assertEqual( @@ -131,7 +136,7 @@ class TestComponentsCore(unittest.TestCase): @patch('homeassistant.config.os.path.isfile', Mock(return_value=True)) @patch('homeassistant.components._LOGGER.error') - @patch('homeassistant.config.process_ha_core_config') + @patch('homeassistant.config.async_process_ha_core_config') def test_reload_core_with_wrong_conf(self, mock_process, mock_error): """Test reload core conf service.""" files = { diff --git a/tests/components/test_logentries.py b/tests/components/test_logentries.py index 4bcef23ee7e..b7e40f7ebb6 100644 --- a/tests/components/test_logentries.py +++ b/tests/components/test_logentries.py @@ -7,10 +7,20 @@ from homeassistant.bootstrap import setup_component import homeassistant.components.logentries as logentries from homeassistant.const import STATE_ON, STATE_OFF, EVENT_STATE_CHANGED +from tests.common import get_test_home_assistant + class TestLogentries(unittest.TestCase): """Test the Logentries component.""" + def setUp(self): # pylint: disable=invalid-name + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant(2) + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + def test_setup_config_full(self): """Test setup with all data.""" config = { @@ -18,12 +28,11 @@ class TestLogentries(unittest.TestCase): 'token': 'secret', } } - hass = mock.MagicMock() - hass.pool.worker_count = 2 - self.assertTrue(setup_component(hass, logentries.DOMAIN, config)) - self.assertTrue(hass.bus.listen.called) + self.hass.bus.listen = mock.MagicMock() + self.assertTrue(setup_component(self.hass, logentries.DOMAIN, config)) + self.assertTrue(self.hass.bus.listen.called) self.assertEqual(EVENT_STATE_CHANGED, - hass.bus.listen.call_args_list[0][0][0]) + self.hass.bus.listen.call_args_list[0][0][0]) def test_setup_config_defaults(self): """Test setup with defaults.""" @@ -32,12 +41,11 @@ class TestLogentries(unittest.TestCase): 'token': 'token', } } - hass = mock.MagicMock() - hass.pool.worker_count = 2 - self.assertTrue(setup_component(hass, logentries.DOMAIN, config)) - self.assertTrue(hass.bus.listen.called) + self.hass.bus.listen = mock.MagicMock() + self.assertTrue(setup_component(self.hass, logentries.DOMAIN, config)) + self.assertTrue(self.hass.bus.listen.called) self.assertEqual(EVENT_STATE_CHANGED, - hass.bus.listen.call_args_list[0][0][0]) + self.hass.bus.listen.call_args_list[0][0][0]) def _setup(self, mock_requests): """Test the setup.""" @@ -49,8 +57,7 @@ class TestLogentries(unittest.TestCase): 'token': 'token' } } - self.hass = mock.MagicMock() - self.hass.pool.worker_count = 2 + self.hass.bus.listen = mock.MagicMock() setup_component(self.hass, logentries.DOMAIN, config) self.handler_method = self.hass.bus.listen.call_args_list[0][0][1] diff --git a/tests/components/test_logger.py b/tests/components/test_logger.py index e0243335dfa..6b290eec638 100644 --- a/tests/components/test_logger.py +++ b/tests/components/test_logger.py @@ -2,11 +2,12 @@ from collections import namedtuple import logging import unittest -from unittest.mock import MagicMock from homeassistant.bootstrap import setup_component from homeassistant.components import logger +from tests.common import get_test_home_assistant + RECORD = namedtuple('record', ('name', 'levelno')) @@ -15,18 +16,18 @@ class TestUpdater(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant(2) self.log_config = {'logger': {'default': 'warning', 'logs': {'test': 'info'}}} def tearDown(self): """Stop everything that was started.""" del logging.root.handlers[-1] + self.hass.stop() def test_logger_setup(self): """Use logger to create a logging filter.""" - hass = MagicMock() - hass.pool.worker_count = 2 - setup_component(hass, logger.DOMAIN, self.log_config) + setup_component(self.hass, logger.DOMAIN, self.log_config) self.assertTrue(len(logging.root.handlers) > 0) handler = logging.root.handlers[-1] @@ -39,9 +40,7 @@ class TestUpdater(unittest.TestCase): def test_logger_test_filters(self): """Test resulting filter operation.""" - hass = MagicMock() - hass.pool.worker_count = 2 - setup_component(hass, logger.DOMAIN, self.log_config) + setup_component(self.hass, logger.DOMAIN, self.log_config) log_filter = logging.root.handlers[-1].filters[0] diff --git a/tests/components/test_panel_custom.py b/tests/components/test_panel_custom.py index 1ef12161bcb..b07c62e441f 100644 --- a/tests/components/test_panel_custom.py +++ b/tests/components/test_panel_custom.py @@ -10,7 +10,8 @@ from homeassistant.components import panel_custom from tests.common import get_test_home_assistant -@patch('homeassistant.components.frontend.setup', return_value=True) +@patch('homeassistant.components.frontend.setup', + autospec=True, return_value=True) class TestPanelCustom(unittest.TestCase): """Test the panel_custom component.""" diff --git a/tests/components/test_rfxtrx.py b/tests/components/test_rfxtrx.py index b26483c8771..9ec3211f959 100644 --- a/tests/components/test_rfxtrx.py +++ b/tests/components/test_rfxtrx.py @@ -4,7 +4,7 @@ import unittest import pytest -from homeassistant.bootstrap import _setup_component +from homeassistant.bootstrap import setup_component from homeassistant.components import rfxtrx as rfxtrx from tests.common import get_test_home_assistant @@ -27,14 +27,14 @@ class TestRFXTRX(unittest.TestCase): def test_default_config(self): """Test configuration.""" - self.assertTrue(_setup_component(self.hass, 'rfxtrx', { + self.assertTrue(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': { 'device': '/dev/serial/by-id/usb' + '-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0', 'dummy': True} })) - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': {}}})) @@ -43,7 +43,7 @@ class TestRFXTRX(unittest.TestCase): def test_valid_config(self): """Test configuration.""" - self.assertTrue(_setup_component(self.hass, 'rfxtrx', { + self.assertTrue(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': { 'device': '/dev/serial/by-id/usb' + '-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0', @@ -51,7 +51,7 @@ class TestRFXTRX(unittest.TestCase): self.hass.config.components.remove('rfxtrx') - self.assertTrue(_setup_component(self.hass, 'rfxtrx', { + self.assertTrue(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': { 'device': '/dev/serial/by-id/usb' + '-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0', @@ -60,11 +60,11 @@ class TestRFXTRX(unittest.TestCase): def test_invalid_config(self): """Test configuration.""" - self.assertFalse(_setup_component(self.hass, 'rfxtrx', { + self.assertFalse(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': {} })) - self.assertFalse(_setup_component(self.hass, 'rfxtrx', { + self.assertFalse(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': { 'device': '/dev/serial/by-id/usb' + '-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0', @@ -72,13 +72,13 @@ class TestRFXTRX(unittest.TestCase): def test_fire_event(self): """Test fire event.""" - self.assertTrue(_setup_component(self.hass, 'rfxtrx', { + self.assertTrue(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': { 'device': '/dev/serial/by-id/usb' + '-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0', 'dummy': True} })) - self.assertTrue(_setup_component(self.hass, 'switch', { + self.assertTrue(setup_component(self.hass, 'switch', { 'switch': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': @@ -116,13 +116,13 @@ class TestRFXTRX(unittest.TestCase): def test_fire_event_sensor(self): """Test fire event.""" - self.assertTrue(_setup_component(self.hass, 'rfxtrx', { + self.assertTrue(setup_component(self.hass, 'rfxtrx', { 'rfxtrx': { 'device': '/dev/serial/by-id/usb' + '-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0', 'dummy': True} })) - self.assertTrue(_setup_component(self.hass, 'sensor', { + self.assertTrue(setup_component(self.hass, 'sensor', { 'sensor': {'platform': 'rfxtrx', 'automatic_add': True, 'devices': diff --git a/tests/components/test_sleepiq.py b/tests/components/test_sleepiq.py index 675673e1653..5bdfba4163d 100644 --- a/tests/components/test_sleepiq.py +++ b/tests/components/test_sleepiq.py @@ -65,11 +65,11 @@ class TestSleepIQ(unittest.TestCase): """Test the setup when no login is configured.""" conf = self.config.copy() del conf['sleepiq']['username'] - assert not bootstrap._setup_component(self.hass, sleepiq.DOMAIN, conf) + assert not bootstrap.setup_component(self.hass, sleepiq.DOMAIN, conf) def test_setup_component_no_password(self): """Test the setup when no password is configured.""" conf = self.config.copy() del conf['sleepiq']['password'] - assert not bootstrap._setup_component(self.hass, sleepiq.DOMAIN, conf) + assert not bootstrap.setup_component(self.hass, sleepiq.DOMAIN, conf) diff --git a/tests/components/test_splunk.py b/tests/components/test_splunk.py index d893a699602..1f6648ce582 100644 --- a/tests/components/test_splunk.py +++ b/tests/components/test_splunk.py @@ -6,10 +6,20 @@ from homeassistant.bootstrap import setup_component import homeassistant.components.splunk as splunk from homeassistant.const import STATE_ON, STATE_OFF, EVENT_STATE_CHANGED +from tests.common import get_test_home_assistant + class TestSplunk(unittest.TestCase): """Test the Splunk component.""" + def setUp(self): # pylint: disable=invalid-name + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant(2) + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + def test_setup_config_full(self): """Test setup with all data.""" config = { @@ -21,12 +31,11 @@ class TestSplunk(unittest.TestCase): } } - hass = mock.MagicMock() - hass.pool.worker_count = 2 - self.assertTrue(setup_component(hass, splunk.DOMAIN, config)) - self.assertTrue(hass.bus.listen.called) + self.hass.bus.listen = mock.MagicMock() + self.assertTrue(setup_component(self.hass, splunk.DOMAIN, config)) + self.assertTrue(self.hass.bus.listen.called) self.assertEqual(EVENT_STATE_CHANGED, - hass.bus.listen.call_args_list[0][0][0]) + self.hass.bus.listen.call_args_list[0][0][0]) def test_setup_config_defaults(self): """Test setup with defaults.""" @@ -37,12 +46,11 @@ class TestSplunk(unittest.TestCase): } } - hass = mock.MagicMock() - hass.pool.worker_count = 2 - self.assertTrue(setup_component(hass, splunk.DOMAIN, config)) - self.assertTrue(hass.bus.listen.called) + self.hass.bus.listen = mock.MagicMock() + self.assertTrue(setup_component(self.hass, splunk.DOMAIN, config)) + self.assertTrue(self.hass.bus.listen.called) self.assertEqual(EVENT_STATE_CHANGED, - hass.bus.listen.call_args_list[0][0][0]) + self.hass.bus.listen.call_args_list[0][0][0]) def _setup(self, mock_requests): """Test the setup.""" @@ -57,8 +65,7 @@ class TestSplunk(unittest.TestCase): } } - self.hass = mock.MagicMock() - self.hass.pool.worker_count = 2 + self.hass.bus.listen = mock.MagicMock() setup_component(self.hass, splunk.DOMAIN, config) self.handler_method = self.hass.bus.listen.call_args_list[0][0][1] diff --git a/tests/components/test_statsd.py b/tests/components/test_statsd.py index ccc494fbc24..eb8782b582c 100644 --- a/tests/components/test_statsd.py +++ b/tests/components/test_statsd.py @@ -9,10 +9,20 @@ import homeassistant.core as ha import homeassistant.components.statsd as statsd from homeassistant.const import (STATE_ON, STATE_OFF, EVENT_STATE_CHANGED) +from tests.common import get_test_home_assistant + class TestStatsd(unittest.TestCase): """Test the StatsD component.""" + def setUp(self): # pylint: disable=invalid-name + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant(2) + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + def test_invalid_config(self): """Test configuration with defaults.""" config = { @@ -37,18 +47,17 @@ class TestStatsd(unittest.TestCase): 'prefix': 'foo', } } - hass = mock.MagicMock() - hass.pool.worker_count = 2 - self.assertTrue(setup_component(hass, statsd.DOMAIN, config)) + self.hass.bus.listen = mock.MagicMock() + self.assertTrue(setup_component(self.hass, statsd.DOMAIN, config)) self.assertEqual(mock_connection.call_count, 1) self.assertEqual( mock_connection.call_args, mock.call(host='host', port=123, prefix='foo') ) - self.assertTrue(hass.bus.listen.called) + self.assertTrue(self.hass.bus.listen.called) self.assertEqual(EVENT_STATE_CHANGED, - hass.bus.listen.call_args_list[0][0][0]) + self.hass.bus.listen.call_args_list[0][0][0]) @mock.patch('statsd.StatsClient') def test_statsd_setup_defaults(self, mock_connection): @@ -62,15 +71,14 @@ class TestStatsd(unittest.TestCase): config['statsd'][statsd.CONF_PORT] = statsd.DEFAULT_PORT config['statsd'][statsd.CONF_PREFIX] = statsd.DEFAULT_PREFIX - hass = mock.MagicMock() - hass.pool.worker_count = 2 - self.assertTrue(setup_component(hass, statsd.DOMAIN, config)) + self.hass.bus.listen = mock.MagicMock() + self.assertTrue(setup_component(self.hass, statsd.DOMAIN, config)) self.assertEqual(mock_connection.call_count, 1) self.assertEqual( mock_connection.call_args, mock.call(host='host', port=8125, prefix='hass') ) - self.assertTrue(hass.bus.listen.called) + self.assertTrue(self.hass.bus.listen.called) @mock.patch('statsd.StatsClient') def test_event_listener_defaults(self, mock_client): @@ -83,11 +91,10 @@ class TestStatsd(unittest.TestCase): config['statsd'][statsd.CONF_RATE] = statsd.DEFAULT_RATE - hass = mock.MagicMock() - hass.pool.worker_count = 2 - setup_component(hass, statsd.DOMAIN, config) - self.assertTrue(hass.bus.listen.called) - handler_method = hass.bus.listen.call_args_list[0][0][1] + self.hass.bus.listen = mock.MagicMock() + setup_component(self.hass, statsd.DOMAIN, config) + self.assertTrue(self.hass.bus.listen.called) + handler_method = self.hass.bus.listen.call_args_list[0][0][1] valid = {'1': 1, '1.0': 1.0, @@ -128,11 +135,10 @@ class TestStatsd(unittest.TestCase): config['statsd'][statsd.CONF_RATE] = statsd.DEFAULT_RATE - hass = mock.MagicMock() - hass.pool.worker_count = 2 - setup_component(hass, statsd.DOMAIN, config) - self.assertTrue(hass.bus.listen.called) - handler_method = hass.bus.listen.call_args_list[0][0][1] + self.hass.bus.listen = mock.MagicMock() + setup_component(self.hass, statsd.DOMAIN, config) + self.assertTrue(self.hass.bus.listen.called) + handler_method = self.hass.bus.listen.call_args_list[0][0][1] valid = {'1': 1, '1.0': 1.0, diff --git a/tests/helpers/test_discovery.py b/tests/helpers/test_discovery.py index 4664549fb77..a0868691e3f 100644 --- a/tests/helpers/test_discovery.py +++ b/tests/helpers/test_discovery.py @@ -127,4 +127,4 @@ class TestHelpersDiscovery: assert 'test_component' in self.hass.config.components assert 'switch' in self.hass.config.components assert len(component_calls) == 1 - assert len(platform_calls) == 2 + assert len(platform_calls) == 1 diff --git a/tests/helpers/test_state.py b/tests/helpers/test_state.py index 0d7b8c46d86..3ef9bd1b03b 100644 --- a/tests/helpers/test_state.py +++ b/tests/helpers/test_state.py @@ -7,6 +7,7 @@ from unittest.mock import patch import homeassistant.core as ha import homeassistant.components as core_components 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.helpers import state from homeassistant.const import ( @@ -63,7 +64,8 @@ class TestStateHelpers(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Run when tests are started.""" self.hass = get_test_home_assistant() - core_components.setup(self.hass, {}) + run_coroutine_threadsafe(core_components.async_setup( + self.hass, {}), self.hass.loop).result() def tearDown(self): # pylint: disable=invalid-name """Stop when tests are finished.""" diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index efe99f86ebd..f0ef9efb2d1 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -3,7 +3,6 @@ import asyncio import logging import os import unittest -from unittest.mock import patch import homeassistant.scripts.check_config as check_config from tests.common import patch_yaml_files, get_test_config_dir @@ -45,12 +44,22 @@ def tearDownModule(self): # pylint: disable=invalid-name os.remove(path) -@patch('asyncio.get_event_loop', return_value=asyncio.new_event_loop()) class TestCheckConfig(unittest.TestCase): """Tests for the homeassistant.scripts.check_config module.""" + 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, AssertionError): + # Py35: RuntimeError + # Py34: AssertionError + asyncio.set_event_loop(asyncio.new_event_loop()) + # pylint: disable=no-self-use,invalid-name - def test_config_platform_valid(self, mock_get_loop): + def test_config_platform_valid(self): """Test a valid platform setup.""" files = { 'light.yaml': BASE_CONFIG + 'light:\n platform: demo', @@ -66,7 +75,7 @@ class TestCheckConfig(unittest.TestCase): 'yaml_files': ['.../light.yaml'] }, res) - def test_config_component_platform_fail_validation(self, mock_get_loop): + def test_config_component_platform_fail_validation(self): """Test errors if component & platform not found.""" files = { 'component.yaml': BASE_CONFIG + 'http:\n password: err123', @@ -104,7 +113,7 @@ class TestCheckConfig(unittest.TestCase): self.assertDictEqual({}, res['secrets']) self.assertListEqual(['.../platform.yaml'], res['yaml_files']) - def test_component_platform_not_found(self, mock_get_loop): + def test_component_platform_not_found(self): """Test errors if component or platform not found.""" files = { 'badcomponent.yaml': BASE_CONFIG + 'beer:', @@ -131,7 +140,7 @@ class TestCheckConfig(unittest.TestCase): self.assertDictEqual({}, res['secrets']) self.assertListEqual(['.../badplatform.yaml'], res['yaml_files']) - def test_secrets(self, mock_get_loop): + def test_secrets(self): """Test secrets config checking method.""" files = { get_test_config_dir('secret.yaml'): ( diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index c84c95f396c..971a90896b7 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -48,11 +48,10 @@ class TestBootstrap: @mock.patch( # prevent .HA_VERISON file from being written 'homeassistant.bootstrap.conf_util.process_ha_config_upgrade', - mock.Mock() - ) + autospec=True) @mock.patch('homeassistant.util.location.detect_location_info', - return_value=None) - def test_from_config_file(self, mock_detect): + autospec=True, return_value=None) + def test_from_config_file(self, mock_upgrade, mock_detect): """Test with configuration file.""" components = ['browser', 'conversation', 'script'] files = { @@ -94,28 +93,33 @@ class TestBootstrap: loader.set_component( 'comp_conf', MockModule('comp_conf', config_schema=config_schema)) - assert not bootstrap._setup_component(self.hass, 'comp_conf', {}) + with assert_setup_component(0): + assert not bootstrap.setup_component(self.hass, 'comp_conf', {}) - assert not bootstrap._setup_component(self.hass, 'comp_conf', { - 'comp_conf': None - }) + with assert_setup_component(0): + assert not bootstrap.setup_component(self.hass, 'comp_conf', { + 'comp_conf': None + }) - assert not bootstrap._setup_component(self.hass, 'comp_conf', { - 'comp_conf': {} - }) + with assert_setup_component(0): + assert not bootstrap.setup_component(self.hass, 'comp_conf', { + 'comp_conf': {} + }) - assert not bootstrap._setup_component(self.hass, 'comp_conf', { - 'comp_conf': { - 'hello': 'world', - 'invalid': 'extra', - } - }) + with assert_setup_component(0): + assert not bootstrap.setup_component(self.hass, 'comp_conf', { + 'comp_conf': { + 'hello': 'world', + 'invalid': 'extra', + } + }) - assert bootstrap._setup_component(self.hass, 'comp_conf', { - 'comp_conf': { - 'hello': 'world', - } - }) + with assert_setup_component(1): + assert bootstrap.setup_component(self.hass, 'comp_conf', { + 'comp_conf': { + 'hello': 'world', + } + }) def test_validate_platform_config(self): """Test validating platform configuration.""" @@ -130,7 +134,7 @@ class TestBootstrap: 'platform_conf.whatever', MockPlatform('whatever')) with assert_setup_component(0): - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'hello': 'world', 'invalid': 'extra', @@ -140,7 +144,7 @@ class TestBootstrap: self.hass.config.components.remove('platform_conf') with assert_setup_component(1): - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', @@ -153,7 +157,7 @@ class TestBootstrap: self.hass.config.components.remove('platform_conf') with assert_setup_component(0): - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'not_existing', 'hello': 'world', @@ -163,7 +167,7 @@ class TestBootstrap: self.hass.config.components.remove('platform_conf') with assert_setup_component(1): - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', @@ -173,7 +177,7 @@ class TestBootstrap: self.hass.config.components.remove('platform_conf') with assert_setup_component(1): - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': [{ 'platform': 'whatever', 'hello': 'world', @@ -184,13 +188,13 @@ class TestBootstrap: # Any falsey platform config will be ignored (None, {}, etc) with assert_setup_component(0) as config: - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': None }) assert 'platform_conf' in self.hass.config.components assert not config['platform_conf'] # empty - assert bootstrap._setup_component(self.hass, 'platform_conf', { + assert bootstrap.setup_component(self.hass, 'platform_conf', { 'platform_conf': {} }) assert 'platform_conf' in self.hass.config.components @@ -235,10 +239,9 @@ class TestBootstrap: """Setup the component.""" result.append(bootstrap.setup_component(self.hass, 'comp')) - with bootstrap._SETUP_LOCK: - thread = threading.Thread(target=setup_component) - thread.start() - self.hass.config.components.append('comp') + thread = threading.Thread(target=setup_component) + thread.start() + self.hass.config.components.append('comp') thread.join() @@ -250,19 +253,19 @@ class TestBootstrap: deps = ['non_existing'] loader.set_component('comp', MockModule('comp', dependencies=deps)) - assert not bootstrap._setup_component(self.hass, 'comp', {}) + assert not bootstrap.setup_component(self.hass, 'comp', {}) assert 'comp' not in self.hass.config.components - self.hass.config.components.append('non_existing') + loader.set_component('non_existing', MockModule('non_existing')) - assert bootstrap._setup_component(self.hass, 'comp', {}) + assert bootstrap.setup_component(self.hass, 'comp', {}) def test_component_failing_setup(self): """Test component that fails setup.""" loader.set_component( 'comp', MockModule('comp', setup=lambda hass, config: False)) - assert not bootstrap._setup_component(self.hass, 'comp', {}) + assert not bootstrap.setup_component(self.hass, 'comp', {}) assert 'comp' not in self.hass.config.components def test_component_exception_setup(self): @@ -273,18 +276,17 @@ class TestBootstrap: loader.set_component('comp', MockModule('comp', setup=exception_setup)) - assert not bootstrap._setup_component(self.hass, 'comp', {}) + assert not bootstrap.setup_component(self.hass, 'comp', {}) assert 'comp' not in self.hass.config.components def test_home_assistant_core_config_validation(self): """Test if we pass in wrong information for HA conf.""" # Extensive HA conf validation testing is done in test_config.py - hass = get_test_home_assistant() assert None is bootstrap.from_config_dict({ 'homeassistant': { 'latitude': 'some string' } - }, hass=hass) + }) def test_component_setup_with_validation_and_dependency(self): """Test all config is passed to dependencies.""" @@ -316,7 +318,7 @@ class TestBootstrap: 'valid': True, }, extra=vol.PREVENT_EXTRA) - mock_setup = mock.MagicMock() + mock_setup = mock.MagicMock(spec_set=True) loader.set_component( 'switch.platform_a', diff --git a/tests/test_config.py b/tests/test_config.py index 4787da5bcde..7537351fb09 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -14,6 +14,7 @@ from homeassistant.const import ( CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__, CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT) from homeassistant.util import location as location_util, dt as dt_util +from homeassistant.util.async import run_coroutine_threadsafe from homeassistant.helpers.entity import Entity from tests.common import ( @@ -34,6 +35,10 @@ def create_file(path): class TestConfig(unittest.TestCase): """Test the configutils.""" + def setUp(self): # pylint: disable=invalid-name + """Initialize a test Home Assistant instance.""" + self.hass = get_test_home_assistant() + def tearDown(self): # pylint: disable=invalid-name """Clean up.""" dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE @@ -44,8 +49,7 @@ class TestConfig(unittest.TestCase): if os.path.isfile(VERSION_PATH): os.remove(VERSION_PATH) - if hasattr(self, 'hass'): - self.hass.stop() + self.hass.stop() def test_create_default_config(self): """Test creation of default config.""" @@ -165,6 +169,7 @@ class TestConfig(unittest.TestCase): self.assertTrue(mock_print.called) def test_core_config_schema(self): + """Test core config schema.""" for value in ( {CONF_UNIT_SYSTEM: 'K'}, {'time_zone': 'non-exist'}, @@ -191,14 +196,14 @@ class TestConfig(unittest.TestCase): def test_entity_customization(self): """Test entity customization through configuration.""" - self.hass = get_test_home_assistant() - config = {CONF_LATITUDE: 50, CONF_LONGITUDE: 50, CONF_NAME: 'Test', CONF_CUSTOMIZE: {'test.test': {'hidden': True}}} - config_util.process_ha_core_config(self.hass, config) + run_coroutine_threadsafe( + config_util.async_process_ha_core_config(self.hass, config), + self.hass.loop).result() entity = Entity() entity.entity_id = 'test.test' @@ -224,7 +229,6 @@ class TestConfig(unittest.TestCase): opened_file = mock_open.return_value opened_file.readline.return_value = ha_version - self.hass = get_test_home_assistant() self.hass.config.path = mock.Mock() config_util.process_ha_config_upgrade(self.hass) @@ -254,7 +258,6 @@ class TestConfig(unittest.TestCase): opened_file = mock_open.return_value opened_file.readline.return_value = ha_version - self.hass = get_test_home_assistant() self.hass.config.path = mock.Mock() config_util.process_ha_config_upgrade(self.hass) @@ -264,82 +267,91 @@ class TestConfig(unittest.TestCase): def test_loading_configuration(self): """Test loading core config onto hass object.""" - config = Config() - hass = mock.Mock(config=config) + self.hass.config = mock.Mock() - config_util.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', - }) + 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', + }), self.hass.loop).result() - assert config.latitude == 60 - assert config.longitude == 50 - assert config.elevation == 25 - assert config.location_name == 'Huis' - assert config.units.name == CONF_UNIT_SYSTEM_IMPERIAL - assert config.time_zone.zone == 'America/New_York' + 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' def test_loading_configuration_temperature_unit(self): """Test backward compatibility when loading core config.""" - config = Config() - hass = mock.Mock(config=config) + self.hass.config = mock.Mock() - config_util.process_ha_core_config(hass, { - 'latitude': 60, - 'longitude': 50, - 'elevation': 25, - 'name': 'Huis', - CONF_TEMPERATURE_UNIT: 'C', - 'time_zone': 'America/New_York', - }) + 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 config.latitude == 60 - assert config.longitude == 50 - assert config.elevation == 25 - assert config.location_name == 'Huis' - assert config.units.name == CONF_UNIT_SYSTEM_METRIC - assert config.time_zone.zone == 'America/New_York' + 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' @mock.patch('homeassistant.util.location.detect_location_info', - return_value=location_util.LocationInfo( + 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', return_value=101) + @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.""" - config = Config() - hass = mock.Mock(config=config) + 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 - config_util.process_ha_core_config(hass, {}) + run_coroutine_threadsafe( + config_util.async_process_ha_core_config( + self.hass, {}), self.hass.loop + ).result() - assert config.latitude == 32.8594 - assert config.longitude == -117.2073 - assert config.elevation == 101 - assert config.location_name == 'San Diego' - assert config.units.name == CONF_UNIT_SYSTEM_METRIC - assert config.units.is_metric - assert config.time_zone.zone == 'America/Los_Angeles' + 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', - return_value=None) + 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.""" - config = Config() - hass = mock.Mock(config=config) + self.hass.config = Config() - config_util.process_ha_core_config(hass, {}) + run_coroutine_threadsafe( + config_util.async_process_ha_core_config( + self.hass, {}), self.hass.loop + ).result() blankConfig = Config() - assert config.latitude == blankConfig.latitude - assert config.longitude == blankConfig.longitude - assert config.elevation == blankConfig.elevation - assert config.location_name == blankConfig.location_name - assert config.units == blankConfig.units - assert config.time_zone == blankConfig.time_zone + 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