120 lines
3.8 KiB
Python
120 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate that renderer conformance fixtures are registered and labeled consistently."""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
TESTS_CMAKE = REPO_ROOT / "tests" / "CMakeLists.txt"
|
|
|
|
REQUIRED_TEST_LABELS = {
|
|
"pp_renderer_api_tests": {"renderer-conformance", "renderer"},
|
|
}
|
|
|
|
OPTIONAL_BACKEND_TEST_LABELS = {
|
|
"pp_renderer_gl_capabilities_tests": {"renderer-conformance", "renderer"},
|
|
"pp_renderer_gl_command_plan_tests": {"renderer-conformance", "renderer"},
|
|
"pp_renderer_gl_gpu_readback_tests": {"renderer-conformance", "renderer", "gpu"},
|
|
}
|
|
|
|
def parse_labels() -> dict[str, set[str]]:
|
|
labels_by_test: dict[str, set[str]] = {}
|
|
text = TESTS_CMAKE.read_text(encoding="utf-8").splitlines()
|
|
i = 0
|
|
while i < len(text):
|
|
line = text[i].strip()
|
|
if not line.startswith("set_tests_properties("):
|
|
i += 1
|
|
continue
|
|
|
|
if "set_tests_properties(" not in line or "PROPERTIES" not in line:
|
|
i += 1
|
|
continue
|
|
after_paren = line.split("set_tests_properties(", 1)[1]
|
|
test_name = after_paren.split()[0].strip()
|
|
test_name = test_name.strip()
|
|
|
|
label_value: str | None = None
|
|
j = i
|
|
while j < len(text):
|
|
search = text[j].strip()
|
|
if search.startswith("LABELS"):
|
|
colon = search.find("\"")
|
|
if colon != -1:
|
|
value = search[colon:].strip()
|
|
if value.startswith("\"") and value.endswith("\""):
|
|
label_value = value[1:-1]
|
|
break
|
|
# Fallback for multiline values: LABELS "a;b"; split on quotes in line.
|
|
quotes = re.findall(r'"([^"]+)"', search)
|
|
if quotes:
|
|
label_value = quotes[0]
|
|
break
|
|
if search == ")" or (search.startswith(")") and "LABELS" not in search):
|
|
break
|
|
j += 1
|
|
|
|
if label_value is not None:
|
|
labels_by_test[test_name] = {label.strip() for label in label_value.split(";") if label.strip()}
|
|
|
|
i = j + 1
|
|
|
|
return labels_by_test
|
|
|
|
|
|
def validate() -> tuple[bool, list[dict[str, Any]]]:
|
|
labels_by_test = parse_labels()
|
|
test_names = set(labels_by_test)
|
|
violations: list[dict[str, Any]] = []
|
|
|
|
for test_name, required_labels in REQUIRED_TEST_LABELS.items():
|
|
actual = labels_by_test.get(test_name)
|
|
if actual is None:
|
|
violations.append({"test": test_name, "kind": "missing-test", "detail": "required conformance test not registered"})
|
|
continue
|
|
|
|
missing = sorted(required_labels - actual)
|
|
if missing:
|
|
violations.append(
|
|
{
|
|
"test": test_name,
|
|
"kind": "missing-label",
|
|
"detail": f"required labels missing: {', '.join(missing)}",
|
|
}
|
|
)
|
|
|
|
for test_name, required_labels in OPTIONAL_BACKEND_TEST_LABELS.items():
|
|
if test_name not in test_names:
|
|
continue
|
|
actual = labels_by_test[test_name]
|
|
missing = sorted(required_labels - actual)
|
|
if missing:
|
|
violations.append(
|
|
{
|
|
"test": test_name,
|
|
"kind": "missing-label",
|
|
"detail": f"required labels missing: {', '.join(missing)}",
|
|
}
|
|
)
|
|
|
|
return (len(violations) == 0), violations
|
|
|
|
|
|
def main() -> int:
|
|
ok, violations = validate()
|
|
payload = {
|
|
"ok": ok,
|
|
"summary": {
|
|
"requiredTestCount": len(REQUIRED_TEST_LABELS),
|
|
"violationCount": len(violations),
|
|
},
|
|
"violations": violations,
|
|
}
|
|
print(payload)
|
|
return 0 if ok else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|