Skip to content

Commit a9c2261

Browse files
Merge branch 'act4' of https://github.com/riscv-non-isa/riscv-arch-test into boot
2 parents 3267bcc + fe87aaf commit a9c2261

2 files changed

Lines changed: 56 additions & 10 deletions

File tree

framework/src/act/act.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from act.build_plan import generate_build_plan
2121
from act.config import CoverageSimulator, load_config
2222
from act.coverreport import print_coverage_summary
23-
from act.parse_test_constraints import generate_test_dict
23+
from act.parse_test_constraints import TestYamlHeaderError, generate_test_dict
2424
from act.parse_udb_config import generate_udb_files, get_config_params, get_implemented_extensions
2525
from act.select_tests import select_tests
2626

@@ -92,7 +92,11 @@ def run_act(
9292
workdir = workdir.absolute()
9393

9494
# Generate test list
95-
full_test_dict = generate_test_dict(test_dir, extensions, exclude)
95+
try:
96+
full_test_dict = generate_test_dict(test_dir, extensions, exclude)
97+
except TestYamlHeaderError as e:
98+
e.print()
99+
raise typer.Exit(1) from None
96100

97101
config_names: list[str] = []
98102
tasks: list[BuildTask] = []

framework/src/act/parse_test_constraints.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,31 @@
99

1010
from pathlib import Path
1111

12-
from pydantic import BaseModel, Field, FilePath
12+
from pydantic import BaseModel, Field, FilePath, ValidationError
13+
from rich.console import Console
14+
from rich.panel import Panel
1315
from ruamel.yaml import YAML
16+
from ruamel.yaml.error import YAMLError
17+
18+
19+
class TestYamlHeaderError(Exception):
20+
"""Raised when a test file's YAML config header is missing, malformed, or fails validation."""
21+
22+
def __init__(self, file: Path, problem: str) -> None:
23+
self.file = file
24+
self.problem = problem
25+
super().__init__(f"Malformed {file.name} YAML header: {problem} ({file})")
26+
27+
def print(self) -> None:
28+
"""Render this error as a formatted panel on stderr."""
29+
body = (
30+
f"[bold red]Malformed YAML header in[/] [underline]{self.file.name}[/]\n\n"
31+
f"[bold]Problem:[/] {self.problem}\n"
32+
f"[bold]File:[/] [cyan]{self.file}[/]"
33+
)
34+
Console(stderr=True).print(
35+
Panel(body, title="[bold red]Test YAML Header Error[/]", border_style="red", expand=False)
36+
)
1437

1538

1639
class TestMetadata(BaseModel):
@@ -55,6 +78,21 @@ def e_ext(self) -> bool:
5578
return self.march.startswith(("rv32e", "rv64e", "rv${XLEN}e"))
5679

5780

81+
def _describe_validation_error(err: ValidationError) -> str:
82+
"""Translate the first Pydantic error into a short human-readable error."""
83+
e = err.errors()[0]
84+
field = ".".join(str(p) for p in e["loc"]) or "<root>"
85+
etype = e["type"]
86+
got = e.get("input")
87+
if etype == "extra_forbidden":
88+
return f"unexpected key '{field}' found"
89+
if etype == "missing":
90+
return f"required key '{field}' is missing"
91+
if etype.startswith(("string_pattern_mismatch", "value_error")):
92+
return f"illegal value for key '{field}': {got!r}"
93+
return f"invalid value for key '{field}': {e['msg']}"
94+
95+
5896
def extract_yaml_config(file: Path) -> TestMetadata:
5997
"""Extract YAML configuration from a test file between START_TEST_CONFIG and END_TEST_CONFIG markers."""
6098
content = file.read_text()
@@ -67,7 +105,7 @@ def extract_yaml_config(file: Path) -> TestMetadata:
67105
end_pos = content.find(end_marker)
68106

69107
if start_pos == -1 or end_pos == -1:
70-
raise ValueError(f"Could not find YAML config section in {file}")
108+
raise TestYamlHeaderError(file, f"missing {start_marker}/{end_marker} markers")
71109

72110
# Extract content between markers
73111
start_pos = content.find("\n", start_pos) + 1 # Skip to next line after start marker
@@ -76,15 +114,19 @@ def extract_yaml_config(file: Path) -> TestMetadata:
76114
yaml_section = content[start_pos:end_pos]
77115

78116
# Process lines to remove comment prefixes
79-
yaml_lines: list[str] = []
80-
for line in yaml_section.split("\n"):
81-
line = line.lstrip("#")
82-
yaml_lines.append(line)
117+
yaml_lines = [line.lstrip("#") for line in yaml_section.split("\n")]
83118
yaml_lines.append(f" test_path: '{file.absolute()}'") # Add test_path to config data
84119

85120
yaml = YAML(typ="safe", pure=True)
86-
config_dict = yaml.load("\n".join(yaml_lines))
87-
return TestMetadata.model_validate(config_dict)
121+
try:
122+
config_dict = yaml.load("\n".join(yaml_lines))
123+
except YAMLError as e:
124+
raise TestYamlHeaderError(file, f"YAML parse error: {e}") from None
125+
126+
try:
127+
return TestMetadata.model_validate(config_dict)
128+
except ValidationError as e:
129+
raise TestYamlHeaderError(file, _describe_validation_error(e)) from None
88130

89131

90132
def generate_test_dict(tests_dir: Path, extensions: str, exclude: str = "") -> dict[str, TestMetadata]:

0 commit comments

Comments
 (0)