Skip to content

Commit 00d0911

Browse files
committed
ControlMode(test[regressions]): Extend xfail coverage to all known control gaps
why: Capture all currently failing control-mode behaviours in targeted xfail tests before fixing them. what: - Add capture_pane prompt repro, environment propagation cases for new-window/split-window, attached_sessions visibility, raise_if_dead bootstrap, and TestServer liveness expectations - Keep strict xfail markers with NamedTuple test_id parametrization
1 parent 3595b0d commit 00d0911

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed

tests/test_control_mode_regressions.py

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from __future__ import annotations
44

55
import contextlib
6+
import shutil
7+
import subprocess
68
import typing as t
79
import uuid
810

@@ -139,3 +141,191 @@ def test_switch_client_raises_without_user_clients() -> None:
139141
finally:
140142
with contextlib.suppress(Exception):
141143
server.kill()
144+
145+
146+
#
147+
# Integration xfails mirroring observed failures
148+
#
149+
150+
151+
@pytest.mark.xfail(
152+
reason="control-mode capture-pane returns trailing blank lines",
153+
strict=True,
154+
)
155+
def test_capture_pane_returns_only_prompt() -> None:
156+
"""capture_pane should mirror subprocess trimming and return single prompt line."""
157+
env = shutil.which("env")
158+
assert env is not None
159+
160+
socket_name = f"libtmux_test_{uuid.uuid4().hex[:8]}"
161+
engine = ControlModeEngine()
162+
server = Server(socket_name=socket_name, engine=engine)
163+
164+
try:
165+
session = server.new_session(
166+
session_name="capture_blank_repro",
167+
attach=True,
168+
window_shell=f"{env} PS1='$ ' sh",
169+
kill_session=True,
170+
)
171+
pane = session.active_window.active_pane
172+
assert pane is not None
173+
pane_contents = "\n".join(pane.capture_pane())
174+
assert pane_contents == "$"
175+
finally:
176+
with contextlib.suppress(Exception):
177+
server.kill()
178+
179+
180+
class EnvPropagationFixture(t.NamedTuple):
181+
"""Fixture for environment propagation regressions."""
182+
183+
test_id: str
184+
environment: dict[str, str]
185+
command: str
186+
expected_value: str
187+
188+
189+
ENV_PROP_CASES = [
190+
pytest.param(
191+
EnvPropagationFixture(
192+
test_id="new_window_single",
193+
environment={"ENV_VAR": "window"},
194+
command="echo $ENV_VAR",
195+
expected_value="window",
196+
),
197+
id="new_window_single",
198+
marks=pytest.mark.xfail(
199+
reason="control-mode loses environment when creating window",
200+
strict=True,
201+
),
202+
),
203+
pytest.param(
204+
EnvPropagationFixture(
205+
test_id="new_window_multiple",
206+
environment={"ENV_VAR_1": "window_1", "ENV_VAR_2": "window_2"},
207+
command="echo $ENV_VAR_1",
208+
expected_value="window_1",
209+
),
210+
id="new_window_multiple",
211+
marks=pytest.mark.xfail(
212+
reason="control-mode loses environment when creating window",
213+
strict=True,
214+
),
215+
),
216+
pytest.param(
217+
EnvPropagationFixture(
218+
test_id="split_window_single",
219+
environment={"ENV_VAR": "pane"},
220+
command="echo $ENV_VAR",
221+
expected_value="pane",
222+
),
223+
id="split_window_single",
224+
marks=pytest.mark.xfail(
225+
reason="control-mode loses environment when splitting window",
226+
strict=True,
227+
),
228+
),
229+
pytest.param(
230+
EnvPropagationFixture(
231+
test_id="split_window_multiple",
232+
environment={"ENV_VAR_1": "pane_1", "ENV_VAR_2": "pane_2"},
233+
command="echo $ENV_VAR_1",
234+
expected_value="pane_1",
235+
),
236+
id="split_window_multiple",
237+
marks=pytest.mark.xfail(
238+
reason="control-mode loses environment when splitting window",
239+
strict=True,
240+
),
241+
),
242+
]
243+
244+
245+
@pytest.mark.parametrize("case", ENV_PROP_CASES)
246+
def test_environment_propagation(case: EnvPropagationFixture) -> None:
247+
"""Environment variables should be visible inside panes."""
248+
env = shutil.which("env")
249+
assert env is not None
250+
251+
socket_name = f"libtmux_test_{uuid.uuid4().hex[:8]}"
252+
engine = ControlModeEngine()
253+
server = Server(socket_name=socket_name, engine=engine)
254+
255+
try:
256+
session = server.new_session(
257+
session_name=f"env_repro_{case.test_id}",
258+
attach=True,
259+
window_name="window_with_environment",
260+
window_shell=f"{env} PS1='$ ' sh",
261+
environment=case.environment,
262+
kill_session=True,
263+
)
264+
pane = session.active_window.active_pane
265+
assert pane is not None
266+
267+
if "split_window" in case.test_id:
268+
pane = session.active_window.split(
269+
attach=True,
270+
environment=case.environment,
271+
)
272+
assert pane is not None
273+
274+
pane.send_keys(case.command, literal=True, suppress_history=False)
275+
output = pane.capture_pane()
276+
assert output[-2] == case.expected_value
277+
finally:
278+
with contextlib.suppress(Exception):
279+
server.kill()
280+
281+
282+
@pytest.mark.xfail(
283+
reason="control-mode client counts as attached; attached_sessions not empty",
284+
strict=True,
285+
)
286+
def test_attached_sessions_empty_when_no_clients() -> None:
287+
"""Attached sessions should be empty on a fresh server."""
288+
socket_name = f"libtmux_test_{uuid.uuid4().hex[:8]}"
289+
engine = ControlModeEngine()
290+
server = Server(socket_name=socket_name, engine=engine)
291+
292+
try:
293+
session = server.new_session(
294+
session_name="attached_session_repro",
295+
attach=True,
296+
kill_session=True,
297+
)
298+
assert session is not None
299+
assert server.attached_sessions == []
300+
finally:
301+
with contextlib.suppress(Exception):
302+
server.kill()
303+
304+
305+
@pytest.mark.xfail(
306+
reason="control-mode is_alive bootstrap hides missing-server errors",
307+
strict=True,
308+
)
309+
def test_raise_if_dead_raises_on_missing_server() -> None:
310+
"""raise_if_dead should raise when tmux server does not exist."""
311+
socket_name = f"libtmux_test_{uuid.uuid4().hex[:8]}"
312+
engine = ControlModeEngine()
313+
server = Server(socket_name=socket_name, engine=engine)
314+
315+
with pytest.raises(subprocess.CalledProcessError):
316+
server.raise_if_dead()
317+
318+
319+
@pytest.mark.xfail(
320+
reason="TestServer with control engine starts server eagerly",
321+
strict=True,
322+
)
323+
def test_testserver_is_alive_false_before_use() -> None:
324+
"""TestServer should report not alive before first use."""
325+
engine = ControlModeEngine()
326+
server = Server(socket_name=f"libtmux_test_{uuid.uuid4().hex[:8]}", engine=engine)
327+
try:
328+
assert server.is_alive() is False
329+
finally:
330+
with contextlib.suppress(Exception):
331+
server.kill()

0 commit comments

Comments
 (0)