Files
StarPilot/openpilot/starpilot/ui/tests/test_network_panel.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

378 lines
19 KiB
Python

"""Native NetworkUI ownership in the large custom settings rail."""
from types import SimpleNamespace as NS
from contextlib import nullcontext
from dataclasses import replace
import tempfile
import unittest
from unittest.mock import Mock, call, patch
import pyray as rl
from openpilot.common.params import Params
from openpilot.starpilot.ui.network_panel import NetworkPanelBridge
from openpilot.starpilot.ui import runtime_app
from openpilot.starpilot.ui.runtime_snapshot import RuntimeSnapshotAdapter
from openpilot.starpilot.ui.settings_state import Destination, DestinationAvailability, SettingsAction, SettingsActionKind
from openpilot.starpilot.ui.shell import ShellMode, ShellRequest
from openpilot.starpilot.ui.tests.test_runtime_snapshot import NOW, ui_fake
from openpilot.system.ui.widgets import DialogResult, Widget
from openpilot.system.ui.widgets import network as native_network
from openpilot.system.ui.lib.wifi_manager import Network, SecurityType
NETWORK = Network("fixture", 75, SecurityType.WPA2, False)
def native_panel(manager):
"""Use real native lifecycle methods without constructing fonts or NetworkManager."""
panel = native_network.NetworkUI.__new__(native_network.NetworkUI)
Widget.__init__(panel)
wifi = native_network.WifiManagerUI.__new__(native_network.WifiManagerUI)
Widget.__init__(wifi)
wifi._wifi_manager = manager
wifi._action_guard = None
wifi._networks = [NETWORK]
advanced = native_network.AdvancedNetworkSettings.__new__(native_network.AdvancedNetworkSettings)
Widget.__init__(advanced)
advanced._wifi_manager = manager
advanced._action_guard = None
panel._wifi_panel = panel._child(wifi)
panel._advanced_panel = panel._child(advanced)
panel._action_guard = None
return panel, wifi, advanced
class NetworkPanelTests(unittest.TestCase):
def setUp(self):
self.manager = Mock()
self.panel, self.wifi, self.advanced = native_panel(self.manager)
self.parked = True
self.selected = True
self.bridge = NetworkPanelBridge(self.panel, lambda: self.parked and self.selected)
def test_large_network_stays_open_onroad_and_revokes_on_navigation(self):
from openpilot.selfdrive.ui.layouts.main import MainState
from openpilot.starpilot.ui.tests.test_runtime_snapshot import TestParkedClockDomains
fixture = TestParkedClockDomains()
fixture.setUp()
fixture.ui.sm['pandaStates'][0].ignitionLine = True
layout = runtime_app.StarMainLayout.__new__(runtime_app.StarMainLayout)
object.__setattr__(layout, '_current_mode', MainState.SETTINGS)
object.__setattr__(layout, 'star', NS(selected=Destination.NETWORK,
connectivity_allowed=fixture.adapter.connectivity_allowed))
bridge = NetworkPanelBridge(self.panel, layout._network_authority)
self.assertTrue(fixture.adapter.confirmed_offroad())
self.assertTrue(bridge.enter())
self.wifi.forget_network(NETWORK)
self.manager.forget_connection.assert_called_once_with('fixture')
fixture.ui.sm['deviceState'].started = True
fixture.ui.started = True
self.wifi.forget_network(NETWORK)
self.assertEqual(self.manager.forget_connection.call_count, 2)
with patch.object(self.panel, 'render'):
self.assertTrue(bridge.render(rl.Rectangle(550, 25, 1560, 1030)))
layout.star.selected = Destination.STAR
self.wifi.forget_network(NETWORK)
self.assertEqual(self.manager.forget_connection.call_count, 2)
self.assertFalse(bridge.render(rl.Rectangle(550, 25, 1560, 1030)))
self.manager.set_active.assert_called_with(False)
def test_native_child_scanning_lifecycle_is_paired_without_frame_restart(self):
self.assertTrue(self.bridge.enter())
self.assertTrue(self.bridge.enter())
self.manager.set_active.assert_called_once_with(True)
self.bridge.leave()
self.bridge.leave()
self.assertEqual(self.manager.set_active.call_args_list, [call(True), call(False)])
self.assertTrue(self.bridge.enter())
self.bridge.leave()
self.assertEqual(self.manager.set_active.call_count, 4)
def test_real_settings_layout_lifecycle_does_not_duplicate_network_scan(self):
from openpilot.selfdrive.ui.layouts.settings.settings import PanelInfo, PanelType, SettingsLayout
settings = SettingsLayout.__new__(SettingsLayout)
Widget.__init__(settings)
native_device = Mock()
settings._current_panel = PanelType.DEVICE
settings._panels = {PanelType.DEVICE: PanelInfo("Device", native_device),
PanelType.NETWORK: PanelInfo("Network", self.panel)}
settings.show_event()
self.assertTrue(self.bridge.enter())
self.bridge.leave()
settings.hide_event()
self.manager.set_active.assert_has_calls([call(True), call(False)])
self.assertEqual(self.manager.set_active.call_count, 2)
native_device.show_event.assert_called_once()
native_device.hide_event.assert_called_once()
def test_offroad_loss_revokes_effect_and_stops_scan(self):
self.assertTrue(self.bridge.enter())
self.parked = False
network = NETWORK
self.wifi.forget_network(network)
self.manager.forget_connection.assert_not_called()
self.assertFalse(self.bridge.render(rl.Rectangle(550, 25, 1560, 1030)))
self.manager.set_active.assert_called_with(False)
self.assertFalse(self.bridge.enter())
def test_native_password_and_forget_callbacks_expire_after_navigation_and_reentry(self):
self.assertTrue(self.bridge.enter())
network = NETWORK
self.wifi.state = native_network.UIState.NEEDS_AUTH
self.wifi._state_network = network
self.wifi._password_retry = False
self.wifi.keyboard = Mock(text="secret123")
with patch.object(native_network.gui_app, "push_widget"):
self.wifi._render(rl.Rectangle(0, 0, 100, 100))
password_callback = self.wifi.keyboard.set_callback.call_args.args[0]
self.bridge.leave()
self.assertTrue(self.bridge.enter())
password_callback(DialogResult.CONFIRM)
self.manager.connect_to_network.assert_not_called()
self.wifi.state = native_network.UIState.SHOW_FORGET_CONFIRM
self.wifi._state_network = network
with patch.object(native_network, "ConfirmDialog", side_effect=lambda *args, **kwargs: NS(callback=kwargs["callback"], set_text=Mock())), \
patch.object(native_network.gui_app, "push_widget") as pushed:
self.wifi._render(rl.Rectangle(0, 0, 100, 100))
forget_callback = pushed.call_args.args[0].callback
self.bridge.leave()
forget_callback(DialogResult.CONFIRM)
self.manager.forget_connection.assert_not_called()
def test_native_apn_and_hidden_network_confirmations_recheck_fresh_authority(self):
with tempfile.TemporaryDirectory() as directory:
params = Params(directory)
self.advanced._params = params
self.advanced._keyboard = Mock(text="fixture.apn")
self.assertTrue(self.bridge.enter())
with patch.object(native_network.gui_app, "push_widget"):
self.advanced._edit_apn()
apn_callback = self.advanced._keyboard.set_callback.call_args.args[0]
self.parked = False
apn_callback(DialogResult.CONFIRM)
self.assertIsNone(params.get("GsmApn"))
self.parked = True
self.bridge.leave()
self.assertTrue(self.bridge.enter())
with patch.object(native_network.gui_app, "push_widget"):
self.advanced._connect_to_hidden_network()
hidden_callback = self.advanced._keyboard.set_callback.call_args.args[0]
self.bridge.leave()
hidden_callback(DialogResult.CONFIRM)
self.manager.connect_to_network.assert_not_called()
def test_nested_hidden_password_from_prior_page_cannot_connect_after_reentry(self):
self.advanced._keyboard = Mock(text="hidden-name")
self.assertTrue(self.bridge.enter())
with patch.object(native_network.gui_app, "push_widget"):
self.advanced._connect_to_hidden_network()
name_callback = self.advanced._keyboard.set_callback.call_args.args[0]
name_callback(DialogResult.CONFIRM)
password_callback = self.advanced._keyboard.set_callback.call_args.args[0]
self.bridge.leave()
self.assertTrue(self.bridge.enter())
self.advanced._keyboard.text = "secret123"
password_callback(DialogResult.CONFIRM)
self.manager.connect_to_network.assert_not_called()
def test_advanced_side_effects_are_silent_after_authority_loss(self):
with tempfile.TemporaryDirectory() as directory:
params = Params(directory)
self.advanced._params = params
self.advanced._prime_state = NS(get_type=lambda: 0)
self.advanced._cell_prime_types = (0,)
self.advanced._roaming_btn = Mock()
self.advanced._apn_btn = Mock()
self.advanced._cellular_metered_btn = Mock()
self.advanced._roaming_action = Mock()
self.advanced._tethering_action = Mock()
self.assertTrue(self.bridge.enter())
self.parked = False
self.advanced._update_state()
self.advanced._toggle_tethering()
self.advanced._toggle_roaming()
self.advanced._toggle_cellular_metered()
self.advanced._toggle_wifi_metered(1)
self.manager.set_ipv4_forward.assert_not_called()
self.manager.set_tethering_active.assert_not_called()
self.manager.set_current_network_metered.assert_not_called()
self.assertIsNone(params.get("GsmRoaming"))
self.assertIsNone(params.get("GsmMetered"))
def test_render_failure_cleans_up_scan_and_revokes_callbacks(self):
self.assertTrue(self.bridge.enter())
with patch.object(self.panel, "render", side_effect=RuntimeError("render failed")):
self.assertFalse(self.bridge.render(rl.Rectangle(550, 25, 1560, 1030)))
self.assertFalse(self.bridge.active)
self.manager.set_active.assert_called_with(False)
self.wifi.forget_network(NETWORK)
self.manager.forget_connection.assert_not_called()
def test_default_native_caller_has_no_new_guard(self):
manager = Mock()
panel, wifi, _advanced = native_panel(manager)
panel.show_event()
wifi.forget_network(NETWORK)
panel.hide_event()
manager.forget_connection.assert_called_once_with("fixture")
def test_connectivity_panels_are_available_offroad_onroad_and_without_fresh_telemetry(self):
ui = ui_fake()
ui.started = False
ui.sm.messages["deviceState"].started = False
ui.sm.messages["pandaStates"][0].ignitionLine = False
state = RuntimeSnapshotAdapter(ui).build(ShellMode.SETTINGS, Destination.NETWORK, now_ns=NOW)
self.assertEqual(state.selected, Destination.NETWORK)
self.assertTrue(state.settings.destination(Destination.NETWORK).available)
ui.started = True
ui.sm.messages["deviceState"].started = True
state = RuntimeSnapshotAdapter(ui).build(ShellMode.SETTINGS, Destination.NETWORK, now_ns=NOW)
self.assertTrue(state.settings.destination(Destination.NETWORK).available)
for service in ('deviceState', 'pandaStates'):
ui.sm.valid[service] = False
for destination in (Destination.NETWORK, Destination.BLUETOOTH):
with self.subTest(destination=destination):
state = RuntimeSnapshotAdapter(ui).build(ShellMode.SETTINGS, destination, now_ns=NOW)
self.assertEqual(state.selected, destination)
self.assertTrue(state.settings.destination(destination).available)
def test_large_bluetooth_opens_and_stays_open_without_connectivity_authority(self):
layout = runtime_app.StarMainLayout.__new__(runtime_app.StarMainLayout)
Widget.__init__(layout)
layout._current_mode = runtime_app.MainState.SETTINGS
layout._network_bridge = Mock()
layout._large_panels = {}
layout._large_destination = None
object.__setattr__(layout, 'star', NS(selected=Destination.BLUETOOTH, connectivity_allowed=Mock(return_value=False),
fonts=NS(profile=runtime_app.Profile.LARGE)))
object.__setattr__(layout, 'page', NS(render=Mock()))
with patch('openpilot.starpilot.ui.bluetooth_large.BluetoothLarge') as factory:
layout._network_destination(Destination.BLUETOOTH)
factory.assert_called_once_with(layout.star.connectivity_allowed)
panel = factory.return_value
panel.show_event.assert_called_once()
layout._render_main_content()
self.assertEqual(layout.star.selected, Destination.BLUETOOTH)
self.assertEqual(layout._large_destination, Destination.BLUETOOTH)
panel.hide_event.assert_not_called()
layout._network_destination(Destination.STAR)
panel.hide_event.assert_called_once()
def test_shell_selection_cancels_press_and_renders_native_pane_in_screen_coordinates(self):
session = runtime_app.StarShellSession.__new__(runtime_app.StarShellSession)
session.slc_actions = None
session.pip_warning = Mock()
session.favorites = Mock()
session.selected = Destination.STAR
session._mode = ShellMode.SETTINGS
session.profile = runtime_app.Profile.LARGE
object.__setattr__(session, "input", NS(cancel=Mock()))
session._on_destination_change = Mock()
session._snapshot_cache = None
session.sidebar_expanded = True
object.__setattr__(session, "view", NS(render=Mock(), settings=NS(render_rail=Mock())))
session.notice = ""
session.network_layer = Mock(return_value=True)
session._emit(ShellRequest("settings", SettingsAction(SettingsActionKind.REQUEST_DESTINATION,
DestinationAvailability(Destination.NETWORK, True))))
self.assertEqual(session.selected, Destination.NETWORK)
session.input.cancel.assert_called_once()
session._on_destination_change.assert_called_once_with(Destination.NETWORK)
state = RuntimeSnapshotAdapter(self._parked_ui()).build(ShellMode.SETTINGS, Destination.NETWORK, now_ns=NOW)
session.snapshot = Mock(return_value=state)
with patch.object(runtime_app, "placed_at", return_value=nullcontext()):
session.render(ShellMode.SETTINGS, rl.Rectangle(10, 20, 2160, 1080))
session.view.settings.render_rail.assert_called_once_with(state.settings, selected=Destination.NETWORK)
rect = session.network_layer.call_args.args[0]
self.assertEqual((rect.x, rect.y, rect.width, rect.height), (560, 45, 1560, 1030))
def back_from_network(_rect):
session._emit(ShellRequest("settings", SettingsAction(SettingsActionKind.CLOSE)))
return False # the bridge revokes authority when Back changes the destination
session.network_layer.side_effect = back_from_network
with patch.object(runtime_app, "placed_at", return_value=nullcontext()):
session.render(ShellMode.SETTINGS, rl.Rectangle(10, 20, 2160, 1080))
self.assertEqual(session.notice, "")
self.assertEqual(session.selected, Destination.STAR)
self.assertEqual(session._on_destination_change.call_args.args[0], Destination.STAR)
self.assertEqual(session.view.settings.render_rail.call_args_list, [
call(state.settings, selected=Destination.NETWORK), call(state.settings, selected=Destination.NETWORK),
])
def test_shell_render_failure_returns_to_star_without_retrying_deactivated_bridge(self):
session = runtime_app.StarShellSession.__new__(runtime_app.StarShellSession)
session.slc_actions = None
session.pip_warning = Mock()
session.favorites = Mock()
session.selected = Destination.NETWORK
session._mode = ShellMode.SETTINGS
session.profile = runtime_app.Profile.LARGE
object.__setattr__(session, "input", NS(cancel=Mock()))
object.__setattr__(session, "view", NS(render=Mock(), settings=NS(render_rail=Mock())))
object.__setattr__(session, "fonts", NS(draw=Mock()))
session._on_destination_change = Mock()
session.notice = ""
session.notice_until = 0.0
session.network_layer = Mock(return_value=False) # bridge already deactivated
network_state = RuntimeSnapshotAdapter(self._parked_ui()).build(ShellMode.SETTINGS, Destination.NETWORK, now_ns=NOW)
session._snapshot_cache = ((), network_state)
session.snapshot = Mock(side_effect=lambda _mode: replace(network_state, selected=session.selected))
with (patch.object(runtime_app, "placed_at", return_value=nullcontext()),
patch.object(runtime_app.time, "monotonic", return_value=100.0),
patch.object(runtime_app.rl, "draw_rectangle")):
session.render(ShellMode.SETTINGS, rl.Rectangle(0, 0, 2160, 1080))
self.assertEqual(session.selected, Destination.STAR)
self.assertIsNone(session._snapshot_cache)
self.assertIn("network panel is unavailable", session.notice)
session.view.settings.render_rail.assert_called_once_with(network_state.settings, selected=Destination.NETWORK)
session.input.cancel.assert_called_once()
session._on_destination_change.assert_called_once_with(Destination.STAR)
session.render(ShellMode.SETTINGS, rl.Rectangle(0, 0, 2160, 1080))
session.network_layer.assert_called_once()
session.view.settings.render_rail.assert_called_once_with(network_state.settings, selected=Destination.NETWORK)
@staticmethod
def _parked_ui():
ui = ui_fake()
ui.started = False
ui.sm.messages["deviceState"].started = False
ui.sm.messages["pandaStates"][0].ignitionLine = False
return ui
def test_native_entrypoint_and_exit_clear_retained_network_selection(self):
from openpilot.selfdrive.ui.layouts.settings.settings import PanelType
layout = runtime_app.StarMainLayout.__new__(runtime_app.StarMainLayout)
layout._network_bridge = Mock()
layout._large_destination = None
layout._large_panels = {}
layout._current_mode = runtime_app.MainState.HOME
object.__setattr__(layout, "star", NS(selected=Destination.STAR, cancel=Mock(), _snapshot_cache="old"))
object.__setattr__(layout, "page", NS(hide_event=Mock(), show_event=Mock()))
def opened_native(_panel):
layout._current_mode = runtime_app.MainState.SETTINGS
with patch.object(runtime_app.MainLayout, "open_settings", side_effect=opened_native) as opened:
layout.open_settings(PanelType.NETWORK)
opened.assert_called_once_with(PanelType.DEVICE)
self.assertEqual(layout.star.selected, Destination.NETWORK)
layout._network_bridge.enter.assert_called_once()
with patch.object(runtime_app.MainLayout, "_set_current_layout", side_effect=lambda mode: setattr(layout, "_current_mode", mode)):
layout._set_current_layout(runtime_app.MainState.ONROAD)
layout._network_bridge.leave.assert_called()
self.assertEqual(layout.star.selected, Destination.STAR)
layout.star.cancel.assert_called_once()
def test_body_transition_releases_network_before_native_layout_change(self):
layout = runtime_app.StarMainLayout.__new__(runtime_app.StarMainLayout)
layout._network_bridge = Mock()
layout._large_destination = None
layout._current_mode = runtime_app.MainState.SETTINGS
object.__setattr__(layout, "star", NS(selected=Destination.NETWORK, cancel=Mock(), _snapshot_cache="old"))
object.__setattr__(layout, "page", NS(hide_event=Mock(), show_event=Mock()))
with patch.object(runtime_app.MainLayout, "_on_body_changed",
side_effect=lambda: layout._set_current_layout(runtime_app.MainState.HOME)), \
patch.object(runtime_app.MainLayout, "_set_current_layout",
side_effect=lambda mode: setattr(layout, "_current_mode", mode)):
layout._on_body_changed()
layout._network_bridge.leave.assert_called_once()
self.assertEqual(layout.star.selected, Destination.STAR)