test(rawcli): end-to-end live-session regression for byte-line entry + completion

Drive the real PromptSession that run() builds (real key bindings + RawCompleter +
complete_while_typing) via a prompt_toolkit pipe input simulating TTY keystrokes, so
the actual rawcli input stack is exercised, not just the handler in isolation. Asserts
every input path yields the correct accepted line (hex byte line, binary via toggle,
mixed hex/binary, function call, command name) and that the completer is queried live
during binary entry (0 queries before the autocomplete fix, since buf.document= reset
complete_state).

prompt()'s internal asyncio.run() nulls the current-loop pointer; the helper saves and
restores it so the shared-loop `_run` convention in other test files is not poisoned.
Full suite stays green (1383).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
michael
2026-07-16 12:41:34 -07:00
parent 6e46916dd9
commit 4b5df12a76

View File

@@ -931,3 +931,65 @@ class TestKeyBindings:
digit[ch].handler(SimpleNamespace(current_buffer=buf, app=app, data=ch))
assert buf.text == "00000100"
assert len(fired) == 8 # one insert_text per digit (0 with document=)
class TestRawcliLiveSession:
"""End-to-end through the REAL PromptSession run() builds (real key bindings + RawCompleter +
complete_while_typing), driven by a prompt_toolkit pipe input that simulates TTY keystrokes.
Exercises the actual rawcli input stack; no device needed for the input layer."""
@staticmethod
def _run_keys(keys):
import asyncio
from prompt_toolkit import PromptSession
from prompt_toolkit.input import create_pipe_input
from prompt_toolkit.output import DummyOutput
from prompt_toolkit.key_binding import KeyBindings
from pm3py.cli.rawcli.entry import install_key_bindings
from pm3py.cli.rawcli.completer import RawCompleter
state = RawSession()
comp = RawCompleter(state)
queried = []
_orig = comp.get_completions
def _rec(document, event):
queried.append(document.text)
yield from _orig(document, event)
comp.get_completions = _rec
kb = KeyBindings()
install_key_bindings(kb, state)
# PromptSession.prompt() runs asyncio.run() internally, which nulls the current-loop
# pointer; save/restore it so the shared-loop `_run` convention in other test files isn't
# poisoned (see tests/test_pyws_plugin.py).
try:
prev_loop = asyncio.get_event_loop()
except RuntimeError:
prev_loop = None
try:
with create_pipe_input() as inp:
session = PromptSession(key_bindings=kb, completer=comp, complete_while_typing=True,
input=inp, output=DummyOutput())
inp.send_text(keys + "\r")
line = session.prompt("> ")
finally:
if prev_loop is not None and not prev_loop.is_closed():
asyncio.set_event_loop(prev_loop)
return line, queried
def test_accepted_line_across_input_paths(self):
# the change touched the digit handler; confirm every input path still yields the right line
toggle = "\x14" # Ctrl-t entry-mode toggle
for keys, expect in [("3004", "30 04"),
(toggle + "00000100", "00000100"),
("30" + toggle + "00000100", "30 00000100"),
("read(4)", "read(4)"),
("identify", "identify")]:
line, _ = self._run_keys(keys)
assert line == expect, f"{keys!r} -> {line!r} != {expect!r}"
def test_live_completion_fires_during_binary_entry(self):
# the fix, in the real session: the completer is queried with the byte-line text while
# typing binary (0 queries before the fix, since buf.document= reset complete_state)
line, queried = self._run_keys("\x14" + "00000100")
byte_q = [q for q in queried if q and q.replace(" ", "").strip("01") == ""]
assert line == "00000100"
assert byte_q, "completer never queried for byte-line text — live menu not firing"