test updates
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
def hello() -> str:
|
def hello() -> str:
|
||||||
return "Hello from ad-test!"
|
return 'Hello from ad-test!'
|
||||||
|
|||||||
@@ -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'},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +47,11 @@ def ad(base_config: AppDaemonConfig):
|
|||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -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}'
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user