add uncommitted files
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .models import CMakeConfig, HarnessConfig, MSBuildConfig, TDDConfig
|
||||
|
||||
DEFAULT_TEST_PATTERNS = (
|
||||
"{stem}_test.cpp",
|
||||
"{stem}_tests.cpp",
|
||||
"test_{stem}.cpp",
|
||||
"{stem}.test.cpp",
|
||||
)
|
||||
|
||||
|
||||
class ConfigError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def _reject_unknown(data: dict[str, Any], allowed: set[str], label: str) -> None:
|
||||
unknown = sorted(set(data) - allowed)
|
||||
if unknown:
|
||||
raise ConfigError(f"{label} contains unexpected field: {unknown[0]}")
|
||||
|
||||
|
||||
def _repo_path(root: Path, raw: str, label: str) -> Path:
|
||||
candidate = Path(raw)
|
||||
if candidate.is_absolute():
|
||||
raise ConfigError(f"{label} must be repository-relative")
|
||||
resolved = (root / candidate).resolve()
|
||||
try:
|
||||
resolved.relative_to(root)
|
||||
except ValueError as exc:
|
||||
raise ConfigError(f"{label} resolves outside the repository") from exc
|
||||
return resolved
|
||||
|
||||
|
||||
def _mapping(value: Any, label: str) -> dict[str, Any]:
|
||||
if not isinstance(value, dict):
|
||||
raise ConfigError(f"{label} must be an object")
|
||||
return value
|
||||
|
||||
|
||||
def _nonempty_string(value: Any, label: str) -> str:
|
||||
if not isinstance(value, str) or not value:
|
||||
raise ConfigError(f"{label} must be a non-empty string")
|
||||
return value
|
||||
|
||||
|
||||
def _string_array(value: Any, label: str) -> tuple[str, ...]:
|
||||
if not isinstance(value, list):
|
||||
raise ConfigError(f"{label} must be an array")
|
||||
if not value or any(not isinstance(item, str) or not item for item in value):
|
||||
raise ConfigError(f"{label} must be a non-empty string array")
|
||||
return tuple(value)
|
||||
|
||||
|
||||
def load_config(root: Path) -> HarnessConfig:
|
||||
root = root.resolve()
|
||||
config_path = root / ".harness" / "config.json"
|
||||
if config_path.is_file():
|
||||
try:
|
||||
data = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
except UnicodeDecodeError as exc:
|
||||
raise ConfigError(
|
||||
f"{config_path}: configuration must be valid UTF-8: {exc}"
|
||||
) from exc
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ConfigError(f"{config_path}: {exc}") from exc
|
||||
else:
|
||||
data = {"version": 1}
|
||||
|
||||
data = _mapping(data, "config")
|
||||
_reject_unknown(data, {"version", "projectType", "cmake", "msbuild", "tdd"}, "config")
|
||||
|
||||
if data.get("version") != 1 or isinstance(data.get("version"), bool):
|
||||
raise ConfigError("version must be 1")
|
||||
project_type = data.get("projectType", "auto")
|
||||
if project_type not in {"auto", "cmake", "msbuild"}:
|
||||
raise ConfigError("projectType must be auto, cmake, or msbuild")
|
||||
|
||||
cmake_data = _mapping(data.get("cmake", {}), "cmake")
|
||||
_reject_unknown(
|
||||
cmake_data,
|
||||
{"sourceDir", "binaryDir", "configurePreset", "buildPreset", "testPreset"},
|
||||
"cmake",
|
||||
)
|
||||
source_dir = _repo_path(
|
||||
root, _nonempty_string(cmake_data.get("sourceDir", "."), "cmake.sourceDir"), "cmake.sourceDir"
|
||||
)
|
||||
binary_dir = None
|
||||
if "binaryDir" in cmake_data:
|
||||
binary_dir = _repo_path(
|
||||
root, _nonempty_string(cmake_data["binaryDir"], "cmake.binaryDir"), "cmake.binaryDir"
|
||||
)
|
||||
preset_keys = ("configurePreset", "buildPreset", "testPreset", "binaryDir")
|
||||
if any(key in cmake_data for key in preset_keys) and not all(key in cmake_data for key in preset_keys):
|
||||
raise ConfigError("cmake configurePreset, buildPreset, testPreset, and binaryDir must be specified together")
|
||||
configure_preset = (
|
||||
_nonempty_string(cmake_data["configurePreset"], "cmake.configurePreset")
|
||||
if "configurePreset" in cmake_data
|
||||
else None
|
||||
)
|
||||
build_preset = (
|
||||
_nonempty_string(cmake_data["buildPreset"], "cmake.buildPreset")
|
||||
if "buildPreset" in cmake_data
|
||||
else None
|
||||
)
|
||||
test_preset = (
|
||||
_nonempty_string(cmake_data["testPreset"], "cmake.testPreset")
|
||||
if "testPreset" in cmake_data
|
||||
else None
|
||||
)
|
||||
|
||||
msbuild_data = _mapping(data.get("msbuild", {}), "msbuild")
|
||||
_reject_unknown(msbuild_data, {"solution", "configuration", "platform", "testCommand"}, "msbuild")
|
||||
solution = None
|
||||
if "solution" in msbuild_data:
|
||||
solution = _repo_path(
|
||||
root, _nonempty_string(msbuild_data["solution"], "msbuild.solution"), "msbuild.solution"
|
||||
)
|
||||
configuration = _nonempty_string(msbuild_data.get("configuration", "Debug"), "msbuild.configuration")
|
||||
platform = _nonempty_string(msbuild_data.get("platform", "x64"), "msbuild.platform")
|
||||
test_command = (
|
||||
_string_array(msbuild_data["testCommand"], "msbuild.testCommand")
|
||||
if "testCommand" in msbuild_data
|
||||
else None
|
||||
)
|
||||
|
||||
tdd_data = _mapping(data.get("tdd", {}), "tdd")
|
||||
_reject_unknown(tdd_data, {"testRoots", "testPatterns", "exclude"}, "tdd")
|
||||
raw_test_roots = _string_array(tdd_data["testRoots"], "tdd.testRoots") if "testRoots" in tdd_data else ("tests", "test")
|
||||
test_roots = tuple(_repo_path(root, item, "tdd.testRoots") for item in raw_test_roots)
|
||||
test_patterns = _string_array(tdd_data["testPatterns"], "tdd.testPatterns") if "testPatterns" in tdd_data else DEFAULT_TEST_PATTERNS
|
||||
if any("{stem}" not in pattern for pattern in test_patterns):
|
||||
raise ConfigError("every tdd.testPatterns entry must contain {stem}")
|
||||
exclude = _string_array(tdd_data["exclude"], "tdd.exclude") if "exclude" in tdd_data else ()
|
||||
for item in exclude:
|
||||
_repo_path(root, item, "tdd.exclude")
|
||||
|
||||
return HarnessConfig(
|
||||
version=1,
|
||||
project_type=project_type,
|
||||
cmake=CMakeConfig(source_dir, binary_dir, configure_preset, build_preset, test_preset),
|
||||
msbuild=MSBuildConfig(solution, configuration, platform, test_command),
|
||||
tdd=TDDConfig(test_roots, test_patterns, exclude),
|
||||
)
|
||||
Reference in New Issue
Block a user