mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 21:43:44 +08:00
14241d72c8
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.
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()
|