Replace progpib.py stub with a proper installable package
Builds a modern, testable API for the Prologix GPIB-ETHERNET controller (GPIB-USB structurally supported but untested, no hardware available): Transport abstraction (Ethernet now, serial later), GpibController for the full "++" command set, a clean-room netfinder discovery client that fixes a real chr()/struct-format bug present in vendortools/nfutil.py, and a CLI (discover/config/info/terminal). vendortools/, pty-gpib-emulator/, sampledata.txt and the manual PDF are kept untouched as reference material. Depends on the sibling bits package (editable, not yet pinned -- see its own commit) for MAC formatting and IEEE-488.2 status-byte bit access. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
97
tests/test_discovery_codec.py
Normal file
97
tests/test_discovery_codec.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Pure, offline tests for the netfinder packet codec in progpib.discovery.
|
||||
|
||||
Fixture bytes are taken from sampledata.txt, a captured Python 3 REPL
|
||||
session against real Prologix GPIB-ETHERNET hardware (all using the same
|
||||
sequence number, 30327 = 0x7677, reused across the whole session).
|
||||
"""
|
||||
|
||||
from progpib.discovery import (
|
||||
_HEADER_LEN,
|
||||
DeviceInfo,
|
||||
IpType,
|
||||
NfCommand,
|
||||
NfResult,
|
||||
_pack_assignment,
|
||||
_pack_header,
|
||||
_pack_identify,
|
||||
_unpack_assignment_reply,
|
||||
_unpack_identify_reply,
|
||||
_unpack_test_reply,
|
||||
)
|
||||
|
||||
SEQ = 30327
|
||||
MAC = b"\x00!i\x01-\xaf"
|
||||
|
||||
|
||||
def test_pack_identify_matches_captured_bytes():
|
||||
assert _pack_identify(SEQ) == b"Z\x00vw\xff\xff\xff\xff\xff\xff\x00\x00"
|
||||
|
||||
|
||||
def test_unpack_identify_reply_matches_captured_session():
|
||||
reply = (
|
||||
b"Z\x01vw\x00!i\x01-\xaf\x00\x00\x00%\x0572\x01\x00\x00\xc0\xa8\x00\xe5"
|
||||
b"\xff\xff\xff\x00\xc0\xa8\x00\x01\x01\x06\x06\x00\x01\x03\x00\x00"
|
||||
b"\x01\x03\x00\x00600" + b"\x00" * 29
|
||||
)
|
||||
assert len(reply) == 76
|
||||
info = _unpack_identify_reply(reply)
|
||||
|
||||
assert info == DeviceInfo(
|
||||
mac=MAC,
|
||||
mode=1,
|
||||
alert=0,
|
||||
ip_type=IpType.DYNAMIC,
|
||||
ip_address="192.168.0.229",
|
||||
netmask="255.255.255.0",
|
||||
gateway="192.168.0.1",
|
||||
app_version="1.6.6.0",
|
||||
boot_version="1.3.0.0",
|
||||
hw_version="1.3.0.0",
|
||||
name="600",
|
||||
uptime_days=37,
|
||||
uptime_hours=5,
|
||||
uptime_minutes=55,
|
||||
uptime_seconds=50,
|
||||
)
|
||||
assert info.mac_str == "00:21:69:01:2d:af"
|
||||
|
||||
|
||||
def test_pack_assignment_matches_captured_bytes():
|
||||
# This regression-pins the chr()->bytes([...]) fix: vendortools/nfutil.py's
|
||||
# MkAssignment raises struct.error on this exact call (see sampledata.txt),
|
||||
# since struct.pack's 'c' format needs a length-1 bytes object, not chr()'s str.
|
||||
packet = _pack_assignment(
|
||||
SEQ,
|
||||
MAC,
|
||||
IpType.DYNAMIC,
|
||||
"192.168.0.229",
|
||||
"255.255.255.0",
|
||||
"192.168.0.1",
|
||||
)
|
||||
expected = (
|
||||
b"Z\x02vw\x00!i\x01-\xaf\x00\x00\x00\x00\x00\x00\xc0\xa8\x00\xe5"
|
||||
b"\xff\xff\xff\x00\xc0\xa8\x00\x01" + b"\x00" * 32
|
||||
)
|
||||
assert len(expected) == 60
|
||||
assert packet == expected
|
||||
|
||||
|
||||
def test_unpack_assignment_reply_matches_captured_bytes():
|
||||
# This datagram is captured mid-session in sampledata.txt as a reply to
|
||||
# an earlier Assignment request (both used seq 30327): header id 0x03 =
|
||||
# ASSIGNMENT_REPLY, followed by a single 0x00 result byte = NF_SUCCESS.
|
||||
reply = b"Z\x03vw\x00!i\x01-\xaf\x00\x00\x00\x00\x00\x00"
|
||||
assert _unpack_assignment_reply(reply) == NfResult.SUCCESS
|
||||
|
||||
|
||||
def test_pack_and_unpack_test_reply_roundtrip():
|
||||
# sampledata.txt has no clean captured TestReply (the byte string shown
|
||||
# there after a MkTest() call is actually a stale AssignmentReply still
|
||||
# sitting in the receive socket's buffer from an earlier command in that
|
||||
# REPL session -- see test_unpack_assignment_reply_matches_captured_bytes
|
||||
# above). So this is a synthetic round-trip instead of a captured fixture.
|
||||
text = "Prologix GPIB-ETHERNET Sim 1.0"
|
||||
body = text.encode("ascii").ljust(32, b"\x00")
|
||||
reply = _pack_header(NfCommand.TEST_REPLY, SEQ, MAC) + body
|
||||
assert len(reply) == _HEADER_LEN + 32
|
||||
assert _unpack_test_reply(reply) == text
|
||||
Reference in New Issue
Block a user