test updates

This commit is contained in:
John Lancaster
2024-09-04 23:50:57 -05:00
parent b673bda1f2
commit 7a8e6f383a
6 changed files with 89 additions and 29 deletions

View File

@@ -20,6 +20,9 @@ build-backend = "hatchling.build"
managed = true managed = true
dev-dependencies = [] dev-dependencies = []
[tool.ruff.format]
quote-style = "single"
[tool.hatch.metadata] [tool.hatch.metadata]
allow-direct-references = true allow-direct-references = true

View File

@@ -1,2 +1,2 @@
def hello() -> str: def hello() -> str:
return "Hello from ad-test!" return 'Hello from ad-test!'

View File

@@ -38,7 +38,7 @@ def base_config() -> AppDaemonConfig:
time_zone='America/Chicago', time_zone='America/Chicago',
config_dir=CONFIG_DIR, config_dir=CONFIG_DIR,
config_file='appdaemon.yaml', config_file='appdaemon.yaml',
module_debug={'_app_management': 'DEBUG'} module_debug={'_app_management': 'DEBUG'},
) )
@@ -48,6 +48,10 @@ def ad(base_config: AppDaemonConfig):
ad = AppDaemon(Logging(), loop, base_config) ad = AppDaemon(Logging(), loop, base_config)
import sys
sys.path.insert(0, (CONFIG_DIR / 'apps/food-repo/src').as_posix())
for cfg in ad.logging.config.values(): for cfg in ad.logging.config.values():
logger = logging.getLogger(cfg['name']) logger = logging.getLogger(cfg['name'])
logger.propagate = True logger.propagate = True

View File

