mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-10 14:33:42 +08:00
Repair UI intake CI typing and spelling
This commit is contained in:
@@ -32,7 +32,7 @@ assert.deepEqual(pure(snapMove, { ...box, x: 253 }, targets, 6), { x: 253, y: 11
|
||||
assert.deepEqual(pure(snapMove, box, bounds, 0), { x: 34, y: 113, guides: [] })
|
||||
const target = (value, line = "start", kind = "widget", axis = "x", label = "Home") => ({ axis, value, line, kind, label })
|
||||
result = pure(snapMove, { ...box, x: 100 }, [target(104), target(146, "center", "center")], 6)
|
||||
assert.equal(result.x, 96) // Equal distance prefers bounds centre.
|
||||
assert.equal(result.x, 96) // Equal distance prefers bounds center.
|
||||
assert.equal(result.guides[0].kind, "center")
|
||||
for (const kinds of [["zone", "widget"], ["widget", "bounds"], ["bounds", "center"]]) {
|
||||
const first = target(104, "start", kinds[0]), second = target(96, "start", kinds[1])
|
||||
|
||||
@@ -236,7 +236,7 @@ invalidSnap.cancelDrag()
|
||||
const traySnap = editor().vm
|
||||
traySnap.layout.current_speed.enabled = false
|
||||
traySnap.startDrag("current_speed", largePoint(2400, 0), true)
|
||||
traySnap.moveDrag(largePoint(324, 550)) // Widget centre follows the tray pointer.
|
||||
traySnap.moveDrag(largePoint(324, 550)) // Widget center follows the tray pointer.
|
||||
assert.equal(traySnap.layout.current_speed.x, 30)
|
||||
assert.equal(traySnap.layout.current_speed.enabled, true)
|
||||
assert.ok(traySnap.state.snapGuides.length)
|
||||
@@ -309,14 +309,14 @@ try {
|
||||
else delete globalThis.localStorage
|
||||
}
|
||||
for (const [guides, expected] of [
|
||||
[[{ axis: "x", kind: "center" }, { axis: "y", kind: "center" }], "Centred on the screen"],
|
||||
[[{ axis: "x", kind: "center" }], "Centred across"],
|
||||
[[{ axis: "y", kind: "center" }], "Centred top to bottom"],
|
||||
[[{ axis: "x", kind: "center" }, { axis: "y", kind: "center" }], "Centered on the screen"],
|
||||
[[{ axis: "x", kind: "center" }], "Centered across"],
|
||||
[[{ axis: "y", kind: "center" }], "Centered top to bottom"],
|
||||
[[{ label: "Home" }, { label: "Home" }, { label: "Work" }], "Lined up with Home and Work"],
|
||||
[[{ label: "the edge" }, { label: "Home" }], "At the edge, lined up with Home"],
|
||||
[[{ axis: "x", kind: "center", label: "the centre" }, { axis: "y", kind: "bounds", label: "the edge" }], "Centred across, at the edge"],
|
||||
[[{ axis: "y", kind: "center", label: "the centre" }, { axis: "x", kind: "widget", label: "Home" }, { axis: "y", kind: "widget", label: "Work" }],
|
||||
"Centred top to bottom, lined up with Home"],
|
||||
[[{ axis: "x", kind: "center", label: "the center" }, { axis: "y", kind: "bounds", label: "the edge" }], "Centered across, at the edge"],
|
||||
[[{ axis: "y", kind: "center", label: "the center" }, { axis: "x", kind: "widget", label: "Home" }, { axis: "y", kind: "widget", label: "Work" }],
|
||||
"Centered top to bottom, lined up with Home"],
|
||||
]) {
|
||||
snapVm.state.snapGuides = guides
|
||||
assert.equal(snapVm.snapSummary, expected)
|
||||
@@ -545,7 +545,7 @@ assert.equal(backdropCalls.at(-1)[3], "braking")
|
||||
vm.selectProfile("large")
|
||||
vm.state.scene = "engaged"
|
||||
vm.color("text", "#ABCDEF81")
|
||||
assert.equal(backdropCalls.length, backdropCount + 5) // Colour edits change the real screen too.
|
||||
assert.equal(backdropCalls.length, backdropCount + 5) // Color edits change the real screen too.
|
||||
vm.color("text", "#ABCDEF80")
|
||||
const draggingCount = backdropCalls.length
|
||||
vm.startDrag("current_speed", event(500, 200))
|
||||
|
||||
@@ -16,13 +16,13 @@ export function snapTargets(profile, layout, id, visible) {
|
||||
const add = (box, kind, label, names) => {
|
||||
for (const axis of ["x", "y"]) {
|
||||
const values = lines(box, axis)
|
||||
// Keep the line role so widget centres cannot attract moving edges.
|
||||
// Keep the line role so widget centers cannot attract moving edges.
|
||||
for (const line of names) targets.push({ axis, value: values[line], kind, label, line })
|
||||
}
|
||||
}
|
||||
const bounds = profile.widgets[id].bounds || profile.bounds
|
||||
add(bounds, "bounds", "the edge", ["start", "end"])
|
||||
add(bounds, "center", "the centre", ["center"])
|
||||
add(bounds, "center", "the center", ["center"])
|
||||
for (const [key, widget] of Object.entries(profile.widgets)) {
|
||||
const at = layout[key]
|
||||
if (key === id || !at?.enabled || !Number.isFinite(at.x) || !Number.isFinite(at.y) ||
|
||||
|
||||
@@ -437,12 +437,12 @@ export const OnroadLayoutPage = {
|
||||
// A finger covers the preview chip, so touch drags show the alignment in the sticky save bar.
|
||||
snapInBar() { return !!(this.state.drag?.touch && this.state.snapGuides.length) },
|
||||
snapSummary() {
|
||||
const guides = this.state.snapGuides, centred = new Set(guides.filter(guide => guide.kind === "center").map(guide => guide.axis))
|
||||
if (centred.size === 2) return "Centred on the screen"
|
||||
// A centred axis is described once; the other axis still says what it lines up with.
|
||||
const labels = [...new Set(guides.filter(guide => !centred.has(guide.axis)).map(guide => guide.label))]
|
||||
const guides = this.state.snapGuides, centered = new Set(guides.filter(guide => guide.kind === "center").map(guide => guide.axis))
|
||||
if (centered.size === 2) return "Centered on the screen"
|
||||
// A centered axis is described once; the other axis still says what it lines up with.
|
||||
const labels = [...new Set(guides.filter(guide => !centered.has(guide.axis)).map(guide => guide.label))]
|
||||
const others = labels.filter(label => label !== "the edge").slice(0, 2)
|
||||
const text = [centred.has("x") ? "centred across" : centred.has("y") ? "centred top to bottom" : "",
|
||||
const text = [centered.has("x") ? "centered across" : centered.has("y") ? "centered top to bottom" : "",
|
||||
labels.includes("the edge") ? "at the edge" : "", others.length ? "lined up with " + others.join(" and ") : ""].filter(Boolean).join(", ")
|
||||
return text && text[0].toUpperCase() + text.slice(1)
|
||||
},
|
||||
@@ -1273,7 +1273,7 @@ export const OnroadLayoutPage = {
|
||||
<option v-for="scene in group.scenes" :key="scene.id" :value="scene.id">{{ scene.label }}</option>
|
||||
</optgroup>
|
||||
</GalaxySelect>
|
||||
<button class="gx-icon-btn gx-layout__bar-toggle" type="button" :aria-pressed="state.snapEnabled" :disabled="!!state.drag || !!state.layerDrag" aria-label="Snap to edges and centres" title="Snap to edges and centres" @click="toggleSnap"><i class="bi bi-magnet" aria-hidden="true"></i></button>
|
||||
<button class="gx-icon-btn gx-layout__bar-toggle" type="button" :aria-pressed="state.snapEnabled" :disabled="!!state.drag || !!state.layerDrag" aria-label="Snap to edges and centers" title="Snap to edges and centers" @click="toggleSnap"><i class="bi bi-magnet" aria-hidden="true"></i></button>
|
||||
<button class="gx-icon-btn gx-layout__bar-toggle" type="button" :aria-pressed="state.showEditingAids" aria-label="Show editing aids" title="Show editing aids" @click="toggleEditingAids"><i class="bi bi-bounding-box" aria-hidden="true"></i></button>
|
||||
<button v-if="!projection && profile.inputZones?.length" class="gx-icon-btn gx-layout__bar-toggle" type="button" :aria-pressed="state.showFavoriteZones" aria-label="Show Favorite tap areas" title="Show Favorite tap areas" @click="state.showFavoriteZones = !state.showFavoriteZones"><i class="bi bi-grid-3x3-gap" aria-hidden="true"></i></button>
|
||||
<button class="gx-icon-btn gx-layout__bar-toggle" type="button" :aria-expanded="state.tipsOpen" aria-controls="gx-layout-tips" aria-label="Preview tips and display behavior" title="Preview tips and display behavior" @click="state.tipsOpen = !state.tipsOpen"><i class="bi bi-question-circle" aria-hidden="true"></i></button>
|
||||
@@ -1362,7 +1362,7 @@ export const OnroadLayoutPage = {
|
||||
<span v-if="!projection && state.showFavoriteZones && state.showEditingAids && state.profile === 'compact'" class="gx-layout__stage-chip gx-layout__stage-chip--below" aria-hidden="true"><i class="bi bi-grid-3x3-gap"></i>Tap areas</span>
|
||||
<div v-show="state.tipsOpen" id="gx-layout-tips" class="gx-layout__tips">
|
||||
<p class="gx-note">Drag widgets to place them. Behind them is the comma's own screen for the chosen scene, including its border, road and alerts. Readings are samples.</p>
|
||||
<p class="gx-note">Widgets snap to the screen edges, the centre and other widgets. Turn snapping off with the magnet button, or hold Option (Alt) while dragging.</p>
|
||||
<p class="gx-note">Widgets snap to the screen edges, the center and other widgets. Turn snapping off with the magnet button, or hold Option (Alt) while dragging.</p>
|
||||
<p class="gx-note">General edit shows samples of all enabled widgets together. Driving scenes hide widgets that would be absent, and the eyes follow the scene. Eye changes stay with that scene; your General edit eyes are remembered.</p>
|
||||
<p class="gx-note">The stopped timer appears in General edit and Stopped timer. Turn off editing aids to see the screen without outlines and guides.</p>
|
||||
<p class="gx-note">The steering wheel and driver monitoring icon can be resized in each layout{{ projection ? ', and the map overlay takes any size and opacity' : '' }}. Existing display preferences and driving state still control when widgets appear.</p>
|
||||
|
||||
@@ -352,7 +352,7 @@ def projection_customization(document, base_customization):
|
||||
dx, dy = shifts.get(key, (0, 0))
|
||||
result['layouts']['large'][key] = {**placement, 'x': placement['x'] - dx, 'y': placement['y'] - dy}
|
||||
from openpilot.starpilot.ui.onroad_customization import rgba
|
||||
# Card opacity is a colour, not a placement: fold it into the fill and keep the native placement shape.
|
||||
# Card opacity is a color, not a placement: fold it into the fill and keep the native placement shape.
|
||||
opacity = result['layouts']['large'].get('cruise_limits', {}).pop('opacity', 100)
|
||||
if opacity != 100:
|
||||
red, green, blue, alpha = rgba(result, 'cardFill', 'large', 'cruise_limits')
|
||||
|
||||
@@ -251,7 +251,8 @@ def test_import_job_labels_metadata_free_package_with_offered_version(tmp_path,
|
||||
def test_status_reports_expiry(tmp_path):
|
||||
cert, root, key_pem, _ = build_identity(days=5)
|
||||
directory = tmp_path / "identity"
|
||||
apk_identity.install_identity({"phone-cert.pem": cert, "phone-key.pem": key_pem, "root-cert.pem": root}, {"subject": "O=CarService", "app_version": "16.2.1-release"}, directory)
|
||||
apk_identity.install_identity({"phone-cert.pem": cert, "phone-key.pem": key_pem, "root-cert.pem": root},
|
||||
{"subject": "O=CarService", "app_version": "16.2.1-release"}, directory)
|
||||
status = apk_identity.identity_status(directory)
|
||||
assert status["installed"] and status["days_left"] in (4, 5) and "renew" in status["warning"]
|
||||
assert status["app_version"] == "16.2.1-release"
|
||||
|
||||
@@ -168,7 +168,9 @@ def test_refresh_retries_before_giving_up_and_reports_pending(monkeypatch, cache
|
||||
assert waits == [apkpure.RETRY_DELAYS[0]]
|
||||
assert pending == [True, True] # the page waits while tries remain
|
||||
assert not apkpure.lookup_pending()
|
||||
assert apkpure.download_status()["url"] == LINK
|
||||
status = apkpure.download_status()
|
||||
assert status is not None
|
||||
assert status["url"] == LINK
|
||||
|
||||
|
||||
def test_refresh_is_single_flight(monkeypatch, cache):
|
||||
|
||||
@@ -3,6 +3,7 @@ import ast
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
from types import SimpleNamespace as NS
|
||||
from typing import Any, cast
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
@@ -29,8 +30,8 @@ def native_poll():
|
||||
source = Path(__file__).parents[3] / 'ui/runtime_app.py'
|
||||
tree = ast.parse(source.read_text())
|
||||
method = next(node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef) and node.name == '_poll_projection_bookmark')
|
||||
namespace = {'ui_state': NS(started=True, started_frame=1, sm=NS(logMonoTime={'carState': 50})),
|
||||
'display_message': Mock(return_value=object())}
|
||||
namespace: dict[str, Any] = {'ui_state': NS(started=True, started_frame=1, sm=NS(logMonoTime={'carState': 50})),
|
||||
'display_message': Mock(return_value=object()), 'cast': cast}
|
||||
exec(compile(ast.Module(body=[method], type_ignores=[]), str(source), 'exec'), namespace)
|
||||
return namespace
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ class Widget(TypedDict):
|
||||
defaultAnchor: NotRequired[str]
|
||||
colors: NotRequired[dict[str, str | None]]
|
||||
resizable: NotRequired[dict[str, int]]
|
||||
sourcesExtent: NotRequired[dict[str, float]]
|
||||
|
||||
|
||||
class LayoutProfile(TypedDict):
|
||||
@@ -151,7 +152,7 @@ PROFILES["compact"]["widgets"][STOPPED_TIMER_WIDGET] = _timer_widget
|
||||
# Restore the Dom road graphic in the gap between the speed card and main speed.
|
||||
# It has no background and appears only with a current selected source.
|
||||
CEM_GAUGE_WIDGET = 'cem_gauge'
|
||||
# The gauge draws centred in a 344 x 300 frame; its box is the part it can actually fill, at this offset inside
|
||||
# The gauge draws centered in a 344 x 300 frame; its box is the part it can actually fill, at this offset inside
|
||||
# that frame. Version 6 and older layouts saved the frame's corner and move by this offset when they load.
|
||||
CEM_GAUGE_FRAME = (344, 300)
|
||||
CEM_GAUGE_INSET = (44, 24)
|
||||
|
||||
@@ -5,6 +5,7 @@ HudRenderer and ExpButton. See the adjacent UI LICENSE for retained source.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
@@ -132,12 +133,12 @@ class UnifiedSpeedWidget:
|
||||
status_size = fit_size(status, FontRole.NORMAL, 30) if status else 30
|
||||
status_ink = self.fonts.horizontal_ink(status, FontRole.NORMAL, status_size) if status else None
|
||||
status_v = self.fonts.vertical_ink(status, FontRole.NORMAL, status_size) if status else None
|
||||
status_h = (status_v[1] - status_v[0]) if status else 0
|
||||
status_h = (cast(tuple[float, float], status_v)[1] - cast(tuple[float, float], status_v)[0]) if status else 0
|
||||
|
||||
footer_size = fit_size(footer, FontRole.NORMAL, 30) if footer else 30
|
||||
footer_ink = self.fonts.horizontal_ink(footer, FontRole.NORMAL, footer_size) if footer else None
|
||||
footer_v = self.fonts.vertical_ink(footer, FontRole.NORMAL, footer_size) if footer else None
|
||||
footer_h = (footer_v[1] - footer_v[0]) if footer else 0
|
||||
footer_h = (cast(tuple[float, float], footer_v)[1] - cast(tuple[float, float], footer_v)[0]) if footer else 0
|
||||
|
||||
if align == 'center':
|
||||
# Every line shares the card's horizontal center, and the entire stack is visually vertically centered.
|
||||
@@ -151,16 +152,21 @@ class UnifiedSpeedWidget:
|
||||
lambda y: self.fonts.draw(value, FontRole.SPEED, size, start(*number_ink), y - number_v[0] + dash_offset, text)
|
||||
))
|
||||
if has_value:
|
||||
unit_v = cast(tuple[float, float], unit_v)
|
||||
segments.append((
|
||||
unit_v[0], unit_h,
|
||||
lambda y: self.fonts.draw(shown.unit, FontRole.NORMAL, unit_size, start(*unit_ink), y - unit_v[0], muted)
|
||||
))
|
||||
if status:
|
||||
status_ink = cast(tuple[float, float], status_ink)
|
||||
status_v = cast(tuple[float, float], status_v)
|
||||
segments.append((
|
||||
status_v[0], status_h,
|
||||
lambda y: self.fonts.draw(status, FontRole.NORMAL, status_size, start(*status_ink), y - status_v[0], accent if active else muted)
|
||||
))
|
||||
if footer:
|
||||
footer_ink = cast(tuple[float, float], footer_ink)
|
||||
footer_v = cast(tuple[float, float], footer_v)
|
||||
segments.append((
|
||||
footer_v[0], footer_h,
|
||||
lambda y: self.fonts.draw(footer, FontRole.NORMAL, footer_size, start(*footer_ink), y - footer_v[0], footer_color)
|
||||
@@ -183,6 +189,7 @@ class UnifiedSpeedWidget:
|
||||
left = start(*number_ink)
|
||||
self.fonts.draw(value, FontRole.SPEED, size, left, number_y, text)
|
||||
if has_value:
|
||||
unit_v = cast(tuple[float, float], unit_v)
|
||||
unit_x = left + number_ink[0] - 14 - unit_ink[1] if align == 'right' else left + number_ink[1] + 14 - unit_ink[0]
|
||||
unit_y = number_y + number_v[1] - unit_v[1]
|
||||
self.fonts.draw(shown.unit, FontRole.NORMAL, unit_size, unit_x, unit_y, muted)
|
||||
|
||||
@@ -13,7 +13,7 @@ import math
|
||||
from pathlib import Path
|
||||
import time
|
||||
import threading
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
from collections.abc import Callable
|
||||
|
||||
import pyray as rl
|
||||
@@ -472,7 +472,7 @@ class StarShellSession:
|
||||
display_message(ui_state.sm, 'carState', now_ns, after_frame=ui_state.started_frame) is not None)
|
||||
stamp = int(ui_state.sm.logMonoTime['carState']) if authorized else 0
|
||||
for _ in range(receiver.drain(now_ns, stamp, authorized=authorized)):
|
||||
publish()
|
||||
cast('Callable[[], None]', publish)()
|
||||
|
||||
def _poll_wheel(self, now_ns):
|
||||
if getattr(ui_state, "replay_clock", None) is not None:
|
||||
|
||||
@@ -169,9 +169,10 @@ def test_galaxy_card_pixels_align_without_reading_font_textures():
|
||||
widget = UnifiedSpeedWidget(fonts)
|
||||
canvas = _Canvas.__new__(_Canvas)
|
||||
canvas.profile, canvas.viewport, canvas.fonts, canvas.target = Profile.LARGE, viewport, fonts, target
|
||||
canvas.view = SimpleNamespace(_ordered_widgets=lambda content, state, widget=widget, **kw: widget.render(content, state))
|
||||
canvas._settle = lambda state: None
|
||||
with patch.dict('openpilot.starpilot.ui.layout_preview_renderer.RUNTIME_PREVIEW_WIDGETS', large=('cruise_limits',)), \
|
||||
with patch.object(canvas, 'view',
|
||||
SimpleNamespace(_ordered_widgets=lambda content, state, widget=widget, **kw: widget.render(content, state)), create=True), \
|
||||
patch.object(canvas, '_settle', lambda state: None), \
|
||||
patch.dict('openpilot.starpilot.ui.layout_preview_renderer.RUNTIME_PREVIEW_WIDGETS', large=('cruise_limits',)), \
|
||||
patch.object(rl, 'load_image_from_texture', side_effect=mobile_readback) as readback:
|
||||
png = canvas.render_widget_art(state, document['layouts']['large'])
|
||||
# A saved 50% background must show on the first frame, without a slider
|
||||
|
||||
@@ -83,7 +83,9 @@ class LayoutPreviewRendererTests(unittest.TestCase):
|
||||
self.assertTrue(sample.visual_preview.layout_sample) # Drawn as the car would, without replay labels.
|
||||
from openpilot.starpilot.ui.onroad_conditional import status
|
||||
# The gauge still reads the real reason, but the mode caption itself is not drawn in the editor.
|
||||
family, detail, _ = status(sample)
|
||||
observed = status(sample)
|
||||
assert observed is not None
|
||||
family, detail, _ = observed
|
||||
self.assertEqual(family, "CEM")
|
||||
self.assertFalse(detail.startswith("PREVIEW"))
|
||||
self.assertEqual(sample_state("cem_lead", document).speed_mps, 0.0)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Scene sketches and eye controls must agree with the native driving presentation."""
|
||||
|
||||
import json
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import NoReturn, cast
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -23,7 +25,7 @@ from openpilot.starpilot.ui.unified_speed_presentation import resolve_unified_sp
|
||||
def test_every_scene_matches_native_border_mode_and_widget_visibility(target):
|
||||
node = shutil.which('node')
|
||||
if node is None:
|
||||
pytest.skip('Node is required for the browser/native scene contract')
|
||||
cast('Callable[[str], NoReturn]', pytest.skip)('Node is required for the browser/native scene contract')
|
||||
profile = Profile.COMPACT if target == 'compact' else Profile.LARGE
|
||||
document = default_document()
|
||||
if target == 'projection':
|
||||
|
||||
@@ -119,10 +119,11 @@ def test_large_wheel_art_is_drawn_inside_its_crop_with_or_without_sidebar(monkey
|
||||
document = projection_customization(projection, document)
|
||||
canvas = renderer._Canvas.__new__(renderer._Canvas)
|
||||
canvas.profile, canvas.viewport = Profile.LARGE, viewport
|
||||
canvas.fonts = NS(profile=Profile.LARGE)
|
||||
monkeypatch.setattr(canvas, 'fonts', NS(profile=Profile.LARGE), raising=False)
|
||||
canvas.target = NS(texture=object())
|
||||
view = OnroadView.__new__(OnroadView)
|
||||
view.fonts, view.projection_viewport = canvas.fonts, viewport
|
||||
monkeypatch.setattr(view, 'fonts', canvas.fonts, raising=False)
|
||||
view.projection_viewport = viewport
|
||||
view.steering_wheel = SteeringWheelWidget(Path('/unused'))
|
||||
view.steering_wheel._texture = object() # Real widget geometry, without loading a GL texture.
|
||||
canvas.view = view
|
||||
|
||||
@@ -116,7 +116,7 @@ def test_custom_placement_disable_layer_migration_preserve_existing_widgets():
|
||||
sample = state(customization=migrated)
|
||||
with patch.object(gauge, '_render_unified_road') as draw:
|
||||
gauge.render(sample)
|
||||
# The saved box is inset 44 x 24 inside the gauge's drawing frame, which is centred at (172, 145 - 60).
|
||||
# The saved box is inset 44 x 24 inside the gauge's drawing frame, which is centered at (172, 145 - 60).
|
||||
assert draw.call_args.args[1:3] == (960. - 44 + 172, 600. - 24 + 85)
|
||||
migrated['layouts']['large'][CEM_GAUGE_WIDGET]['enabled'] = False
|
||||
draw.reset_mock()
|
||||
|
||||
@@ -155,5 +155,5 @@ class DrawerNativeTest(unittest.TestCase):
|
||||
widget = UnifiedSpeedWidget(Mock(spec=BitmapFonts, **vars(fonts)))
|
||||
widget._draw_sources(rl.Rectangle(88, 545, 320, 76), state)
|
||||
self.assertEqual([text for text, _ in drawn], ['Dash', '56', 'Vision', '56'])
|
||||
self.assertIn(('56', FontRole.MEDIUM), drawn) # The accepted source's reading is emphasised.
|
||||
self.assertIn(('56', FontRole.MEDIUM), drawn) # The accepted source's reading is emphasized.
|
||||
self.assertIn(('56', FontRole.NORMAL), drawn)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Regression checks for qualified values, saved geometry and request feedback."""
|
||||
from dataclasses import replace
|
||||
from types import SimpleNamespace as N
|
||||
from unittest.mock import Mock
|
||||
|
||||
from openpilot.starpilot.ui.onroad_state import ObservationKind, OnroadInput, OnroadState, SlcActionKind, SlcUiRequest, SpeedLimitObservation
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, cast
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
@@ -197,7 +198,7 @@ def test_projection_bookmark_is_saved_once_without_rendering_starpilot(frame, mo
|
||||
source = Path(__file__).parents[3] / 'starpilot/ui/runtime_app.py'
|
||||
method = next(node for node in ast.walk(ast.parse(source.read_text()))
|
||||
if isinstance(node, ast.FunctionDef) and node.name == '_poll_projection_bookmark')
|
||||
namespace = {'ui_state': state, 'display_message': Mock(return_value=object())}
|
||||
namespace: dict[str, Any] = {'ui_state': state, 'display_message': Mock(return_value=object()), 'cast': cast}
|
||||
exec(compile(ast.Module(body=[method], type_ignores=[]), str(source), 'exec'), namespace)
|
||||
# Short Unix socket path for macOS. The real transport and native dispatch run
|
||||
# while the top widget is unrelated to StarPilot, or no widget is drawn at all.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "ab34357011d34391030f316138a394c2390c042aabf3cae579a0ac5bd0472920", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "e10d74d6606301a472810871c5349040d6db434e66388e860ac574d5d1f8d043", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user