feat(perception): resume full graph on fresh input without restarting models
This commit is contained in:
@@ -21,6 +21,67 @@ def pilot(monkeypatch):
|
||||
return lambda name: importlib.import_module(name)
|
||||
|
||||
|
||||
def test_full_graph_reset_replaces_temporal_state_not_models(pilot):
|
||||
module = pilot("pilot_graph")
|
||||
root = Path(__file__).resolve().parents[1] / "config/perception"
|
||||
graph = module.JointGraph.__new__(module.JointGraph)
|
||||
graph.surface = module.K1LocalSurfaceShadowEstimator()
|
||||
graph.store = module.CurrentStore(
|
||||
module.load_geometry_profile(root / "m4-geometry-association-v1.json")
|
||||
)
|
||||
profile = module.load_temporal_motion_profile(root / "m4-temporal-motion-v1.json")
|
||||
graph.temporal = module.BoundedSpatialTemporalProvider(
|
||||
point_resolver=graph.store, profile=profile
|
||||
)
|
||||
graph.motion = module.ClassIndependentMotionEstimator(profile=profile)
|
||||
graph.rolling = module.RollingLocalObstacleMapProvider(
|
||||
pose_resolver=graph.store,
|
||||
profile=module.load_rolling_map_profile(root / "m4-rolling-local-map-v1.json"),
|
||||
)
|
||||
graph.threat = module.DualEvidenceReplayThreatProvider(
|
||||
body_frame_resolver=graph.store,
|
||||
profile=module.load_replay_threat_profile(root / "m4-replay-threat-v3.json"),
|
||||
)
|
||||
graph.temporal_resets = 0
|
||||
graph.backend = graph.detector = graph.tgs = model = object()
|
||||
for cache in (
|
||||
graph.surface._cache,
|
||||
graph.store.body_history,
|
||||
graph.temporal._components,
|
||||
graph.rolling._cells,
|
||||
):
|
||||
cache["old"] = object()
|
||||
graph.surface._previous_surface = (1, 2, 3, 4)
|
||||
graph.temporal._previous_sequence = graph.rolling._previous_sequence = 32
|
||||
old_store = graph.store
|
||||
state = graph.reset_temporal()
|
||||
assert set(state["previous"].values()) == {1}
|
||||
assert set(state["current"].values()) == {0}
|
||||
assert graph.temporal._previous_sequence is graph.rolling._previous_sequence is None
|
||||
assert graph.surface._previous_surface is None and graph.temporal_resets == 1
|
||||
assert graph.store is not old_store
|
||||
assert (
|
||||
graph.temporal.point_resolver
|
||||
is graph.rolling.pose_resolver
|
||||
is graph.threat.body_frame_resolver
|
||||
is graph.store
|
||||
)
|
||||
assert graph.backend is graph.detector is graph.tgs is model
|
||||
assert graph.temporal.profile is graph.motion.profile is profile
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"values", [["0:1"], ["10:0"], ["10:5001"], ["a:1"], ["10:1", "9:1"], ["1:1"] * 5]
|
||||
)
|
||||
def test_source_gap_plan_is_bounded(pilot, values):
|
||||
with pytest.raises(ValueError):
|
||||
pilot("pilot_binary_source").input_gaps(values)
|
||||
|
||||
|
||||
def test_source_gap_plan_preserves_explicit_sequence_and_duration(pilot):
|
||||
assert pilot("pilot_binary_source").input_gaps(["16:150", "72:2200"]) == [(16, 150), (72, 2200)]
|
||||
|
||||
|
||||
def test_pending_overflow_is_explicit_and_does_not_evict_active(pilot):
|
||||
queue = pilot("pilot_queue").Mailbox(capacity=2, byte_limit=100)
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ def test_active_old_callback_cannot_publish_or_be_freed_early(harness):
|
||||
run.check_current(run.start) # In-flight IPC can drain without poisoning the child.
|
||||
with pytest.raises(StreamSuspended):
|
||||
run.begin_input(run.start)
|
||||
with pytest.raises(ValueError, match="still owns"):
|
||||
with pytest.raises(StreamSuspended, match="still active"):
|
||||
run.begin_input(run.start)
|
||||
run.mailbox.release(packet, discard_reason="input-gap")
|
||||
epoch = run.begin_input(run.start)
|
||||
|
||||
Reference in New Issue
Block a user