cpp对齐官方
This commit is contained in:
108
tests/test_capture_fixtures.py
Normal file
108
tests/test_capture_fixtures.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import socket
|
||||
import struct
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from go1_pro_sdk import Blowfish, parse_low_state
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CAPTURES = ROOT / "data/captures"
|
||||
STATE_FILE = ROOT / "go1_pro_sdk/_data/blowfish_state.bin"
|
||||
|
||||
if not CAPTURES.is_dir():
|
||||
pytest.skip("private data/captures fixtures are not available", allow_module_level=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def blowfish():
|
||||
return Blowfish.from_state_file(str(STATE_FILE))
|
||||
|
||||
|
||||
def _pcap_udp_packets(path):
|
||||
packets = []
|
||||
with path.open("rb") as stream:
|
||||
header = stream.read(24)
|
||||
assert len(header) == 24
|
||||
magic = header[:4]
|
||||
assert magic in (b"\xd4\xc3\xb2\xa1", b"\xa1\xb2\xc3\xd4")
|
||||
endian = "<" if magic == b"\xd4\xc3\xb2\xa1" else ">"
|
||||
linktype = struct.unpack(endian + "I", header[20:24])[0]
|
||||
assert linktype == 113 # Linux cooked capture v1
|
||||
|
||||
while True:
|
||||
packet_header = stream.read(16)
|
||||
if not packet_header:
|
||||
break
|
||||
assert len(packet_header) == 16
|
||||
_, _, captured_length, _ = struct.unpack(endian + "IIII", packet_header)
|
||||
frame = stream.read(captured_length)
|
||||
assert len(frame) == captured_length
|
||||
assert struct.unpack(">H", frame[14:16])[0] == 0x0800
|
||||
ip_offset = 16
|
||||
ip_header_length = (frame[ip_offset] & 0x0F) * 4
|
||||
assert frame[ip_offset + 9] == 17
|
||||
udp_offset = ip_offset + ip_header_length
|
||||
source_port, target_port, udp_length, _ = struct.unpack(
|
||||
">HHHH", frame[udp_offset:udp_offset + 8]
|
||||
)
|
||||
payload = frame[udp_offset + 8:udp_offset + udp_length]
|
||||
packets.append((
|
||||
socket.inet_ntoa(frame[ip_offset + 12:ip_offset + 16]),
|
||||
socket.inet_ntoa(frame[ip_offset + 16:ip_offset + 20]),
|
||||
source_port,
|
||||
target_port,
|
||||
payload,
|
||||
))
|
||||
return packets
|
||||
|
||||
|
||||
def test_real_lowcmd_full_cipher_plain_pair(blowfish):
|
||||
cipher = (CAPTURES / "real_lowcmd.bin").read_bytes()
|
||||
plain = (CAPTURES / "real_lowcmd_decrypted.bin").read_bytes()
|
||||
assert len(cipher) == len(plain) == 616
|
||||
assert blowfish.decrypt_ecb(cipher) == plain
|
||||
assert blowfish.encrypt_ecb(plain) == cipher
|
||||
assert plain[:22] == bytes.fromhex(
|
||||
"feefff00000000000000000000000000000000003ac0"
|
||||
)
|
||||
assert plain[22] == 0x0A # This fixture contains active servo commands.
|
||||
|
||||
|
||||
def test_real_lowcmd_pcap_matches_extracted_first_frame():
|
||||
packets = _pcap_udp_packets(CAPTURES / "real_lowcmd.pcap")
|
||||
assert len(packets) == 1406
|
||||
assert {(src, dst, sport, dport) for src, dst, sport, dport, _ in packets} == {
|
||||
("192.168.123.161", "192.168.123.10", 8008, 8007)
|
||||
}
|
||||
assert {len(payload) for *_, payload in packets} == {616}
|
||||
assert packets[0][-1] == (CAPTURES / "real_lowcmd.bin").read_bytes()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"filename,soc,temperatures",
|
||||
[
|
||||
("mcu_response.bin", 76, [52, 42, 40]),
|
||||
("mcu_response_new.bin", 19, [79, 50, 51]),
|
||||
],
|
||||
)
|
||||
def test_real_lowstate_samples(blowfish, filename, soc, temperatures):
|
||||
cipher = (CAPTURES / filename).read_bytes()
|
||||
assert len(cipher) == 858
|
||||
plain = blowfish.decrypt_ecb(cipher[:856])
|
||||
assert plain[:4] == b"\xfe\xef\xff\x00"
|
||||
state = parse_low_state(plain)
|
||||
assert state.bms.SOC == soc
|
||||
assert [motor.temperature for motor in state.motorState[:3]] == temperatures
|
||||
assert state.wirelessRemote[:2] == b"\x55\x51"
|
||||
|
||||
|
||||
def test_memory_dump_reproduces_packaged_blowfish_state():
|
||||
with tarfile.open(CAPTURES / "blowfish_dump.tar.gz", "r:gz") as archive:
|
||||
member = archive.extractfile("./rw_7f86a1f000.bin")
|
||||
assert member is not None
|
||||
memory = member.read()
|
||||
extracted = memory[0x232190:0x232190 + 4168]
|
||||
assert extracted == STATE_FILE.read_bytes()
|
||||
Reference in New Issue
Block a user