feat(trace): scriptable on_frame callback + quiet mode + SimSession entries
SniffSession/SimSession.start_15693 gain on_frame(frame) and quiet kwargs (defaults preserve today's print behavior). Each frame is a plain dict {protocol, direction, timestamp, duration, data, data_hex, decoded[, crc_fail]} with no ANSI. SimSession now accumulates frames (entries property + clear(), mirroring SniffSession) and auto-streams when trace or on_frame is set; printing is gated on trace and not quiet. TraceFormatter.decode() exposes the annotation without color; format()/print() untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
104
tests/test_trace_scripting.py
Normal file
104
tests/test_trace_scripting.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""Scriptable trace access: on_frame callback, quiet mode, entries accumulator."""
|
||||
import struct
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pm3py.trace.format import TraceFormatter
|
||||
from pm3py.trace import decode_15693
|
||||
|
||||
INV = bytes.fromhex("260100f60a") # ISO15693 INVENTORY request + CRC
|
||||
|
||||
|
||||
def _fmt(mode="sniff"):
|
||||
return TraceFormatter(mode=mode, decoder=decode_15693, crc_len=2, is_tty=False)
|
||||
|
||||
|
||||
def _sniff_trace(direction, payload):
|
||||
# CMD_HF_SNIFF_STREAM entry: {protocol,direction,duration,timestamp} + payload
|
||||
return struct.pack("<BBHI", 0, direction, 0, 0) + payload
|
||||
|
||||
|
||||
def _sim_trace_frame(direction, payload):
|
||||
from pm3py.core.transport import RESP_POSTAMBLE_NOCRC
|
||||
from pm3py.core.protocol import RESP_PREAMBLE_MAGIC
|
||||
from pm3py.sim.sim_session import CMD_HF_ISO15693_SIM_TRACE
|
||||
data = bytes([direction]) + payload
|
||||
header = struct.pack("<IHbbH", RESP_PREAMBLE_MAGIC, len(data) | 0x8000,
|
||||
0, 0, CMD_HF_ISO15693_SIM_TRACE)
|
||||
return header + data + struct.pack("<H", RESP_POSTAMBLE_NOCRC)
|
||||
|
||||
|
||||
def _run_sim_reader(session, frame):
|
||||
ser = MagicMock()
|
||||
ser.in_waiting = len(frame)
|
||||
n = 0
|
||||
def side(_):
|
||||
nonlocal n
|
||||
n += 1
|
||||
if n == 1:
|
||||
return frame
|
||||
session._active = False
|
||||
return b""
|
||||
ser.read.side_effect = side
|
||||
session._trace_reader(ser)
|
||||
|
||||
|
||||
# ---------------- SniffSession ----------------
|
||||
|
||||
def test_sniff_on_frame_and_quiet(capsys):
|
||||
from pm3py.sniff.session import SniffSession
|
||||
s = SniffSession(MagicMock())
|
||||
s._formatter = _fmt()
|
||||
frames = []
|
||||
s._on_frame = frames.append
|
||||
s._quiet = True
|
||||
|
||||
s._handle_trace(_sniff_trace(0, INV))
|
||||
|
||||
assert len(frames) == 1 and len(s.entries) == 1
|
||||
f = frames[0]
|
||||
assert f["direction"] == 0
|
||||
assert f["data"] == INV and f["data_hex"] == INV.hex()
|
||||
assert f["decoded"] == "INVENTORY"
|
||||
assert capsys.readouterr().out == "" # quiet -> no console output
|
||||
|
||||
|
||||
def test_sniff_default_still_prints(capsys):
|
||||
from pm3py.sniff.session import SniffSession
|
||||
s = SniffSession(MagicMock())
|
||||
s._formatter = _fmt()
|
||||
# defaults preserved: _on_frame=None, _quiet=False
|
||||
s._handle_trace(_sniff_trace(0, INV))
|
||||
assert len(s.entries) == 1
|
||||
assert capsys.readouterr().out != "" # prints as before
|
||||
|
||||
|
||||
# ---------------- SimSession ----------------
|
||||
|
||||
def test_sim_on_frame_and_quiet(capsys):
|
||||
from pm3py.sim.sim_session import SimSession
|
||||
s = SimSession()
|
||||
s._formatter = _fmt(mode="sim")
|
||||
s._active = True
|
||||
frames = []
|
||||
s._on_frame = frames.append
|
||||
s._print_frames = False # quiet
|
||||
|
||||
_run_sim_reader(s, _sim_trace_frame(0, INV))
|
||||
|
||||
assert len(frames) == 1 and len(s.entries) == 1
|
||||
f = frames[0]
|
||||
assert f["direction"] == 0 and f["data"] == INV
|
||||
assert f["data_hex"] == INV.hex()
|
||||
assert f["decoded"] == "INVENTORY" and f["crc_fail"] is False
|
||||
assert capsys.readouterr().out == ""
|
||||
|
||||
|
||||
def test_sim_entries_accumulate_and_clear():
|
||||
from pm3py.sim.sim_session import SimSession
|
||||
s = SimSession()
|
||||
s._formatter = _fmt(mode="sim")
|
||||
s._active = True
|
||||
_run_sim_reader(s, _sim_trace_frame(0, INV))
|
||||
assert len(s.entries) == 1
|
||||
s.clear()
|
||||
assert s.entries == []
|
||||
Reference in New Issue
Block a user