core/script/hassfest/coverage.py

136 lines
3.9 KiB
Python

"""Validate coverage files."""
from __future__ import annotations
from pathlib import Path
from .model import Config, Integration
DONT_IGNORE = (
"config_flow.py",
"device_action.py",
"device_condition.py",
"device_trigger.py",
"diagnostics.py",
"group.py",
"intent.py",
"logbook.py",
"media_source.py",
"scene.py",
)
# They were violating when we introduced this check
# Need to be fixed in a future PR.
ALLOWED_IGNORE_VIOLATIONS = {
("advantage_air", "diagnostics.py"),
("androidtv", "diagnostics.py"),
("asuswrt", "diagnostics.py"),
("aussie_broadband", "diagnostics.py"),
("doorbird", "logbook.py"),
("ecowitt", "diagnostics.py"),
("elkm1", "scene.py"),
("fibaro", "scene.py"),
("hunterdouglas_powerview", "diagnostics.py"),
("hunterdouglas_powerview", "scene.py"),
("jellyfin", "media_source.py"),
("launch_library", "diagnostics.py"),
("lcn", "scene.py"),
("lifx_cloud", "scene.py"),
("lutron", "scene.py"),
("lutron_caseta", "scene.py"),
("nanoleaf", "diagnostics.py"),
("nanoleaf", "device_trigger.py"),
("nut", "diagnostics.py"),
("open_meteo", "diagnostics.py"),
("overkiz", "diagnostics.py"),
("overkiz", "scene.py"),
("philips_js", "diagnostics.py"),
("radio_browser", "media_source.py"),
("rfxtrx", "diagnostics.py"),
("screenlogic", "diagnostics.py"),
("sonos", "diagnostics.py"),
("stookalert", "diagnostics.py"),
("stookwijzer", "diagnostics.py"),
("synology_dsm", "diagnostics.py"),
("system_bridge", "media_source.py"),
("tractive", "diagnostics.py"),
("tuya", "diagnostics.py"),
("tuya", "scene.py"),
("upb", "scene.py"),
("velbus", "diagnostics.py"),
("velux", "scene.py"),
("verisure", "diagnostics.py"),
("vicare", "diagnostics.py"),
("xbox", "media_source.py"),
("xiaomi_miio", "diagnostics.py"),
("yale_smart_alarm", "diagnostics.py"),
}
def validate(integrations: dict[str, Integration], config: Config) -> None:
"""Validate coverage."""
coverage_path = config.root / ".coveragerc"
not_found: list[str] = []
checking = False
with coverage_path.open("rt") as fp:
for line in fp:
line = line.strip()
if not line or line.startswith("#"):
continue
if not checking:
if line == "omit =":
checking = True
continue
# Finished
if line == "[report]":
break
path = Path(line)
# Discard wildcard
path_exists = path
while "*" in path_exists.name:
path_exists = path_exists.parent
if not path_exists.exists():
not_found.append(line)
continue
if not line.startswith("homeassistant/components/") or len(path.parts) != 4:
continue
integration_path = path.parent
integration = integrations[integration_path.name]
if (
path.parts[-1] == "*"
and Path(f"tests/components/{integration.domain}/__init__.py").exists()
):
integration.add_error(
"coverage",
"has tests and should not use wildcard in .coveragerc file",
)
for check in DONT_IGNORE:
if path.parts[-1] not in {"*", check}:
continue
if (integration_path.name, check) in ALLOWED_IGNORE_VIOLATIONS:
continue
if (integration_path / check).exists():
integration.add_error(
"coverage",
f"{check} must not be ignored by the .coveragerc file",
)
if not_found:
raise RuntimeError(
f".coveragerc references files that don't exist: {', '.join(not_found)}."
)