Files
home-assistant-operating-sy…/tests/smoke_test/test_basic.py
Jan Čermák a5906f52e3 Make tests involving reboot more robust (#4131)
The tests that are involving reboots are flaky and fail when waiting for the
command to return or when waiting for a new login prompt. To mitigate this, do
not use run_check, as it needs the shell prompt to reappear, and instead use
sendline and wait up to a minute for the GRUB message.

(cherry picked from commit 9803f5fb4f)
2025-07-03 17:05:44 +02:00

145 lines
5.4 KiB
Python

import logging
from time import sleep
import pytest
_LOGGER = logging.getLogger(__name__)
@pytest.mark.dependency()
@pytest.mark.timeout(120)
def test_init(shell):
def check_container_running(container_name):
out = shell.run_check(
f"docker container inspect -f '{{{{.State.Status}}}}' {container_name} || true"
)
return "running" in out
# wait for important containers first
while True:
if check_container_running("homeassistant") and check_container_running("hassio_supervisor"):
break
sleep(1)
# wait for system ready
while True:
output = "\n".join(shell.run_check("ha os info || true"))
if "System is not ready" not in output:
break
sleep(1)
output = shell.run_check("ha os info")
_LOGGER.info("%s", "\n".join(output))
def test_rauc_status(shell, shell_json):
rauc_status = shell.run_check("rauc status --output-format=shell --detailed")
# RAUC_BOOT_PRIMARY won't be set if correct grub env is missing
assert "RAUC_BOOT_PRIMARY='kernel.0'" in rauc_status
assert "rauc-WARNING" not in "\n".join(rauc_status)
os_info = shell_json("ha os info --no-progress --raw-json")
expected_version = os_info.get("data", {}).get("version")
assert expected_version is not None and expected_version != ""
boot_slots = filter(lambda x: "RAUC_SYSTEM_SLOTS=" in x, rauc_status)
boot_slots = next(boot_slots, "").replace("RAUC_SYSTEM_SLOTS='", "").replace("'", "")
assert boot_slots != ""
booted_idx = boot_slots.split(" ").index("kernel.0")
assert booted_idx >= 0
assert f"RAUC_SLOT_STATUS_BUNDLE_VERSION_{booted_idx + 1}='{expected_version}'" in rauc_status
def test_dmesg(shell):
output = shell.run_check("dmesg")
_LOGGER.info("%s", "\n".join(output))
@pytest.mark.dependency(depends=["test_init"])
def test_supervisor_logs(shell):
output = shell.run_check("ha su logs")
_LOGGER.info("%s", "\n".join(output))
@pytest.mark.dependency(depends=["test_init"])
def test_landing_page(shell):
web_index = shell.run_check("curl http://localhost:8123")
assert "</html>" in " ".join(web_index)
def test_systemctl_status(shell):
output = shell.run_check("systemctl --no-pager -l status -a || true")
_LOGGER.info("%s", "\n".join(output))
def test_systemctl_check_no_failed(shell):
output = shell.run_check("systemctl --no-pager -l list-units --state=failed")
assert "0 loaded units listed." in output, f"Some units failed:\n{"\n".join(output)}"
def test_systemctl_no_cycles(shell):
# we don't have systemd-analyze available, so check it naively using grep
output = shell.run_check("journalctl -b0 | grep 'ordering cycle' || true")
assert not output, f"Found Systemd dependency cycles:\n{"\n".join(output)}"
def test_host_connectivity(shell):
output = shell.run_check("curl -f https://checkonline.home-assistant.io/online.txt")
assert "NetworkManager is online" in output
output = shell.run_check("nmcli network connectivity check")
assert "full" in output, f"Connectivity check failed, nmcli reports: {output}"
@pytest.mark.dependency(depends=["test_init"])
@pytest.mark.timeout(10)
def test_supervisor_connectivity(shell):
# checks URL used by connectivity checks via docker0 bridge
output = shell.run_check("docker exec -ti hassio_supervisor curl -f https://checkonline.home-assistant.io/online.txt")
assert "NetworkManager is online" in output
@pytest.mark.dependency(depends=["test_init"])
@pytest.mark.timeout(10)
def test_hassio_connectivity(shell):
# checks URL used by connectivity checks via hassio bridge
output = shell.run_check("docker exec -ti hassio_cli curl -f https://checkonline.home-assistant.io/online.txt")
assert "NetworkManager is online" in output
@pytest.mark.dependency(depends=["test_init"])
def test_custom_swap_size(shell, target):
output = shell.run_check("stat -c '%s' /mnt/data/swapfile")
# set new swap size to half of the previous size - round to 4k blocks
new_swap_size = (int(output[0]) // 2 // 4096) * 4096
shell.console.sendline(f"echo 'SWAPSIZE={new_swap_size/1024/1024}M' > /etc/default/haos-swapfile; reboot")
shell.console.expect("Booting `Slot ", timeout=60)
# reactivate ShellDriver to handle login again
target.deactivate(shell)
target.activate(shell)
output = shell.run_check("stat -c '%s' /mnt/data/swapfile")
assert int(output[0]) == new_swap_size, f"Incorrect swap size {new_swap_size}B: {output}"
@pytest.mark.dependency(depends=["test_custom_swap_size"])
def test_no_swap(shell, target):
shell.console.sendline("echo 'SWAPSIZE=0' > /etc/default/haos-swapfile; reboot")
shell.console.expect("Booting `Slot ", timeout=60)
# reactivate ShellDriver to handle login again
target.deactivate(shell)
target.activate(shell)
output = shell.run_check("systemctl --no-pager -l list-units --state=failed")
assert "0 loaded units listed." in output, f"Some units failed:\n{"\n".join(output)}"
swapon = shell.run_check("swapon --show")
assert swapon == [], f"Swapfile still exists: {swapon}"
def test_kernel_not_tainted(shell):
"""Check if the kernel is not tainted - do it at the end of the
test suite to increase the chance of catching issues."""
output = shell.run_check("cat /proc/sys/kernel/tainted")
assert "\n".join(output) == "0", f"Kernel tainted: {output}"