[AZ-270] [AZ-272] [AZ-279] [AZ-281] [AZ-283] Compose root + FDR schema + 3 Layer-1 helpers

AZ-270: composition root with strategy registry, tier-gated lookup,
topo-order construction, all-or-nothing teardown, StrategyNotLinkedError
payload.
AZ-272: orjson-backed FdrRecord serialise/parse with forward-compat for
unknown payload + top-level fields and canonical overrun-record shape.
AZ-279: pyproj-backed WGS84/ECEF/ENU + OSM slippy-map tile math with
WgsConversionError for shape/range/zoom guards.
AZ-281: strict EngineFilenameSchema build/parse/matches_host with
anchored regex + enum validation; round-trip identity by construction.
AZ-283: dtype-preserving (fp16/fp32) single + batch L2 normaliser with
zero-norm safety and descriptor_metric() source-of-truth.
pyproject.toml pins pyproj>=3.6 and orjson>=3.9 (named-backend deps per
the AZ-272 / AZ-279 contracts). New DTOs LatLonAlt + BoundingBox and
EngineCacheKey + HostCapabilities land in _types/ to back the helper
contracts.
203 unit tests pass (64 new). Review verdict: PASS_WITH_WARNINGS;
findings are perf-NFR deferrals + dep amendment + minor docstring polish.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-11 02:03:36 +03:00
parent 8e71f6c002
commit 3acc7f33dd
24 changed files with 2381 additions and 97 deletions
+270
View File
@@ -0,0 +1,270 @@
"""AZ-270 — Composition Root AC tests.
Verifies the contract at ``_docs/02_document/contracts/shared_config/composition_root_protocol.md`` v1.0.0.
"""
from __future__ import annotations
import ast
from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path
import pytest
from gps_denied_onboard.config import Config
from gps_denied_onboard.runtime_root import (
RuntimeRoot,
StrategyNotLinkedError,
clear_strategy_registry,
compose_operator,
compose_root,
list_registered_strategies,
register_strategy,
)
_REPO_ROOT = Path(__file__).resolve().parents[2]
@dataclass(frozen=True)
class _C1Block:
strategy: str = "okvis2"
@dataclass(frozen=True)
class _C4Block:
strategy: str = "opencv_gtsam"
@dataclass(frozen=True)
class _C5Block:
strategy: str = "gtsam_isam2"
@dataclass(frozen=True)
class _C11Block:
strategy: str = "ardupilot_tile_manager"
@dataclass
class _OrderRecorder:
constructed: list[str]
@pytest.fixture(autouse=True)
def _isolated_registry() -> Iterator[None]:
"""Reset the strategy registry around every test."""
clear_strategy_registry()
yield
clear_strategy_registry()
@pytest.fixture
def _airborne_env(monkeypatch: pytest.MonkeyPatch) -> None:
for name, value in (
("GPS_DENIED_FC_PROFILE", "ardupilot_plane"),
("GPS_DENIED_TIER", "1"),
("DB_URL", "postgresql+psycopg://gps_denied:dev@db:5432/gps_denied"),
("CAMERA_CALIBRATION_PATH", "/etc/gps-denied/calib.yml"),
("LOG_LEVEL", "INFO"),
("LOG_SINK", "console"),
("INFERENCE_BACKEND", "pytorch_fp16"),
("FDR_PATH", "/var/lib/gps-denied/fdr"),
("TILE_CACHE_PATH", "/var/lib/gps-denied/tiles"),
("MAVLINK_SIGNING_KEY", "ZZZZZZZZ"),
):
monkeypatch.setenv(name, value)
@pytest.fixture
def _operator_env(monkeypatch: pytest.MonkeyPatch) -> None:
for name, value in (
("GPS_DENIED_FC_PROFILE", "ardupilot_plane"),
("GPS_DENIED_TIER", "1"),
("DB_URL", "postgresql+psycopg://gps_denied:dev@db:5432/gps_denied"),
("CAMERA_CALIBRATION_PATH", "/etc/gps-denied/calib.yml"),
("LOG_LEVEL", "INFO"),
("LOG_SINK", "console"),
("INFERENCE_BACKEND", "pytorch_fp16"),
("FDR_PATH", "/var/lib/gps-denied/fdr"),
("TILE_CACHE_PATH", "/var/lib/gps-denied/tiles"),
("SATELLITE_PROVIDER_URL", "http://localhost:8080"),
):
monkeypatch.setenv(name, value)
def _register_okvis2(recorder: _OrderRecorder) -> None:
def factory(config: Config, components: dict[str, object]) -> object:
recorder.constructed.append("c1_vio")
return ("c1_vio", "okvis2")
register_strategy("c1_vio", "okvis2", factory, tier="airborne")
def _register_c4(recorder: _OrderRecorder) -> None:
def factory(config: Config, components: dict[str, object]) -> object:
recorder.constructed.append("c4_pose")
return ("c4_pose", "opencv_gtsam")
register_strategy("c4_pose", "opencv_gtsam", factory, tier="airborne")
def _register_c5(recorder: _OrderRecorder) -> None:
def factory(config: Config, components: dict[str, object]) -> object:
assert "c1_vio" in components, "c5_state factory ran before c1_vio existed"
assert "c4_pose" in components, "c5_state factory ran before c4_pose existed"
recorder.constructed.append("c5_state")
return ("c5_state", "gtsam_isam2")
register_strategy(
"c5_state",
"gtsam_isam2",
factory,
tier="airborne",
depends_on=("c1_vio", "c4_pose"),
)
def test_ac1_default_deployment_composes(_airborne_env: None) -> None:
recorder = _OrderRecorder(constructed=[])
_register_okvis2(recorder)
_register_c4(recorder)
_register_c5(recorder)
config = Config.with_blocks(
c1_vio=_C1Block(),
c4_pose=_C4Block(),
c5_state=_C5Block(),
)
root = compose_root(config)
assert isinstance(root, RuntimeRoot)
assert root.binary == "airborne"
assert set(root.components.keys()) == {"c1_vio", "c4_pose", "c5_state"}
def test_ac2_strategy_not_linked_raises_with_payload(_airborne_env: None) -> None:
# Only okvis2 is registered; config asks for vins_mono.
recorder = _OrderRecorder(constructed=[])
_register_okvis2(recorder)
config = Config.with_blocks(c1_vio=_C1Block(strategy="vins_mono"))
with pytest.raises(StrategyNotLinkedError) as info:
compose_root(config)
assert info.value.strategy_name == "vins_mono"
assert info.value.component_slug == "c1_vio"
assert info.value.available_strategies == ["okvis2"]
def test_ac3_operator_excludes_airborne_only(_operator_env: None) -> None:
# c1_vio is registered as airborne; an operator config that references it must fail.
recorder = _OrderRecorder(constructed=[])
_register_okvis2(recorder)
config = Config.with_blocks(c1_vio=_C1Block())
with pytest.raises(StrategyNotLinkedError) as info:
compose_operator(config)
assert info.value.component_slug == "c1_vio"
assert "airborne" in info.value.reason or "tier" in info.value.reason
def test_ac4_runtime_root_smoke_exit_zero(_airborne_env: None) -> None:
# A Config with no component blocks must compose cleanly (every required
# component is hard-wired by its bootstrap; no strategy to resolve).
config = Config()
root = compose_root(config)
assert isinstance(root, RuntimeRoot)
assert root.components == {}
def test_ac5_construction_order_respects_dependencies(_airborne_env: None) -> None:
recorder = _OrderRecorder(constructed=[])
# Register in reverse order to make the topological pass non-trivial.
_register_c5(recorder)
_register_c4(recorder)
_register_okvis2(recorder)
config = Config.with_blocks(
c1_vio=_C1Block(),
c4_pose=_C4Block(),
c5_state=_C5Block(),
)
root = compose_root(config)
# Dependencies must construct strictly before dependents.
assert recorder.constructed.index("c1_vio") < recorder.constructed.index("c5_state")
assert recorder.constructed.index("c4_pose") < recorder.constructed.index("c5_state")
assert root.construction_order[-1] == "c5_state"
def test_ac6_only_compose_root_imports_concrete_strategies() -> None:
"""Architecture lint: no module under ``components.*`` imports another component's concrete strategy.
We accept only:
* the composition root (``runtime_root.py``);
* per-component public re-exports inside the component's own subpackage
(e.g. ``components.c5_state`` importing ``components.c5_state.interface``).
Imports across components (e.g. ``components.c5_state`` importing
``components.c1_vio.okvis2``) are violations.
"""
components_root = _REPO_ROOT / "src" / "gps_denied_onboard" / "components"
violations: list[str] = []
for module_path in components_root.rglob("*.py"):
own_component = module_path.relative_to(components_root).parts[0]
try:
tree = ast.parse(module_path.read_text(encoding="utf-8"))
except SyntaxError:
continue
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
if node.module.startswith("gps_denied_onboard.components."):
referenced = node.module.split(".")[2]
if referenced != own_component:
violations.append(
f"{module_path.relative_to(_REPO_ROOT)} imports {node.module}"
)
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith("gps_denied_onboard.components."):
referenced = alias.name.split(".")[2]
if referenced != own_component:
violations.append(
f"{module_path.relative_to(_REPO_ROOT)} imports {alias.name}"
)
assert not violations, (
"components.* may not import other components — only the composition root may; "
f"violations: {violations}"
)
def test_nfr_reliability_partial_construction_closed_on_failure(_airborne_env: None) -> None:
closed: list[str] = []
class _Closable:
def __init__(self, slug: str) -> None:
self.slug = slug
def close(self) -> None:
closed.append(self.slug)
def good_factory(config: Config, components: dict[str, object]) -> _Closable:
return _Closable("c1_vio")
def failing_factory(config: Config, components: dict[str, object]) -> object:
raise RuntimeError("boom from c5_state factory")
register_strategy("c1_vio", "okvis2", good_factory, tier="airborne")
register_strategy(
"c5_state",
"gtsam_isam2",
failing_factory,
tier="airborne",
depends_on=("c1_vio",),
)
config = Config.with_blocks(c1_vio=_C1Block(), c5_state=_C5Block())
with pytest.raises(RuntimeError, match=r"boom"):
compose_root(config)
assert closed == ["c1_vio"], "prior instances must be .close()d on mid-composition failure"
def test_list_registered_strategies_returns_sorted_names() -> None:
register_strategy("c1_vio", "okvis2", lambda c, m: None, tier="airborne")
register_strategy("c1_vio", "vins_mono", lambda c, m: None, tier="airborne")
register_strategy("c2_vpr", "netvlad", lambda c, m: None, tier="airborne")
assert list_registered_strategies("c1_vio") == ["okvis2", "vins_mono"]
assert list_registered_strategies("c2_vpr") == ["netvlad"]
assert list_registered_strategies("c99_unknown") == []
+256
View File
@@ -0,0 +1,256 @@
"""AZ-272 — FdrRecord schema + versioned serialiser AC tests.
Verifies the contract at ``_docs/02_document/contracts/shared_fdr_client/fdr_record_schema.md`` v1.0.0.
"""
from __future__ import annotations
import ast
from pathlib import Path
import orjson
import pytest
from gps_denied_onboard.fdr_client import (
CURRENT_SCHEMA_VERSION,
KNOWN_KINDS,
MAX_INLINE_BLOB_BYTES,
OVERRUN_KIND,
OVERRUN_PRODUCER_ID,
FdrRecord,
FdrSchemaError,
parse,
serialise,
)
_TS = "2026-05-11T00:00:00.000000Z"
def _kind_payload(kind: str) -> dict[str, object]:
"""Return a minimal valid payload for each v1.0.0 kind."""
if kind == "log":
return {
"level": "INFO",
"component": "c2_vpr",
"frame_id": 42,
"kind": "vpr.warmup",
"msg": "loaded",
"kv": {"model": "salad"},
}
if kind == "vio.tick":
return {
"frame_id": 1,
"R": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
"t": [0.0, 0.0, 0.0],
"P": [[1.0]],
"last_anchor_age_ms": 100,
}
if kind == "state.tick":
return {
"frame_id": 1,
"fused_pose": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"covariance_2x2": [[1.0, 0.0], [0.0, 1.0]],
"estimator_label": "primary",
}
if kind == "tile_match":
return {
"frame_id": 1,
"tile_id": "tile-001",
"score": 0.85,
"match_count": 124,
"ransac_inliers": 96,
}
if kind == "overrun":
return {"producer_id": "c1_vio", "dropped_count": 42}
if kind == "segment_rollover":
return {
"old_segment": "seg-0001",
"new_segment": "seg-0002",
"total_bytes_after": 1024 * 1024,
}
if kind == "failed_tile_thumbnail":
return {"frame_id": 1, "tile_id": "tile-002", "jpeg_bytes_b64": "AAAAAA=="}
if kind == "mid_flight_tile_snapshot":
return {"snapshot_path": "/var/lib/gps-denied/snap.dat", "captured_at": _TS}
if kind == "flight_header":
return {
"flight_id": "f-0001",
"started_at": _TS,
"schema_version": CURRENT_SCHEMA_VERSION,
"build_info": {"commit": "abc123"},
}
if kind == "flight_footer":
return {
"flight_id": "f-0001",
"ended_at": _TS,
"records_written": 12345,
"records_dropped": 0,
}
raise AssertionError(f"unhandled kind in fixture: {kind!r}")
def _make_record(kind: str, **overrides: object) -> FdrRecord:
"""Build a minimal valid record for ``kind`` so each AC test can mutate the field it cares about."""
if kind == OVERRUN_KIND:
producer_id = overrides.pop("producer_id", OVERRUN_PRODUCER_ID)
else:
producer_id = overrides.pop("producer_id", "c2_vpr")
return FdrRecord(
schema_version=int(overrides.pop("schema_version", CURRENT_SCHEMA_VERSION)),
ts=str(overrides.pop("ts", _TS)),
producer_id=str(producer_id),
kind=str(overrides.pop("kind", kind)),
payload=dict(overrides.pop("payload", _kind_payload(kind))), # type: ignore[arg-type]
)
@pytest.mark.parametrize("kind", sorted(KNOWN_KINDS))
def test_ac1_roundtrip_every_known_kind(kind: str) -> None:
# Arrange
record = _make_record(kind)
# Act
decoded = parse(serialise(record))
# Assert
assert decoded == record
def test_ac2_forward_compatible_unknown_payload_field_preserved() -> None:
# Arrange — synthesise a future v1.1 record adding an unknown payload field.
payload = _kind_payload("log") | {"new_field": "x"}
wire = orjson.dumps(
{
"schema_version": 1,
"ts": _TS,
"producer_id": "c2_vpr",
"kind": "log",
"payload": payload,
}
)
# Act
record = parse(wire)
# Assert
assert record.payload.get("extra", {}).get("new_field") == "x"
assert "new_field" not in record.payload
# Known fields still parse out cleanly.
assert record.payload["msg"] == "loaded"
def test_ac2b_forward_compatible_unknown_top_level_field_preserved() -> None:
wire = orjson.dumps(
{
"schema_version": 1,
"ts": _TS,
"producer_id": "c2_vpr",
"kind": "log",
"payload": _kind_payload("log"),
"trailing": "future-field",
}
)
record = parse(wire)
assert record.extra == {"trailing": "future-field"}
def test_ac3_unknown_future_kind_returned_opaquely() -> None:
wire = orjson.dumps(
{
"schema_version": 1,
"ts": _TS,
"producer_id": "future.producer",
"kind": "future.kind",
"payload": {"foo": 1},
}
)
record = parse(wire)
assert record.kind == "future.kind"
assert record.payload == {"foo": 1}
assert record.extra == {}
def test_ac4_missing_schema_version_raises() -> None:
wire = orjson.dumps(
{
"ts": _TS,
"producer_id": "c2_vpr",
"kind": "log",
"payload": _kind_payload("log"),
}
)
with pytest.raises(FdrSchemaError, match=r"schema_version"):
parse(wire)
def test_ac4_non_integer_schema_version_raises() -> None:
wire = orjson.dumps(
{
"schema_version": "1.0",
"ts": _TS,
"producer_id": "c2_vpr",
"kind": "log",
"payload": _kind_payload("log"),
}
)
with pytest.raises(FdrSchemaError, match=r"schema_version"):
parse(wire)
def test_ac5_overrun_missing_dropped_count_rejected_on_parse() -> None:
wire = orjson.dumps(
{
"schema_version": 1,
"ts": _TS,
"producer_id": OVERRUN_PRODUCER_ID,
"kind": OVERRUN_KIND,
"payload": {"producer_id": "c1_vio"},
}
)
with pytest.raises(FdrSchemaError, match=r"dropped_count"):
parse(wire)
def test_ac5_overrun_zero_dropped_count_rejected_on_serialise() -> None:
record = _make_record(OVERRUN_KIND, payload={"producer_id": "c1_vio", "dropped_count": 0})
with pytest.raises(FdrSchemaError, match=r"dropped_count"):
serialise(record)
def test_ac6_empty_producer_id_rejected_on_serialise() -> None:
record = _make_record("log", producer_id="")
with pytest.raises(FdrSchemaError, match=r"producer_id"):
serialise(record)
def test_nfr_oversized_inline_blob_rejected() -> None:
blob = b"\x00" * (MAX_INLINE_BLOB_BYTES + 1)
payload = _kind_payload("failed_tile_thumbnail") | {"jpeg_bytes": blob}
record = _make_record("failed_tile_thumbnail", payload=payload)
with pytest.raises(FdrSchemaError, match=r"sidecar path"):
serialise(record)
def test_nfr_serialise_is_pure_byte_identical() -> None:
record = _make_record("log")
first = serialise(record)
second = serialise(record)
assert first == second
def test_no_upward_imports_to_components() -> None:
"""Layer-0/cross-cutting: ``fdr_client.records`` must not import any component."""
module_path = (
Path(__file__).resolve().parents[2]
/ "src"
/ "gps_denied_onboard"
/ "fdr_client"
/ "records.py"
)
tree = ast.parse(module_path.read_text(encoding="utf-8"))
bad: list[str] = []
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
if node.module.startswith("gps_denied_onboard.components"):
bad.append(node.module)
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith("gps_denied_onboard.components"):
bad.append(alias.name)
assert not bad, f"fdr_client.records must not import components.*; found: {bad}"
+122
View File
@@ -0,0 +1,122 @@
"""AZ-279 — WgsConverter helper AC tests.
Verifies the contract at ``_docs/02_document/contracts/shared_helpers/wgs_converter.md`` v1.0.0.
"""
from __future__ import annotations
import ast
import math
from pathlib import Path
import numpy as np
import pytest
from gps_denied_onboard._types.geo import BoundingBox, LatLonAlt
from gps_denied_onboard.helpers import (
MAX_ZOOM,
WEB_MERCATOR_MAX_LAT_DEG,
WgsConversionError,
WgsConverter,
)
def test_ac1_ecef_roundtrip() -> None:
# Arrange
samples = [
LatLonAlt(50.0, 30.0, 100.0),
LatLonAlt(-33.9, 151.2, 25.0),
LatLonAlt(0.0, 0.0, 0.0),
LatLonAlt(80.0, -120.0, 1500.0),
LatLonAlt(-60.0, 179.999, -50.0),
]
# Act / Assert
for p in samples:
ecef = WgsConverter.latlonalt_to_ecef(p)
back = WgsConverter.ecef_to_latlonalt(ecef)
assert math.isclose(back.lat_deg, p.lat_deg, abs_tol=1e-9), (back, p)
assert math.isclose(back.lon_deg, p.lon_deg, abs_tol=1e-9), (back, p)
assert math.isclose(back.alt_m, p.alt_m, abs_tol=1e-6), (back, p)
def test_ac2_enu_roundtrip_within_10_km() -> None:
origin = LatLonAlt(50.0, 30.0, 100.0)
# ~10 km away in NE direction
p = LatLonAlt(50.07, 30.10, 250.0)
enu = WgsConverter.latlonalt_to_local_enu(origin, p)
back = WgsConverter.local_enu_to_latlonalt(origin, enu)
horizontal_m = math.hypot(
(back.lat_deg - p.lat_deg) * 111_320.0,
(back.lon_deg - p.lon_deg) * 111_320.0 * math.cos(math.radians(p.lat_deg)),
)
vertical_m = abs(back.alt_m - p.alt_m)
assert horizontal_m < 1.0, f"horizontal residual {horizontal_m} m > 1 m"
assert vertical_m < 0.01, f"vertical residual {vertical_m} m > 1 cm"
def test_ac3_slippy_map_tile_roundtrip_z18_contains_input() -> None:
zoom, lat, lon = 18, 50.45, 30.52
x, y = WgsConverter.latlon_to_tile_xy(zoom, lat, lon)
bounds = WgsConverter.tile_xy_to_latlon_bounds(zoom, x, y)
assert isinstance(bounds, BoundingBox)
assert bounds.contains(lat, lon)
# OSM-pinned reference for (lat=50.45, lon=30.52, z=18); precomputed via
# the slippy-map formula and matching satellite-provider's on-disk layout.
assert (x, y) == (153295, 88392)
def test_ac4_web_mercator_latitude_range_guard() -> None:
with pytest.raises(WgsConversionError, match=r"Web-Mercator"):
WgsConverter.latlon_to_tile_xy(18, 95.0, 0.0)
def test_ac5_zoom_range_guard() -> None:
with pytest.raises(WgsConversionError, match=r"zoom"):
WgsConverter.latlon_to_tile_xy(MAX_ZOOM + 3, 50.0, 30.0)
with pytest.raises(WgsConversionError, match=r"zoom"):
WgsConverter.tile_xy_to_latlon_bounds(MAX_ZOOM + 3, 0, 0)
def test_ac6_tile_xy_range_guard() -> None:
with pytest.raises(WgsConversionError, match=r"tile"):
WgsConverter.tile_xy_to_latlon_bounds(18, 1 << 18, 0)
def test_ac7_ecef_shape_contract() -> None:
with pytest.raises(WgsConversionError, match=r"shape"):
WgsConverter.ecef_to_latlonalt(np.array([1.0, 2.0], dtype=np.float64))
def test_ac8_determinism_byte_equal_outputs() -> None:
p = LatLonAlt(50.0, 30.0, 100.0)
first = WgsConverter.latlonalt_to_ecef(p)
second = WgsConverter.latlonalt_to_ecef(p)
assert first.tobytes() == second.tobytes()
def test_ac9_no_upward_imports_to_components() -> None:
module_path = (
Path(__file__).resolve().parents[2]
/ "src"
/ "gps_denied_onboard"
/ "helpers"
/ "wgs_converter.py"
)
tree = ast.parse(module_path.read_text(encoding="utf-8"))
bad: list[str] = []
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
if node.module.startswith("gps_denied_onboard.components"):
bad.append(node.module)
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith("gps_denied_onboard.components"):
bad.append(alias.name)
assert not bad, f"wgs_converter must not import components.*; found: {bad}"
def test_invariant_web_mercator_max_lat_close_to_documented_value() -> None:
# Sanity bound: documented constant matches the Mercator-projection-valid
# latitude (arctan(sinh(pi))) within rounding.
expected = math.degrees(math.atan(math.sinh(math.pi)))
assert math.isclose(WEB_MERCATOR_MAX_LAT_DEG, expected, abs_tol=1e-9)
@@ -0,0 +1,100 @@
"""AZ-281 — EngineFilenameSchema helper AC tests.
Verifies the contract at ``_docs/02_document/contracts/shared_helpers/engine_filename_schema.md`` v1.0.0.
"""
from __future__ import annotations
import ast
import random
from pathlib import Path
import pytest
from gps_denied_onboard._types.manifests import EngineCacheKey, HostCapabilities
from gps_denied_onboard.helpers import EngineFilenameSchema, EngineFilenameSchemaError
def test_ac1_reference_example_builds_exact_string() -> None:
assert (
EngineFilenameSchema.build("ultravpr", 87, "6.2", "10.3", "fp16")
== "ultravpr__sm87_jp6.2_trt10.3_fp16.engine"
)
def test_ac2_roundtrip_identity_over_10_random_tuples() -> None:
rng = random.Random(2026)
for _ in range(10):
model = rng.choice(["ultravpr", "netvlad", "megaloc", "selavpr", "salad", "mix_vpr"])
sm = rng.choice([72, 86, 87])
jp = rng.choice(["5.1", "6.0", "6.2", "6.3"])
trt = rng.choice(["8.6", "10.0", "10.3", "10.4"])
prec = rng.choice(["fp16", "int8", "mixed"])
filename = EngineFilenameSchema.build(model, sm, jp, trt, prec)
parsed = EngineFilenameSchema.parse(filename)
assert parsed == EngineCacheKey(model, sm, jp, trt, prec)
def test_ac3_matches_host_exact_match() -> None:
filename = EngineFilenameSchema.build("ultravpr", 87, "6.2", "10.3", "fp16")
host = HostCapabilities(sm=87, jetpack="6.2", trt="10.3")
assert EngineFilenameSchema.matches_host(filename, host) is True
def test_ac4_matches_host_tuple_mismatch_returns_false() -> None:
filename = EngineFilenameSchema.build("ultravpr", 87, "6.2", "10.3", "fp16")
host_mismatch = HostCapabilities(sm=72, jetpack="6.2", trt="10.3")
assert EngineFilenameSchema.matches_host(filename, host_mismatch) is False
host_mismatch_trt = HostCapabilities(sm=87, jetpack="6.2", trt="10.4")
assert EngineFilenameSchema.matches_host(filename, host_mismatch_trt) is False
def test_ac5_precision_enum_strictness() -> None:
with pytest.raises(EngineFilenameSchemaError, match=r"precision"):
EngineFilenameSchema.build("ultravpr", 87, "6.2", "10.3", "bf16")
def test_ac6_model_name_character_set_rejection() -> None:
with pytest.raises(EngineFilenameSchemaError, match=r"a-z0-9_"):
EngineFilenameSchema.build("UltraVPR", 87, "6.2", "10.3", "fp16")
def test_ac7_reserved_separator_collision_rejected() -> None:
with pytest.raises(EngineFilenameSchemaError, match=r"__"):
EngineFilenameSchema.build("ultra__vpr", 87, "6.2", "10.3", "fp16")
def test_ac8_three_segment_version_rejected() -> None:
with pytest.raises(EngineFilenameSchemaError, match=r"major.*minor"):
EngineFilenameSchema.build("ultravpr", 87, "6.2.1", "10.3", "fp16")
def test_ac9_parse_rejects_malformed_filename() -> None:
with pytest.raises(EngineFilenameSchemaError):
EngineFilenameSchema.parse("not_an_engine_file.engine")
def test_ac10_parse_requires_engine_suffix() -> None:
with pytest.raises(EngineFilenameSchemaError, match=r"\.engine"):
EngineFilenameSchema.parse("ultravpr__sm87_jp6.2_trt10.3_fp16")
def test_ac11_no_upward_imports_to_components() -> None:
module_path = (
Path(__file__).resolve().parents[2]
/ "src"
/ "gps_denied_onboard"
/ "helpers"
/ "engine_filename_schema.py"
)
tree = ast.parse(module_path.read_text(encoding="utf-8"))
bad: list[str] = []
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
if node.module.startswith("gps_denied_onboard.components"):
bad.append(node.module)
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith("gps_denied_onboard.components"):
bad.append(alias.name)
assert not bad, f"engine_filename_schema must not import components.*; found: {bad}"
@@ -0,0 +1,122 @@
"""AZ-283 — DescriptorNormaliser helper AC tests.
Verifies the contract at ``_docs/02_document/contracts/shared_helpers/descriptor_normaliser.md`` v1.0.0.
"""
from __future__ import annotations
import ast
from pathlib import Path
import numpy as np
import pytest
from gps_denied_onboard.helpers import DescriptorNormaliser, DescriptorNormaliserError
def test_ac1_unit_vector_example() -> None:
out = DescriptorNormaliser.l2_normalise(np.array([3.0, 4.0], dtype=np.float32))
np.testing.assert_allclose(out, np.array([0.6, 0.8], dtype=np.float32), atol=1e-6)
assert float(np.linalg.norm(out)) == pytest.approx(1.0, abs=1e-6)
def test_ac2_batch_normalisation() -> None:
batch = np.array([[3.0, 4.0], [1.0, 0.0]], dtype=np.float32)
out = DescriptorNormaliser.l2_normalise_batch(batch)
np.testing.assert_allclose(out[0], np.array([0.6, 0.8], dtype=np.float32), atol=1e-6)
np.testing.assert_allclose(out[1], np.array([1.0, 0.0], dtype=np.float32), atol=1e-6)
for row in out:
assert float(np.linalg.norm(row)) == pytest.approx(1.0, abs=1e-6)
def test_ac3_fp16_dtype_preservation() -> None:
rng = np.random.default_rng(2026)
x = rng.standard_normal(512).astype(np.float16)
out = DescriptorNormaliser.l2_normalise(x)
assert out.dtype == np.float16
assert float(np.linalg.norm(out.astype(np.float32))) == pytest.approx(1.0, abs=1e-3)
def test_ac4_fp32_dtype_preservation() -> None:
rng = np.random.default_rng(2026)
x = rng.standard_normal(512).astype(np.float32)
out = DescriptorNormaliser.l2_normalise(x)
assert out.dtype == np.float32
assert float(np.linalg.norm(out)) == pytest.approx(1.0, abs=1e-6)
def test_ac5_zero_vector_handling() -> None:
zeros = np.zeros(128, dtype=np.float32)
out = DescriptorNormaliser.l2_normalise(zeros)
np.testing.assert_array_equal(out, zeros)
assert not np.any(np.isnan(out))
def test_ac5b_zero_row_in_batch_remains_zero() -> None:
batch = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]], dtype=np.float32)
out = DescriptorNormaliser.l2_normalise_batch(batch)
np.testing.assert_array_equal(out[0], np.zeros(3, dtype=np.float32))
np.testing.assert_allclose(out[1], np.array([1.0, 0.0, 0.0], dtype=np.float32))
def test_ac6_idempotence_fp32() -> None:
rng = np.random.default_rng(2026)
x = rng.standard_normal(64).astype(np.float32)
once = DescriptorNormaliser.l2_normalise(x)
twice = DescriptorNormaliser.l2_normalise(once)
assert once.tobytes() == twice.tobytes()
def test_ac7_idempotence_fp16_within_half_precision_tol() -> None:
rng = np.random.default_rng(2026)
x = rng.standard_normal(64).astype(np.float16)
once = DescriptorNormaliser.l2_normalise(x)
twice = DescriptorNormaliser.l2_normalise(once)
np.testing.assert_allclose(twice.astype(np.float32), once.astype(np.float32), atol=1e-3)
def test_ac8_no_in_place_mutation() -> None:
x = np.array([3.0, 4.0, 0.0], dtype=np.float32)
snapshot = x.copy()
_ = DescriptorNormaliser.l2_normalise(x)
np.testing.assert_array_equal(x, snapshot)
def test_ac9_metric_is_inner_product_exact_string() -> None:
assert DescriptorNormaliser.descriptor_metric() == "inner_product"
def test_ac10_float64_dtype_rejected() -> None:
with pytest.raises(DescriptorNormaliserError, match=r"float16.*float32|float32.*float16"):
DescriptorNormaliser.l2_normalise(np.array([1.0, 2.0], dtype=np.float64))
def test_ac11_shape_contract_single_rejects_2d() -> None:
with pytest.raises(DescriptorNormaliserError, match=r"1-D"):
DescriptorNormaliser.l2_normalise(np.zeros((2, 3), dtype=np.float32))
def test_ac11_shape_contract_batch_rejects_1d() -> None:
with pytest.raises(DescriptorNormaliserError, match=r"2-D"):
DescriptorNormaliser.l2_normalise_batch(np.zeros(128, dtype=np.float32))
def test_ac12_no_upward_imports_to_components() -> None:
module_path = (
Path(__file__).resolve().parents[2]
/ "src"
/ "gps_denied_onboard"
/ "helpers"
/ "descriptor_normaliser.py"
)
tree = ast.parse(module_path.read_text(encoding="utf-8"))
bad: list[str] = []
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
if node.module.startswith("gps_denied_onboard.components"):
bad.append(node.module)
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith("gps_denied_onboard.components"):
bad.append(alias.name)
assert not bad, f"descriptor_normaliser must not import components.*; found: {bad}"
+9 -3
View File
@@ -1,7 +1,13 @@
"""Runtime-root env-var fail-fast — AZ-263 AC-8."""
"""Runtime-root env-var fail-fast — AZ-263 AC-8 (updated by AZ-270 to pass a Config).
AZ-270 swapped ``compose_root()`` to ``compose_root(config: Config)``; the
env-var fail-fast still happens inside ``compose_root`` before any factory
construction, so this AC-8 contract is intact.
"""
import pytest
from gps_denied_onboard.config import Config
from gps_denied_onboard.runtime_root import ConfigurationError, compose_root
@@ -23,7 +29,7 @@ def test_compose_root_fails_fast_on_missing_required(monkeypatch: pytest.MonkeyP
# Act / Assert
with pytest.raises(ConfigurationError) as excinfo:
compose_root()
compose_root(Config())
assert "Missing required environment variable" in str(excinfo.value)
@@ -45,7 +51,7 @@ def test_compose_root_names_the_first_missing_var(monkeypatch: pytest.MonkeyPatc
# Act
with pytest.raises(ConfigurationError) as excinfo:
compose_root()
compose_root(Config())
# Assert
msg = str(excinfo.value)