mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
a82935f326
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services. 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: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: Shane Smiskol <shane@smiskol.com> 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: pharmacomaniac <blittle65@gmail.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
116 lines
5.1 KiB
Python
116 lines
5.1 KiB
Python
"""Pure bounded network/launcher readers; never imports native vehicle modules."""
|
|
|
|
import ast
|
|
from pathlib import Path
|
|
import threading
|
|
import json
|
|
import subprocess
|
|
from types import SimpleNamespace
|
|
import unittest
|
|
|
|
from openpilot.starpilot.galaxy.local_access import LocalAccess
|
|
from openpilot.starpilot.galaxy.tmux_live import TmuxLive, MAX_BYTES
|
|
|
|
|
|
def interface(name='wlan0', local='192.168.50.21', flags=None):
|
|
return {'ifname': name, 'flags': ['UP'] if flags is None else flags, 'addr_info': [{'family': 'inet', 'scope': 'global', 'local': local}]}
|
|
|
|
|
|
class TestNetworkDiagnostics(unittest.TestCase):
|
|
def addresses(self, inventory):
|
|
calls = []
|
|
|
|
def run(args, **kwargs):
|
|
calls.append((args, kwargs))
|
|
return SimpleNamespace(stdout=json.dumps(inventory).encode())
|
|
|
|
return LocalAccess(run=run), calls
|
|
|
|
def console(self, raw, panes=b'%0\t0\t0\n'):
|
|
calls = []
|
|
|
|
def run(args, **kwargs):
|
|
calls.append((args, kwargs))
|
|
if args[1] == 'list-panes':
|
|
return SimpleNamespace(stdout=panes)
|
|
kwargs['stdout'].write(raw)
|
|
return SimpleNamespace()
|
|
|
|
return TmuxLive(run=run), calls
|
|
|
|
def test_active_lan_addresses_deduplicate_and_exclude_loopback_public(self):
|
|
source, calls = self.addresses(
|
|
[interface(), interface('eth0'), interface('lo', '127.0.0.1', ['UP', 'LOOPBACK']), interface('vpn', '100.64.0.1'), interface('down', flags=[])]
|
|
)
|
|
result = source.snapshot()
|
|
self.assertTrue(result['available'])
|
|
self.assertEqual(len(result['addresses']), 1)
|
|
self.assertEqual(result['addresses'][0]['url'], 'http://192.168.50.21:8082/')
|
|
self.assertEqual(calls[0][0], ['ip', '-j', '-4', 'address', 'show', 'up'])
|
|
self.assertEqual(calls[0][1]['timeout'], 1)
|
|
source.snapshot()
|
|
self.assertEqual(len(calls), 1)
|
|
|
|
def test_network_result_caps_at_frontend_contract(self):
|
|
source, _ = self.addresses([interface(f'eth{i}', f'10.0.0.{i + 1}') for i in range(64)])
|
|
result = source.snapshot()
|
|
self.assertEqual(len(result['addresses']), 32)
|
|
|
|
def test_malformed_interfaces_skip_without_inventing_addresses(self):
|
|
source, _ = self.addresses([None, 'bad', {'ifname': 'x', 'flags': 'UP', 'addr_info': []}, {'ifname': 'x', 'flags': ['UP'], 'addr_info': {}}, interface()])
|
|
self.assertEqual(len(source.snapshot()['addresses']), 1)
|
|
source, _ = self.addresses({'bad': 'inventory'})
|
|
self.assertFalse(source.snapshot()['available'])
|
|
|
|
def test_large_inventory_and_reader_errors_fail_closed(self):
|
|
for run in [lambda *_a, **_k: SimpleNamespace(stdout=b'x' * 65537), lambda *_a, **_k: (_ for _ in ()).throw(subprocess.TimeoutExpired('ip', 1))]:
|
|
self.assertFalse(LocalAccess(run=run).snapshot()['available'])
|
|
|
|
def test_console_uses_exact_readonly_launcher_pane_and_cache(self):
|
|
source, calls = self.console(b'first\nsecond\n')
|
|
result = source.snapshot()
|
|
self.assertTrue(result['available'])
|
|
self.assertEqual(result['text'], 'first\nsecond')
|
|
self.assertEqual(result['pane'], 'comma:0.0')
|
|
self.assertEqual(calls[1][0], ['tmux', 'capture-pane', '-p', '-t', '%0', '-S', '-300', '-E', '-'])
|
|
source.snapshot()
|
|
self.assertEqual(len(calls), 2)
|
|
|
|
def test_console_bounds_utf8_replacement_bytes_and_line_count(self):
|
|
for raw in [b'\xff' * MAX_BYTES, b'\xc3' + b'a' * MAX_BYTES, b'line\n' * 500]:
|
|
source, _ = self.console(raw)
|
|
result = source.snapshot()
|
|
self.assertTrue(result['available'])
|
|
self.assertTrue(result['truncated'])
|
|
self.assertLessEqual(len(result['text'].encode()), MAX_BYTES)
|
|
self.assertLessEqual(len(result['text'].splitlines()), 300)
|
|
|
|
def test_dead_or_malformed_pane_does_not_capture(self):
|
|
for panes in [b'%0\t0\t1\n', b'bad\t0\t0\n', b'%1\t1\t0\n']:
|
|
source, calls = self.console(b'', panes)
|
|
self.assertFalse(source.snapshot()['available'])
|
|
self.assertEqual(len(calls), 1)
|
|
|
|
def test_settings_diagnostics_projects_only_existing_allowlisted_rows(self):
|
|
path = Path(__file__).parents[1] / 'settings.py'
|
|
tree = ast.parse(path.read_text())
|
|
method = next(
|
|
node for cls in tree.body if isinstance(cls, ast.ClassDef) for node in cls.body if isinstance(node, ast.FunctionDef) and node.name == 'diagnostics'
|
|
)
|
|
namespace = {}
|
|
exec(compile(ast.Module(body=[method], type_ignores=[]), str(path), 'exec'), namespace)
|
|
pages = []
|
|
ctx = SimpleNamespace(cp=SimpleNamespace(carFingerprint='car', brand='brand', openpilotLongitudinalControl=True, steerControlType='torque'), metric=True)
|
|
|
|
def state(page, received):
|
|
self.assertIs(received, ctx)
|
|
pages.append(page)
|
|
return SimpleNamespace(title=page, rows=[SimpleNamespace(label='Setting', value='Shown', source=b'not-public')])
|
|
|
|
owner = SimpleNamespace(lock=threading.Lock(), context=SimpleNamespace(sample=lambda: ctx), _state=state)
|
|
result = namespace['diagnostics'](owner)
|
|
self.assertEqual(pages, ['torque', 'lane', 'lane_change', 'aol', 'conditional', 'profiles', 'slc', 'curve'])
|
|
self.assertEqual(result['sections'][0]['rows'], [{'label': 'Setting', 'value': 'Shown'}])
|
|
self.assertTrue(result['vehicle']['available'])
|
|
self.assertNotIn('not-public', json.dumps(result))
|