Skip to content

Commit 32921ba

Browse files
committed
ControlMode(test[encoding]): Reproduce non-UTF-8 stdout decoding
why: ControlMode opens its own tmux -C subprocess with text=True, so it has the same locale-decoding risk as tmux_cmd but through a separate stdout surface. what: - Add a strict xfail regression test for non-ASCII control protocol output under LC_CTYPE=C - Drive display-message through the control client and assert FORMAT_SEPARATOR survives stdout decoding
1 parent d8b91ae commit 32921ba

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

tests/test_control_mode.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22

33
from __future__ import annotations
44

5+
import locale
6+
import os
7+
import select
8+
import sys
59
import typing as t
610

11+
import pytest
12+
713
from libtmux._internal.control_mode import ControlMode
14+
from libtmux.formats import FORMAT_SEPARATOR
815

916
if t.TYPE_CHECKING:
1017
from libtmux.server import Server
@@ -60,3 +67,41 @@ def test_control_mode_client_name_matches_spawned_client(
6067
assert first.client_name != second.client_name
6168
assert (str(first._proc.pid), first.client_name) in clients
6269
assert (str(second._proc.pid), second.client_name) in clients
70+
71+
72+
@pytest.mark.xfail(
73+
reason=(
74+
"ControlMode passes text=True without encoding='utf-8' to Popen; "
75+
"on non-UTF-8 locales control protocol output is decoded with the "
76+
"locale encoding. See: https://github.com/tmux-python/libtmux/issues/678"
77+
),
78+
strict=True,
79+
)
80+
@pytest.mark.skipif(
81+
sys.flags.utf8_mode != 0,
82+
reason="PYTHONUTF8 mode forces UTF-8, masking the locale bug",
83+
)
84+
def test_control_mode_stdout_preserves_non_ascii_output(
85+
control_mode: t.Callable[[], ControlMode],
86+
) -> None:
87+
"""Control-mode stdout must preserve non-ASCII tmux output."""
88+
old_lc_ctype = locale.setlocale(locale.LC_CTYPE)
89+
try:
90+
locale.setlocale(locale.LC_CTYPE, "C")
91+
with control_mode() as ctl:
92+
os.write(
93+
ctl._write_fd,
94+
f"display-message -p '{FORMAT_SEPARATOR}'\n".encode(),
95+
)
96+
97+
for _ in range(20):
98+
ready, _, _ = select.select([ctl.stdout], [], [], 1)
99+
assert ready, "timed out waiting for control-mode output"
100+
101+
line = ctl.stdout.readline()
102+
if FORMAT_SEPARATOR in line:
103+
break
104+
else:
105+
pytest.fail("FORMAT_SEPARATOR U+241E not found in control output")
106+
finally:
107+
locale.setlocale(locale.LC_CTYPE, old_lc_ctype)

0 commit comments

Comments
 (0)