Add Safe Mode (#30723)
* Store last working HTTP settings * Add safe mode * Fix tests * Add cloud to safe mode * Update logging text * Fix camera tests leaving files behind * Make emulated_hue tests not leave files behind * Make logbook tests not leave files behind * Make tts tests not leave files behind * Make image_processing tests not leave files behind * Make manual_mqtt tests not leave files behind
This commit is contained in:
parent
c4673ddee1
commit
5fdc60e067
35 changed files with 480 additions and 430 deletions
|
@ -278,6 +278,7 @@ homeassistant/components/rfxtrx/* @danielhiversen
|
|||
homeassistant/components/ring/* @balloob
|
||||
homeassistant/components/rmvtransport/* @cgtobi
|
||||
homeassistant/components/roomba/* @pschmitt
|
||||
homeassistant/components/safe_mode/* @home-assistant/core
|
||||
homeassistant/components/saj/* @fredericvl
|
||||
homeassistant/components/samsungtv/* @escoand
|
||||
homeassistant/components/scene/* @home-assistant/core
|
||||
|
|
|
@ -6,13 +6,10 @@ import platform
|
|||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from typing import TYPE_CHECKING, Any, Dict, List
|
||||
from typing import List
|
||||
|
||||
from homeassistant.const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant import core
|
||||
|
||||
|
||||
def set_loop() -> None:
|
||||
"""Attempt to use different loop."""
|
||||
|
@ -78,19 +75,6 @@ def ensure_config_path(config_dir: str) -> None:
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
async def ensure_config_file(hass: "core.HomeAssistant", config_dir: str) -> str:
|
||||
"""Ensure configuration file exists."""
|
||||
import homeassistant.config as config_util
|
||||
|
||||
config_path = await config_util.async_ensure_config_exists(hass, config_dir)
|
||||
|
||||
if config_path is None:
|
||||
print("Error getting configuration path")
|
||||
sys.exit(1)
|
||||
|
||||
return config_path
|
||||
|
||||
|
||||
def get_arguments() -> argparse.Namespace:
|
||||
"""Get parsed passed in arguments."""
|
||||
import homeassistant.config as config_util
|
||||
|
@ -107,7 +91,7 @@ def get_arguments() -> argparse.Namespace:
|
|||
help="Directory that contains the Home Assistant configuration",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--demo-mode", action="store_true", help="Start Home Assistant in demo mode"
|
||||
"--safe-mode", action="store_true", help="Start Home Assistant in safe mode"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--debug", action="store_true", help="Start Home Assistant in debug mode"
|
||||
|
@ -253,35 +237,21 @@ def cmdline() -> List[str]:
|
|||
|
||||
async def setup_and_run_hass(config_dir: str, args: argparse.Namespace) -> int:
|
||||
"""Set up Home Assistant and run."""
|
||||
from homeassistant import bootstrap, core
|
||||
from homeassistant import bootstrap
|
||||
|
||||
hass = core.HomeAssistant()
|
||||
|
||||
if args.demo_mode:
|
||||
config: Dict[str, Any] = {"frontend": {}, "demo": {}}
|
||||
bootstrap.async_from_config_dict(
|
||||
config,
|
||||
hass,
|
||||
hass = await bootstrap.async_setup_hass(
|
||||
config_dir=config_dir,
|
||||
verbose=args.verbose,
|
||||
skip_pip=args.skip_pip,
|
||||
log_rotate_days=args.log_rotate_days,
|
||||
log_file=args.log_file,
|
||||
log_no_color=args.log_no_color,
|
||||
)
|
||||
else:
|
||||
config_file = await ensure_config_file(hass, config_dir)
|
||||
print("Config directory:", config_dir)
|
||||
await bootstrap.async_from_config_file(
|
||||
config_file,
|
||||
hass,
|
||||
verbose=args.verbose,
|
||||
skip_pip=args.skip_pip,
|
||||
log_rotate_days=args.log_rotate_days,
|
||||
log_file=args.log_file,
|
||||
log_no_color=args.log_no_color,
|
||||
safe_mode=args.safe_mode,
|
||||
)
|
||||
|
||||
if hass is None:
|
||||
return 1
|
||||
|
||||
if args.open_ui and hass.config.api is not None:
|
||||
import webbrowser
|
||||
|
||||
|
@ -358,7 +328,7 @@ def main() -> int:
|
|||
|
||||
return scripts.run(args.script)
|
||||
|
||||
config_dir = os.path.join(os.getcwd(), args.config)
|
||||
config_dir = os.path.abspath(os.path.join(os.getcwd(), args.config))
|
||||
ensure_config_path(config_dir)
|
||||
|
||||
# Daemon functions
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
"""Provide methods to bootstrap a Home Assistant instance."""
|
||||
import asyncio
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
|
@ -11,6 +10,7 @@ from typing import Any, Dict, Optional, Set
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config as conf_util, config_entries, core, loader
|
||||
from homeassistant.components import http
|
||||
from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_CLOSE,
|
||||
REQUIRED_NEXT_PYTHON_DATE,
|
||||
|
@ -42,25 +42,20 @@ STAGE_1_INTEGRATIONS = {
|
|||
}
|
||||
|
||||
|
||||
async 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,
|
||||
log_file: Any = None,
|
||||
log_no_color: bool = False,
|
||||
async def async_setup_hass(
|
||||
*,
|
||||
config_dir: str,
|
||||
verbose: bool,
|
||||
log_rotate_days: int,
|
||||
log_file: str,
|
||||
log_no_color: bool,
|
||||
skip_pip: bool,
|
||||
safe_mode: bool,
|
||||
) -> Optional[core.HomeAssistant]:
|
||||
"""Try to configure Home Assistant from a configuration dictionary.
|
||||
"""Set up Home Assistant."""
|
||||
hass = core.HomeAssistant()
|
||||
hass.config.config_dir = config_dir
|
||||
|
||||
Dynamically loads required components and its dependencies.
|
||||
This method is a coroutine.
|
||||
"""
|
||||
start = time()
|
||||
|
||||
if enable_log:
|
||||
async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color)
|
||||
|
||||
hass.config.skip_pip = skip_pip
|
||||
|
@ -69,6 +64,54 @@ async def async_from_config_dict(
|
|||
"Skipping pip installation of required modules. This may cause issues"
|
||||
)
|
||||
|
||||
if not await conf_util.async_ensure_config_exists(hass):
|
||||
_LOGGER.error("Error getting configuration path")
|
||||
return None
|
||||
|
||||
_LOGGER.info("Config directory: %s", config_dir)
|
||||
|
||||
config_dict = None
|
||||
|
||||
if not safe_mode:
|
||||
await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass)
|
||||
|
||||
try:
|
||||
config_dict = await conf_util.async_hass_config_yaml(hass)
|
||||
except HomeAssistantError as err:
|
||||
_LOGGER.error(
|
||||
"Failed to parse configuration.yaml: %s. Falling back to safe mode",
|
||||
err,
|
||||
)
|
||||
else:
|
||||
if not is_virtual_env():
|
||||
await async_mount_local_lib_path(config_dir)
|
||||
|
||||
await async_from_config_dict(config_dict, hass)
|
||||
finally:
|
||||
clear_secret_cache()
|
||||
|
||||
if safe_mode or config_dict is None:
|
||||
_LOGGER.info("Starting in safe mode")
|
||||
|
||||
http_conf = (await http.async_get_last_config(hass)) or {}
|
||||
|
||||
await async_from_config_dict(
|
||||
{"safe_mode": {}, "http": http_conf}, hass,
|
||||
)
|
||||
|
||||
return hass
|
||||
|
||||
|
||||
async def async_from_config_dict(
|
||||
config: Dict[str, Any], hass: core.HomeAssistant
|
||||
) -> Optional[core.HomeAssistant]:
|
||||
"""Try to configure Home Assistant from a configuration dictionary.
|
||||
|
||||
Dynamically loads required components and its dependencies.
|
||||
This method is a coroutine.
|
||||
"""
|
||||
start = time()
|
||||
|
||||
core_config = config.get(core.DOMAIN, {})
|
||||
|
||||
try:
|
||||
|
@ -83,14 +126,6 @@ async def async_from_config_dict(
|
|||
)
|
||||
return None
|
||||
|
||||
# Make a copy because we are mutating it.
|
||||
config = OrderedDict(config)
|
||||
|
||||
# Merge packages
|
||||
await conf_util.merge_packages_config(
|
||||
hass, config, core_config.get(conf_util.CONF_PACKAGES, {})
|
||||
)
|
||||
|
||||
hass.config_entries = config_entries.ConfigEntries(hass, config)
|
||||
await hass.config_entries.async_initialize()
|
||||
|
||||
|
@ -116,46 +151,6 @@ async def async_from_config_dict(
|
|||
return hass
|
||||
|
||||
|
||||
async def async_from_config_file(
|
||||
config_path: str,
|
||||
hass: core.HomeAssistant,
|
||||
verbose: bool = False,
|
||||
skip_pip: bool = True,
|
||||
log_rotate_days: Any = None,
|
||||
log_file: Any = None,
|
||||
log_no_color: bool = False,
|
||||
) -> Optional[core.HomeAssistant]:
|
||||
"""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
|
||||
|
||||
if not is_virtual_env():
|
||||
await async_mount_local_lib_path(config_dir)
|
||||
|
||||
async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color)
|
||||
|
||||
await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass)
|
||||
|
||||
try:
|
||||
config_dict = await hass.async_add_executor_job(
|
||||
conf_util.load_yaml_config_file, config_path
|
||||
)
|
||||
except HomeAssistantError as err:
|
||||
_LOGGER.error("Error loading %s: %s", config_path, err)
|
||||
return None
|
||||
finally:
|
||||
clear_secret_cache()
|
||||
|
||||
return await async_from_config_dict(
|
||||
config_dict, hass, enable_log=False, skip_pip=skip_pip
|
||||
)
|
||||
|
||||
|
||||
@core.callback
|
||||
def async_enable_logging(
|
||||
hass: core.HomeAssistant,
|
||||
|
@ -269,6 +264,7 @@ def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]:
|
|||
domains = set(key.split(" ")[0] for key in config.keys() if key != core.DOMAIN)
|
||||
|
||||
# Add config entry domains
|
||||
if "safe_mode" not in config:
|
||||
domains.update(hass.config_entries.async_domains())
|
||||
|
||||
# Make sure the Hass.io component is loaded
|
||||
|
|
|
@ -13,7 +13,7 @@ from yarl import URL
|
|||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.components.http.view import HomeAssistantView
|
||||
from homeassistant.config import find_config_file, load_yaml_config_file
|
||||
from homeassistant.config import async_hass_config_yaml
|
||||
from homeassistant.const import CONF_NAME, EVENT_THEMES_UPDATED
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
@ -362,11 +362,10 @@ def _async_setup_themes(hass, themes):
|
|||
else:
|
||||
_LOGGER.warning("Theme %s is not defined.", name)
|
||||
|
||||
@callback
|
||||
def reload_themes(_):
|
||||
async def reload_themes(_):
|
||||
"""Reload themes."""
|
||||
path = find_config_file(hass.config.config_dir)
|
||||
new_themes = load_yaml_config_file(path)[DOMAIN].get(CONF_THEMES, {})
|
||||
config = await async_hass_config_yaml(hass)
|
||||
new_themes = config[DOMAIN].get(CONF_THEMES, {})
|
||||
hass.data[DATA_THEMES] = new_themes
|
||||
if hass.data[DATA_DEFAULT_THEME] not in new_themes:
|
||||
hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
|
||||
|
|
|
@ -3,7 +3,7 @@ from ipaddress import ip_network
|
|||
import logging
|
||||
import os
|
||||
import ssl
|
||||
from typing import Optional
|
||||
from typing import Optional, cast
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp.web_exceptions import HTTPMovedPermanently
|
||||
|
@ -14,7 +14,10 @@ from homeassistant.const import (
|
|||
EVENT_HOMEASSISTANT_STOP,
|
||||
SERVER_PORT,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import storage
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.loader import bind_hass
|
||||
import homeassistant.util as hass_util
|
||||
from homeassistant.util import ssl as ssl_util
|
||||
|
||||
|
@ -56,6 +59,9 @@ NO_LOGIN_ATTEMPT_THRESHOLD = -1
|
|||
|
||||
MAX_CLIENT_SIZE: int = 1024 ** 2 * 16
|
||||
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
|
||||
|
||||
HTTP_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -85,6 +91,13 @@ HTTP_SCHEMA = vol.Schema(
|
|||
CONFIG_SCHEMA = vol.Schema({DOMAIN: HTTP_SCHEMA}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
@bind_hass
|
||||
async def async_get_last_config(hass: HomeAssistant) -> Optional[dict]:
|
||||
"""Return the last known working config."""
|
||||
store = storage.Store(hass, STORAGE_VERSION, STORAGE_KEY)
|
||||
return cast(Optional[dict], await store.async_load())
|
||||
|
||||
|
||||
class ApiConfig:
|
||||
"""Configuration settings for API server."""
|
||||
|
||||
|
@ -151,6 +164,10 @@ async def async_setup(hass, config):
|
|||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_server)
|
||||
await server.start()
|
||||
|
||||
# If we are set up successful, we store the HTTP settings for safe mode.
|
||||
store = storage.Store(hass, STORAGE_VERSION, STORAGE_KEY)
|
||||
await store.async_save(conf)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_server)
|
||||
|
||||
hass.http = server
|
||||
|
|
15
homeassistant/components/safe_mode/__init__.py
Normal file
15
homeassistant/components/safe_mode/__init__.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
"""The Safe Mode integration."""
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
DOMAIN = "safe_mode"
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: dict):
|
||||
"""Set up the Safe Mode component."""
|
||||
persistent_notification.async_create(
|
||||
hass,
|
||||
"Home Assistant is running in safe mode. Check [the error log](/developer-tools/logs) to see what went wrong.",
|
||||
"Safe Mode",
|
||||
)
|
||||
return True
|
12
homeassistant/components/safe_mode/manifest.json
Normal file
12
homeassistant/components/safe_mode/manifest.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"domain": "safe_mode",
|
||||
"name": "Safe Mode",
|
||||
"config_flow": false,
|
||||
"documentation": "https://www.home-assistant.io/integrations/safe_mode",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"zeroconf": [],
|
||||
"homekit": {},
|
||||
"dependencies": ["frontend", "config", "persistent_notification", "cloud"],
|
||||
"codeowners": ["@home-assistant/core"]
|
||||
}
|
|
@ -226,35 +226,34 @@ def get_default_config_dir() -> str:
|
|||
return os.path.join(data_dir, CONFIG_DIR_NAME) # type: ignore
|
||||
|
||||
|
||||
async def async_ensure_config_exists(
|
||||
hass: HomeAssistant, config_dir: str
|
||||
) -> Optional[str]:
|
||||
async def async_ensure_config_exists(hass: HomeAssistant) -> bool:
|
||||
"""Ensure a configuration file exists in given configuration directory.
|
||||
|
||||
Creating a default one if needed.
|
||||
Return path to the configuration file.
|
||||
Return boolean if configuration dir is ready to go.
|
||||
"""
|
||||
config_path = find_config_file(config_dir)
|
||||
config_path = hass.config.path(YAML_CONFIG_FILE)
|
||||
|
||||
if config_path is None:
|
||||
print("Unable to find configuration. Creating default one in", config_dir)
|
||||
config_path = await async_create_default_config(hass, config_dir)
|
||||
if os.path.isfile(config_path):
|
||||
return True
|
||||
|
||||
return config_path
|
||||
print(
|
||||
"Unable to find configuration. Creating default one in", hass.config.config_dir
|
||||
)
|
||||
return await async_create_default_config(hass)
|
||||
|
||||
|
||||
async def async_create_default_config(
|
||||
hass: HomeAssistant, config_dir: str
|
||||
) -> Optional[str]:
|
||||
async def async_create_default_config(hass: HomeAssistant) -> bool:
|
||||
"""Create a default configuration file in given configuration directory.
|
||||
|
||||
Return path to new config file if success, None if failed.
|
||||
This method needs to run in an executor.
|
||||
Return if creation was successful.
|
||||
"""
|
||||
return await hass.async_add_executor_job(_write_default_config, config_dir)
|
||||
return await hass.async_add_executor_job(
|
||||
_write_default_config, hass.config.config_dir
|
||||
)
|
||||
|
||||
|
||||
def _write_default_config(config_dir: str) -> Optional[str]:
|
||||
def _write_default_config(config_dir: str) -> bool:
|
||||
"""Write the default config."""
|
||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||
secret_path = os.path.join(config_dir, SECRET_YAML)
|
||||
|
@ -288,11 +287,11 @@ def _write_default_config(config_dir: str) -> Optional[str]:
|
|||
with open(scene_yaml_path, "wt"):
|
||||
pass
|
||||
|
||||
return config_path
|
||||
return True
|
||||
|
||||
except OSError:
|
||||
print("Unable to create default configuration file", config_path)
|
||||
return None
|
||||
return False
|
||||
|
||||
|
||||
async def async_hass_config_yaml(hass: HomeAssistant) -> Dict:
|
||||
|
@ -300,35 +299,16 @@ async def async_hass_config_yaml(hass: HomeAssistant) -> Dict:
|
|||
|
||||
This function allow a component inside the asyncio loop to reload its
|
||||
configuration by itself. Include package merge.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
|
||||
def _load_hass_yaml_config() -> Dict:
|
||||
path = find_config_file(hass.config.config_dir)
|
||||
if path is None:
|
||||
raise HomeAssistantError(
|
||||
f"Config file not found in: {hass.config.config_dir}"
|
||||
)
|
||||
config = load_yaml_config_file(path)
|
||||
return config
|
||||
|
||||
# Not using async_add_executor_job because this is an internal method.
|
||||
config = await hass.loop.run_in_executor(None, _load_hass_yaml_config)
|
||||
config = await hass.loop.run_in_executor(
|
||||
None, load_yaml_config_file, hass.config.path(YAML_CONFIG_FILE)
|
||||
)
|
||||
core_config = config.get(CONF_CORE, {})
|
||||
await merge_packages_config(hass, config, core_config.get(CONF_PACKAGES, {}))
|
||||
return config
|
||||
|
||||
|
||||
def find_config_file(config_dir: Optional[str]) -> Optional[str]:
|
||||
"""Look in given directory for supported configuration files."""
|
||||
if config_dir is None:
|
||||
return None
|
||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||
|
||||
return config_path if os.path.isfile(config_path) else None
|
||||
|
||||
|
||||
def load_yaml_config_file(config_path: str) -> Dict[Any, Any]:
|
||||
"""Parse a YAML configuration file.
|
||||
|
||||
|
@ -382,8 +362,7 @@ def process_ha_config_upgrade(hass: HomeAssistant) -> None:
|
|||
|
||||
if version_obj < LooseVersion("0.92"):
|
||||
# 0.92 moved google/tts.py to google_translate/tts.py
|
||||
config_path = find_config_file(hass.config.config_dir)
|
||||
assert config_path is not None
|
||||
config_path = hass.config.path(YAML_CONFIG_FILE)
|
||||
|
||||
with open(config_path, "rt", encoding="utf-8") as config_file:
|
||||
config_raw = config_file.read()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Helper to check the configuration file."""
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
import attr
|
||||
|
@ -10,10 +11,10 @@ from homeassistant.config import (
|
|||
CONF_CORE,
|
||||
CONF_PACKAGES,
|
||||
CORE_CONFIG_SCHEMA,
|
||||
YAML_CONFIG_FILE,
|
||||
_format_config_error,
|
||||
config_per_platform,
|
||||
extract_domain_configs,
|
||||
find_config_file,
|
||||
load_yaml_config_file,
|
||||
merge_packages_config,
|
||||
)
|
||||
|
@ -62,7 +63,6 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> HomeAssistantConfig
|
|||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
config_dir = hass.config.config_dir
|
||||
result = HomeAssistantConfig()
|
||||
|
||||
def _pack_error(
|
||||
|
@ -79,9 +79,9 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> HomeAssistantConfig
|
|||
result.add_error(_format_config_error(ex, domain, config), domain, config)
|
||||
|
||||
# Load configuration.yaml
|
||||
config_path = hass.config.path(YAML_CONFIG_FILE)
|
||||
try:
|
||||
config_path = await hass.async_add_executor_job(find_config_file, config_dir)
|
||||
if not config_path:
|
||||
if not await hass.async_add_executor_job(os.path.isfile, config_path):
|
||||
return result.add_error("File configuration.yaml not found.")
|
||||
config = await hass.async_add_executor_job(load_yaml_config_file, config_path)
|
||||
except FileNotFoundError:
|
||||
|
|
|
@ -32,13 +32,14 @@ def run(args):
|
|||
os.makedirs(config_dir)
|
||||
|
||||
hass = HomeAssistant()
|
||||
config_path = hass.loop.run_until_complete(async_run(hass, config_dir))
|
||||
hass.config.config_dir = config_dir
|
||||
config_path = hass.loop.run_until_complete(async_run(hass))
|
||||
print("Configuration file:", config_path)
|
||||
return 0
|
||||
|
||||
|
||||
async def async_run(hass, config_dir):
|
||||
async def async_run(hass):
|
||||
"""Make sure config exists."""
|
||||
path = await config_util.async_ensure_config_exists(hass, config_dir)
|
||||
path = await config_util.async_ensure_config_exists(hass)
|
||||
await hass.async_stop(force=True)
|
||||
return path
|
||||
|
|
|
@ -55,7 +55,7 @@ async def _async_process_dependencies(
|
|||
"""Ensure all dependencies are set up."""
|
||||
blacklisted = [dep for dep in dependencies if dep in loader.DEPENDENCY_BLACKLIST]
|
||||
|
||||
if blacklisted and name != "default_config":
|
||||
if blacklisted and name not in ("default_config", "safe_mode"):
|
||||
_LOGGER.error(
|
||||
"Unable to set up dependencies of %s: "
|
||||
"found blacklisted dependencies: %s",
|
||||
|
|
|
@ -496,7 +496,6 @@ async def test_reload_config_service(hass, calls, hass_admin_user, hass_read_onl
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
with pytest.raises(Unauthorized):
|
||||
await common.async_reload(hass, Context(user_id=hass_read_only_user.id))
|
||||
await hass.async_block_till_done()
|
||||
|
@ -551,7 +550,6 @@ async def test_reload_config_when_invalid_config(hass, calls):
|
|||
autospec=True,
|
||||
return_value={automation.DOMAIN: "not valid"},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
await common.async_reload(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -590,7 +588,6 @@ async def test_reload_config_handles_load_fails(hass, calls):
|
|||
"homeassistant.config.load_yaml_config_file",
|
||||
side_effect=HomeAssistantError("bla"),
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
await common.async_reload(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
|
|
@ -6,24 +6,15 @@ from unittest.mock import PropertyMock, mock_open, patch
|
|||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import camera, http
|
||||
from homeassistant.components import camera
|
||||
from homeassistant.components.camera.const import DOMAIN, PREF_PRELOAD_STREAM
|
||||
from homeassistant.components.camera.prefs import CameraEntityPreferences
|
||||
from homeassistant.components.websocket_api.const import TYPE_RESULT
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ENTITY_PICTURE,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.setup import async_setup_component, setup_component
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import (
|
||||
assert_setup_component,
|
||||
get_test_home_assistant,
|
||||
get_test_instance_port,
|
||||
mock_coro,
|
||||
)
|
||||
from tests.common import mock_coro
|
||||
from tests.components.camera import common
|
||||
|
||||
|
||||
|
@ -55,96 +46,53 @@ def setup_camera_prefs(hass):
|
|||
return common.mock_camera_prefs(hass, "camera.demo_camera")
|
||||
|
||||
|
||||
class TestSetupCamera:
|
||||
"""Test class for setup camera."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_setup_component(self):
|
||||
"""Set up demo platform on camera component."""
|
||||
config = {camera.DOMAIN: {"platform": "demo"}}
|
||||
|
||||
with assert_setup_component(1, camera.DOMAIN):
|
||||
setup_component(self.hass, camera.DOMAIN, config)
|
||||
|
||||
|
||||
class TestGetImage:
|
||||
"""Test class for camera."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
setup_component(
|
||||
self.hass,
|
||||
http.DOMAIN,
|
||||
{http.DOMAIN: {http.CONF_SERVER_PORT: get_test_instance_port()}},
|
||||
@pytest.fixture
|
||||
async def image_mock_url(hass):
|
||||
"""Fixture for get_image tests."""
|
||||
await async_setup_component(
|
||||
hass, camera.DOMAIN, {camera.DOMAIN: {"platform": "demo"}}
|
||||
)
|
||||
|
||||
config = {camera.DOMAIN: {"platform": "demo"}}
|
||||
|
||||
setup_component(self.hass, camera.DOMAIN, config)
|
||||
async def test_get_image_from_camera(hass, image_mock_url):
|
||||
"""Grab an image from camera entity."""
|
||||
|
||||
state = self.hass.states.get("camera.demo_camera")
|
||||
self.url = "{0}{1}".format(
|
||||
self.hass.config.api.base_url, state.attributes.get(ATTR_ENTITY_PICTURE)
|
||||
)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch(
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.camera_image",
|
||||
autospec=True,
|
||||
return_value=b"Test",
|
||||
)
|
||||
def test_get_image_from_camera(self, mock_camera):
|
||||
"""Grab an image from camera entity."""
|
||||
self.hass.start()
|
||||
|
||||
image = asyncio.run_coroutine_threadsafe(
|
||||
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
|
||||
).result()
|
||||
) as mock_camera:
|
||||
image = await camera.async_get_image(hass, "camera.demo_camera")
|
||||
|
||||
assert mock_camera.called
|
||||
assert image.content == b"Test"
|
||||
|
||||
def test_get_image_without_exists_camera(self):
|
||||
|
||||
async def test_get_image_without_exists_camera(hass, image_mock_url):
|
||||
"""Try to get image without exists camera."""
|
||||
with patch(
|
||||
"homeassistant.helpers.entity_component.EntityComponent.get_entity",
|
||||
return_value=None,
|
||||
), pytest.raises(HomeAssistantError):
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
|
||||
).result()
|
||||
await camera.async_get_image(hass, "camera.demo_camera")
|
||||
|
||||
def test_get_image_with_timeout(self):
|
||||
|
||||
async def test_get_image_with_timeout(hass, image_mock_url):
|
||||
"""Try to get image with timeout."""
|
||||
with patch(
|
||||
"homeassistant.components.camera.Camera.async_camera_image",
|
||||
side_effect=asyncio.TimeoutError,
|
||||
), pytest.raises(HomeAssistantError):
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
|
||||
).result()
|
||||
await camera.async_get_image(hass, "camera.demo_camera")
|
||||
|
||||
def test_get_image_fails(self):
|
||||
|
||||
async def test_get_image_fails(hass, image_mock_url):
|
||||
"""Try to get image with timeout."""
|
||||
with patch(
|
||||
"homeassistant.components.camera.Camera.async_camera_image",
|
||||
return_value=mock_coro(None),
|
||||
), pytest.raises(HomeAssistantError):
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
|
||||
).result()
|
||||
await camera.async_get_image(hass, "camera.demo_camera")
|
||||
|
||||
|
||||
async def test_snapshot_service(hass, mock_camera):
|
||||
|
|
|
@ -4,10 +4,11 @@ import unittest
|
|||
from unittest.mock import patch
|
||||
|
||||
from aiohttp.hdrs import CONTENT_TYPE
|
||||
import defusedxml.ElementTree as ET
|
||||
import requests
|
||||
|
||||
from homeassistant import const, setup
|
||||
from homeassistant.components import emulated_hue, http
|
||||
from homeassistant.components import emulated_hue
|
||||
|
||||
from tests.common import get_test_home_assistant, get_test_instance_port
|
||||
|
||||
|
@ -28,10 +29,6 @@ class TestEmulatedHue(unittest.TestCase):
|
|||
"""Set up the class."""
|
||||
cls.hass = hass = get_test_home_assistant()
|
||||
|
||||
setup.setup_component(
|
||||
hass, http.DOMAIN, {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}
|
||||
)
|
||||
|
||||
with patch("homeassistant.components.emulated_hue.UPNPResponderThread"):
|
||||
setup.setup_component(
|
||||
hass,
|
||||
|
@ -52,8 +49,6 @@ class TestEmulatedHue(unittest.TestCase):
|
|||
|
||||
def test_description_xml(self):
|
||||
"""Test the description."""
|
||||
import defusedxml.ElementTree as ET
|
||||
|
||||
result = requests.get(BRIDGE_URL_BASE.format("/description.xml"), timeout=5)
|
||||
|
||||
assert result.status_code == 200
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""The tests for Home Assistant frontend."""
|
||||
import re
|
||||
from unittest.mock import patch
|
||||
|
||||
from asynctest import patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.frontend import (
|
||||
|
@ -173,7 +173,7 @@ async def test_themes_reload_themes(hass, hass_ws_client):
|
|||
client = await hass_ws_client(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.frontend.load_yaml_config_file",
|
||||
"homeassistant.components.frontend.async_hass_config_yaml",
|
||||
return_value={DOMAIN: {CONF_THEMES: {"sad": {"primary-color": "blue"}}}},
|
||||
):
|
||||
await hass.services.async_call(
|
||||
|
|
|
@ -429,7 +429,6 @@ class TestComponentsGroup(unittest.TestCase):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
common.reload(self.hass)
|
||||
self.hass.block_till_done()
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ async def test_reload_config_service(hass):
|
|||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value={"scene": {"name": "Hallo", "entities": {"light.kitchen": "on"}}},
|
||||
), patch("homeassistant.config.find_config_file", return_value=""):
|
||||
):
|
||||
await hass.services.async_call("scene", "reload", blocking=True)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -28,7 +28,7 @@ async def test_reload_config_service(hass):
|
|||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value={"scene": {"name": "Bye", "entities": {"light.kitchen": "on"}}},
|
||||
), patch("homeassistant.config.find_config_file", return_value=""):
|
||||
):
|
||||
await hass.services.async_call("scene", "reload", blocking=True)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
|
|
@ -240,3 +240,16 @@ async def test_cors_defaults(hass):
|
|||
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert mock_setup.mock_calls[0][1][1] == ["https://cast.home-assistant.io"]
|
||||
|
||||
|
||||
async def test_storing_config(hass, aiohttp_client, aiohttp_unused_port):
|
||||
"""Test that we store last working config."""
|
||||
config = {http.CONF_SERVER_PORT: aiohttp_unused_port()}
|
||||
|
||||
await async_setup_component(hass, http.DOMAIN, {http.DOMAIN: config})
|
||||
|
||||
await hass.async_start()
|
||||
|
||||
assert await hass.components.http.async_get_last_config() == http.HTTP_SCHEMA(
|
||||
config
|
||||
)
|
||||
|
|
|
@ -77,8 +77,6 @@ class TestImageProcessing:
|
|||
)
|
||||
def test_get_image_from_camera(self, mock_camera):
|
||||
"""Grab an image from camera entity."""
|
||||
self.hass.start()
|
||||
|
||||
common.scan(self.hass, entity_id="image_processing.test")
|
||||
self.hass.block_till_done()
|
||||
|
||||
|
|
|
@ -240,7 +240,6 @@ async def test_reload(hass, hass_admin_user):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_RELOAD,
|
||||
|
|
|
@ -345,7 +345,6 @@ async def test_reload(hass, hass_admin_user, hass_read_only_user):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
with pytest.raises(Unauthorized):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -338,7 +338,6 @@ async def test_reload(hass, hass_admin_user, hass_read_only_user):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
with pytest.raises(Unauthorized):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -415,7 +415,6 @@ async def test_reload(hass, hass_admin_user, hass_read_only_user):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
with pytest.raises(Unauthorized):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -288,7 +288,6 @@ async def test_reload(hass, hass_admin_user, hass_read_only_user):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
with pytest.raises(Unauthorized):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -48,7 +48,6 @@ class TestComponentLogbook(unittest.TestCase):
|
|||
self.hass = get_test_home_assistant()
|
||||
init_recorder_component(self.hass) # Force an in memory DB
|
||||
assert setup_component(self.hass, logbook.DOMAIN, self.EMPTY_CONFIG)
|
||||
self.hass.start()
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
@ -90,7 +89,7 @@ class TestComponentLogbook(unittest.TestCase):
|
|||
dt_util.utcnow() + timedelta(hours=1),
|
||||
)
|
||||
)
|
||||
assert len(events) == 2
|
||||
assert len(events) == 1
|
||||
|
||||
assert 1 == len(calls)
|
||||
last_call = calls[-1]
|
||||
|
|
|
@ -269,9 +269,6 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase):
|
|||
|
||||
entity_id = "alarm_control_panel.test"
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert STATE_ALARM_DISARMED == self.hass.states.get(entity_id).state
|
||||
|
||||
common.alarm_arm_home(self.hass, "abc")
|
||||
|
@ -1471,9 +1468,6 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase):
|
|||
|
||||
entity_id = "alarm_control_panel.test"
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert STATE_ALARM_DISARMED == self.hass.states.get(entity_id).state
|
||||
|
||||
common.alarm_arm_home(self.hass, "def")
|
||||
|
|
|
@ -753,7 +753,7 @@ async def test_reload(hass, hass_admin_user):
|
|||
{"name": "Person 3", "id": "id-3"},
|
||||
]
|
||||
},
|
||||
), patch("homeassistant.config.find_config_file", return_value=""):
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_RELOAD,
|
||||
|
|
1
tests/components/safe_mode/__init__.py
Normal file
1
tests/components/safe_mode/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Tests for the Safe Mode integration."""
|
9
tests/components/safe_mode/test_init.py
Normal file
9
tests/components/safe_mode/test_init.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""Tests for safe mode integration."""
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
async def test_works(hass):
|
||||
"""Test safe mode works."""
|
||||
assert await async_setup_component(hass, "safe_mode", {})
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
|
@ -229,7 +229,6 @@ class TestScriptComponent(unittest.TestCase):
|
|||
"script": {"test2": {"sequence": [{"delay": {"seconds": 5}}]}}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
reload(self.hass)
|
||||
self.hass.block_till_done()
|
||||
|
||||
|
@ -262,7 +261,6 @@ async def test_service_descriptions(hass):
|
|||
assert not descriptions[DOMAIN]["test"]["fields"]
|
||||
|
||||
# Test 2: has "fields" but no "description"
|
||||
await hass.services.async_call(DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
return_value={
|
||||
|
@ -279,7 +277,6 @@ async def test_service_descriptions(hass):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
await hass.services.async_call(DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||
|
||||
descriptions = await async_get_all_descriptions(hass)
|
||||
|
|
|
@ -259,7 +259,6 @@ async def test_config_reload(hass, hass_admin_user, hass_read_only_user):
|
|||
}
|
||||
},
|
||||
):
|
||||
with patch("homeassistant.config.find_config_file", return_value=""):
|
||||
with pytest.raises(Unauthorized):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -24,6 +24,7 @@ from tests.common import (
|
|||
get_test_home_assistant,
|
||||
get_test_instance_port,
|
||||
mock_service,
|
||||
mock_storage,
|
||||
)
|
||||
|
||||
|
||||
|
@ -45,6 +46,8 @@ class TestTTS:
|
|||
self.hass = get_test_home_assistant()
|
||||
self.demo_provider = DemoProvider("en")
|
||||
self.default_tts_cache = self.hass.config.path(tts.DEFAULT_CACHE_DIR)
|
||||
self.mock_storage = mock_storage()
|
||||
self.mock_storage.__enter__()
|
||||
|
||||
setup_component(
|
||||
self.hass,
|
||||
|
@ -55,6 +58,7 @@ class TestTTS:
|
|||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
self.mock_storage.__exit__(None, None, None)
|
||||
|
||||
if os.path.isdir(self.default_tts_cache):
|
||||
shutil.rmtree(self.default_tts_cache)
|
||||
|
|
|
@ -120,7 +120,7 @@ def test_secrets(isfile_patch, loop):
|
|||
|
||||
@patch("os.path.isfile", return_value=True)
|
||||
def test_package_invalid(isfile_patch, loop):
|
||||
"""Test a valid platform setup."""
|
||||
"""Test an invalid package."""
|
||||
files = {
|
||||
YAML_CONFIG_FILE: BASE_CONFIG + (" packages:\n p1:\n" ' group: ["a"]')
|
||||
}
|
||||
|
|
|
@ -3,18 +3,23 @@
|
|||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock
|
||||
|
||||
from asynctest import patch
|
||||
import pytest
|
||||
|
||||
from homeassistant import bootstrap
|
||||
import homeassistant.config as config_util
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
flush_store,
|
||||
get_test_config_dir,
|
||||
mock_coro,
|
||||
mock_integration,
|
||||
patch_yaml_files,
|
||||
)
|
||||
|
||||
ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE
|
||||
|
@ -23,26 +28,6 @@ VERSION_PATH = os.path.join(get_test_config_dir(), config_util.VERSION_FILE)
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# prevent .HA_VERSION file from being written
|
||||
@patch("homeassistant.bootstrap.conf_util.process_ha_config_upgrade", Mock())
|
||||
@patch(
|
||||
"homeassistant.util.location.async_detect_location_info",
|
||||
Mock(return_value=mock_coro(None)),
|
||||
)
|
||||
@patch("os.path.isfile", Mock(return_value=True))
|
||||
@patch("os.access", Mock(return_value=True))
|
||||
@patch("homeassistant.bootstrap.async_enable_logging", Mock(return_value=True))
|
||||
def test_from_config_file(hass):
|
||||
"""Test with configuration file."""
|
||||
components = set(["browser", "conversation", "script"])
|
||||
files = {"config.yaml": "".join(f"{comp}:\n" for comp in components)}
|
||||
|
||||
with patch_yaml_files(files, True):
|
||||
yield from bootstrap.async_from_config_file("config.yaml", hass)
|
||||
|
||||
assert components == hass.config.components
|
||||
|
||||
|
||||
@patch("homeassistant.bootstrap.async_enable_logging", Mock())
|
||||
@asyncio.coroutine
|
||||
def test_home_assistant_core_config_validation(hass):
|
||||
|
@ -54,33 +39,6 @@ def test_home_assistant_core_config_validation(hass):
|
|||
assert result is None
|
||||
|
||||
|
||||
async def test_async_from_config_file_not_mount_deps_folder(loop):
|
||||
"""Test that we not mount the deps folder inside async_from_config_file."""
|
||||
hass = Mock(async_add_executor_job=Mock(side_effect=lambda *args: mock_coro()))
|
||||
|
||||
with patch("homeassistant.bootstrap.is_virtual_env", return_value=False), patch(
|
||||
"homeassistant.bootstrap.async_enable_logging", return_value=mock_coro()
|
||||
), patch(
|
||||
"homeassistant.bootstrap.async_mount_local_lib_path", return_value=mock_coro()
|
||||
) as mock_mount, patch(
|
||||
"homeassistant.bootstrap.async_from_config_dict", return_value=mock_coro()
|
||||
):
|
||||
|
||||
await bootstrap.async_from_config_file("mock-path", hass)
|
||||
assert len(mock_mount.mock_calls) == 1
|
||||
|
||||
with patch("homeassistant.bootstrap.is_virtual_env", return_value=True), patch(
|
||||
"homeassistant.bootstrap.async_enable_logging", return_value=mock_coro()
|
||||
), patch(
|
||||
"homeassistant.bootstrap.async_mount_local_lib_path", return_value=mock_coro()
|
||||
) as mock_mount, patch(
|
||||
"homeassistant.bootstrap.async_from_config_dict", return_value=mock_coro()
|
||||
):
|
||||
|
||||
await bootstrap.async_from_config_file("mock-path", hass)
|
||||
assert len(mock_mount.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_load_hassio(hass):
|
||||
"""Test that we load Hass.io component."""
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
|
@ -233,3 +191,169 @@ async def test_setup_after_deps_not_present(hass, caplog):
|
|||
assert "first_dep" not in hass.config.components
|
||||
assert "second_dep" in hass.config.components
|
||||
assert order == ["root", "second_dep"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_is_virtual_env():
|
||||
"""Mock enable logging."""
|
||||
with patch(
|
||||
"homeassistant.bootstrap.is_virtual_env", return_value=False
|
||||
) as is_virtual_env:
|
||||
yield is_virtual_env
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_enable_logging():
|
||||
"""Mock enable logging."""
|
||||
with patch("homeassistant.bootstrap.async_enable_logging") as enable_logging:
|
||||
yield enable_logging
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_mount_local_lib_path():
|
||||
"""Mock enable logging."""
|
||||
with patch(
|
||||
"homeassistant.bootstrap.async_mount_local_lib_path"
|
||||
) as mount_local_lib_path:
|
||||
yield mount_local_lib_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_process_ha_config_upgrade():
|
||||
"""Mock enable logging."""
|
||||
with patch(
|
||||
"homeassistant.config.process_ha_config_upgrade"
|
||||
) as process_ha_config_upgrade:
|
||||
yield process_ha_config_upgrade
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_ensure_config_exists():
|
||||
"""Mock enable logging."""
|
||||
with patch(
|
||||
"homeassistant.config.async_ensure_config_exists", return_value=True
|
||||
) as ensure_config_exists:
|
||||
yield ensure_config_exists
|
||||
|
||||
|
||||
async def test_setup_hass(
|
||||
mock_enable_logging,
|
||||
mock_is_virtual_env,
|
||||
mock_mount_local_lib_path,
|
||||
mock_ensure_config_exists,
|
||||
mock_process_ha_config_upgrade,
|
||||
):
|
||||
"""Test it works."""
|
||||
verbose = Mock()
|
||||
log_rotate_days = Mock()
|
||||
log_file = Mock()
|
||||
log_no_color = Mock()
|
||||
|
||||
with patch(
|
||||
"homeassistant.config.async_hass_config_yaml", return_value={"browser": {}}
|
||||
):
|
||||
hass = await bootstrap.async_setup_hass(
|
||||
config_dir=get_test_config_dir(),
|
||||
verbose=verbose,
|
||||
log_rotate_days=log_rotate_days,
|
||||
log_file=log_file,
|
||||
log_no_color=log_no_color,
|
||||
skip_pip=True,
|
||||
safe_mode=False,
|
||||
)
|
||||
|
||||
assert "browser" in hass.config.components
|
||||
|
||||
assert len(mock_enable_logging.mock_calls) == 1
|
||||
assert mock_enable_logging.mock_calls[0][1] == (
|
||||
hass,
|
||||
verbose,
|
||||
log_rotate_days,
|
||||
log_file,
|
||||
log_no_color,
|
||||
)
|
||||
assert len(mock_mount_local_lib_path.mock_calls) == 1
|
||||
assert len(mock_ensure_config_exists.mock_calls) == 1
|
||||
assert len(mock_process_ha_config_upgrade.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_setup_hass_invalid_yaml(
|
||||
mock_enable_logging,
|
||||
mock_is_virtual_env,
|
||||
mock_mount_local_lib_path,
|
||||
mock_ensure_config_exists,
|
||||
mock_process_ha_config_upgrade,
|
||||
):
|
||||
"""Test it works."""
|
||||
with patch(
|
||||
"homeassistant.config.async_hass_config_yaml", side_effect=HomeAssistantError
|
||||
):
|
||||
hass = await bootstrap.async_setup_hass(
|
||||
config_dir=get_test_config_dir(),
|
||||
verbose=False,
|
||||
log_rotate_days=10,
|
||||
log_file="",
|
||||
log_no_color=False,
|
||||
skip_pip=True,
|
||||
safe_mode=False,
|
||||
)
|
||||
|
||||
assert "safe_mode" in hass.config.components
|
||||
assert len(mock_mount_local_lib_path.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_setup_hass_config_dir_nonexistent(
|
||||
mock_enable_logging,
|
||||
mock_is_virtual_env,
|
||||
mock_mount_local_lib_path,
|
||||
mock_ensure_config_exists,
|
||||
mock_process_ha_config_upgrade,
|
||||
):
|
||||
"""Test it works."""
|
||||
mock_ensure_config_exists.return_value = False
|
||||
|
||||
assert (
|
||||
await bootstrap.async_setup_hass(
|
||||
config_dir=get_test_config_dir(),
|
||||
verbose=False,
|
||||
log_rotate_days=10,
|
||||
log_file="",
|
||||
log_no_color=False,
|
||||
skip_pip=True,
|
||||
safe_mode=False,
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
async def test_setup_hass_safe_mode(
|
||||
hass,
|
||||
mock_enable_logging,
|
||||
mock_is_virtual_env,
|
||||
mock_mount_local_lib_path,
|
||||
mock_ensure_config_exists,
|
||||
mock_process_ha_config_upgrade,
|
||||
):
|
||||
"""Test it works."""
|
||||
# Add a config entry to storage.
|
||||
MockConfigEntry(domain="browser").add_to_hass(hass)
|
||||
hass.config_entries._async_schedule_save()
|
||||
await flush_store(hass.config_entries._store)
|
||||
|
||||
with patch("homeassistant.components.browser.setup") as browser_setup:
|
||||
hass = await bootstrap.async_setup_hass(
|
||||
config_dir=get_test_config_dir(),
|
||||
verbose=False,
|
||||
log_rotate_days=10,
|
||||
log_file="",
|
||||
log_no_color=False,
|
||||
skip_pip=True,
|
||||
safe_mode=True,
|
||||
)
|
||||
|
||||
assert "safe_mode" in hass.config.components
|
||||
assert len(mock_mount_local_lib_path.mock_calls) == 0
|
||||
|
||||
# Validate we didn't try to set up config entry.
|
||||
assert "browser" not in hass.config.components
|
||||
assert len(browser_setup.mock_calls) == 0
|
||||
|
|
|
@ -82,7 +82,7 @@ def teardown():
|
|||
|
||||
async def test_create_default_config(hass):
|
||||
"""Test creation of default config."""
|
||||
await config_util.async_create_default_config(hass, CONFIG_DIR)
|
||||
await config_util.async_create_default_config(hass)
|
||||
|
||||
assert os.path.isfile(YAML_PATH)
|
||||
assert os.path.isfile(SECRET_PATH)
|
||||
|
@ -91,20 +91,13 @@ async def test_create_default_config(hass):
|
|||
assert os.path.isfile(AUTOMATIONS_PATH)
|
||||
|
||||
|
||||
def test_find_config_file_yaml():
|
||||
"""Test if it finds a YAML config file."""
|
||||
create_file(YAML_PATH)
|
||||
|
||||
assert YAML_PATH == config_util.find_config_file(CONFIG_DIR)
|
||||
|
||||
|
||||
async def test_ensure_config_exists_creates_config(hass):
|
||||
"""Test that calling ensure_config_exists.
|
||||
|
||||
If not creates a new config file.
|
||||
"""
|
||||
with mock.patch("builtins.print") as mock_print:
|
||||
await config_util.async_ensure_config_exists(hass, CONFIG_DIR)
|
||||
await config_util.async_ensure_config_exists(hass)
|
||||
|
||||
assert os.path.isfile(YAML_PATH)
|
||||
assert mock_print.called
|
||||
|
@ -113,7 +106,7 @@ async def test_ensure_config_exists_creates_config(hass):
|
|||
async def test_ensure_config_exists_uses_existing_config(hass):
|
||||
"""Test that calling ensure_config_exists uses existing config."""
|
||||
create_file(YAML_PATH)
|
||||
await config_util.async_ensure_config_exists(hass, CONFIG_DIR)
|
||||
await config_util.async_ensure_config_exists(hass)
|
||||
|
||||
with open(YAML_PATH) as f:
|
||||
content = f.read()
|
||||
|
@ -172,13 +165,9 @@ async def test_create_default_config_returns_none_if_write_error(hass):
|
|||
|
||||
Non existing folder returns None.
|
||||
"""
|
||||
hass.config.config_dir = os.path.join(CONFIG_DIR, "non_existing_dir/")
|
||||
with mock.patch("builtins.print") as mock_print:
|
||||
assert (
|
||||
await config_util.async_create_default_config(
|
||||
hass, os.path.join(CONFIG_DIR, "non_existing_dir/")
|
||||
)
|
||||
is None
|
||||
)
|
||||
assert await config_util.async_create_default_config(hass) is False
|
||||
assert mock_print.called
|
||||
|
||||
|
||||
|
@ -331,7 +320,6 @@ def test_config_upgrade_same_version(hass):
|
|||
assert opened_file.write.call_count == 0
|
||||
|
||||
|
||||
@mock.patch("homeassistant.config.find_config_file", mock.Mock())
|
||||
def test_config_upgrade_no_file(hass):
|
||||
"""Test update of version on upgrade, with no version file."""
|
||||
mock_open = mock.mock_open()
|
||||
|
|
Loading…
Add table
Reference in a new issue