mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-22 01:32:11 +08:00
229 lines
8.4 KiB
Python
229 lines
8.4 KiB
Python
import sys
|
|
import types
|
|
import unittest
|
|
from unittest.mock import MagicMock
|
|
|
|
# Create minimal mocks for imports so the tests can run headlessly.
|
|
rl = types.SimpleNamespace(
|
|
Rectangle=lambda x=0, y=0, width=0, height=0: types.SimpleNamespace(x=x, y=y, width=width, height=height),
|
|
check_collision_point_rec=lambda p, r: (r.x <= p.x <= r.x + r.width) and (r.y <= p.y <= r.y + r.height),
|
|
)
|
|
sys.modules["pyray"] = rl
|
|
|
|
ui_state = types.SimpleNamespace(
|
|
is_metric=True,
|
|
params=MagicMock(),
|
|
)
|
|
sys.modules["openpilot.selfdrive.ui.ui_state"] = types.SimpleNamespace(ui_state=ui_state)
|
|
|
|
# Mock openpilot.system.ui.widgets
|
|
widgets_mod = types.ModuleType("openpilot.system.ui.widgets")
|
|
class MockWidget:
|
|
def __init__(self):
|
|
self.rect = rl.Rectangle()
|
|
self._children = []
|
|
def _child(self, w):
|
|
self._children.append(w)
|
|
return w
|
|
def set_rect(self, rect):
|
|
self.rect = rect
|
|
def render(self, rect):
|
|
self.rect = rect
|
|
widgets_mod.Widget = MockWidget
|
|
sys.modules["openpilot.system.ui.widgets"] = widgets_mod
|
|
|
|
# Expose concrete widgets module with a mock base class
|
|
widgets_starpilot_base = types.ModuleType("openpilot.selfdrive.ui.onroad.starpilot.widgets.base")
|
|
class LayoutWidget(MockWidget):
|
|
def __init__(self, name: str, priority: int):
|
|
super().__init__()
|
|
self.name = name
|
|
self.priority = priority
|
|
widgets_starpilot_base.LayoutWidget = LayoutWidget
|
|
sys.modules["openpilot.selfdrive.ui.onroad.starpilot.widgets.base"] = widgets_starpilot_base
|
|
|
|
|
|
# Now we can import our WidgetLayoutManager
|
|
from openpilot.selfdrive.ui.onroad.starpilot.widget_layout_manager import WidgetLayoutManager
|
|
|
|
|
|
class DummyLayoutWidget(LayoutWidget):
|
|
def __init__(self, name: str, priority: int, width: float, height: float, visible: bool = True):
|
|
super().__init__(name, priority)
|
|
self._width = width
|
|
self._height = height
|
|
self._visible = visible
|
|
|
|
@property
|
|
def is_visible(self) -> bool:
|
|
return self._visible
|
|
|
|
def get_size(self) -> tuple[float, float]:
|
|
return self._width, self._height
|
|
|
|
|
|
class TestWidgetLayoutManager(unittest.TestCase):
|
|
def setUp(self):
|
|
# Set up content rect matching screen resolution 2160x1080 inside borders
|
|
self.content_rect = rl.Rectangle(30, 30, 2100, 1020)
|
|
self.layout_manager = WidgetLayoutManager(self.content_rect)
|
|
|
|
def test_priority_sorting(self):
|
|
w1 = DummyLayoutWidget("widget1", priority=3, width=100, height=100)
|
|
w2 = DummyLayoutWidget("widget2", priority=1, width=100, height=100)
|
|
w3 = DummyLayoutWidget("widget3", priority=2, width=100, height=100)
|
|
|
|
self.layout_manager.register_widget("left", w1)
|
|
self.layout_manager.register_widget("left", w2)
|
|
self.layout_manager.register_widget("left", w3)
|
|
|
|
# Should be sorted: w2 (1), w3 (2), w1 (3)
|
|
self.assertEqual(self.layout_manager.zones["left"][0], w2)
|
|
self.assertEqual(self.layout_manager.zones["left"][1], w3)
|
|
self.assertEqual(self.layout_manager.zones["left"][2], w1)
|
|
|
|
def test_left_zone_vertical_stacking(self):
|
|
# Left zone widgets stack vertically
|
|
# Center X anchor should be rect.x + 60 + 172 // 2 = 30 + 60 + 86 = 176
|
|
w1 = DummyLayoutWidget("set_speed", priority=1, width=200, height=204)
|
|
w2 = DummyLayoutWidget("speed_limit", priority=2, width=176, height=176)
|
|
|
|
self.layout_manager.register_widget("left", w1)
|
|
self.layout_manager.register_widget("left", w2)
|
|
|
|
self.layout_manager.update_layout(self.content_rect, is_rhd=False)
|
|
|
|
# w1 should be at y = rect.y + 45 = 30 + 45 = 75
|
|
# center is 176, so x = 176 - 200/2 = 76
|
|
self.assertEqual(w1.rect.x, 76)
|
|
self.assertEqual(w1.rect.y, 75)
|
|
self.assertEqual(w1.rect.width, 200)
|
|
self.assertEqual(w1.rect.height, 204)
|
|
|
|
# w2 should stack below w1: y = 75 + 204 + 15 (spacing) = 294
|
|
# center is 176, so x = 176 - 176/2 = 88
|
|
self.assertEqual(w2.rect.x, 88)
|
|
self.assertEqual(w2.rect.y, 294)
|
|
|
|
def test_bottom_zone_lhd_stacking(self):
|
|
# Bottom zone: LHD horizontal stacking from left to right starting at x = 146
|
|
# Y center should be content_rect.y + content_rect.height - 146 = 30 + 1020 - 146 = 904
|
|
w1 = DummyLayoutWidget("personality", priority=1, width=192, height=192)
|
|
w2 = DummyLayoutWidget("driver_monitor", priority=2, width=192, height=192)
|
|
w3 = DummyLayoutWidget("third", priority=3, width=192, height=192)
|
|
|
|
self.layout_manager.register_widget("bottom", w1)
|
|
self.layout_manager.register_widget("bottom", w2)
|
|
self.layout_manager.register_widget("bottom", w3)
|
|
|
|
self.layout_manager.update_layout(self.content_rect, is_rhd=False)
|
|
|
|
# w1 (first): center_x = content_rect.x + 146 = 30 + 146 = 176
|
|
# so x = 176 - 192/2 = 80
|
|
# y = 904 - 192/2 = 808
|
|
self.assertEqual(w1.rect.x, 80)
|
|
self.assertEqual(w1.rect.y, 808)
|
|
|
|
# w2 (second): center_x = 176 + 192 + 15 = 383
|
|
# so x = 383 - 192/2 = 287
|
|
self.assertEqual(w2.rect.x, 287)
|
|
self.assertEqual(w2.rect.y, 808)
|
|
|
|
# w3 (third): center_x = 383 + 192 + 15 = 590
|
|
# so x = 590 - 192/2 = 494
|
|
self.assertEqual(w3.rect.x, 494)
|
|
self.assertEqual(w3.rect.y, 808)
|
|
|
|
|
|
def test_bottom_zone_rhd_stacking(self):
|
|
# Bottom zone: RHD horizontal stacking such that last widget center is at width - 146
|
|
w1 = DummyLayoutWidget("personality", priority=1, width=192, height=192)
|
|
w2 = DummyLayoutWidget("driver_monitor", priority=2, width=192, height=192)
|
|
w3 = DummyLayoutWidget("third", priority=3, width=192, height=192)
|
|
|
|
self.layout_manager.register_widget("bottom", w1)
|
|
self.layout_manager.register_widget("bottom", w2)
|
|
self.layout_manager.register_widget("bottom", w3)
|
|
|
|
self.layout_manager.update_layout(self.content_rect, is_rhd=True)
|
|
|
|
# Total width of widgets = 192 + 15 + 192 + 15 + 192 = 606
|
|
# Last widget center should be width - 146 = 2130 - 146 = 1984
|
|
# Middle widget (w2) center should be 1984 - 192 - 15 = 1777
|
|
# First widget (w1) center should be 1777 - 192 - 15 = 1570
|
|
|
|
# w1: center_x = 1570, so x = 1570 - 192/2 = 1474
|
|
self.assertEqual(w1.rect.x, 1474)
|
|
|
|
# w2: center_x = 1777, so x = 1777 - 192/2 = 1681
|
|
self.assertEqual(w2.rect.x, 1681)
|
|
|
|
# w3: center_x = 1984, so x = 1984 - 192/2 = 1888
|
|
self.assertEqual(w3.rect.x, 1888)
|
|
|
|
def test_dynamic_visibility_shifting(self):
|
|
# Left zone: w1, w2, w3. w2 becomes invisible. w3 should shift up to occupy w2's space.
|
|
w1 = DummyLayoutWidget("w1", priority=1, width=100, height=100)
|
|
w2 = DummyLayoutWidget("w2", priority=2, width=100, height=100, visible=False)
|
|
w3 = DummyLayoutWidget("w3", priority=3, width=100, height=100)
|
|
|
|
self.layout_manager.register_widget("left", w1)
|
|
self.layout_manager.register_widget("left", w2)
|
|
self.layout_manager.register_widget("left", w3)
|
|
|
|
self.layout_manager.update_layout(self.content_rect, is_rhd=False)
|
|
|
|
# w1: y = 75
|
|
self.assertEqual(w1.rect.y, 75)
|
|
# w2: is invisible, should not be set (or rather, is skipped in stacking)
|
|
# w3: should stack directly below w1: y = 75 + 100 + 15 = 190
|
|
self.assertEqual(w3.rect.y, 190)
|
|
|
|
def test_dynamic_repositioning_on_rect_change(self):
|
|
# Register a widget
|
|
w1 = DummyLayoutWidget("w1", priority=1, width=100, height=100)
|
|
self.layout_manager.register_widget("right", w1)
|
|
|
|
# Initial layout
|
|
self.layout_manager.update_layout(self.content_rect, is_rhd=False)
|
|
# center_x = 30 + 2100 - 146 = 1984. x = 1984 - 50 = 1934
|
|
initial_x = w1.rect.x
|
|
|
|
# New layout with different rect
|
|
new_rect = rl.Rectangle(0, 0, 1920, 1080)
|
|
self.layout_manager.update_layout(new_rect, is_rhd=False)
|
|
# center_x = 0 + 1920 - 146 = 1774. x = 1774 - 50 = 1724
|
|
new_x = w1.rect.x
|
|
|
|
self.assertNotEqual(initial_x, new_x)
|
|
self.assertEqual(initial_x, 1934)
|
|
self.assertEqual(new_x, 1724)
|
|
|
|
def test_right_zone_vertical_stacking(self):
|
|
# Right zone widgets stack vertically
|
|
# X anchor is self.content_rect.x + self.content_rect.width - 146
|
|
w1 = DummyLayoutWidget("pedal_icons", priority=1, width=100, height=200)
|
|
w2 = DummyLayoutWidget("other", priority=2, width=100, height=100)
|
|
|
|
self.layout_manager.register_widget("right", w1)
|
|
self.layout_manager.register_widget("right", w2)
|
|
|
|
self.layout_manager.update_layout(self.content_rect, is_rhd=False)
|
|
|
|
# center_x = 30 + 2100 - 146 = 1984
|
|
# w1 x = 1984 - 50 = 1934
|
|
# w1 y = 30 + 45 = 75
|
|
self.assertEqual(w1.rect.x, 1934)
|
|
self.assertEqual(w1.rect.y, 75)
|
|
|
|
# w2 x = 1934
|
|
# w2 y = 75 + 200 + 15 = 290
|
|
self.assertEqual(w2.rect.x, 1934)
|
|
self.assertEqual(w2.rect.y, 290)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|
|
|