Skip to content

Commit caf8e7c

Browse files
committed
ControlLogs(fix): register control contexts in diagnostics
1 parent b406953 commit caf8e7c

File tree

2 files changed

+50
-26
lines changed

2 files changed

+50
-26
lines changed

conftest.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import pytest
2121
from _pytest.doctest import DoctestItem
2222

23+
from libtmux._internal.engines.control_protocol import CommandContext, ControlProtocol
2324
from libtmux.pane import Pane
2425
from libtmux.pytest_plugin import USING_ZSH
2526
from libtmux.server import Server
@@ -123,7 +124,7 @@ def control_sandbox(
123124
def control_client_logs(
124125
control_sandbox: t.ContextManager[Server],
125126
tmp_path_factory: pytest.TempPathFactory,
126-
) -> t.Iterator[tuple[subprocess.Popen[str], pathlib.Path, pathlib.Path]]:
127+
) -> t.Iterator[tuple[subprocess.Popen[str], ControlProtocol]]:
127128
"""Spawn a raw tmux -C client against the sandbox and log stdout/stderr."""
128129
base = tmp_path_factory.mktemp("ctrl_logs")
129130
stdout_path = base / "control_stdout.log"
@@ -141,16 +142,22 @@ def control_client_logs(
141142
]
142143
# Ensure ctrltest exists
143144
server.cmd("new-session", "-d", "-s", "ctrltest")
145+
stdout_path.open("w+", buffering=1)
146+
stderr_f = stderr_path.open("w+", buffering=1)
144147
proc = subprocess.Popen(
145148
cmd,
146149
stdin=subprocess.PIPE,
147-
stdout=stdout_path.open("w+", buffering=1),
148-
stderr=stderr_path.open("w+", buffering=1),
150+
stdout=subprocess.PIPE,
151+
stderr=stderr_f,
149152
text=True,
150153
bufsize=1,
151154
)
155+
proto = ControlProtocol()
156+
# tmux -C will emit a %begin/%end pair for this initial attach-session;
157+
# queue a matching context so the parser has a pending command.
158+
proto.register_command(CommandContext(argv=list(cmd)))
152159
try:
153-
yield proc, stdout_path, stderr_path
160+
yield proc, proto
154161
finally:
155162
with contextlib.suppress(Exception):
156163
if proc.stdin:

tests/test_control_client_logs.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,63 @@
1010

1111

1212
@pytest.mark.engines(["control"])
13-
@pytest.mark.xfail(
14-
reason="log file may be empty due to control client buffering",
15-
strict=True,
16-
)
1713
def test_control_client_lists_clients(
18-
control_client_logs: tuple[t.Any, t.Any, t.Any],
14+
control_client_logs: tuple[t.Any, ControlProtocol],
1915
) -> None:
2016
"""Raw control client should report itself with control-mode flag."""
21-
proc, stdout_path, _stderr_path = control_client_logs
17+
proc, proto = control_client_logs
2218

2319
assert proc.stdin is not None
20+
list_ctx = CommandContext(
21+
argv=[
22+
"tmux",
23+
"list-clients",
24+
"-F",
25+
"#{client_pid} #{client_flags} #{session_name}",
26+
],
27+
)
28+
proto.register_command(list_ctx)
29+
detach_ctx = CommandContext(argv=["tmux", "detach-client"])
30+
proto.register_command(detach_ctx)
2431
proc.stdin.write('list-clients -F"#{client_pid} #{client_flags} #{session_name}"\n')
32+
proc.stdin.write("detach-client\n")
2533
proc.stdin.flush()
2634

27-
lines = stdout_path.read_text().splitlines()
28-
assert any(len(line.split()) >= 2 and "C" in line.split()[1] for line in lines)
35+
stdout_data, _ = proc.communicate(timeout=5)
36+
for line in stdout_data.splitlines():
37+
proto.feed_line(line.rstrip("\n"))
38+
39+
assert list_ctx.done.wait(timeout=0.5)
40+
result = proto.build_result(list_ctx)
41+
saw_control_flag = any(
42+
len(parts := line.split()) >= 2
43+
and ("C" in parts[1] or "control-mode" in parts[1])
44+
for line in result.stdout
45+
)
46+
assert saw_control_flag
2947

3048

3149
@pytest.mark.engines(["control"])
3250
def test_control_client_capture_stream_parses(
33-
control_client_logs: tuple[t.Any, t.Any, t.Any],
51+
control_client_logs: tuple[t.Any, ControlProtocol],
3452
) -> None:
3553
"""Ensure ControlProtocol can parse raw stream from the logged control client."""
36-
proc, stdout_path, _stderr_path = control_client_logs
54+
proc, proto = control_client_logs
3755
assert proc.stdin is not None
3856

57+
display_ctx = CommandContext(argv=["tmux", "display-message", "-p", "hello"])
58+
proto.register_command(display_ctx)
59+
detach_ctx = CommandContext(argv=["tmux", "detach-client"])
60+
proto.register_command(detach_ctx)
3961
proc.stdin.write("display-message -p hello\n")
62+
proc.stdin.write("detach-client\n")
4063
proc.stdin.flush()
4164

42-
lines = stdout_path.read_text().splitlines()
43-
44-
proto = ControlProtocol()
45-
ctx = CommandContext(argv=["tmux", "display-message", "-p", "hello"])
46-
proto.register_command(ctx)
65+
stdout_data, _ = proc.communicate(timeout=5)
4766

48-
# Feed lines; expect begin/payload/end
49-
for line in lines:
50-
proto.feed_line(line)
67+
for line in stdout_data.splitlines():
68+
proto.feed_line(line.rstrip("\n"))
5169

52-
# If tmux emitted begin/end, ctx should be done
53-
if ctx.done.is_set():
54-
result = proto.build_result(ctx)
55-
assert "hello" in result.stdout
70+
assert display_ctx.done.wait(timeout=0.5)
71+
result = proto.build_result(display_ctx)
72+
assert "hello" in result.stdout or "hello" in "".join(result.stdout)

0 commit comments

Comments
 (0)