Files
StarPilot/openpilot/starpilot/galaxy/tests/test_network_diagnostics.py
T
firestar5683 a82935f326 User interfaces and Android Auto
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>
2026-10-14 20:02:39 -05:00

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))