File tree Expand file tree Collapse file tree 7 files changed +71
-23
lines changed
Expand file tree Collapse file tree 7 files changed +71
-23
lines changed Original file line number Diff line number Diff line change 1010import dataclasses
1111import logging
1212import pathlib
13+ import time
1314import typing as t
1415import warnings
1516
@@ -350,9 +351,25 @@ def capture_pane(
350351 if end is not None :
351352 cmd .extend (["-E" , str (end )])
352353 output = self .cmd (* cmd ).stdout
353- # Control mode (and tmux in general) can include viewport-sized blank lines.
354- while output and output [- 1 ].strip () == "" :
355- output .pop ()
354+
355+ def _trim (lines : list [str ]) -> list [str ]:
356+ trimmed = list (lines )
357+ while trimmed and trimmed [- 1 ].strip () == "" :
358+ trimmed .pop ()
359+ return trimmed
360+
361+ output = _trim (output )
362+
363+ # In control mode, capture-pane can race the shell: the first capture
364+ # right after send-keys may return only the echoed command. Retry
365+ # briefly to allow the prompt/output to land.
366+ engine_name = self .server .engine .__class__ .__name__
367+ if engine_name == "ControlModeEngine" and not output :
368+ deadline = time .monotonic () + 0.35
369+ while not output and time .monotonic () < deadline :
370+ time .sleep (0.05 )
371+ output = _trim (self .cmd (* cmd ).stdout )
372+
356373 return output
357374
358375 def send_keys (
Original file line number Diff line number Diff line change 1+ """Test helpers for control-mode flakiness handling."""
2+
3+ from __future__ import annotations
4+
5+ import time
6+ import typing as t
7+
8+ from libtmux .pane import Pane
9+
10+
11+ def wait_for_line (
12+ pane : Pane ,
13+ predicate : t .Callable [[str ], bool ],
14+ * ,
15+ timeout : float = 1.0 ,
16+ interval : float = 0.05 ,
17+ ) -> list [str ]:
18+ """Poll capture_pane until a line satisfies ``predicate``.
19+
20+ Returns the final capture buffer (may be empty if timeout elapses).
21+ """
22+ deadline = time .monotonic () + timeout
23+ last : list [str ] = []
24+ while time .monotonic () < deadline :
25+ captured = pane .capture_pane ()
26+ last = [captured ] if isinstance (captured , str ) else list (captured )
27+ if any (predicate (line ) for line in last ):
28+ break
29+ time .sleep (interval )
30+ return last
Original file line number Diff line number Diff line change 66import shutil
77import typing as t
88
9- import pytest
10-
119if t .TYPE_CHECKING :
1210 from libtmux .session import Session
1311
@@ -77,9 +75,6 @@ def test_set_width(session: Session) -> None:
7775
7876def test_capture_pane (session : Session ) -> None :
7977 """Verify Pane.capture_pane()."""
80- if session .server .engine .__class__ .__name__ == "ControlModeEngine" :
81- pytest .xfail ("control-mode capture-pane normalization pending" )
82-
8378 env = shutil .which ("env" )
8479 assert env is not None , "Cannot find usable `env` in PATH."
8580
Original file line number Diff line number Diff line change 44
55import logging
66import shutil
7- import time
87import typing as t
98
109import pytest
1413from libtmux .pane import Pane
1514from libtmux .server import Server
1615from libtmux .window import Window
16+ from tests .helpers import wait_for_line
1717
1818if t .TYPE_CHECKING :
1919 from libtmux .session import Session
@@ -417,11 +417,14 @@ def test_split_window_with_environment(
417417 environment = environment ,
418418 )
419419 assert pane is not None
420- # wait a bit for the prompt to be ready as the test gets flaky otherwise
421- time .sleep (0.05 )
422420 for k , v in environment .items ():
423421 pane .send_keys (f"echo ${ k } " )
424- assert pane .capture_pane ()[- 2 ] == v
422+
423+ def _match (line : str , expected : str = v ) -> bool :
424+ return line .strip () == expected
425+
426+ lines = wait_for_line (pane , _match )
427+ assert any (_match (line ) for line in lines )
425428
426429
427430@pytest .mark .skipif (
Original file line number Diff line number Diff line change @@ -67,9 +67,6 @@ def test_set_width(session: Session) -> None:
6767
6868def test_capture_pane (session : Session ) -> None :
6969 """Verify Pane.capture_pane()."""
70- if session .server .engine .__class__ .__name__ == "ControlModeEngine" :
71- pytest .xfail ("control-mode capture-pane trailing prompt normalization pending" )
72-
7370 env = shutil .which ("env" )
7471 assert env is not None , "Cannot find usable `env` in PATH."
7572
Original file line number Diff line number Diff line change 1717from libtmux .test .constants import TEST_SESSION_PREFIX
1818from libtmux .test .random import namer
1919from libtmux .window import Window
20+ from tests .helpers import wait_for_line
2021
2122if t .TYPE_CHECKING :
2223 from libtmux ._internal .types import StrPath
@@ -330,9 +331,6 @@ def test_new_window_with_environment(
330331 environment : dict [str , str ],
331332) -> None :
332333 """Verify new window with environment vars."""
333- if session .server .engine .__class__ .__name__ == "ControlModeEngine" :
334- pytest .xfail ("control-mode -e propagation still pending" )
335-
336334 env = shutil .which ("env" )
337335 assert env is not None , "Cannot find usable `env` in PATH."
338336
@@ -346,7 +344,12 @@ def test_new_window_with_environment(
346344 assert pane is not None
347345 for k , v in environment .items ():
348346 pane .send_keys (f"echo ${ k } " )
349- assert pane .capture_pane ()[- 2 ] == v
347+
348+ def _match (line : str , expected : str = v ) -> bool :
349+ return line .strip () == expected
350+
351+ lines = wait_for_line (pane , _match )
352+ assert any (_match (line ) for line in lines )
350353
351354
352355@pytest .mark .skipif (
Original file line number Diff line number Diff line change 55import logging
66import pathlib
77import shutil
8- import time
98import typing as t
109
1110import pytest
2120from libtmux .pane import Pane
2221from libtmux .server import Server
2322from libtmux .window import Window
23+ from tests .helpers import wait_for_line
2424
2525if t .TYPE_CHECKING :
2626 from libtmux ._internal .types import StrPath
@@ -456,11 +456,14 @@ def test_split_with_environment(
456456 environment = environment ,
457457 )
458458 assert pane is not None
459- # wait a bit for the prompt to be ready as the test gets flaky otherwise
460- time .sleep (0.05 )
461459 for k , v in environment .items ():
462460 pane .send_keys (f"echo ${ k } " )
463- assert pane .capture_pane ()[- 2 ] == v
461+
462+ def _match (line : str , expected : str = v ) -> bool :
463+ return line .strip () == expected
464+
465+ lines = wait_for_line (pane , _match )
466+ assert any (_match (line ) for line in lines )
464467
465468
466469@pytest .mark .skipif (
You can’t perform that action at this time.
0 commit comments