import ast from dataclasses import dataclass, field from enum import Enum from pathlib import Path class FixtureClass(str, Enum): UNIT = "unit" MOCK_APP = "mock_app" LIVE_GUI = "live_gui" HEADLESS = "headless" OPT_IN = "opt_in" PERFORMANCE = "performance" class Speed(str, Enum): FAST = "fast" MEDIUM = "medium" SLOW = "slow" VERY_SLOW = "very_slow" @dataclass(frozen=True) class CategoryRecord: filename: str fixture_class: FixtureClass subsystems: list[str] speed: Speed batch_group: str notes: str = "" test_order: dict[str, int] = field(default_factory=dict) source: str = "auto" warnings: list[str] = field(default_factory=list) _OPT_IN_PREFIXES: tuple[str, ...] = ("test_clean_install", "test_docker_build") _PERF_KEYWORDS: tuple[str, ...] = ("perf", "stress", "phase_3_final", "phase_4_stress") _FIXTURE_ARGS: dict[FixtureClass, frozenset[str]] = { FixtureClass.LIVE_GUI: frozenset({"live_gui"}), FixtureClass.MOCK_APP: frozenset({"mock_app", "app_instance"}), } def _fixture_args_used(source: str) -> set[str]: try: tree = ast.parse(source) except SyntaxError: return set() found: set[str] = set() for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): found.update(a.arg for a in node.args.args) found.update(a.arg for a in node.args.kwonlyargs) found.update(a.arg for a in node.args.posonlyargs) return found def _classify_fixture_class(path: Path, source: str) -> FixtureClass: name = path.name for prefix in _OPT_IN_PREFIXES: if name.startswith(prefix): return FixtureClass.OPT_IN args = _fixture_args_used(source) for fc, fixture_names in _FIXTURE_ARGS.items(): if args & fixture_names: return fc lowered = name.lower() for kw in _PERF_KEYWORDS: if kw in lowered: return FixtureClass.PERFORMANCE return FixtureClass.UNIT def auto_classify(path: Path, durations: dict[str, float] | None = None) -> CategoryRecord: source = path.read_text(encoding="utf-8", errors="replace") fixture_class = _classify_fixture_class(path, source) return CategoryRecord( filename=path.name, fixture_class=fixture_class, subsystems=[], speed=Speed.MEDIUM, batch_group="", source="auto", )