diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index f1136123999..66312f7283a 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -342,7 +342,11 @@ def async_enable_logging( err_log_path, backupCount=1 ) - err_handler.doRollover() + try: + err_handler.doRollover() + except OSError as err: + _LOGGER.error("Error rolling over log file: %s", err) + err_handler.setLevel(logging.INFO if verbose else logging.WARNING) err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt)) diff --git a/homeassistant/components/device_automation/__init__.py b/homeassistant/components/device_automation/__init__.py index 945774da0b4..89a3f8f6408 100644 --- a/homeassistant/components/device_automation/__init__.py +++ b/homeassistant/components/device_automation/__init__.py @@ -4,6 +4,7 @@ from __future__ import annotations import asyncio from collections.abc import Iterable, Mapping from functools import wraps +import logging from types import ModuleType from typing import Any @@ -27,7 +28,6 @@ from .exceptions import DeviceNotFound, InvalidDeviceAutomationConfig DOMAIN = "device_automation" - DEVICE_TRIGGER_BASE_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( { vol.Required(CONF_PLATFORM): "device", @@ -174,6 +174,13 @@ async def _async_get_device_automations( device_results, InvalidDeviceAutomationConfig ): continue + if isinstance(device_results, Exception): + logging.getLogger(__name__).error( + "Unexpected error fetching device %ss", + automation_type, + exc_info=device_results, + ) + continue for automation in device_results: combined_results[automation["device_id"]].append(automation) diff --git a/homeassistant/components/device_automation/trigger.py b/homeassistant/components/device_automation/trigger.py index a1b6e53c5c3..1a63dcb9e9b 100644 --- a/homeassistant/components/device_automation/trigger.py +++ b/homeassistant/components/device_automation/trigger.py @@ -7,6 +7,8 @@ from homeassistant.components.device_automation import ( ) from homeassistant.const import CONF_DOMAIN +from .exceptions import InvalidDeviceAutomationConfig + # mypy: allow-untyped-defs, no-check-untyped-defs TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA) @@ -17,10 +19,13 @@ async def async_validate_trigger_config(hass, config): platform = await async_get_device_automation_platform( hass, config[CONF_DOMAIN], "trigger" ) - if hasattr(platform, "async_validate_trigger_config"): - return await getattr(platform, "async_validate_trigger_config")(hass, config) + if not hasattr(platform, "async_validate_trigger_config"): + return platform.TRIGGER_SCHEMA(config) - return platform.TRIGGER_SCHEMA(config) + try: + return await getattr(platform, "async_validate_trigger_config")(hass, config) + except InvalidDeviceAutomationConfig as err: + raise vol.Invalid(str(err) or "Invalid trigger configuration") from err async def async_attach_trigger(hass, config, action, automation_info): diff --git a/homeassistant/components/hdmi_cec/switch.py b/homeassistant/components/hdmi_cec/switch.py index 3764766275e..a268d7cfe79 100644 --- a/homeassistant/components/hdmi_cec/switch.py +++ b/homeassistant/components/hdmi_cec/switch.py @@ -4,6 +4,7 @@ from __future__ import annotations import logging from homeassistant.components.switch import DOMAIN, SwitchEntity +from homeassistant.const import STATE_OFF, STATE_ON from . import ATTR_NEW, CecEntity @@ -34,17 +35,25 @@ class CecSwitchEntity(CecEntity, SwitchEntity): def turn_on(self, **kwargs) -> None: """Turn device on.""" self._device.turn_on() - self._attr_is_on = True + self._state = STATE_ON self.schedule_update_ha_state(force_refresh=False) def turn_off(self, **kwargs) -> None: """Turn device off.""" self._device.turn_off() - self._attr_is_on = False + self._state = STATE_OFF self.schedule_update_ha_state(force_refresh=False) def toggle(self, **kwargs): """Toggle the entity.""" self._device.toggle() - self._attr_is_on = not self._attr_is_on + if self._state == STATE_ON: + self._state = STATE_OFF + else: + self._state = STATE_ON self.schedule_update_ha_state(force_refresh=False) + + @property + def is_on(self) -> bool: + """Return True if entity is on.""" + return self._state == STATE_ON diff --git a/homeassistant/components/hue/device_trigger.py b/homeassistant/components/hue/device_trigger.py index ea91cd07d8c..77561e47dc5 100644 --- a/homeassistant/components/hue/device_trigger.py +++ b/homeassistant/components/hue/device_trigger.py @@ -118,12 +118,16 @@ async def async_validate_trigger_config(hass, config): trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) - if ( - not device - or device.model not in REMOTES - or trigger not in REMOTES[device.model] - ): - raise InvalidDeviceAutomationConfig + if not device: + raise InvalidDeviceAutomationConfig("Device {config[CONF_DEVICE_ID]} not found") + + if device.model not in REMOTES: + raise InvalidDeviceAutomationConfig( + f"Device model {device.model} is not a remote" + ) + + if trigger not in REMOTES[device.model]: + raise InvalidDeviceAutomationConfig("Device does not support trigger {trigger}") return config diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index b049b3a2d2c..62f7b2dbd73 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -32,6 +32,8 @@ from .const import ( _LOGGER = logging.getLogger(__name__) +SENSOR_KEYS = [desc.key for desc in SENSOR_TYPES] + CONFIG_SCHEMA = vol.Schema( vol.All( # Deprecated in Home Assistant 2021.6 @@ -46,8 +48,8 @@ CONFIG_SCHEMA = vol.Schema( ): cv.positive_time_period, vol.Optional(CONF_MANUAL, default=False): cv.boolean, vol.Optional( - CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES) - ): vol.All(cv.ensure_list, [vol.In(list(SENSOR_TYPES))]), + CONF_MONITORED_CONDITIONS, default=list(SENSOR_KEYS) + ): vol.All(cv.ensure_list, [vol.In(list(SENSOR_KEYS))]), } ) }, diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index 6e9441534ab..63ad6acb181 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -289,7 +289,7 @@ class Scanner: def _async_unsee(self, header_st: str | None, header_location: str | None) -> None: """If we see a device in a new location, unsee the original location.""" if header_st is not None: - self.seen.remove((header_st, header_location)) + self.seen.discard((header_st, header_location)) async def _async_process_entry(self, headers: Mapping[str, str]) -> None: """Process SSDP entries.""" diff --git a/homeassistant/components/starline/sensor.py b/homeassistant/components/starline/sensor.py index 26834cc384c..9ce3aa3bc08 100644 --- a/homeassistant/components/starline/sensor.py +++ b/homeassistant/components/starline/sensor.py @@ -90,7 +90,8 @@ async def async_setup_entry(hass, entry, async_add_entities): sensor for device in account.api.devices.values() for description in SENSOR_TYPES - if (sensor := StarlineSensor(account, device, description)).state is not None + if (sensor := StarlineSensor(account, device, description)).native_value + is not None ] async_add_entities(entities) diff --git a/homeassistant/components/template/trigger_entity.py b/homeassistant/components/template/trigger_entity.py index 84ad4072b66..c80620b0453 100644 --- a/homeassistant/components/template/trigger_entity.py +++ b/homeassistant/components/template/trigger_entity.py @@ -69,7 +69,7 @@ class TriggerEntity(update_coordinator.CoordinatorEntity): # We make a copy so our initial render is 'unknown' and not 'unavailable' self._rendered = dict(self._static_rendered) - self._parse_result = set() + self._parse_result = {CONF_AVAILABILITY} @property def name(self): diff --git a/homeassistant/components/usb/__init__.py b/homeassistant/components/usb/__init__.py index 679f2e1caa2..13f18216cca 100644 --- a/homeassistant/components/usb/__init__.py +++ b/homeassistant/components/usb/__init__.py @@ -112,7 +112,7 @@ class USBDiscovery: if not sys.platform.startswith("linux"): return info = await system_info.async_get_system_info(self.hass) - if info.get("docker") and not info.get("hassio"): + if info.get("docker"): return from pyudev import ( # pylint: disable=import-outside-toplevel diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 55266d02389..a4f7343f0e0 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -326,11 +326,6 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN): device = discovery_info["device"] manufacturer = discovery_info["manufacturer"] description = discovery_info["description"] - # The Nortek sticks are a special case since they - # have a Z-Wave and a Zigbee radio. We need to reject - # the Zigbee radio. - if vid == "10C4" and pid == "8A2A" and "Z-Wave" not in description: - return self.async_abort(reason="not_zwave_device") # Zooz uses this vid/pid, but so do 2652 sticks if vid == "10C4" and pid == "EA60" and "2652" in description: return self.async_abort(reason="not_zwave_device") diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index 7953e33d6e3..ad8ec22befb 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -9,7 +9,7 @@ "iot_class": "local_push", "usb": [ {"vid":"0658","pid":"0200","known_devices":["Aeotec Z-Stick Gen5+", "Z-WaveMe UZB"]}, - {"vid":"10C4","pid":"8A2A","known_devices":["Nortek HUSBZB-1"]}, + {"vid":"10C4","pid":"8A2A","description":"*z-wave*","known_devices":["Nortek HUSBZB-1"]}, {"vid":"10C4","pid":"EA60","known_devices":["Aeotec Z-Stick 7", "Silicon Labs UZB-7", "Zooz ZST10 700"]} ] } diff --git a/homeassistant/const.py b/homeassistant/const.py index 1ec5ae0e311..ae0e3255beb 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,7 +5,7 @@ from typing import Final MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 9 -PATCH_VERSION: Final = "1" +PATCH_VERSION: Final = "2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) diff --git a/homeassistant/generated/usb.py b/homeassistant/generated/usb.py index 477a762ae62..844c09fea40 100644 --- a/homeassistant/generated/usb.py +++ b/homeassistant/generated/usb.py @@ -32,7 +32,8 @@ USB = [ { "domain": "zwave_js", "vid": "10C4", - "pid": "8A2A" + "pid": "8A2A", + "description": "*z-wave*" }, { "domain": "zwave_js", diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index 551f91b2b54..0ff339169a7 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -14,6 +14,7 @@ from unittest.mock import patch from homeassistant import core from homeassistant.config import get_default_config_dir from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import area_registry, device_registry, entity_registry from homeassistant.helpers.check_config import async_check_ha_config_file from homeassistant.util.yaml import Secrets import homeassistant.util.yaml.loader as yaml_loader @@ -229,6 +230,9 @@ async def async_check_config(config_dir): """Check the HA config.""" hass = core.HomeAssistant() hass.config.config_dir = config_dir + await area_registry.async_load(hass) + await device_registry.async_load(hass) + await entity_registry.async_load(hass) components = await async_check_ha_config_file(hass) await hass.async_stop(force=True) return components diff --git a/tests/components/device_automation/test_init.py b/tests/components/device_automation/test_init.py index 13190ed4b32..93d64e97959 100644 --- a/tests/components/device_automation/test_init.py +++ b/tests/components/device_automation/test_init.py @@ -1,4 +1,6 @@ """The test for light device automation.""" +from unittest.mock import patch + import pytest from homeassistant.components import device_automation @@ -443,6 +445,28 @@ async def test_async_get_device_automations_all_devices_action( assert len(result[device_entry.id]) == 3 +async def test_async_get_device_automations_all_devices_action_exception_throw( + hass, device_reg, entity_reg, caplog +): + """Test we get can fetch all the actions when no device id is passed and can handle one throwing an exception.""" + await async_setup_component(hass, "device_automation", {}) + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create("light", "test", "5678", device_id=device_entry.id) + with patch( + "homeassistant.components.light.device_trigger.async_get_triggers", + side_effect=KeyError, + ): + result = await device_automation.async_get_device_automations(hass, "trigger") + assert device_entry.id in result + assert len(result[device_entry.id]) == 0 + assert "KeyError" in caplog.text + + async def test_websocket_get_trigger_capabilities( hass, hass_ws_client, device_reg, entity_reg ): diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index 43b7fd98cd0..b285d3b0f3c 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -991,9 +991,6 @@ async def test_location_change_evicts_prior_location_from_cache(hass, aioclient_ @callback def _callback(*_): - import pprint - - pprint.pprint(mock_ssdp_response) hass.async_create_task(listener.async_callback(mock_ssdp_response)) listener.async_start = _async_callback @@ -1050,3 +1047,113 @@ async def test_location_change_evicts_prior_location_from_cache(hass, aioclient_ mock_init.mock_calls[0][2]["data"][ssdp.ATTR_SSDP_LOCATION] == mock_good_ip_ssdp_response["location"] ) + + +async def test_location_change_with_overlapping_udn_st_combinations( + hass, aioclient_mock +): + """Test handling when a UDN and ST broadcast multiple locations.""" + mock_get_ssdp = { + "test_integration": [ + {"manufacturer": "test_manufacturer", "modelName": "test_model"} + ] + } + + hue_response = """ + + +test_manufacturer +test_model + + + """ + + aioclient_mock.get( + "http://192.168.72.1:49154/wps_device.xml", + text=hue_response.format(ip_address="192.168.72.1"), + ) + aioclient_mock.get( + "http://192.168.72.1:49152/wps_device.xml", + text=hue_response.format(ip_address="192.168.72.1"), + ) + ssdp_response_without_location = { + "ST": "upnp:rootdevice", + "_udn": "uuid:a793d3cc-e802-44fb-84f4-5a30f33115b6", + "USN": "uuid:a793d3cc-e802-44fb-84f4-5a30f33115b6::upnp:rootdevice", + "EXT": "", + } + + port_49154_response = CaseInsensitiveDict( + **ssdp_response_without_location, + **{"LOCATION": "http://192.168.72.1:49154/wps_device.xml"}, + ) + port_49152_response = CaseInsensitiveDict( + **ssdp_response_without_location, + **{"LOCATION": "http://192.168.72.1:49152/wps_device.xml"}, + ) + mock_ssdp_response = port_49154_response + + def _generate_fake_ssdp_listener(*args, **kwargs): + listener = SSDPListener(*args, **kwargs) + + async def _async_callback(*_): + pass + + @callback + def _callback(*_): + hass.async_create_task(listener.async_callback(mock_ssdp_response)) + + listener.async_start = _async_callback + listener.async_search = _callback + return listener + + with patch( + "homeassistant.components.ssdp.async_get_ssdp", + return_value=mock_get_ssdp, + ), patch( + "homeassistant.components.ssdp.SSDPListener", + new=_generate_fake_ssdp_listener, + ), patch.object( + hass.config_entries.flow, "async_init" + ) as mock_init: + assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}}) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200)) + await hass.async_block_till_done() + assert len(mock_init.mock_calls) == 1 + assert mock_init.mock_calls[0][1][0] == "test_integration" + assert mock_init.mock_calls[0][2]["context"] == { + "source": config_entries.SOURCE_SSDP + } + assert ( + mock_init.mock_calls[0][2]["data"][ssdp.ATTR_SSDP_LOCATION] + == port_49154_response["location"] + ) + + mock_init.reset_mock() + mock_ssdp_response = port_49152_response + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=400)) + await hass.async_block_till_done() + assert mock_init.mock_calls[0][1][0] == "test_integration" + assert mock_init.mock_calls[0][2]["context"] == { + "source": config_entries.SOURCE_SSDP + } + assert ( + mock_init.mock_calls[0][2]["data"][ssdp.ATTR_SSDP_LOCATION] + == port_49152_response["location"] + ) + + mock_init.reset_mock() + mock_ssdp_response = port_49154_response + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=600)) + await hass.async_block_till_done() + assert mock_init.mock_calls[0][1][0] == "test_integration" + assert mock_init.mock_calls[0][2]["context"] == { + "source": config_entries.SOURCE_SSDP + } + assert ( + mock_init.mock_calls[0][2]["data"][ssdp.ATTR_SSDP_LOCATION] + == port_49154_response["location"] + ) diff --git a/tests/components/template/test_sensor.py b/tests/components/template/test_sensor.py index df5c43aa58b..a606c2ec62b 100644 --- a/tests/components/template/test_sensor.py +++ b/tests/components/template/test_sensor.py @@ -1038,6 +1038,7 @@ async def test_trigger_entity(hass): "unique_id": "via_list-id", "device_class": "battery", "unit_of_measurement": "%", + "availability": "{{ True }}", "state": "{{ trigger.event.data.beer + 1 }}", "picture": "{{ '/local/dogs.png' }}", "icon": "{{ 'mdi:pirate' }}", @@ -1197,3 +1198,44 @@ async def test_config_top_level(hass): assert state.state == "5" assert state.attributes["device_class"] == "battery" assert state.attributes["state_class"] == "measurement" + + +async def test_trigger_entity_available(hass): + """Test trigger entity availability works.""" + assert await async_setup_component( + hass, + "template", + { + "template": [ + { + "trigger": {"platform": "event", "event_type": "test_event"}, + "sensor": [ + { + "name": "Maybe Available", + "availability": "{{ trigger and trigger.event.data.beer == 2 }}", + "state": "{{ trigger.event.data.beer }}", + }, + ], + }, + ], + }, + ) + + await hass.async_block_till_done() + + # Sensors are unknown if never triggered + state = hass.states.get("sensor.maybe_available") + assert state is not None + assert state.state == STATE_UNKNOWN + + hass.bus.async_fire("test_event", {"beer": 2}) + await hass.async_block_till_done() + + state = hass.states.get("sensor.maybe_available") + assert state.state == "2" + + hass.bus.async_fire("test_event", {"beer": 1}) + await hass.async_block_till_done() + + state = hass.states.get("sensor.maybe_available") + assert state.state == "unavailable" diff --git a/tests/components/usb/test_init.py b/tests/components/usb/test_init.py index 6ba21222052..b09dad9ebe4 100644 --- a/tests/components/usb/test_init.py +++ b/tests/components/usb/test_init.py @@ -52,55 +52,6 @@ def mock_venv(): yield -@pytest.mark.skipif( - not sys.platform.startswith("linux"), - reason="Only works on linux", -) -async def test_discovered_by_observer_before_started(hass, operating_system): - """Test a device is discovered by the observer before started.""" - - async def _mock_monitor_observer_callback(callback): - await hass.async_add_executor_job( - callback, MagicMock(action="add", device_path="/dev/new") - ) - - def _create_mock_monitor_observer(monitor, callback, name): - hass.async_create_task(_mock_monitor_observer_callback(callback)) - return MagicMock() - - new_usb = [{"domain": "test1", "vid": "3039", "pid": "3039"}] - - mock_comports = [ - MagicMock( - device=slae_sh_device.device, - vid=12345, - pid=12345, - serial_number=slae_sh_device.serial_number, - manufacturer=slae_sh_device.manufacturer, - description=slae_sh_device.description, - ) - ] - - with patch( - "homeassistant.components.usb.async_get_usb", return_value=new_usb - ), patch( - "homeassistant.components.usb.comports", return_value=mock_comports - ), patch( - "pyudev.MonitorObserver", new=_create_mock_monitor_observer - ): - assert await async_setup_component(hass, "usb", {"usb": {}}) - await hass.async_block_till_done() - - with patch("homeassistant.components.usb.comports", return_value=[]), patch.object( - hass.config_entries.flow, "async_init" - ) as mock_config_flow: - hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) - await hass.async_block_till_done() - - assert len(mock_config_flow.mock_calls) == 1 - assert mock_config_flow.mock_calls[0][1][0] == "test1" - - @pytest.mark.skipif( not sys.platform.startswith("linux"), reason="Only works on linux", diff --git a/tests/components/zwave_js/test_config_flow.py b/tests/components/zwave_js/test_config_flow.py index 5e994a2ac7a..757dc6d5364 100644 --- a/tests/components/zwave_js/test_config_flow.py +++ b/tests/components/zwave_js/test_config_flow.py @@ -756,10 +756,7 @@ async def test_usb_discovery_already_running(hass, supervisor, addon_running): @pytest.mark.parametrize( "discovery_info", - [ - NORTEK_ZIGBEE_DISCOVERY_INFO, - CP2652_ZIGBEE_DISCOVERY_INFO, - ], + [CP2652_ZIGBEE_DISCOVERY_INFO], ) async def test_abort_usb_discovery_aborts_specific_devices( hass, supervisor, addon_options, discovery_info diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index ea6048dfc9e..1a96568f8ef 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -27,14 +27,23 @@ async def apply_stop_hass(stop_hass): """Make sure all hass are stopped.""" +@pytest.fixture +def mock_is_file(): + """Mock is_file.""" + # All files exist except for the old entity registry file + with patch( + "os.path.isfile", lambda path: not path.endswith("entity_registry.yaml") + ): + yield + + def normalize_yaml_files(check_dict): """Remove configuration path from ['yaml_files'].""" root = get_test_config_dir() return [key.replace(root, "...") for key in sorted(check_dict["yaml_files"].keys())] -@patch("os.path.isfile", return_value=True) -def test_bad_core_config(isfile_patch, loop): +def test_bad_core_config(mock_is_file, loop): """Test a bad core config setup.""" files = {YAML_CONFIG_FILE: BAD_CORE_CONFIG} with patch_yaml_files(files): @@ -43,8 +52,7 @@ def test_bad_core_config(isfile_patch, loop): assert res["except"]["homeassistant"][1] == {"unit_system": "bad"} -@patch("os.path.isfile", return_value=True) -def test_config_platform_valid(isfile_patch, loop): +def test_config_platform_valid(mock_is_file, loop): """Test a valid platform setup.""" files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: demo"} with patch_yaml_files(files): @@ -57,8 +65,7 @@ def test_config_platform_valid(isfile_patch, loop): assert len(res["yaml_files"]) == 1 -@patch("os.path.isfile", return_value=True) -def test_component_platform_not_found(isfile_patch, loop): +def test_component_platform_not_found(mock_is_file, loop): """Test errors if component or platform not found.""" # Make sure they don't exist files = {YAML_CONFIG_FILE: BASE_CONFIG + "beer:"} @@ -89,8 +96,7 @@ def test_component_platform_not_found(isfile_patch, loop): assert len(res["yaml_files"]) == 1 -@patch("os.path.isfile", return_value=True) -def test_secrets(isfile_patch, loop): +def test_secrets(mock_is_file, loop): """Test secrets config checking method.""" secrets_path = get_test_config_dir("secrets.yaml") @@ -121,8 +127,7 @@ def test_secrets(isfile_patch, loop): ] -@patch("os.path.isfile", return_value=True) -def test_package_invalid(isfile_patch, loop): +def test_package_invalid(mock_is_file, loop): """Test an invalid package.""" files = { YAML_CONFIG_FILE: BASE_CONFIG + (" packages:\n p1:\n" ' group: ["a"]') diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 929cbbf6e81..3eeb06d056c 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -56,11 +56,14 @@ async def test_home_assistant_core_config_validation(hass): assert result is None -async def test_async_enable_logging(hass): +async def test_async_enable_logging(hass, caplog): """Test to ensure logging is migrated to the queue handlers.""" with patch("logging.getLogger"), patch( "homeassistant.bootstrap.async_activate_log_queue_handler" - ) as mock_async_activate_log_queue_handler: + ) as mock_async_activate_log_queue_handler, patch( + "homeassistant.bootstrap.logging.handlers.RotatingFileHandler.doRollover", + side_effect=OSError, + ): bootstrap.async_enable_logging(hass) mock_async_activate_log_queue_handler.assert_called_once() mock_async_activate_log_queue_handler.reset_mock() @@ -75,6 +78,8 @@ async def test_async_enable_logging(hass): for f in glob.glob("testing_config/home-assistant.log*"): os.remove(f) + assert "Error rolling over log file" in caplog.text + async def test_load_hassio(hass): """Test that we load Hass.io component."""