Align UI fixtures and complete Galaxy setting descriptions

This commit is contained in:
firestar5683
2026-10-23 06:36:08 -05:00
parent 43007a6318
commit 6d201df86a
7 changed files with 44 additions and 30 deletions
@@ -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
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "c9ec54f6350ee809cabd08299a450e30256f15dfa211fec98f77b59494699c85", "schema_version": 1}
{"protected_inventory_sha256": "22e0802e6262911b9e33f947e76ef062d02177488c9ef902f1b8a6f028ddf2a4", "schema_version": 1}