From 614022defbef8b491d6516d1ab64fcf737a789b2 Mon Sep 17 00:00:00 2001 From: Isaac Barham Date: Fri, 4 Sep 2026 11:51:24 -0400 Subject: [PATCH] test(ford): retain queued commands in turn-release regression Continue the same allocator through a short turn and cancellation so release checks retain command lead as well as nominal coefficient state. Reject earlier geometry buildup that keeps charging after cancellation. No production controller changes. Assisted-by: OpenAI Codex --- .../controls/tests/test_ford_shared_path.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/openpilot/selfdrive/controls/tests/test_ford_shared_path.py b/openpilot/selfdrive/controls/tests/test_ford_shared_path.py index 0a4b6fc897..62a4a9f9f1 100644 --- a/openpilot/selfdrive/controls/tests/test_ford_shared_path.py +++ b/openpilot/selfdrive/controls/tests/test_ford_shared_path.py @@ -59,6 +59,24 @@ class TestSharedRequest(unittest.TestCase): class TestContributionAllocator(unittest.TestCase): + def test_short_turn_release_preserves_command_history_not_just_coefficient_state(self): + for sign in (-1, 1): + allocator = ContributionAllocator(initial_state=(0.0, 0.0, 0.0)) + preferred = FordPath(True, sign * 3.5, sign * 0.5, 0.0) + requested = sum(contributions((preferred.path_offset, preferred.path_angle, 0.0), 8.0)) + for _ in range(30): + allocator.allocate(requested, preferred, 8.0) + allocator.advance(allocator.dt) + initial_total = sign * sum(contributions(allocator.state, 8.0)) + # Continue the same allocator. Recreating it with command=state hides + # queued commands that can keep building after the request disappears. + for _ in range(30): + command = allocator.allocate(0.0, FordPath(True), 8.0) + self.assertEqual(command.curvature, 0.0) + allocator.advance(allocator.dt) + self.assertLessEqual(sign * sum(contributions(allocator.state, 8.0)), initial_total + allocator.tolerance(8.0)) + self.assertLessEqual(abs(sum(contributions(allocator.state, 8.0))), allocator.tolerance(8.0)) + def test_nominal_plateau_does_not_erase_requested_fast_geometry(self): allocator = ContributionAllocator(initial_state=(0.0, 0.0, 0.0)) preferred = FordPath(True, 2.0, 0.12, 0.0, 0.0)