@@ -3,16 +3,14 @@ import logging
import re import re
from pathlib import Path from pathlib import Path
import food.menu
import pytest import pytest
from appdaemon.appdaemon import AppDaemon from appdaemon.appdaemon import AppDaemon
from git import Repo from git import Repo
from . import utils
from .utils import ( from .utils import (
count_error_lines,
get_app_orders, get_app_orders,
get_load_order, get_load_order,
get_loaded_apps,
reset_file, reset_file,
) )
@@ -20,6 +18,8 @@ INDENT = ' ' * 4
def test_file(ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo): def test_file(ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo):
import food.menu
module_file = Path(food.menu.__file__) module_file = Path(food.menu.__file__)
file_content = module_file.read_text() file_content = module_file.read_text()
@@ -40,7 +40,11 @@ def test_file(ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo
reset_file(config_repo, module_file) reset_file(config_repo, module_file)
def test_file_with_error(ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo): def test_file_with_error(
ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo
):
import food.menu
module_file = Path(food.menu.__file__) module_file = Path(food.menu.__file__)
file_content = module_file.read_text().splitlines() file_content = module_file.read_text().splitlines()
@@ -61,23 +65,38 @@ def test_file_with_error(ad: AppDaemon, caplog: pytest.LogCaptureFixture, config
reset_file(config_repo, module_file) reset_file(config_repo, module_file)
def test_modification_child(ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo): def test_modification_child(
ad: AppDaemon, caplog: pytest.LogCaptureFixture, config_repo: Repo
):
assert isinstance(ad, AppDaemon) assert isinstance(ad, AppDaemon)
ad.loop.run_until_complete(asyncio.sleep(2.5))
module_file = Path(__file__).parents[2] / 'conf/apps/family/child.py' module_file = Path(__file__).parents[2] / 'conf/apps/family/child.py'
file_content = module_file.read_text() utils.inject_into_file(module_file, 2, 'raise ImportError')
file_content += (INDENT * 2) + "self.log(f'Modified {self.__class__.__name__}')\n"
module_file.write_text(file_content)
try: try:
with caplog.at_level(logging.DEBUG): with caplog.at_level(logging.DEBUG):
ad.loop.run_until_complete(asyncio.sleep(1.0)) ad.loop.run_until_complete(asyncio.sleep(1.0))
assert count_error_lines(caplog) == 0 assert "Error importing 'child'" in caplog.text
assert get_loaded_apps(caplog) == {'child'}
assert get_app_orders(caplog, 'stop') == ['child', 'parent', 'grand-parent'] load_order = utils.get_load_order(caplog)
assert get_app_orders(caplog, 'start') == ['child', 'parent', 'grand-parent'] assert load_order == ['child', 'sibling'] # sibling is a variant of child
stopping = get_app_orders(caplog, 'stop', ordered=False)
assert stopping is not None, 'Failed to get app stop order'
assert stopping == {'child_app', 'sibling_app'}
# start_order = get_app_orders(caplog, 'start')
# assert start_order is None
# assert start_order == ['child', 'parent', 'grand-parent'], f'Wrong app start order: {start_order}'
finally: finally:
reset_file(config_repo, module_file) reset_file(config_repo, module_file)
ad.loop.run_until_complete(asyncio.sleep(1.0)) ad.loop.run_until_complete(asyncio.sleep(1.0))
start_order = get_app_orders(caplog, 'start', ordered=False)
assert start_order == {
'child_app',
'sibling_app',
}, f'Wrong app start order: {start_order}'

View File

@@ -5,7 +5,7 @@ from typing import List
import pytest import pytest
from appdaemon.appdaemon import AppDaemon from appdaemon.appdaemon import AppDaemon
from .utils import get_load_order, count_error_lines from .utils import count_error_lines, get_load_order
def validate_app_dependencies(ad: AppDaemon): def validate_app_dependencies(ad: AppDaemon):

View File

@@ -1,7 +1,7 @@
import asyncio import asyncio
import re import re
from pathlib import Path from pathlib import Path
from typing import List from typing import Generator, List, Literal
import pytest import pytest
from appdaemon.appdaemon import AppDaemon from appdaemon.appdaemon import AppDaemon
@@ -13,23 +13,47 @@ async def delayed_stop(ad: AppDaemon, delay: float):
ad.stop() ad.stop()
def get_load_order(caplog: pytest.LogCaptureFixture) -> List[str]: def get_load_order(
for record in caplog.records: caplog: pytest.LogCaptureFixture, ordered: bool = True
if record.name == 'AppDaemon._app_management': ) -> Generator[str, None, None]:
if 'Determined module load order' in record.msg: records = (
return record.args[0] record.args[0]
for record in get_logger_records(caplog, 'AppDaemon._app_management')
if 'Determined module load order' in record.msg
)
try:
result = list(records)[-1]
if not ordered:
result = set(result)
return result
except Exception:
return
def get_logger_records(caplog: pytest.LogCaptureFixture, name: str): def get_logger_records(caplog: pytest.LogCaptureFixture, logger_name: str):
for record in caplog.records: for record in caplog.records:
if record.name == name: if record.name == logger_name:
yield record yield record
def get_app_orders(caplog: pytest.LogCaptureFixture, phase: str) -> List[str]: def get_app_orders(
for record in get_logger_records(caplog, 'AppDaemon._app_management'): caplog: pytest.LogCaptureFixture,
if re.search(f'App {phase} order', record.msg): phase: Literal['start', 'stop'],
return record.args[0] ordered: bool = True,
) -> list[str]:
"""Extracts the app start/stop order from the captured log lines"""
records = (
record.args[0]
for record in get_logger_records(caplog, 'AppDaemon._app_management')
if re.search(f'App {phase} order', record.msg)
)
try:
result = list(records)[-1]
if not ordered:
result = set(result)
return result
except Exception:
return
def get_loaded_apps(caplog: pytest.LogCaptureFixture): def get_loaded_apps(caplog: pytest.LogCaptureFixture):
@@ -39,7 +63,9 @@ def get_loaded_apps(caplog: pytest.LogCaptureFixture):
def count_error_lines(caplog: pytest.LogCaptureFixture) -> int: def count_error_lines(caplog: pytest.LogCaptureFixture) -> int:
error_log_lines = [msg for name, lvl, msg in caplog.record_tuples if name.startswith('Error')] error_log_lines = [
msg for name, lvl, msg in caplog.record_tuples if name.startswith('Error')
]
return len(error_log_lines) return len(error_log_lines)
@@ -47,3 +73,11 @@ def reset_file(repo: Repo, changed: Path):
if not changed.is_absolute(): if not changed.is_absolute():
changed = Path(repo.working_tree_dir) / changed changed = Path(repo.working_tree_dir) / changed
repo.git.checkout('HEAD', '--', changed) repo.git.checkout('HEAD', '--', changed)
def inject_into_file(file: Path, pos: int, line: str):
content = file.read_text()
lines = content.splitlines()
lines.insert(pos, line)
new_content = '\n'.join(lines)
file.write_text(new_content)