mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
58427d61d9
Select qualified speed-limit sources, preserve acceptance and override decisions, and publish distinct control and display coordinates. Carry accepted and pending source identity with each displayed decision. Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
119 lines
5.9 KiB
Python
119 lines
5.9 KiB
Python
"""The adopted SI schedule, legacy zero tail and invalid-source boundaries."""
|
|
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
from openpilot.common.params import Params
|
|
from openpilot.starpilot.speed_limits import offset_document as od
|
|
from openpilot.starpilot.speed_limits.runtime_settings import parse, read_params
|
|
|
|
|
|
def offset_at(schedule, speed):
|
|
return next((band.offset_mps for band in schedule.bands
|
|
if band.lower_mps <= speed and (band.upper_mps is None or speed < band.upper_mps)), None)
|
|
|
|
|
|
class OffsetDocumentTests(unittest.TestCase):
|
|
def test_all_legacy_edges_and_frozen_zero_tail(self):
|
|
epsilon = 0.00001
|
|
for metric, bounds, conversion in ((False, od.IMPERIAL_BOUNDS, od.MPH_TO_MPS),
|
|
(True, od.METRIC_BOUNDS, od.KPH_TO_MPS)):
|
|
with self.subTest(metric=metric):
|
|
values = {'SpeedLimitController': True, 'IsMetric': metric,
|
|
**{f'Offset{i}': float(i) for i in range(1, 8)}}
|
|
schedule = parse(values).offsets
|
|
self.assertEqual(len(schedule.bands), 8)
|
|
for index in range(1, 8):
|
|
edge = bounds[index]
|
|
with self.subTest(edge=edge):
|
|
self.assertAlmostEqual(offset_at(schedule, edge - epsilon), index * conversion)
|
|
expected = (index + 1) * conversion if index < 7 else 0.0
|
|
self.assertAlmostEqual(offset_at(schedule, edge), expected)
|
|
self.assertAlmostEqual(offset_at(schedule, edge + epsilon), expected)
|
|
self.assertEqual(schedule.bands[-1].lower_mps, bounds[-1])
|
|
self.assertEqual(schedule.bands[-1].offset_mps, 0.0)
|
|
|
|
def test_adopted_document_is_sole_numeric_authority(self):
|
|
document = od.adopt_legacy(False, (0, 1, -2, 3, 4, 5, 7))
|
|
encoded = od.encode(document)
|
|
self.assertEqual(od.decode(encoded), document)
|
|
expected = document.schedule()
|
|
for metric in (False, True, b'corrupt'):
|
|
settings = parse({'SpeedLimitController': True, 'SLCOffsetSchedule': document,
|
|
'IsMetric': metric, 'Offset3': float('nan'), 'Offset7': 'bad'})
|
|
self.assertTrue(settings.enabled)
|
|
self.assertEqual(settings.offsets, expected)
|
|
self.assertEqual(offset_at(expected, od.IMPERIAL_BOUNDS[-1]), 0.0)
|
|
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
params = Params(directory)
|
|
params.put_bool('SpeedLimitController', True, block=True)
|
|
Path(params.get_param_path('SLCOffsetSchedule')).write_bytes(encoded)
|
|
Path(params.get_param_path('IsMetric')).write_bytes(b'bad')
|
|
Path(params.get_param_path('Offset3')).write_bytes(b'bad')
|
|
original_read = Path.read_bytes
|
|
def no_legacy_read(path):
|
|
if path.name == 'IsMetric' or path.name.startswith('Offset'):
|
|
raise AssertionError(f'adopted runtime read legacy numeric source: {path.name}')
|
|
return original_read(path)
|
|
with mock.patch.object(Path, 'read_bytes', no_legacy_read):
|
|
self.assertEqual(read_params(params).offsets, expected)
|
|
self.assertTrue(read_params(params).enabled)
|
|
|
|
def test_needs_review_marker_prevents_legacy_reactivation(self):
|
|
self.assertIs(od.decode(od.encode(od.NEEDS_REVIEW)), od.NEEDS_REVIEW)
|
|
settings = parse({'SpeedLimitController': True, 'SLCOffsetSchedule': od.NEEDS_REVIEW})
|
|
self.assertFalse(settings.enabled)
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
params = Params(directory)
|
|
params.put_bool('SpeedLimitController', True, block=True)
|
|
Path(params.get_param_path('SLCOffsetSchedule')).write_bytes(od.encode(od.NEEDS_REVIEW))
|
|
params.put_bool('IsMetric', True, block=True)
|
|
self.assertFalse(read_params(params).enabled)
|
|
|
|
def test_registered_json_params_roundtrip_and_preserved_legacy_bytes(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
params = Params(directory)
|
|
params.put_bool('SpeedLimitController', True, block=True)
|
|
legacy = Path(params.get_param_path('Offset7'))
|
|
legacy.write_bytes(b'5.0000')
|
|
document = od.adopt_legacy(False, (0, 1, 2, 3, 4, 5, 5))
|
|
params.put('SLCOffsetSchedule', od.to_value(document), block=True)
|
|
self.assertEqual(params.get('SLCOffsetSchedule'), od.to_value(document))
|
|
self.assertEqual(read_params(params).offsets, document.schedule())
|
|
self.assertEqual(legacy.read_bytes(), b'5.0000')
|
|
params.put('SLCOffsetSchedule', od.to_value(od.NEEDS_REVIEW), block=True)
|
|
self.assertEqual(params.get('SLCOffsetSchedule'), od.to_value(od.NEEDS_REVIEW))
|
|
self.assertFalse(read_params(params).enabled)
|
|
self.assertEqual(legacy.read_bytes(), b'5.0000')
|
|
|
|
def test_malformed_documents_never_fall_back_to_legacy(self):
|
|
bad = (b'', b'{', b'\xff', b'{}', b'{"version":2}', b'{"version":1,"state":"other"}',
|
|
b'{"version":1,"state":"needs_review","extra":1}',
|
|
b'{"version":1,"version":1,"state":"needs_review"}',
|
|
b'{"version":1,"bounds_mps":[0,1,2,3,4,5,6,7],"offsets_mps":[0,0,0,0,0,0,NaN]}',
|
|
b'[' * 1100 + b'0' + b']' * 1100,
|
|
b'x' * (od.MAX_DOCUMENT_BYTES + 1),
|
|
b'x' * (1024 * 1024))
|
|
for raw in bad:
|
|
with self.subTest(raw=raw[:30]):
|
|
with self.assertRaises(ValueError):
|
|
od.decode(raw)
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
params = Params(directory)
|
|
params.put_bool('SpeedLimitController', True, block=True)
|
|
Path(params.get_param_path('SLCOffsetSchedule')).write_bytes(raw)
|
|
self.assertFalse(read_params(params).enabled)
|
|
valid = od.adopt_legacy(True, (0, 0, 0, 0, 0, 0, 0))
|
|
for altered in ({'version': True, 'bounds_mps': valid.bounds_mps, 'offsets_mps': valid.offsets_mps},
|
|
{'version': 1, 'bounds_mps': (0, 1, 1, 3, 4, 5, 6, 7), 'offsets_mps': valid.offsets_mps},
|
|
{'version': 1, 'bounds_mps': valid.bounds_mps, 'offsets_mps': (0, 0, 0, 0, 0, 0, True)}):
|
|
with self.assertRaises(ValueError):
|
|
od.validate(altered)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|