mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 21:41:13 +00:00
[AZ-558] Route C8 outbound encoder bytes through MavlinkTransport seam
All FC adapter outbound MAVLink bytes now go through the AZ-401 MavlinkTransport seam (NoopMavlinkTransport in replay, SerialMavlinkTransport in live). New helpers in _outbound_mavlink_payloads.py extract encode/pack/seq-bump so the four AP _send sites and the iNav statustext _send site become encode -> pack -> transport.write. TlogReplayFcAdapter emits real AP-shape MAVLink bytes through the injected NoopMavlinkTransport, satisfying replay protocol Invariant 5 and unblocking AZ-401 AC-9. Closes AZ-558. Also unskips AZ-401 AC-9 and AZ-404 AC-4b. Live wire output remains byte-identical (proven via two-instance MAVLink byte-equivalence tests). AST scan asserts no .mav.<name>_send( calls remain in the retrofit set (AP / iNav / tlog adapters). Out of scope (logged in review): GCS adapter retrofit; airborne live strategy registration that would activate the SerialMavlinkTransport factory injection path. Tests: 2110 passed, 92 environmental skips, 1 unrelated pre-existing macOS cold-start flake deselected. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -266,19 +266,84 @@ class _ModeBranchScanner(ast.NodeVisitor):
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# AC-4b: Encoder byte-equality (BLOCKED on AZ-558)
|
||||
# AC-4b: Encoder byte-equality (closed by AZ-558)
|
||||
|
||||
|
||||
@pytest.mark.skip(
|
||||
reason=(
|
||||
"AC-4b blocked on AZ-558: C8 encoders still bypass the "
|
||||
"MavlinkTransport seam by calling mav.*_send directly. The "
|
||||
"CapturingMavlinkTransport fixture in _helpers.py is ready; "
|
||||
"this test unskips when AZ-558 lands."
|
||||
def test_ac4_encoder_byte_equality_via_transport_seam() -> None:
|
||||
"""AZ-404 AC-4b / AZ-558 AC-2: encoders write the same bytes through
|
||||
the :class:`MavlinkTransport` seam regardless of mode.
|
||||
|
||||
Constructive equivalence: pymavlink's ``MAVLink.send`` and our
|
||||
retrofit (``mav.X_encode → msg.pack(mav) → transport.write``)
|
||||
both invoke ``pack`` on the same MAVLink instance with the same
|
||||
pre-bump ``mav.seq``. Run both paths with two MAVLink instances
|
||||
initialised identically; the resulting bytes are equal by
|
||||
construction.
|
||||
|
||||
AZ-558's unit suite (``test_az558_outbound_transport_seam.py``)
|
||||
covers this for every retrofitted message kind (GPS_INPUT,
|
||||
NAMED_VALUE_FLOAT, STATUSTEXT, multi-message seq alignment); this
|
||||
e2e variant double-checks the contract holds against the live
|
||||
pymavlink ``MAVLink.send`` integration so a future pymavlink
|
||||
upgrade that changes the framing surface fails this test loudly.
|
||||
"""
|
||||
# Arrange
|
||||
import io
|
||||
|
||||
from pymavlink.dialects.v20 import ardupilotmega as _mavlink
|
||||
|
||||
from gps_denied_onboard.components.c8_fc_adapter._outbound_mavlink_payloads import (
|
||||
encode_gps_input,
|
||||
send_via_transport,
|
||||
)
|
||||
)
|
||||
def test_ac4_encoder_byte_equality() -> None:
|
||||
raise NotImplementedError("blocked on AZ-558 — see skip reason")
|
||||
|
||||
from tests.e2e.replay._helpers import CapturingMavlinkTransport
|
||||
|
||||
legacy_buf = io.BytesIO()
|
||||
legacy = _mavlink.MAVLink(file=legacy_buf, srcSystem=1, srcComponent=1)
|
||||
new = _mavlink.MAVLink(file=None, srcSystem=1, srcComponent=1)
|
||||
capture = CapturingMavlinkTransport()
|
||||
|
||||
# Three deterministic GPS_INPUT messages of varying intensity to
|
||||
# cover the encoded-payload range.
|
||||
samples = [
|
||||
dict(
|
||||
time_usec=t * 100_000,
|
||||
gps_id=0,
|
||||
ignore_flags=0,
|
||||
time_week_ms=0,
|
||||
time_week=0,
|
||||
fix_type=3,
|
||||
lat=int((50.0 + t * 0.0001) * 1e7),
|
||||
lon=int((30.0 + t * 0.0001) * 1e7),
|
||||
alt=100.0 + t * 0.5,
|
||||
hdop=0.0,
|
||||
vdop=0.0,
|
||||
vn=0.0,
|
||||
ve=0.0,
|
||||
vd=0.0,
|
||||
speed_accuracy=0.0,
|
||||
horiz_accuracy=2.0 + t * 0.1,
|
||||
vert_accuracy=0.0,
|
||||
satellites_visible=10,
|
||||
yaw=0,
|
||||
)
|
||||
for t in range(3)
|
||||
]
|
||||
|
||||
# Act
|
||||
for s in samples:
|
||||
legacy.gps_input_send(*s.values())
|
||||
msg = encode_gps_input(new, **s)
|
||||
send_via_transport(new, msg, capture)
|
||||
|
||||
# Assert
|
||||
assert legacy_buf.getvalue() == capture.captured_concat, (
|
||||
"AZ-404 AC-4b violated: encoder byte stream differs between "
|
||||
"MAVLink.send and encode→pack→transport.write paths"
|
||||
)
|
||||
assert capture.bytes_written() > 0
|
||||
assert legacy.seq == new.seq
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user