modify harness framework
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import json
|
||||
from pathlib import Path, PureWindowsPath
|
||||
from typing import Any
|
||||
|
||||
from .discovery import DiscoveryError, validate_csproj
|
||||
from .models import DotnetConfig, HarnessConfig, TDDConfig
|
||||
|
||||
DEFAULT_TEST_PATTERNS = ("{stem}Tests.cs", "{stem}Test.cs")
|
||||
|
||||
|
||||
class ConfigError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def _mapping(value: Any, allowed: set[str], label: str) -> dict:
|
||||
if not isinstance(value, dict):
|
||||
raise ConfigError(f"{label} must be an object")
|
||||
unknown = sorted(set(value) - allowed)
|
||||
if unknown:
|
||||
raise ConfigError(f"{label} contains unexpected field: {unknown[0]}; use version 2 .NET configuration")
|
||||
return value
|
||||
|
||||
|
||||
def _string(value: Any, label: str) -> str:
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
raise ConfigError(f"{label} must be a non-empty string")
|
||||
return value
|
||||
|
||||
|
||||
def _array(value: Any, label: str, *, allow_empty=False) -> tuple[str, ...]:
|
||||
if not isinstance(value, list) or (not value and not allow_empty):
|
||||
raise ConfigError(f"{label} must be a {'non-empty ' if not allow_empty else ''}string array")
|
||||
return tuple(_string(item, label) for item in value)
|
||||
|
||||
|
||||
def _repo_path(root: Path, raw: str, label: str) -> Path:
|
||||
raw = _string(raw, label)
|
||||
windows = PureWindowsPath(raw)
|
||||
if windows.drive or windows.root or Path(raw).is_absolute():
|
||||
raise ConfigError(f"{label} must be repository-relative")
|
||||
resolved = (root / raw.replace("\\", "/")).resolve()
|
||||
if not resolved.is_relative_to(root):
|
||||
raise ConfigError(f"{label} resolves outside the repository")
|
||||
return resolved
|
||||
|
||||
|
||||
def load_config(root: Path) -> HarnessConfig:
|
||||
root = root.resolve()
|
||||
path = root / ".harness/config.json"
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8")) if path.exists() else {"version": 2}
|
||||
except (OSError, ValueError) as exc:
|
||||
raise ConfigError(f"cannot read configuration {path}: {exc}") from exc
|
||||
data = _mapping(data, {"version", "projectType", "dotnet", "tdd"}, "config")
|
||||
if type(data.get("version")) is not int or data["version"] != 2:
|
||||
raise ConfigError("version must be 2; rewrite legacy configuration using the .NET config example")
|
||||
project_type = data.get("projectType", "auto")
|
||||
if project_type not in ("auto", "dotnet"):
|
||||
raise ConfigError("projectType must be auto or dotnet")
|
||||
dotnet = _mapping(data.get("dotnet", {}), {"target", "configuration", "testProjects"}, "dotnet")
|
||||
target = _repo_path(root, dotnet["target"], "dotnet.target") if "target" in dotnet else None
|
||||
if target is not None and (target.suffix.lower() not in {".sln", ".csproj"} or not target.is_file()):
|
||||
raise ConfigError("dotnet.target must be an existing .sln or SDK-style .csproj")
|
||||
configuration = _string(dotnet.get("configuration", "Debug"), "dotnet.configuration")
|
||||
tests = tuple(_repo_path(root, item, "dotnet.testProjects") for item in _array(dotnet["testProjects"], "dotnet.testProjects")) if "testProjects" in dotnet else ()
|
||||
for test in tests:
|
||||
try:
|
||||
validate_csproj(test)
|
||||
except DiscoveryError as exc:
|
||||
raise ConfigError(f"dotnet.testProjects: {exc}") from exc
|
||||
tdd = _mapping(data.get("tdd", {}), {"testRoots", "testPatterns", "exclude"}, "tdd")
|
||||
roots = _array(tdd["testRoots"], "tdd.testRoots") if "testRoots" in tdd else ("tests",)
|
||||
test_roots = tuple(_repo_path(root, item, "tdd.testRoots") for item in roots)
|
||||
patterns = _array(tdd["testPatterns"], "tdd.testPatterns") if "testPatterns" in tdd else DEFAULT_TEST_PATTERNS
|
||||
for pattern in patterns:
|
||||
if "{stem}" not in pattern or not pattern.endswith(".cs") or any(char in pattern.replace("{stem}", "") for char in "{}/\\"):
|
||||
raise ConfigError("tdd.testPatterns must be C# filenames containing only the {stem} placeholder")
|
||||
exclude = _array(tdd["exclude"], "tdd.exclude", allow_empty=True) if "exclude" in tdd else ()
|
||||
for item in exclude:
|
||||
_repo_path(root, item, "tdd.exclude")
|
||||
return HarnessConfig(2, project_type, DotnetConfig(target, configuration, tests), TDDConfig(test_roots, patterns, exclude))
|
||||
Reference in New Issue
Block a user