mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
718a2c3ddd
Discard a preempted clock pair and try up to three fresh pairs while retaining the 1 ms accepted span and all existing freshness/resume gates. Yield after receive-only decoder construction so normal UI collection refreshes before full physical admission, Offroad request or input. Recorded d5 clock failure; reproduced timing regressions; 250 focused native cases, lint/type checks and independent source/evidence review. No vehicle commands, device deployment or physical cure claim.
189 lines
7.5 KiB
Python
189 lines
7.5 KiB
Python
"""Actual touch dispatch and navigation around a newly parked game launch."""
|
|
|
|
from types import SimpleNamespace as NS
|
|
from unittest.mock import Mock
|
|
|
|
import pyray as rl
|
|
import pytest
|
|
|
|
from openpilot.starpilot.ui.spacepilot import native
|
|
from openpilot.starpilot.ui.spacepilot.state import FiveTaps
|
|
from openpilot.starpilot.ui.tests.test_spacepilot_large import adapter as adapter
|
|
from openpilot.starpilot.ui.tests.test_spacepilot_session import setup as setup, offroad
|
|
from openpilot.starpilot.ui.tests.test_spacepilot_state import mature_gate, parked_ui, read
|
|
from openpilot.system.ui.lib.application import MouseEvent, MousePos, gui_app
|
|
|
|
|
|
@pytest.mark.parametrize('spacing', [.06, .2, .7, 1.2])
|
|
def test_five_releases_accept_natural_fast_and_slow_paces(spacing):
|
|
taps = FiveTaps()
|
|
for index in range(4):
|
|
assert not taps.tap(10 + index * spacing)
|
|
assert taps.tap(10 + 4 * spacing)
|
|
|
|
|
|
def test_touch_noise_and_expired_sequences_still_do_not_launch():
|
|
taps = FiveTaps()
|
|
for now in (10., 10., 10.01, 10.02, 10.03):
|
|
assert not taps.tap(now)
|
|
assert taps.count == 1
|
|
for now in (12., 12.2, 12.4, 12.6):
|
|
assert not taps.tap(now)
|
|
assert taps.count == 4
|
|
assert taps.tap(12.8)
|
|
|
|
|
|
@pytest.fixture
|
|
def version(monkeypatch):
|
|
monkeypatch.setattr(gui_app, 'texture', lambda *_a, **_k: NS(width=64, height=64))
|
|
monkeypatch.setattr(gui_app, 'font', lambda *_a: rl.Font())
|
|
monkeypatch.setattr(native, 'ui_state', NS(params=NS(get=lambda _: '0.10.3')))
|
|
monkeypatch.setattr(native, 'live_witness', lambda: read(parked_ui()))
|
|
widget = native.VersionButton(Mock())
|
|
widget.set_rect(rl.Rectangle(0, 0, 402, 180))
|
|
widget._sub_label.set_rect(rl.Rectangle(40, 110, 322, 46))
|
|
widget.show_event()
|
|
yield widget
|
|
widget.hide_event()
|
|
|
|
|
|
def events(pos, index):
|
|
now = 10 + index * .2
|
|
return [MouseEvent(pos, 0, True, False, True, now), MouseEvent(pos, 0, False, True, False, now + .04)]
|
|
|
|
|
|
@pytest.mark.parametrize('batched', [False, True])
|
|
@pytest.mark.parametrize('profile', ['c4', 'big'])
|
|
def test_original_touch_times_and_valid_immature_park_count_all_five(version, adapter, monkeypatch, batched, profile):
|
|
widget = version if profile == 'c4' else adapter
|
|
widget.gate.reset()
|
|
bounds = version._sub_label.rect if profile == 'c4' else adapter._version_bounds()
|
|
pos = MousePos(bounds.x + 20, bounds.y + 10)
|
|
monkeypatch.setattr(native.time, 'monotonic', lambda: 11.)
|
|
pushed = Mock()
|
|
monkeypatch.setattr(gui_app, 'push_widget', pushed)
|
|
if batched:
|
|
widget._update_state()
|
|
monkeypatch.setattr(gui_app, '_mouse_events', [e for i in range(5) for e in events(pos, i)])
|
|
widget._process_mouse_events()
|
|
else:
|
|
for i in range(5):
|
|
widget._update_state()
|
|
monkeypatch.setattr(gui_app, '_mouse_events', events(pos, i))
|
|
widget._process_mouse_events()
|
|
pushed.assert_called_once()
|
|
game = pushed.call_args.args[0]
|
|
assert isinstance(game, native.SpacePilotPage)
|
|
assert not game.gate.ready
|
|
assert game.session.stage == native.Stage.VERIFYING
|
|
game.session.close()
|
|
|
|
|
|
@pytest.mark.parametrize('profile', ['c4', 'big'])
|
|
def test_repeated_release_without_new_press_does_not_count(version, adapter, monkeypatch, profile):
|
|
widget = version if profile == 'c4' else adapter
|
|
bounds = version._sub_label.rect if profile == 'c4' else adapter._version_bounds()
|
|
pos = MousePos(bounds.x + 20, bounds.y + 10)
|
|
push = Mock()
|
|
monkeypatch.setattr(gui_app, 'push_widget', push)
|
|
monkeypatch.setattr(gui_app, '_mouse_events', events(pos, 0))
|
|
widget._process_mouse_events()
|
|
for i in range(1, 10):
|
|
monkeypatch.setattr(gui_app, '_mouse_events', [events(pos, i)[1]])
|
|
widget._process_mouse_events()
|
|
assert widget.taps.count == 1
|
|
push.assert_not_called()
|
|
|
|
|
|
@pytest.mark.parametrize('brake_pressed', [False, True])
|
|
@pytest.mark.parametrize('delay', [0, 400_000_000])
|
|
def test_fresh_park_page_prepares_after_dwell_reset_and_keeps_actual_owner_gates(setup, monkeypatch, brake_pressed, delay):
|
|
game, ui, owner, clock = setup
|
|
ui.sm['carState'].brakePressed = brake_pressed
|
|
monkeypatch.setattr(native, 'ui_state', ui)
|
|
monkeypatch.setattr(native, 'live_witness', lambda: read(ui, clock.now))
|
|
monkeypatch.setattr(native, 'GameSession', lambda *_a, **_k: game)
|
|
monkeypatch.setattr(gui_app, '_nav_stack', [Mock()])
|
|
monkeypatch.setattr(gui_app, '_nav_stack_ticks', [])
|
|
gate = mature_gate() if delay else native.ParkGate()
|
|
page = native.SpacePilotPage(gate, Mock())
|
|
clock.refresh(ui, delay)
|
|
gui_app.push_widget(page)
|
|
try:
|
|
page._tick()
|
|
assert not page.closed and game.stage == native.Stage.VERIFYING
|
|
assert owner.snapshot().mode == 'auto' and not game.playable()
|
|
assert game.gate.since is None # Reader setup yields before using this frame's snapshot.
|
|
clock.refresh(ui)
|
|
page._tick()
|
|
assert owner.snapshot().mode == 'auto' and not game.playable()
|
|
assert game.gate.since == clock.now
|
|
for _ in range(9):
|
|
clock.refresh(ui)
|
|
page._tick()
|
|
assert owner.snapshot().mode == 'auto' and not game.playable()
|
|
clock.refresh(ui)
|
|
page._tick()
|
|
assert game.stage == native.Stage.STOPPING and owner.snapshot().mode == 'offroad'
|
|
assert not game.playable()
|
|
offroad(ui)
|
|
for _ in range(12):
|
|
clock.refresh(ui)
|
|
page._tick()
|
|
assert game.stage == native.Stage.PLAYING and game.playable()
|
|
assert not page.closed
|
|
finally:
|
|
page.hide_event()
|
|
|
|
|
|
@pytest.mark.parametrize('condition', ['park', 'brake', 'drive', 'speed', 'alert', 'stale', 'closed', 'other_widget'])
|
|
def test_actual_c4_startup_redirect_only_yields_to_fresh_open_parked_game(monkeypatch, condition):
|
|
from openpilot.starpilot.ui import runtime_app
|
|
from openpilot.selfdrive.ui.mici.layouts import main
|
|
ui = parked_ui()
|
|
ui.sm['carState'].brakePressed = condition == 'brake'
|
|
if condition == 'drive':
|
|
ui.sm['carState'].gearShifter = 'drive'
|
|
elif condition == 'speed':
|
|
ui.sm['carState'].standstill = False
|
|
ui.sm['carState'].vEgo = .2
|
|
elif condition == 'alert':
|
|
ui.sm['selfdriveState'].alertStatus = 'critical'
|
|
elif condition == 'stale':
|
|
ui.sm.logMonoTime['carState'] -= 1_000_000_000
|
|
monkeypatch.setattr(native, 'live_witness', lambda: read(ui))
|
|
monkeypatch.setattr(main, 'ui_state', ui)
|
|
monkeypatch.setattr(rl, 'get_time', lambda: 10.)
|
|
owner = object.__new__(runtime_app.StarMiciMainLayout)
|
|
owner._prev_onroad, owner._prev_standstill = True, True
|
|
owner._onroad_time_delay = 5.
|
|
owner._onboarding_window = Mock()
|
|
page = object.__new__(native.SpacePilotPage)
|
|
page.closed = condition == 'closed'
|
|
page.session = Mock(stage=native.Stage.CLOSED if page.closed else native.Stage.VERIFYING)
|
|
monkeypatch.setattr(gui_app, 'get_active_widget', lambda: object() if condition == 'other_widget' else page)
|
|
monkeypatch.setattr(gui_app, 'widget_in_stack', lambda _: False)
|
|
pop = Mock()
|
|
monkeypatch.setattr(gui_app, 'pop_widgets_to', pop)
|
|
owner._handle_transitions()
|
|
assert owner._onroad_time_delay is None
|
|
if condition in ('park', 'brake'):
|
|
pop.assert_not_called()
|
|
else:
|
|
assert pop.called
|
|
|
|
|
|
def test_unsafe_park_at_show_still_closes_before_request(setup, monkeypatch):
|
|
game, ui, owner, clock = setup
|
|
ui.sm['carState'].gearShifter = 'drive'
|
|
monkeypatch.setattr(native, 'ui_state', ui)
|
|
monkeypatch.setattr(native, 'live_witness', lambda: read(ui, clock.now))
|
|
monkeypatch.setattr(native, 'GameSession', lambda *_a, **_k: game)
|
|
monkeypatch.setattr(gui_app, '_nav_stack', [Mock()])
|
|
monkeypatch.setattr(gui_app, '_nav_stack_ticks', [])
|
|
page = native.SpacePilotPage(mature_gate(), Mock())
|
|
gui_app.push_widget(page)
|
|
page._tick()
|
|
assert page.closed and game.stage == native.Stage.CLOSED
|
|
assert owner.snapshot().mode == 'auto'
|