mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 17:13:54 +08:00
Align UI fixtures and complete Galaxy setting descriptions
This commit is contained in:
@@ -6,6 +6,9 @@ This presentation catalog supplies the purpose before any genuine prerequisite.
|
||||
from openpilot.starpilot.ui.long_profile_feature import CATEGORY_HELP, VALUE_HELP
|
||||
|
||||
DESCRIPTIONS = {
|
||||
'lane_change:turn_signal_guidance': 'Uses your turn signal to help the model choose a turning path at intersections.',
|
||||
'AlphaLongitudinalEnabled': ('Requests StarPilot speed control instead of factory ACC. May disable Automatic Emergency Braking. ' +
|
||||
'Applies after the next startup; exact vehicle takeover checks remain required.'),
|
||||
'lane_change:enabled': 'Enables lane-change assistance after you signal. With Automatic Lane Changes off, give the wheel a nudge to begin.',
|
||||
'lane_change:pace': 'Sets how quickly steering moves into the adjacent lane. Higher values change lanes more quickly; lower values make steering gentler.',
|
||||
'lane_change:speed': 'Sets the lowest speed at which lane-change assistance can begin.',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -183,11 +183,12 @@ def make_session(monkeypatch, request, profile, camera):
|
||||
monkeypatch.setattr(runtime_app, 'ui_state', NS(params=object(), CP=None, sm={}))
|
||||
monkeypatch.delenv('SP_ONROAD_VISUAL_PREVIEW', raising=False)
|
||||
def view(*_, camera_layer, **__):
|
||||
return NS(camera_layer=camera_layer, onroad=NS(unified_speed=NS(source_bounds=Mock()), compact_sidebar=NS()))
|
||||
return NS(camera_layer=camera_layer, onroad=NS(unified_speed=NS(source_bounds=Mock()), compact_sidebar=NS(), jetlink_status=Mock()))
|
||||
monkeypatch.setattr(runtime_app, 'ShellView', view)
|
||||
session = runtime_app.StarShellSession(profile, camera)
|
||||
if receiver := session._projection_bookmarks:
|
||||
request.addfinalizer(receiver.close)
|
||||
for owner in (session._projection_bookmarks, session._projection_slc, session.onroad_map, session.onroad_jetlink):
|
||||
if owner is not None:
|
||||
request.addfinalizer(owner.close)
|
||||
return session
|
||||
|
||||
|
||||
|
||||
@@ -534,7 +534,7 @@ def test_frozen_default_draw_geometry_and_neutral_colors():
|
||||
assert circle.call_args.args[:3] == (1684, 171, 96)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("speed,size", [(65, 58), (105, 58)])
|
||||
@pytest.mark.parametrize("speed,size", [(65, 50), (105, 42)])
|
||||
def test_compact_us_sign_keeps_camera_visible_at_saved_position(speed, size):
|
||||
document = default_document()
|
||||
document["layouts"]["compact"]["speed_limit"].update(x=200, y=30)
|
||||
@@ -547,12 +547,12 @@ def test_compact_us_sign_keeps_camera_visible_at_saved_position(speed, size):
|
||||
with patch.object(rl, "draw_rectangle_rounded") as fill, \
|
||||
patch.object(rl, "draw_rectangle_rounded_lines_ex") as outline:
|
||||
hud._speed_limit_sign(state)
|
||||
fill.assert_called_once()
|
||||
fill.assert_not_called()
|
||||
outline.assert_called_once()
|
||||
rect = outline.call_args.args[0]
|
||||
assert (rect.x, rect.y, rect.width, rect.height) == (202, 32, 114, 128)
|
||||
assert (rect.x, rect.y, rect.width, rect.height) == (210, 38, 100, 116)
|
||||
assert [(call.args[0], call.args[2]) for call in foreground_calls(fonts.draw)] == [("SPEED", 20), ('LIMIT', 20), (str(speed), size)]
|
||||
assert foreground_calls(fonts.draw)[0].args[3:5] == (254, 41)
|
||||
assert foreground_calls(fonts.draw)[0].args[3:5] == (255, 48)
|
||||
|
||||
|
||||
def test_compact_drag_keeps_animation_state_and_separate_origins():
|
||||
|
||||
@@ -186,7 +186,7 @@ def test_valid_vision_limit_has_persistent_sign_without_action_receipt():
|
||||
shown = replace(road(), appearance=replace(road().appearance, show_speed_limit_sign=True), speed_limit=valid)
|
||||
with patch.object(rl, 'draw_rectangle_rounded') as fill, patch.object(rl, 'draw_rectangle_rounded_lines_ex') as outline:
|
||||
renderer._speed_limit_sign(shown)
|
||||
fill.assert_called_once()
|
||||
fill.assert_not_called()
|
||||
outline.assert_called_once()
|
||||
assert [call.args[0] for call in foreground_calls(fonts.draw)] == ['SPEED', 'LIMIT', '55']
|
||||
outline.reset_mock()
|
||||
@@ -194,9 +194,10 @@ def test_valid_vision_limit_has_persistent_sign_without_action_receipt():
|
||||
outline.assert_not_called()
|
||||
renderer._speed_limit_sign(replace(road(speed_limit=valid), appearance=replace(shown.appearance, show_speed_limit_sign=False)))
|
||||
outline.assert_not_called()
|
||||
with patch.object(renderer._speed_limit_widget.shapes, 'circle') as circle:
|
||||
with patch.object(rl, 'draw_circle') as circle, patch.object(rl, 'draw_ring') as ring:
|
||||
renderer._speed_limit_sign(replace(shown, appearance=replace(shown.appearance, use_vienna_sign=True)))
|
||||
circle.assert_called_once()
|
||||
ring.assert_called_once()
|
||||
|
||||
|
||||
def test_pending_confirmation_omits_redundant_speed_heading():
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
@@ -26,6 +27,13 @@ from openpilot.starpilot.ui.unified_speed_presentation import resolve_unified_sp
|
||||
MPH = .44704
|
||||
|
||||
|
||||
def published_cruise_mph(mph, *, software_target=True):
|
||||
"""Expected card precision, then the Float32 carState wire seen by the planner."""
|
||||
kph = round(round(mph) * 1.609344, 1) if software_target else mph * 1.609344
|
||||
wire_kph, = struct.unpack('<f', struct.pack('<f', kph))
|
||||
return wire_kph * (1 / 3.6) / MPH
|
||||
|
||||
|
||||
@pytest.mark.parametrize('document', [False, True])
|
||||
@pytest.mark.parametrize('offset,delta', [(-25, 0), (-30, 0), (-24, 2), (-5, 0)])
|
||||
def test_negative_offsets_cannot_remove_the_native_slc_ceiling(tmp_path, document, offset, delta):
|
||||
@@ -43,7 +51,7 @@ def test_negative_offsets_cannot_remove_the_native_slc_ceiling(tmp_path, documen
|
||||
assert not output.message.slcState.hasCeiling
|
||||
assert output.message.slcState.acceptedAdjustedLimit == pytest.approx(adjusted)
|
||||
assert output.message.slcState.effectiveClusterTarget == pytest.approx(adjusted)
|
||||
assert target == pytest.approx(round(adjusted / MPH))
|
||||
assert target == pytest.approx(published_cruise_mph(round(adjusted / MPH)), rel=0, abs=1e-10)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('confirm', [False, True])
|
||||
@@ -71,9 +79,9 @@ def test_live_offset_edits_keep_the_accepted_limit_and_native_ceiling(tmp_path,
|
||||
assert output.result.state.acceptance.accepted == accepted
|
||||
assert not output.message.slcState.hasPending
|
||||
assert not output.message.slcState.hasCeiling
|
||||
assert target == pytest.approx(45 if source_gap else 45 + offset)
|
||||
assert target == pytest.approx(published_cruise_mph(45 if source_gap else 45 + offset), rel=0, abs=1e-10)
|
||||
if source_gap:
|
||||
assert drive.step(2500)[2] == pytest.approx(40)
|
||||
assert drive.step(2500)[2] == pytest.approx(published_cruise_mph(40), rel=0, abs=1e-10)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('accepted_first', [False, True])
|
||||
@@ -98,7 +106,7 @@ def test_offset_edits_invalidate_old_prompts_without_accepting_a_new_limit(tmp_p
|
||||
assert output.message.slcState.decisionId != old_id
|
||||
assert output.message.slcState.presentationId != old.message.slcState.presentationId
|
||||
assert output.message.slcState.hasAccepted == accepted_first
|
||||
assert target == pytest.approx(45 if accepted_first else 70)
|
||||
assert target == pytest.approx(published_cruise_mph(45 if accepted_first else 70, software_target=accepted_first), rel=0, abs=1e-10)
|
||||
|
||||
|
||||
def test_offset_edits_preserve_rejected_limits(tmp_path):
|
||||
@@ -119,7 +127,7 @@ def test_offset_edits_preserve_rejected_limits(tmp_path):
|
||||
assert not output.message.slcState.hasPending
|
||||
assert output.result.state.acceptance.rejected == rejected
|
||||
assert output.message.slcState.acceptedSpeedLimit == pytest.approx(45 * MPH)
|
||||
assert target == pytest.approx(45)
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10)
|
||||
|
||||
|
||||
class RetentionDrive:
|
||||
@@ -201,12 +209,12 @@ def test_compact_rejection_or_timeout_dismisses_actions_and_retains_the_native_c
|
||||
first, _, _ = drive.step(0)
|
||||
approved, _, target = drive.step(50, request=Action(drive.runtime.session_id, 1, first.message.slcState.decisionId, 0, 'accept'))
|
||||
accepted = approved.result.state.acceptance.accepted
|
||||
assert target == pytest.approx(50)
|
||||
assert target == pytest.approx(published_cruise_mph(50), rel=0, abs=1e-10)
|
||||
|
||||
proposed, view, target = drive.step(100, limit=limit)
|
||||
assert slc_controls(Profile.COMPACT, view) == ()
|
||||
assert [control.label for control in slc_controls(Profile.COMPACT, replace(view, slc_prompt_open=True))] == ['Accept', 'Decline']
|
||||
assert target == pytest.approx(50)
|
||||
assert target == pytest.approx(published_cruise_mph(50), rel=0, abs=1e-10)
|
||||
request = None
|
||||
if answer == 'reject':
|
||||
# Use the visible Keep button, including its normal dispatcher validation.
|
||||
@@ -228,7 +236,7 @@ def test_compact_rejection_or_timeout_dismisses_actions_and_retains_the_native_c
|
||||
assert view.alert.size == AlertSize.NONE
|
||||
assert view.speed_limit.kind == ObservationKind.VALID and view.speed_limit.action_enabled
|
||||
assert slc_controls(Profile.COMPACT, view) == ()
|
||||
assert target == pytest.approx(50)
|
||||
assert target == pytest.approx(published_cruise_mph(50), rel=0, abs=1e-10)
|
||||
|
||||
# The former recovery area cannot claim a tap or adopt the rejected limit.
|
||||
emitted = []
|
||||
@@ -244,7 +252,7 @@ def test_compact_rejection_or_timeout_dismisses_actions_and_retains_the_native_c
|
||||
assert candidate in output.result.state.acceptance.rejected
|
||||
assert not output.message.slcState.hasPending
|
||||
assert slc_controls(Profile.COMPACT, view) == ()
|
||||
assert target == pytest.approx(50)
|
||||
assert target == pytest.approx(published_cruise_mph(50), rel=0, abs=1e-10)
|
||||
assert physical_actions.pending_confirmation(drive.ui.sm, NOW + (elapsed + delay) * 1_000_000,
|
||||
long_active=True) is None
|
||||
# A different candidate still gets the normal two-choice confirmation.
|
||||
@@ -252,7 +260,7 @@ def test_compact_rejection_or_timeout_dismisses_actions_and_retains_the_native_c
|
||||
assert output.message.slcState.hasPending
|
||||
controls = slc_controls(Profile.COMPACT, replace(view, slc_prompt_open=True))
|
||||
assert [control.request.kind for control in controls] == [SlcActionKind.ACCEPT, SlcActionKind.REJECT]
|
||||
assert target == pytest.approx(50)
|
||||
assert target == pytest.approx(published_cruise_mph(50), rel=0, abs=1e-10)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('keep', [False, True])
|
||||
@@ -271,7 +279,7 @@ def test_maps_loss_keeps_latest_display_independent_of_control_fallback(tmp_path
|
||||
elif loss == 'externalGpsStale':
|
||||
changes = {'gpsMonoTime': BOOT - 300_000_000}
|
||||
output, view, target = drive.step(251, **changes)
|
||||
assert target == pytest.approx(45)
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10)
|
||||
assert output.message.slcState.acceptedSpeedLimit == pytest.approx(45 * MPH)
|
||||
assert not output.message.slcState.hasCeiling
|
||||
assert output.message.slcState.hasAcceptedAdjustedLimit
|
||||
@@ -291,16 +299,16 @@ def test_long_gap_confirmation_and_recovery_keep_the_accepted_limit(tmp_path, an
|
||||
assert first.message.slcState.hasPending and not first.message.slcState.hasAccepted
|
||||
drive.step(50, request=Action(drive.runtime.session_id, 1, first.message.slcState.decisionId, 0, 'accept'))
|
||||
_, _, target = drive.step(100, status='noCoverage')
|
||||
assert target == pytest.approx(45)
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10)
|
||||
_, view, target = drive.step(35_000, received=False)
|
||||
assert target == pytest.approx(45)
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10)
|
||||
assert resolve_unified_speed(view).status == 'Unavailable'
|
||||
pending, _, target = drive.step(40_000, limit=limit, wayId=124)
|
||||
assert target == pytest.approx(45) and pending.message.slcState.hasPending
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10) and pending.message.slcState.hasPending
|
||||
action = None if answer == 'timeout' else Action(drive.runtime.session_id, 2, pending.message.slcState.decisionId, 0, answer)
|
||||
drive.step(71_000 if answer == 'timeout' else 40_050, limit=limit, wayId=124, request=action)
|
||||
output, view, target = drive.step(300_000, received=False)
|
||||
assert target == pytest.approx(limit if answer == 'accept' else 45)
|
||||
assert target == pytest.approx(published_cruise_mph(limit if answer == 'accept' else 45), rel=0, abs=1e-10)
|
||||
assert not output.message.slcState.hasPending
|
||||
assert resolve_unified_speed(view).posted_text == str(limit)
|
||||
# A dead SLC publisher cannot make the widget claim retained control indefinitely.
|
||||
@@ -315,14 +323,14 @@ def test_unchanged_map_speed_does_not_accept_new_identity_or_keep_old_prompt(tmp
|
||||
first, _, _ = drive.step(0)
|
||||
accepted, _, _ = drive.step(50, request=Action(drive.runtime.session_id, 1, first.message.slcState.decisionId, 0, 'accept'))
|
||||
unchanged, _, target = drive.step(100, wayId=124)
|
||||
assert target == pytest.approx(45)
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10)
|
||||
assert not unchanged.message.slcState.hasPending
|
||||
assert unchanged.result.acceptance.history_write is None
|
||||
assert unchanged.result.acceptance.state.accepted == accepted.result.acceptance.state.accepted
|
||||
pending, _, _ = drive.step(150, limit=50, wayId=124)
|
||||
assert pending.message.slcState.hasPending
|
||||
recovered, _, target = drive.step(200)
|
||||
assert target == pytest.approx(45) and recovered.message.slcState.hasPending
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10) and recovered.message.slcState.hasPending
|
||||
assert not drive.step(250)[0].result.errors
|
||||
|
||||
|
||||
@@ -331,7 +339,7 @@ def test_retention_respects_lower_max_secondary_source_and_reset(tmp_path):
|
||||
drive.step(0)
|
||||
drive.sm['carState'].vCruise = drive.sm['carState'].vCruiseCluster = 40 * MPH * 3.6
|
||||
_, view, target = drive.step(300, received=False)
|
||||
assert target == pytest.approx(40)
|
||||
assert target == pytest.approx(published_cruise_mph(40, software_target=False), rel=0, abs=1e-10)
|
||||
assert resolve_unified_speed(view).status == 'Unavailable'
|
||||
drive.runtime.reset()
|
||||
output, _, _ = drive.step(400, received=False)
|
||||
@@ -339,7 +347,7 @@ def test_retention_respects_lower_max_secondary_source_and_reset(tmp_path):
|
||||
secondary = RetentionDrive(tmp_path / 'secondary', dashboard=True)
|
||||
secondary.step(0)
|
||||
output, _, target = secondary.step(300, status='noCoverage')
|
||||
assert target == pytest.approx(45)
|
||||
assert target == pytest.approx(published_cruise_mph(45), rel=0, abs=1e-10)
|
||||
assert output.message.slcState.automaticAdoptionPending
|
||||
assert output.message.slcState.acceptedSource == 'map'
|
||||
|
||||
@@ -352,6 +360,7 @@ def test_compact_sign_keeps_accepted_value_without_pulsing_on_recovery(tmp_path)
|
||||
fonts = Mock()
|
||||
fonts.measure.return_value = Mock(width=20, height=20)
|
||||
fonts.vertical_ink.return_value = (0, 20)
|
||||
fonts.horizontal_ink.return_value = (0, 20)
|
||||
renderer = CompactHudRenderer(fonts, Path('/unused'))
|
||||
with patch('openpilot.starpilot.ui.onroad_compact_widgets.rl.draw_rectangle_rounded_lines_ex'), \
|
||||
patch('openpilot.starpilot.ui.onroad_compact_widgets.rl.draw_circle'), \
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "c9ec54f6350ee809cabd08299a450e30256f15dfa211fec98f77b59494699c85", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "22e0802e6262911b9e33f947e76ef062d02177488c9ef902f1b8a6f028ddf2a4", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user