mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-13 12:16:29 +00:00
Finish out effort of adding and enabling blockbuster in tests (#5735)
* Finish out effort of adding and enabling blockbuster * Skip getting addon file size until securetar fixed * Fix test for devcontainer and blocking I/O * Fix docker fixture and load_config to post_init
This commit is contained in:
parent
23e03a95f4
commit
e1c9c8b786
@ -601,7 +601,9 @@ class Backup(JobGroup):
|
|||||||
ATTR_SLUG: addon.slug,
|
ATTR_SLUG: addon.slug,
|
||||||
ATTR_NAME: addon.name,
|
ATTR_NAME: addon.name,
|
||||||
ATTR_VERSION: addon.version,
|
ATTR_VERSION: addon.version,
|
||||||
ATTR_SIZE: addon_file.size,
|
# Bug - addon_file.size used to give us this information
|
||||||
|
# It always returns 0 in current securetar. Skipping until fixed
|
||||||
|
ATTR_SIZE: 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -640,7 +642,7 @@ class Backup(JobGroup):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# If exists inside backup
|
# If exists inside backup
|
||||||
if not addon_file.path.exists():
|
if not await self.sys_run_in_executor(addon_file.path.exists):
|
||||||
raise BackupError(f"Can't find backup {addon_slug}", _LOGGER.error)
|
raise BackupError(f"Can't find backup {addon_slug}", _LOGGER.error)
|
||||||
|
|
||||||
# Perform a restore
|
# Perform a restore
|
||||||
|
@ -55,7 +55,7 @@ async def initialize_coresys() -> CoreSys:
|
|||||||
coresys = await CoreSys().load_config()
|
coresys = await CoreSys().load_config()
|
||||||
|
|
||||||
# Initialize core objects
|
# Initialize core objects
|
||||||
coresys.docker = await DockerAPI(coresys).load_config()
|
coresys.docker = await DockerAPI(coresys).post_init()
|
||||||
coresys.resolution = await ResolutionManager(coresys).load_config()
|
coresys.resolution = await ResolutionManager(coresys).load_config()
|
||||||
await coresys.resolution.load_modules()
|
await coresys.resolution.load_modules()
|
||||||
coresys.jobs = await JobManager(coresys).load_config()
|
coresys.jobs = await JobManager(coresys).load_config()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Manager for Supervisor Docker."""
|
"""Manager for Supervisor Docker."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
from functools import partial
|
||||||
from ipaddress import IPv4Address
|
from ipaddress import IPv4Address
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -105,19 +107,42 @@ class DockerAPI:
|
|||||||
|
|
||||||
def __init__(self, coresys: CoreSys):
|
def __init__(self, coresys: CoreSys):
|
||||||
"""Initialize Docker base wrapper."""
|
"""Initialize Docker base wrapper."""
|
||||||
self.docker: DockerClient = DockerClient(
|
self._docker: DockerClient | None = None
|
||||||
base_url=f"unix:/{str(SOCKET_DOCKER)}", version="auto", timeout=900
|
self._network: DockerNetwork | None = None
|
||||||
)
|
self._info: DockerInfo | None = None
|
||||||
self.network: DockerNetwork = DockerNetwork(self.docker)
|
|
||||||
self._info: DockerInfo = DockerInfo.new(self.docker.info())
|
|
||||||
self.config: DockerConfig = DockerConfig()
|
self.config: DockerConfig = DockerConfig()
|
||||||
self._monitor: DockerMonitor = DockerMonitor(coresys)
|
self._monitor: DockerMonitor = DockerMonitor(coresys)
|
||||||
|
|
||||||
async def load_config(self) -> Self:
|
async def post_init(self) -> Self:
|
||||||
"""Load config in executor."""
|
"""Post init actions that must be done in event loop."""
|
||||||
|
self._docker = await asyncio.get_running_loop().run_in_executor(
|
||||||
|
None,
|
||||||
|
partial(
|
||||||
|
DockerClient,
|
||||||
|
base_url=f"unix:/{str(SOCKET_DOCKER)}",
|
||||||
|
version="auto",
|
||||||
|
timeout=900,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self._network = DockerNetwork(self._docker)
|
||||||
|
self._info = DockerInfo.new(self.docker.info())
|
||||||
await self.config.read_data()
|
await self.config.read_data()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def docker(self) -> DockerClient:
|
||||||
|
"""Get docker API client."""
|
||||||
|
if not self._docker:
|
||||||
|
raise RuntimeError("Docker API Client not initialized!")
|
||||||
|
return self._docker
|
||||||
|
|
||||||
|
@property
|
||||||
|
def network(self) -> DockerNetwork:
|
||||||
|
"""Get Docker network."""
|
||||||
|
if not self._network:
|
||||||
|
raise RuntimeError("Docker Network not initialized!")
|
||||||
|
return self._network
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def images(self) -> ImageCollection:
|
def images(self) -> ImageCollection:
|
||||||
"""Return API images."""
|
"""Return API images."""
|
||||||
@ -136,6 +161,8 @@ class DockerAPI:
|
|||||||
@property
|
@property
|
||||||
def info(self) -> DockerInfo:
|
def info(self) -> DockerInfo:
|
||||||
"""Return local docker info."""
|
"""Return local docker info."""
|
||||||
|
if not self._info:
|
||||||
|
raise RuntimeError("Docker Info not initialized!")
|
||||||
return self._info
|
return self._info
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -54,10 +54,16 @@ class AppArmorControl(CoreSysAttributes):
|
|||||||
|
|
||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Load available profiles."""
|
"""Load available profiles."""
|
||||||
for content in self.sys_config.path_apparmor.iterdir():
|
|
||||||
if not content.is_file():
|
def find_profiles() -> set[str]:
|
||||||
continue
|
profiles: set[str] = set()
|
||||||
self._profiles.add(content.name)
|
for content in self.sys_config.path_apparmor.iterdir():
|
||||||
|
if not content.is_file():
|
||||||
|
continue
|
||||||
|
profiles.add(content.name)
|
||||||
|
return profiles
|
||||||
|
|
||||||
|
self._profiles = await self.sys_run_in_executor(find_profiles)
|
||||||
|
|
||||||
_LOGGER.info("Loading AppArmor Profiles: %s", self._profiles)
|
_LOGGER.info("Loading AppArmor Profiles: %s", self._profiles)
|
||||||
|
|
||||||
|
@ -292,9 +292,12 @@ class MountManager(FileConfiguration, CoreSysAttributes):
|
|||||||
where.as_posix(),
|
where.as_posix(),
|
||||||
)
|
)
|
||||||
path = self.sys_config.path_emergency / mount.name
|
path = self.sys_config.path_emergency / mount.name
|
||||||
if not path.exists():
|
|
||||||
path.mkdir(mode=0o444)
|
|
||||||
|
|
||||||
|
def emergency_mkdir():
|
||||||
|
if not path.exists():
|
||||||
|
path.mkdir(mode=0o444)
|
||||||
|
|
||||||
|
await self.sys_run_in_executor(emergency_mkdir)
|
||||||
path = self.sys_config.local_to_extern_path(path)
|
path = self.sys_config.local_to_extern_path(path)
|
||||||
|
|
||||||
self._bound_mounts[mount.name] = bound_mount = BoundMount(
|
self._bound_mounts[mount.name] = bound_mount = BoundMount(
|
||||||
|
@ -30,9 +30,7 @@ class CheckCoreSecurity(CheckBase):
|
|||||||
# Security issue < 2021.1.5 & Custom components
|
# Security issue < 2021.1.5 & Custom components
|
||||||
try:
|
try:
|
||||||
if self.sys_homeassistant.version < AwesomeVersion("2021.1.5"):
|
if self.sys_homeassistant.version < AwesomeVersion("2021.1.5"):
|
||||||
if Path(
|
if await self.sys_run_in_executor(self._custom_components_exists):
|
||||||
self.sys_config.path_homeassistant, "custom_components"
|
|
||||||
).exists():
|
|
||||||
self.sys_resolution.create_issue(
|
self.sys_resolution.create_issue(
|
||||||
IssueType.SECURITY,
|
IssueType.SECURITY,
|
||||||
ContextType.CORE,
|
ContextType.CORE,
|
||||||
@ -49,9 +47,14 @@ class CheckCoreSecurity(CheckBase):
|
|||||||
return False
|
return False
|
||||||
except AwesomeVersionException:
|
except AwesomeVersionException:
|
||||||
return True
|
return True
|
||||||
if not Path(self.sys_config.path_homeassistant, "custom_components").exists():
|
return await self.sys_run_in_executor(self._custom_components_exists)
|
||||||
return False
|
|
||||||
return True
|
def _custom_components_exists(self) -> bool:
|
||||||
|
"""Return true if custom components folder exists.
|
||||||
|
|
||||||
|
Must be run in executor.
|
||||||
|
"""
|
||||||
|
return Path(self.sys_config.path_homeassistant, "custom_components").exists()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def issue(self) -> IssueType:
|
def issue(self) -> IssueType:
|
||||||
|
@ -1679,15 +1679,17 @@ async def test_skip_homeassistant_database(
|
|||||||
coresys.homeassistant.backups_exclude_database = exclude_db_setting
|
coresys.homeassistant.backups_exclude_database = exclude_db_setting
|
||||||
|
|
||||||
test_file = coresys.config.path_homeassistant / "configuration.yaml"
|
test_file = coresys.config.path_homeassistant / "configuration.yaml"
|
||||||
(test_db := coresys.config.path_homeassistant / "home-assistant_v2.db").touch()
|
test_db = coresys.config.path_homeassistant / "home-assistant_v2.db"
|
||||||
(
|
test_db_wal = coresys.config.path_homeassistant / "home-assistant_v2.db-wal"
|
||||||
test_db_wal := coresys.config.path_homeassistant / "home-assistant_v2.db-wal"
|
test_db_shm = coresys.config.path_homeassistant / "home-assistant_v2.db-shm"
|
||||||
).touch()
|
|
||||||
(
|
|
||||||
test_db_shm := coresys.config.path_homeassistant / "home-assistant_v2.db-shm"
|
|
||||||
).touch()
|
|
||||||
|
|
||||||
write_json_file(test_file, {"default_config": {}})
|
def setup_1():
|
||||||
|
test_db.touch()
|
||||||
|
test_db_wal.touch()
|
||||||
|
test_db_shm.touch()
|
||||||
|
write_json_file(test_file, {"default_config": {}})
|
||||||
|
|
||||||
|
await coresys.run_in_executor(setup_1)
|
||||||
|
|
||||||
kwargs = {} if exclude_db_setting else {"homeassistant_exclude_database": True}
|
kwargs = {} if exclude_db_setting else {"homeassistant_exclude_database": True}
|
||||||
if partial_backup:
|
if partial_backup:
|
||||||
@ -1697,9 +1699,12 @@ async def test_skip_homeassistant_database(
|
|||||||
else:
|
else:
|
||||||
backup: Backup = await coresys.backups.do_backup_full(**kwargs)
|
backup: Backup = await coresys.backups.do_backup_full(**kwargs)
|
||||||
|
|
||||||
test_file.unlink()
|
def setup_2():
|
||||||
write_json_file(test_db, {"hello": "world"})
|
test_file.unlink()
|
||||||
write_json_file(test_db_wal, {"hello": "world"})
|
write_json_file(test_db, {"hello": "world"})
|
||||||
|
write_json_file(test_db_wal, {"hello": "world"})
|
||||||
|
|
||||||
|
await coresys.run_in_executor(setup_2)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch.object(HomeAssistantCore, "update"),
|
patch.object(HomeAssistantCore, "update"),
|
||||||
@ -1707,10 +1712,13 @@ async def test_skip_homeassistant_database(
|
|||||||
):
|
):
|
||||||
await coresys.backups.do_restore_partial(backup, homeassistant=True)
|
await coresys.backups.do_restore_partial(backup, homeassistant=True)
|
||||||
|
|
||||||
assert read_json_file(test_file) == {"default_config": {}}
|
def test_assertions():
|
||||||
assert read_json_file(test_db) == {"hello": "world"}
|
assert read_json_file(test_file) == {"default_config": {}}
|
||||||
assert read_json_file(test_db_wal) == {"hello": "world"}
|
assert read_json_file(test_db) == {"hello": "world"}
|
||||||
assert not test_db_shm.exists()
|
assert read_json_file(test_db_wal) == {"hello": "world"}
|
||||||
|
assert not test_db_shm.exists()
|
||||||
|
|
||||||
|
await coresys.run_in_executor(test_assertions)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern")
|
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern")
|
||||||
|
@ -64,21 +64,16 @@ from .dbus_service_mocks.network_manager import NetworkManager as NetworkManager
|
|||||||
# pylint: disable=redefined-outer-name, protected-access
|
# pylint: disable=redefined-outer-name, protected-access
|
||||||
|
|
||||||
|
|
||||||
# This commented out code is left in intentionally
|
@pytest.fixture(autouse=True)
|
||||||
# Intent is to enable this for all tests at all times as an autouse fixture
|
def blockbuster() -> BlockBuster:
|
||||||
# Findings from PR were growing too big so disabling temporarily to create a checkpoint
|
|
||||||
# @pytest.fixture(autouse=True)
|
|
||||||
def blockbuster(request: pytest.FixtureRequest) -> BlockBuster:
|
|
||||||
"""Raise for blocking I/O in event loop."""
|
"""Raise for blocking I/O in event loop."""
|
||||||
# Excluded modules doesn't seem to stop test code from raising for blocking I/O
|
# Only scanning supervisor code for now as that's our primary interest
|
||||||
# Defaulting to only scanning supervisor core code seems like the best we can do easily
|
# This will still raise for tests that call utilities in supervisor code that block
|
||||||
# Added a parameter so we could potentially go module by module in test and eliminate blocking I/O
|
# But it will ignore calls to libraries and such that do blocking I/O directly from tests
|
||||||
# Then we could tell it to scan everything by default. That will require more follow-up work
|
# Removing that would be nice but a todo for the future
|
||||||
|
|
||||||
# pylint: disable-next=contextmanager-generator-missing-cleanup
|
# pylint: disable-next=contextmanager-generator-missing-cleanup
|
||||||
with blockbuster_ctx(
|
with blockbuster_ctx(scanned_modules=["supervisor"]) as bb:
|
||||||
scanned_modules=getattr(request, "param", ["supervisor"])
|
|
||||||
) as bb:
|
|
||||||
yield bb
|
yield bb
|
||||||
|
|
||||||
|
|
||||||
@ -118,7 +113,7 @@ async def docker() -> DockerAPI:
|
|||||||
),
|
),
|
||||||
patch("supervisor.docker.manager.DockerAPI.unload"),
|
patch("supervisor.docker.manager.DockerAPI.unload"),
|
||||||
):
|
):
|
||||||
docker_obj = DockerAPI(MagicMock())
|
docker_obj = await DockerAPI(MagicMock()).post_init()
|
||||||
docker_obj.config._data = {"registries": {}}
|
docker_obj.config._data = {"registries": {}}
|
||||||
with patch("supervisor.docker.monitor.DockerMonitor.load"):
|
with patch("supervisor.docker.monitor.DockerMonitor.load"):
|
||||||
await docker_obj.load()
|
await docker_obj.load()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Test dbus interface."""
|
"""Test dbus interface."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from dbus_fast.aio.message_bus import MessageBus
|
from dbus_fast.aio.message_bus import MessageBus
|
||||||
@ -145,9 +146,11 @@ async def test_proxy_missing_properties_interface(dbus_session_bus: MessageBus):
|
|||||||
proxy.object_path = DBUS_OBJECT_BASE
|
proxy.object_path = DBUS_OBJECT_BASE
|
||||||
proxy.properties_interface = "test.no.properties.interface"
|
proxy.properties_interface = "test.no.properties.interface"
|
||||||
|
|
||||||
async def mock_introspect(*args, **kwargs):
|
def mock_introspect(*args, **kwargs):
|
||||||
"""Return introspection without properties."""
|
"""Return introspection without properties."""
|
||||||
return load_fixture("test_no_properties_interface.xml")
|
return asyncio.get_running_loop().run_in_executor(
|
||||||
|
None, load_fixture, "test_no_properties_interface.xml"
|
||||||
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch.object(MessageBus, "introspect", new=mock_introspect),
|
patch.object(MessageBus, "introspect", new=mock_introspect),
|
||||||
|
@ -113,6 +113,10 @@ async def test_get_checks(coresys: CoreSys):
|
|||||||
|
|
||||||
async def test_dynamic_check_loader(coresys: CoreSys):
|
async def test_dynamic_check_loader(coresys: CoreSys):
|
||||||
"""Test dynamic check loader, this ensures that all checks have defined a setup function."""
|
"""Test dynamic check loader, this ensures that all checks have defined a setup function."""
|
||||||
coresys.resolution.check.load_modules()
|
|
||||||
for check in await coresys.run_in_executor(get_valid_modules, "checks"):
|
def load_modules():
|
||||||
|
coresys.resolution.check.load_modules()
|
||||||
|
return get_valid_modules("checks")
|
||||||
|
|
||||||
|
for check in await coresys.run_in_executor(load_modules):
|
||||||
assert check in coresys.resolution.check._checks
|
assert check in coresys.resolution.check._checks
|
||||||
|
@ -58,7 +58,9 @@ async def test_add_invalid_repository(coresys: CoreSys, store_manager: StoreMana
|
|||||||
current + ["http://example.com"], add_with_errors=True
|
current + ["http://example.com"], add_with_errors=True
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not store_manager.get_from_url("http://example.com").validate()
|
assert not await coresys.run_in_executor(
|
||||||
|
store_manager.get_from_url("http://example.com").validate
|
||||||
|
)
|
||||||
|
|
||||||
assert "http://example.com" in coresys.store.repository_urls
|
assert "http://example.com" in coresys.store.repository_urls
|
||||||
assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REMOVE
|
assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REMOVE
|
||||||
@ -176,11 +178,15 @@ async def test_preinstall_valid_repository(
|
|||||||
"""Test add core repository valid."""
|
"""Test add core repository valid."""
|
||||||
with patch("supervisor.store.repository.Repository.load", return_value=None):
|
with patch("supervisor.store.repository.Repository.load", return_value=None):
|
||||||
await store_manager.update_repositories(BUILTIN_REPOSITORIES)
|
await store_manager.update_repositories(BUILTIN_REPOSITORIES)
|
||||||
assert store_manager.get("core").validate()
|
|
||||||
assert store_manager.get("local").validate()
|
def validate():
|
||||||
assert store_manager.get("a0d7b954").validate()
|
assert store_manager.get("core").validate()
|
||||||
assert store_manager.get("5c53de3b").validate()
|
assert store_manager.get("local").validate()
|
||||||
assert store_manager.get("d5369777").validate()
|
assert store_manager.get("a0d7b954").validate()
|
||||||
|
assert store_manager.get("5c53de3b").validate()
|
||||||
|
assert store_manager.get("d5369777").validate()
|
||||||
|
|
||||||
|
await coresys.run_in_executor(validate)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("use_update", [True, False])
|
@pytest.mark.parametrize("use_update", [True, False])
|
||||||
|
@ -146,7 +146,9 @@ async def test_update_unavailable_addon(
|
|||||||
):
|
):
|
||||||
"""Test updating addon when new version not available for system."""
|
"""Test updating addon when new version not available for system."""
|
||||||
addon_config = dict(
|
addon_config = dict(
|
||||||
load_yaml_fixture("addons/local/ssh/config.yaml"),
|
await coresys.run_in_executor(
|
||||||
|
load_yaml_fixture, "addons/local/ssh/config.yaml"
|
||||||
|
),
|
||||||
version=AwesomeVersion("10.0.0"),
|
version=AwesomeVersion("10.0.0"),
|
||||||
**config,
|
**config,
|
||||||
)
|
)
|
||||||
@ -201,7 +203,9 @@ async def test_install_unavailable_addon(
|
|||||||
):
|
):
|
||||||
"""Test updating addon when new version not available for system."""
|
"""Test updating addon when new version not available for system."""
|
||||||
addon_config = dict(
|
addon_config = dict(
|
||||||
load_yaml_fixture("addons/local/ssh/config.yaml"),
|
await coresys.run_in_executor(
|
||||||
|
load_yaml_fixture, "addons/local/ssh/config.yaml"
|
||||||
|
),
|
||||||
version=AwesomeVersion("10.0.0"),
|
version=AwesomeVersion("10.0.0"),
|
||||||
**config,
|
**config,
|
||||||
)
|
)
|
||||||
|
@ -80,9 +80,11 @@ async def test_ingress_save_data(coresys: CoreSys, tmp_supervisor_data: Path):
|
|||||||
)
|
)
|
||||||
await ingress.save_data()
|
await ingress.save_data()
|
||||||
|
|
||||||
assert config_file.exists()
|
def get_config():
|
||||||
data = read_json_file(config_file)
|
assert config_file.exists()
|
||||||
assert data == {
|
return read_json_file(config_file)
|
||||||
|
|
||||||
|
assert await coresys.run_in_executor(get_config) == {
|
||||||
"session": {session: ANY},
|
"session": {session: ANY},
|
||||||
"session_data": {
|
"session_data": {
|
||||||
session: {"user": {"id": "123", "displayname": "Test", "username": "test"}}
|
session: {"user": {"id": "123", "displayname": "Test", "username": "test"}}
|
||||||
|
@ -55,7 +55,7 @@ async def test_apparmor_multiple_profiles(caplog: pytest.LogCaptureFixture):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_apparmor_profile_adjust(tmp_path: Path):
|
def test_apparmor_profile_adjust(tmp_path: Path):
|
||||||
"""Test apparmor profile adjust."""
|
"""Test apparmor profile adjust."""
|
||||||
profile_out = tmp_path / "apparmor_out.txt"
|
profile_out = tmp_path / "apparmor_out.txt"
|
||||||
adjust_profile("test", get_fixture_path("apparmor_valid.txt"), profile_out)
|
adjust_profile("test", get_fixture_path("apparmor_valid.txt"), profile_out)
|
||||||
@ -63,7 +63,7 @@ async def test_apparmor_profile_adjust(tmp_path: Path):
|
|||||||
assert profile_out.read_text(encoding="utf-8") == TEST_PROFILE
|
assert profile_out.read_text(encoding="utf-8") == TEST_PROFILE
|
||||||
|
|
||||||
|
|
||||||
async def test_apparmor_profile_adjust_mediate(tmp_path: Path):
|
def test_apparmor_profile_adjust_mediate(tmp_path: Path):
|
||||||
"""Test apparmor profile adjust when name matches a flag."""
|
"""Test apparmor profile adjust when name matches a flag."""
|
||||||
profile_out = tmp_path / "apparmor_out.txt"
|
profile_out = tmp_path / "apparmor_out.txt"
|
||||||
adjust_profile("test", get_fixture_path("apparmor_valid_mediate.txt"), profile_out)
|
adjust_profile("test", get_fixture_path("apparmor_valid_mediate.txt"), profile_out)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Test dbus utility."""
|
"""Test dbus utility."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
from dbus_fast import ErrorType
|
from dbus_fast import ErrorType
|
||||||
@ -48,9 +49,11 @@ async def fixture_test_service(dbus_session_bus: MessageBus) -> TestInterface:
|
|||||||
async def test_missing_properties_interface(dbus_session_bus: MessageBus):
|
async def test_missing_properties_interface(dbus_session_bus: MessageBus):
|
||||||
"""Test introspection missing properties interface."""
|
"""Test introspection missing properties interface."""
|
||||||
|
|
||||||
async def mock_introspect(*args, **kwargs):
|
def mock_introspect(*args, **kwargs):
|
||||||
"""Return introspection without properties."""
|
"""Return introspection without properties."""
|
||||||
return load_fixture("test_no_properties_interface.xml")
|
return asyncio.get_running_loop().run_in_executor(
|
||||||
|
None, load_fixture, "test_no_properties_interface.xml"
|
||||||
|
)
|
||||||
|
|
||||||
with patch.object(MessageBus, "introspect", new=mock_introspect):
|
with patch.object(MessageBus, "introspect", new=mock_introspect):
|
||||||
service = await DBus.connect(
|
service = await DBus.connect(
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from supervisor.bootstrap import initialize_coresys
|
from supervisor.bootstrap import initialize_coresys
|
||||||
|
|
||||||
|
|
||||||
async def test_sentry_disabled_by_default(supervisor_name):
|
@pytest.mark.usefixtures("supervisor_name", "docker")
|
||||||
|
async def test_sentry_disabled_by_default():
|
||||||
"""Test diagnostics off by default."""
|
"""Test diagnostics off by default."""
|
||||||
with (
|
with (
|
||||||
patch("supervisor.bootstrap.initialize_system"),
|
patch("supervisor.bootstrap.initialize_system"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user