mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-09 22:13:43 +08:00
80fbb4f6db
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Kacper Rączy <gfw.kra@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
157 lines
8.5 KiB
Python
157 lines
8.5 KiB
Python
import http.client
|
|
import json
|
|
import tempfile
|
|
import threading
|
|
import unittest
|
|
from unittest.mock import patch
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
from openpilot.common.params import Params
|
|
from openpilot.starpilot.galaxy.access import GalaxyAccessOwner
|
|
from openpilot.starpilot.galaxy.server import make_server
|
|
from openpilot.starpilot.galaxy.vehicle_selection import VehicleSelectionGateway
|
|
from openpilot.starpilot.saved_document import WriteResult
|
|
from openpilot.starpilot.vehicle_selection import KEY, VehicleSelectionOwner, choices, read_selection
|
|
|
|
|
|
class Context:
|
|
def __init__(self):
|
|
self.allowed = True
|
|
self.reported = None
|
|
|
|
def parked(self):
|
|
return self.allowed
|
|
|
|
def sample(self):
|
|
return SimpleNamespace(cp=self.reported)
|
|
|
|
|
|
class VehicleSelectionHttpTest(unittest.TestCase):
|
|
def setUp(self):
|
|
temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(temporary.cleanup)
|
|
self.params = Params(temporary.name)
|
|
self.context = Context()
|
|
self.gateway = VehicleSelectionGateway(self.params, self.context)
|
|
access = GalaxyAccessOwner(Path(temporary.name) / 'access')
|
|
self.assertTrue(access.configure('password123', lambda: True))
|
|
self.server = make_server(port=0, owner=access, vehicle_selection=self.gateway)
|
|
self.worker = threading.Thread(target=self.server.serve_forever, kwargs={'poll_interval': 0.01}, daemon=True)
|
|
self.worker.start()
|
|
self.addCleanup(self.stop)
|
|
|
|
def stop(self):
|
|
self.server.shutdown()
|
|
self.worker.join(timeout=2)
|
|
self.server.server_close()
|
|
|
|
def request(self, path, payload=None, cookie=''):
|
|
connection = http.client.HTTPConnection('127.0.0.1', self.server.server_port, timeout=3)
|
|
headers = {'Cookie': cookie}
|
|
if payload is not None:
|
|
headers.update({'Content-Type': 'application/json', 'Origin': f'http://127.0.0.1:{self.server.server_port}'})
|
|
try:
|
|
connection.request('POST' if payload is not None else 'GET', path,
|
|
body=json.dumps(payload) if payload is not None else None, headers=headers)
|
|
response = connection.getresponse()
|
|
return response.status, json.loads(response.read()), dict(response.getheaders())
|
|
finally:
|
|
connection.close()
|
|
|
|
def login(self):
|
|
code, _, headers = self.request('/api/auth/login', {'password': 'password123'})
|
|
self.assertEqual(code, 200)
|
|
return headers['Set-Cookie'].split(';', 1)[0]
|
|
|
|
def test_authenticated_auto_manual_confirmation_and_reported_identity(self):
|
|
self.assertEqual(self.request('/api/vehicle-selection')[0], 401)
|
|
cookie = self.login()
|
|
code, page, _ = self.request('/api/vehicle-selection', cookie=cookie)
|
|
self.assertEqual(code, 200)
|
|
self.assertTrue(page['parked'] and page['valid'])
|
|
self.assertIsNone(page['selected'])
|
|
self.assertEqual(page['selectedLabel'], 'Auto detection')
|
|
self.assertEqual(page['selectionMode'], 'auto')
|
|
self.assertIsNone(page['reported'])
|
|
self.assertNotIn('MOCK', [item['platform'] for item in page['choices']])
|
|
self.assertEqual(page['choices'], [{'platform': choice.platform, 'make': choice.make, 'label': choice.label}
|
|
for choice in choices()])
|
|
selected = page['choices'][0]
|
|
self.context.reported = SimpleNamespace(carFingerprint=selected['platform'], notCar=False)
|
|
other_cookie = self.login()
|
|
self.assertEqual(self.request('/api/vehicle-selection/preview',
|
|
{'view': page['view'], 'platform': selected['platform']}, other_cookie)[0], 409)
|
|
code, preview, _ = self.request('/api/vehicle-selection/preview', {'view': page['view'], 'platform': selected['platform']}, cookie)
|
|
self.assertEqual(code, 200)
|
|
self.assertIn('next start', preview['question'])
|
|
self.assertIsNone(read_selection(self.params).raw)
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': preview['intent'], 'confirmed': True}, cookie)[0], 200)
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': preview['intent'], 'confirmed': True}, cookie)[0], 409)
|
|
readback = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
self.assertEqual(readback['selected'], selected['platform'])
|
|
self.assertEqual(readback['reported']['platform'], selected['platform'])
|
|
self.assertNotEqual(readback['selectedLabel'], 'Auto detection')
|
|
self.assertEqual(readback['selectionMode'], 'manual')
|
|
self.assertEqual(self.request('/api/auth/logout', {}, cookie)[0], 200)
|
|
self.assertEqual(self.request('/api/vehicle-selection', cookie=cookie)[0], 401)
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': preview['intent'], 'confirmed': True}, cookie)[0], 401)
|
|
|
|
def test_runtime_report_is_not_replaced_by_manually_selected_editor_context(self):
|
|
cookie = self.login()
|
|
first, second = self.gateway.catalog[:2]
|
|
self.context.reported = SimpleNamespace(carFingerprint=str(second.platform), notCar=False)
|
|
self.context.runtime_vehicle = lambda: (SimpleNamespace(carFingerprint=str(first.platform), notCar=False,
|
|
fingerprintSource='fw'), b'actual-runtime-cp')
|
|
page = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
self.assertEqual(page['reported']['platform'], str(first.platform))
|
|
self.assertEqual(page['reported']['origin'], 'detected')
|
|
self.context.runtime_vehicle = lambda: (SimpleNamespace(carFingerprint=str(first.platform), notCar=False,
|
|
fingerprintSource='fixed'), b'manual-runtime-cp')
|
|
page = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
self.assertEqual(page['reported']['origin'], 'manual')
|
|
self.assertEqual(page['selectionMode'], 'auto') # Next start can differ from the last running selection.
|
|
|
|
def test_invalid_saved_source_explicit_auto_repair_and_parked_revocation(self):
|
|
cookie = self.login()
|
|
Path(self.params.get_param_path(KEY)).write_bytes(b'bad-document')
|
|
page = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
self.assertFalse(page['valid'])
|
|
self.assertEqual(page['selectedLabel'], 'Needs review')
|
|
self.assertEqual(self.request('/api/vehicle-selection/preview',
|
|
{'view': page['view'], 'platform': page['choices'][0]['platform']}, cookie)[0], 409)
|
|
code, repair, _ = self.request('/api/vehicle-selection/preview', {'view': page['view'], 'platform': None}, cookie)
|
|
self.assertEqual(code, 200)
|
|
self.context.allowed = False
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': repair['intent'], 'confirmed': True}, cookie)[0], 409)
|
|
self.assertEqual(Path(self.params.get_param_path(KEY)).read_bytes(), b'bad-document')
|
|
self.context.allowed = True
|
|
page = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
repair = self.request('/api/vehicle-selection/preview', {'view': page['view'], 'platform': None}, cookie)[1]
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': repair['intent'], 'confirmed': True}, cookie)[0], 200)
|
|
self.assertTrue(read_selection(self.params).valid)
|
|
self.assertIsNone(read_selection(self.params).platform)
|
|
|
|
def test_stale_view_and_bad_request_never_write(self):
|
|
cookie = self.login()
|
|
page = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
self.params.put(KEY, {'version': 1, 'platform': None}, block=True)
|
|
self.assertEqual(self.request('/api/vehicle-selection/preview', {'view': page['view'], 'platform': None}, cookie)[0], 409)
|
|
self.assertEqual(self.request('/api/vehicle-selection/preview', {'view': 123, 'platform': None}, cookie)[0], 400)
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': 'unknown', 'confirmed': True}, cookie)[0], 409)
|
|
self.assertIsNone(read_selection(self.params).platform)
|
|
|
|
def test_postcommit_uncertainty_requires_readback_before_retry(self):
|
|
cookie = self.login()
|
|
page = self.request('/api/vehicle-selection', cookie=cookie)[1]
|
|
intent = self.request('/api/vehicle-selection/preview', {'view': page['view'], 'platform': None}, cookie)[1]['intent']
|
|
with patch.object(VehicleSelectionOwner, 'choose', return_value=WriteResult(True, False)):
|
|
code, result, _ = self.request('/api/vehicle-selection/confirm', {'intent': intent, 'confirmed': True}, cookie)
|
|
self.assertEqual(code, 409)
|
|
self.assertEqual(result['code'], 'unverified')
|
|
self.assertEqual(self.request('/api/vehicle-selection/confirm', {'intent': intent, 'confirmed': True}, cookie)[0], 409)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|