Files
IQ.Pilot/iqpilot/tools/test_tools_local_route.py
2026-09-02 16:46:45 -05:00

121 lines
3.8 KiB
Python

"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import os
import pty
import select
import shutil
import subprocess
import time
from pathlib import Path
import pytest
import iqpilot.cereal.messaging as messaging
from iqpilot.cereal import log
TOOLS_DIR = Path(__file__).parent
CABANA_BIN = TOOLS_DIR / "cabana" / "_cabana"
JOTPLUGGLER_BIN = TOOLS_DIR / "jotpluggler" / "jotpluggler"
DONGLE_ID = "0000000000000000"
TIMESTAMP = "2024-01-01--00-00-00"
ROUTE = f"{DONGLE_ID}|{TIMESTAMP}"
def write_rlog(path: Path, n_frames: int = 200):
with open(path, "wb") as f:
cp = messaging.new_message('carParams')
cp.carParams.carFingerprint = "TOYOTA_RAV4_TSS2"
cp.carParams.brand = "toyota"
f.write(cp.to_bytes())
for i in range(n_frames):
msg = messaging.new_message('can', 2)
msg.logMonoTime = int(i * 1e7)
for j, addr in enumerate((0x1D2, 0x260)):
msg.can[j].address = addr
msg.can[j].src = 0
msg.can[j].dat = bytes([i % 256] * 8)
f.write(msg.to_bytes())
@pytest.fixture(scope="module")
def local_route(tmp_path_factory):
data_dir = tmp_path_factory.mktemp("routes")
for seg in range(2):
seg_dir = data_dir / f"{DONGLE_ID}|{TIMESTAMP}--{seg}"
seg_dir.mkdir()
write_rlog(seg_dir / "rlog")
return data_dir
def run(cmd, timeout=180):
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, env=os.environ.copy(),
cwd=TOOLS_DIR.parent)
def cabana_command(*args):
command = [str(CABANA_BIN), *args]
if os.uname().sysname == "Linux" and shutil.which("xvfb-run"):
command = ["xvfb-run", "-a", *command]
return command
def test_jotpluggler_renders_a_local_route(local_route, tmp_path):
assert JOTPLUGGLER_BIN.exists(), "jotpluggler not built"
out = tmp_path / "plot.png"
result = run([str(JOTPLUGGLER_BIN), "--data-dir", str(local_route),
"--sync-load", "--output", str(out), ROUTE])
assert result.returncode == 0, result.stdout + result.stderr
assert out.is_file(), result.stdout + result.stderr
assert out.stat().st_size > 5000, f"suspiciously small render: {out.stat().st_size} bytes"
def test_cabana_loads_a_local_route(local_route):
assert CABANA_BIN.exists(), "cabana not built"
master, slave = pty.openpty()
proc = subprocess.Popen(cabana_command("--data_dir", str(local_route), "--no-vipc", ROUTE),
stdout=slave, stderr=slave,
env=os.environ.copy(), cwd=TOOLS_DIR.parent)
os.close(slave)
loaded = f"loaded route {ROUTE} with 2 valid segments"
output = bytearray()
deadline = time.monotonic() + 60
try:
while time.monotonic() < deadline:
ready, _, _ = select.select([master], [], [], min(1, deadline - time.monotonic()))
if not ready:
if proc.poll() is not None:
break
continue
try:
output.extend(os.read(master, 4096))
except OSError:
break
if loaded.encode() in output:
break
finally:
proc.kill()
proc.wait()
os.close(master)
out = output.decode(errors="replace")
assert "failed to load route" not in out, out
assert "invalid route format" not in out, out
assert loaded in out, out
def test_replay_logreader_reports_load_stats(local_route):
assert shutil.which("python3") is not None
header = (TOOLS_DIR / "replay" / "logreader.h").read_text()
for accessor in ("compressed_size", "decompressed_size", "download_seconds",
"decompress_seconds", "parse_seconds"):
assert f"{accessor}() const" in header
def test_can_capnp_field_matches_extractor_codegen():
assert 'busTimeDEPRECATED' in log.CanData.schema.fields
gen = (TOOLS_DIR / "jotpluggler" / "generate_event_extractors.py").read_text()
assert "getBusTimeDEPRECATED()" in gen