@@ -302,6 +302,89 @@ def test_attached_sessions_empty_when_no_clients() -> None:
302302 server .kill ()
303303
304304
305+ class CapturePaneFixture (t .NamedTuple ):
306+ """Fixture for capture-pane variants that should trim blanks."""
307+
308+ test_id : str
309+ start : t .Literal ["-" ] | int | None
310+ end : t .Literal ["-" ] | int | None
311+ expected : str
312+
313+
314+ CAPTURE_PANE_CASES = [
315+ pytest .param (
316+ CapturePaneFixture (
317+ test_id = "default" ,
318+ start = None ,
319+ end = None ,
320+ expected = "$" ,
321+ ),
322+ id = "capture_default" ,
323+ marks = pytest .mark .xfail (
324+ reason = "control-mode capture-pane returns extra blank lines" ,
325+ strict = True ,
326+ ),
327+ ),
328+ pytest .param (
329+ CapturePaneFixture (
330+ test_id = "start_history" ,
331+ start = - 2 ,
332+ end = None ,
333+ expected = '$ printf "%s"\n $ clear -x\n $' ,
334+ ),
335+ id = "capture_start" ,
336+ marks = pytest .mark .xfail (
337+ reason = "control-mode capture-pane start preserves trailing blanks" ,
338+ strict = True ,
339+ ),
340+ ),
341+ pytest .param (
342+ CapturePaneFixture (
343+ test_id = "end_zero" ,
344+ start = None ,
345+ end = 0 ,
346+ expected = '$ printf "%s"' ,
347+ ),
348+ id = "capture_end_zero" ,
349+ marks = pytest .mark .xfail (
350+ reason = "control-mode capture-pane end preserves trailing blanks" ,
351+ strict = True ,
352+ ),
353+ ),
354+ ]
355+
356+
357+ @pytest .mark .parametrize ("case" , CAPTURE_PANE_CASES )
358+ def test_capture_pane_variants (case : CapturePaneFixture ) -> None :
359+ """capture-pane variants should return trimmed output like subprocess engine."""
360+ env = shutil .which ("env" )
361+ assert env is not None
362+
363+ socket_name = f"libtmux_test_{ uuid .uuid4 ().hex [:8 ]} "
364+ engine = ControlModeEngine ()
365+ server = Server (socket_name = socket_name , engine = engine )
366+
367+ try :
368+ session = server .new_session (
369+ session_name = f"capture_variant_{ case .test_id } " ,
370+ attach = True ,
371+ window_shell = f"{ env } PS1='$ ' sh" ,
372+ kill_session = True ,
373+ )
374+ pane = session .active_window .active_pane
375+ assert pane is not None
376+
377+ pane .send_keys (r'printf "%s"' , literal = True , suppress_history = False )
378+ pane .send_keys ("clear -x" , literal = True , suppress_history = False )
379+
380+ output_lines = pane .capture_pane (start = case .start , end = case .end )
381+ output = "\n " .join (output_lines )
382+ assert output == case .expected
383+ finally :
384+ with contextlib .suppress (Exception ):
385+ server .kill ()
386+
387+
305388@pytest .mark .xfail (
306389 reason = "control-mode is_alive bootstrap hides missing-server errors" ,
307390 strict = True ,
@@ -329,3 +412,34 @@ def test_testserver_is_alive_false_before_use() -> None:
329412 finally :
330413 with contextlib .suppress (Exception ):
331414 server .kill ()
415+
416+
417+ @pytest .mark .xfail (
418+ reason = (
419+ "server.kill with control engine can raise ControlModeConnectionError on EOF"
420+ ),
421+ strict = True ,
422+ )
423+ def test_server_kill_handles_control_eof_gracefully () -> None :
424+ """server.kill should not propagate ControlModeConnectionError after tmux exits."""
425+ socket_name = f"libtmux_test_{ uuid .uuid4 ().hex [:8 ]} "
426+ engine = ControlModeEngine ()
427+ server = Server (socket_name = socket_name , engine = engine )
428+
429+ try :
430+ session = server .new_session (
431+ session_name = "kill_eof_repro" ,
432+ attach = False ,
433+ kill_session = True ,
434+ )
435+ assert session is not None
436+ # Simulate tmux disappearing before control client issues kill-server.
437+ subprocess .run (
438+ ["tmux" , "-L" , socket_name , "kill-server" ],
439+ check = False ,
440+ capture_output = True ,
441+ )
442+ server .kill ()
443+ finally :
444+ with contextlib .suppress (Exception ):
445+ server .kill ()
0 commit comments