import struct from pm3py.core.transport import encode_ng_frame, decode_response_frame, encode_mix_frame, _crc_to_wire from pm3py.core.protocol import Cmd, CMD_PREAMBLE_MAGIC, CMD_POSTAMBLE_NOCRC, RESP_PREAMBLE_MAGIC, RESP_POSTAMBLE_NOCRC, crc16_a def test_encode_ng_frame_ping_no_payload(): frame = encode_ng_frame(Cmd.PING, b"") # 8 byte preamble + 0 payload + 2 postamble = 10 bytes assert len(frame) == 10 magic, length_ng, cmd = struct.unpack_from(" OLD data frames -> ACK ----------------------------- import asyncio from unittest.mock import AsyncMock from pm3py.core.transport import PM3Transport, PM3Response from pm3py.core.protocol import PM3_CMD_DATA_SIZE def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) def _bigbuf_frame(offset, data): # OLD-format CMD_DOWNLOADED_BIGBUF: oldarg=[offset, len, tracelen], data padded to 512 return PM3Response(cmd=int(Cmd.DOWNLOADED_BIGBUF), status=0, reason=0, ng=False, data=data + b"\x00" * (PM3_CMD_DATA_SIZE - len(data)), oldarg=[offset, len(data), 4242]) def _ack(): return PM3Response(cmd=int(Cmd.ACK), status=0, reason=0, ng=False, data=b"", oldarg=[1, 0, 0]) def _transport_with(frames): t = PM3Transport("/dev/null") t.send_frame = AsyncMock() t._read_any_response = AsyncMock(side_effect=frames) return t def test_download_bulk_count_terminated(): payload = bytes((i * 7) % 256 for i in range(1224)) t = _transport_with([_bigbuf_frame(0, payload[0:512]), _bigbuf_frame(512, payload[512:1024]), _bigbuf_frame(1024, payload[1024:1224]), _ack()]) got = _run(t.download_bulk(Cmd.DOWNLOAD_BIGBUF, 0, 1224, Cmd.DOWNLOADED_BIGBUF, Cmd.ACK)) assert got == payload # exactly count bytes, reassembled in order assert t.send_frame.call_count == 1 # ONE request, not one per chunk def test_download_bulk_ack_terminated_early(): # device has fewer bytes than requested -> stops at the ACK, returns what came payload = bytes(range(200)) t = _transport_with([_bigbuf_frame(0, payload), _ack()]) got = _run(t.download_bulk(Cmd.DOWNLOAD_BIGBUF, 0, 99999, Cmd.DOWNLOADED_BIGBUF, Cmd.ACK)) assert got == payload def test_download_bulk_uses_oldarg_length_not_padding(): # the OLD frame is always 512 bytes; only oldarg[1] bytes are valid (rest is padding) t = _transport_with([_bigbuf_frame(0, b"\xAA\xBB\xCC"), _ack()]) got = _run(t.download_bulk(Cmd.DOWNLOAD_BIGBUF, 0, 3, Cmd.DOWNLOADED_BIGBUF, Cmd.ACK)) assert got == b"\xAA\xBB\xCC" # trimmed to oldarg length, not 512 of padding def test_download_bulk_drains_stale_input_first(): # a bulk download must flush stale bytes before sending, so OLD frames start byte-aligned payload = bytes(range(100)) t = _transport_with([_bigbuf_frame(0, payload), _ack()]) calls = [] async def fake_drain(quiet=0.08): calls.append("drain") return 7 orig_send = t.send_frame async def tracking_send(frame): calls.append("send") return await orig_send(frame) t._drain_input = fake_drain t.send_frame = tracking_send _run(t.download_bulk(Cmd.DOWNLOAD_BIGBUF, 0, 100, Cmd.DOWNLOADED_BIGBUF, Cmd.ACK)) assert calls[0] == "drain" and calls[1] == "send" # drained BEFORE the request went out def _resp_frame(cmd, data=b"", status=0): # build a no-CRC NG response frame the way the device sends one length_ng = len(data) | 0x8000 pre = struct.pack("