mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-29 09:33:48 +08:00
Merge preview and CI fixes with Toyota filter support
This commit is contained in:
@@ -340,8 +340,9 @@ class _Canvas:
|
||||
for key in RUNTIME_PREVIEW_WIDGETS[str(self.profile)]:
|
||||
logical_width, logical_height = widget_size(state.customization, self.profile, key)
|
||||
# The restored compact US sign adds an offset row beyond its saved base size.
|
||||
if (self.profile == Profile.COMPACT and key == 'speed_limit' and
|
||||
not state.appearance.use_vienna_sign and resolve_unified_speed(state).offset_text):
|
||||
compact_offset = (self.profile == Profile.COMPACT and key == 'speed_limit' and
|
||||
not state.appearance.use_vienna_sign and bool(resolve_unified_speed(state).offset_text))
|
||||
if compact_offset:
|
||||
logical_height += 44
|
||||
position = positions[key]
|
||||
widget = PROFILES[str(self.profile)]['widgets'][key]
|
||||
@@ -349,6 +350,8 @@ class _Canvas:
|
||||
crop_y = max(0, math.floor(position['y'] - widget.get('visualInsetTop', 0)))
|
||||
offset_x, offset_y = crop_x - position['x'], crop_y - position['y']
|
||||
width, height = math.ceil(logical_width - offset_x), math.ceil(logical_height - offset_y)
|
||||
if compact_offset and 0 <= crop_y < canvas_height:
|
||||
height = min(height, canvas_height - crop_y)
|
||||
if x + width > 1024:
|
||||
x, y, row_height = 0, y + row_height, 0
|
||||
sprites[key] = {'x': x, 'y': y, 'width': width, 'height': height, 'offsetX': offset_x, 'offsetY': offset_y}
|
||||
|
||||
@@ -238,9 +238,10 @@ def test_compact_preview_sprite_keeps_the_sign_offset_arrow_and_override_cross()
|
||||
rl.init_window(128, 128, 'SLC preview crop fixture')
|
||||
canvas = _Canvas(Profile.COMPACT)
|
||||
try:
|
||||
for y, scene in product((20, 0), ('slc_pending', 'slc_retained')):
|
||||
for y, scene in product((20, 0, 64, 108), ('slc_pending', 'slc_retained')):
|
||||
document = default_document()
|
||||
document['layouts']['compact']['speed_limit']['y'] = y
|
||||
assert validate_document(document)['layouts']['compact']['speed_limit']['y'] == y
|
||||
with patch.dict('openpilot.starpilot.ui.layout_preview_renderer.RUNTIME_PREVIEW_WIDGETS', compact=('speed_limit',)):
|
||||
png = canvas.render_widget_art(sample_state(scene, document), document['layouts']['compact'])
|
||||
with Image.open(io.BytesIO(png)) as sheet:
|
||||
@@ -256,7 +257,9 @@ def test_compact_preview_sprite_keeps_the_sign_offset_arrow_and_override_cross()
|
||||
rl.unload_image(image)
|
||||
assert np.array_equal(direct[..., 3], np.asarray(rebuilt)[..., 3])
|
||||
assert slot['offsetX'] == slot['offsetY'] == 0
|
||||
assert (slot['width'], slot['height']) == (118, 176)
|
||||
assert (slot['width'], slot['height']) == (118, min(176, Profile.COMPACT.size[1] - y))
|
||||
if scene == 'slc_retained':
|
||||
assert np.any(direct[..., 3]), (y, scene)
|
||||
finally:
|
||||
canvas.close()
|
||||
if owns_context:
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "1b232d611440fc932c2a7cc7abb80e2f32fa1dad49f470afbf2121771de57960", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "74afd213d9a908bdda84394f6eb7485f856ce018fb9e3c4aee9345851669a752", "schema_version": 1}
|
||||
|
||||
@@ -238,6 +238,8 @@ def complete_mutation_artifact(path, summary, *, digest_out=None):
|
||||
|
||||
|
||||
def check_import_origins(python, env, output, *, full_catalog=False):
|
||||
sidecar = output.with_suffix(".origins.json")
|
||||
sidecar.unlink(missing_ok=True)
|
||||
script = """
|
||||
import json
|
||||
from pathlib import Path
|
||||
@@ -278,14 +280,15 @@ print(json.dumps({'msgq_visionipc': str(Path(msgq.visionipc.visionipc_pyx.__file
|
||||
"cereal_messaging": ROOT / "openpilot/cereal", "msgq_ipc": ROOT / "msgq_repo"}
|
||||
if full_catalog:
|
||||
expected["msgq_visionipc"] = ROOT / "msgq_repo"
|
||||
return all(isinstance(origins.get(name), str) and Path(origins[name]).is_relative_to(path)
|
||||
for name, path in expected.items())
|
||||
valid = all(isinstance(origins.get(name), str) and Path(origins[name]).is_relative_to(path)
|
||||
for name, path in expected.items())
|
||||
if valid:
|
||||
sidecar.write_text(json.dumps(origins, sort_keys=True) + "\n")
|
||||
return valid
|
||||
|
||||
|
||||
def imported_module_hashes(output):
|
||||
origins = {}
|
||||
for line in output.read_text().splitlines():
|
||||
origins.update(json.loads(line))
|
||||
origins = json.loads(output.with_suffix(".origins.json").read_text())
|
||||
return {name: {"path": path, "sha256": hashlib.sha256(Path(path).read_bytes()).hexdigest()}
|
||||
for name, path in origins.items()}
|
||||
|
||||
|
||||
@@ -56,8 +56,42 @@ class TestSafetyQualificationRunner(TestCase):
|
||||
Path(path).write_bytes(b"native")
|
||||
log = root / "imports.log"
|
||||
log.write_text("\n".join(json.dumps({name: path}) for name, path in modules.items()) + "\n")
|
||||
log.with_suffix(".origins.json").write_text(json.dumps(modules))
|
||||
self.assertEqual(set(qualification.imported_module_hashes(log)), set(modules))
|
||||
|
||||
def test_validated_origins_sidecar_retains_multiline_stdout_and_stderr(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
root = Path(td)
|
||||
prefixes = {"opendbc": "opendbc_repo", "panda": "panda", "msgq": "msgq_repo",
|
||||
"openpilot": "openpilot", "libsafety_py": "opendbc_repo",
|
||||
"can_parser": "opendbc_repo", "can_packer": "opendbc_repo", "can_dbc": "opendbc_repo",
|
||||
"cereal_log": "openpilot/cereal", "cereal_messaging": "openpilot/cereal", "msgq_ipc": "msgq_repo",
|
||||
"msgq_visionipc": "msgq_repo"}
|
||||
origins = {name: str(root / prefix / name) for name, prefix in prefixes.items()}
|
||||
for path in origins.values():
|
||||
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(path).write_bytes(b"actual provenance fixture")
|
||||
first = {name: path for name, path in origins.items() if name != "msgq_visionipc"}
|
||||
stdout = json.dumps(first) + "\n" + json.dumps({"msgq_visionipc": origins["msgq_visionipc"]}) + "\n"
|
||||
warning = "kj/filesystem-disk-unix.c++:1734: warning: PWD environment variable doesn't match current directory\n"
|
||||
log = root / "imports.log"
|
||||
sidecar = log.with_suffix(".origins.json")
|
||||
with patch.object(qualification, "ROOT", root), patch.object(qualification.subprocess, "run",
|
||||
return_value=subprocess.CompletedProcess([], 0, stdout, warning)):
|
||||
self.assertTrue(qualification.check_import_origins(sys.executable, {}, log, full_catalog=True))
|
||||
self.assertEqual(log.read_text(), stdout + warning)
|
||||
self.assertEqual(json.loads(sidecar.read_text()), origins)
|
||||
self.assertEqual(set(qualification.imported_module_hashes(log)), set(origins))
|
||||
foreign = dict(origins, msgq_visionipc="/foreign/visionipc.so")
|
||||
for code, invalid in ((0, json.dumps(foreign)), (0, "bad stdout"), (1, stdout)):
|
||||
sidecar.write_text(json.dumps(origins)) # Never reuse earlier success after a failed probe.
|
||||
with self.subTest(code=code, stdout=invalid), patch.object(qualification, "ROOT", root), \
|
||||
patch.object(qualification.subprocess, "run", return_value=subprocess.CompletedProcess([], code, invalid, warning)):
|
||||
self.assertFalse(qualification.check_import_origins(sys.executable, {}, log, full_catalog=True))
|
||||
self.assertFalse(sidecar.exists())
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
qualification.imported_module_hashes(log)
|
||||
|
||||
def test_source_identity_includes_gate_and_workflow_inputs(self):
|
||||
identity = qualification.source_identity()
|
||||
self.assertFalse(any("/obj/" in path or "/gen/" in path for path in identity))
|
||||
@@ -456,7 +490,25 @@ class TestSafetyQualificationRunner(TestCase):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
output = Path(td)
|
||||
gate = qualification.Gate("coverage", output, "/usr/bin/python3")
|
||||
code, text = gate.run("timeout", [sys.executable, "-c", "import time; print('partial', flush=True); time.sleep(10)"], timeout=0.05)
|
||||
ready = output / "child-ready"
|
||||
script = "import pathlib,sys,time; print('partial', flush=True); pathlib.Path(sys.argv[1]).touch(); time.sleep(10)"
|
||||
spawn = subprocess.Popen
|
||||
|
||||
def started_child(*args, **kwargs):
|
||||
proc = spawn(*args, **kwargs)
|
||||
deadline = time.monotonic() + 5
|
||||
while not ready.exists():
|
||||
if proc.poll() is not None or time.monotonic() >= deadline:
|
||||
if proc.poll() is None:
|
||||
os.killpg(proc.pid, signal.SIGKILL)
|
||||
proc.communicate()
|
||||
self.fail("Timeout fixture never became ready")
|
||||
time.sleep(.005)
|
||||
return proc
|
||||
|
||||
# Exercise the real timeout after the child's partial output is actually flushed.
|
||||
with patch.object(qualification.subprocess, "Popen", side_effect=started_child):
|
||||
code, text = gate.run("timeout", [sys.executable, "-c", script, str(ready)], timeout=0.05)
|
||||
self.assertEqual(code, 124)
|
||||
self.assertIn("Timed out", text)
|
||||
self.assertIn("partial", (output / "timeout.log").read_text())
|
||||
|
||||
Reference in New Issue
Block a user