@@ -419,3 +419,173 @@ def test_server_kill_handles_control_eof_gracefully() -> None:
419419 finally :
420420 with contextlib .suppress (Exception ):
421421 server .kill ()
422+
423+
424+ #
425+ # New repros for remaining control-mode failures in full suite
426+ #
427+
428+
429+ class AttachedSessionsFixture (t .NamedTuple ):
430+ """Fixture for attached_sessions filtering failures."""
431+
432+ test_id : str
433+ expect_empty : bool
434+
435+
436+ ATTACHED_SESSIONS_CASES = [
437+ pytest .param (
438+ AttachedSessionsFixture (
439+ test_id = "control_client_hidden" ,
440+ expect_empty = True ,
441+ ),
442+ id = "attached_control_client_hidden" ,
443+ marks = pytest .mark .xfail (
444+ reason = "control client still appears in attached_sessions" ,
445+ strict = True ,
446+ ),
447+ ),
448+ ]
449+
450+
451+ @pytest .mark .parametrize ("case" , ATTACHED_SESSIONS_CASES )
452+ def test_attached_sessions_filters_control_client (
453+ case : AttachedSessionsFixture ,
454+ ) -> None :
455+ """Attached sessions should exclude the control-mode client itself."""
456+ socket_name = f"libtmux_test_{ uuid .uuid4 ().hex [:8 ]} "
457+ engine = ControlModeEngine ()
458+ server = Server (socket_name = socket_name , engine = engine )
459+
460+ try :
461+ # Start a user session; control client should remain hidden.
462+ server .new_session (session_name = "user_session" , attach = False , kill_session = True )
463+ attached = server .attached_sessions
464+ if case .expect_empty :
465+ assert attached == []
466+ finally :
467+ with contextlib .suppress (Exception ):
468+ server .kill ()
469+
470+
471+ class BadSessionNameFixture (t .NamedTuple ):
472+ """Fixture for switch_client behavior with control client present."""
473+
474+ test_id : str
475+ session_name : str
476+ expect_exception : type [BaseException ] | None
477+
478+
479+ BAD_SESSION_NAME_CASES = [
480+ pytest .param (
481+ BadSessionNameFixture (
482+ test_id = "switch_client_should_raise" ,
483+ session_name = "hey moo" ,
484+ expect_exception = exc .LibTmuxException ,
485+ ),
486+ id = "switch_client_bad_name" ,
487+ marks = pytest .mark .xfail (
488+ reason = "control client makes switch_client succeed instead of raising" ,
489+ strict = True ,
490+ ),
491+ ),
492+ ]
493+
494+
495+ @pytest .mark .parametrize ("case" , BAD_SESSION_NAME_CASES )
496+ def test_switch_client_respects_bad_session_names (
497+ case : BadSessionNameFixture ,
498+ ) -> None :
499+ """switch_client should reject invalid names even with control client attached."""
500+ socket_name = f"libtmux_test_{ uuid .uuid4 ().hex [:8 ]} "
501+ engine = ControlModeEngine ()
502+ server = Server (socket_name = socket_name , engine = engine )
503+ try :
504+ session = server .new_session (
505+ session_name = "hey moomoo" ,
506+ attach = False ,
507+ kill_session = True ,
508+ )
509+ assert session is not None
510+ assert case .expect_exception is not None
511+ with pytest .raises (case .expect_exception ):
512+ server .switch_client (f"{ case .session_name } moo" )
513+ finally :
514+ with contextlib .suppress (Exception ):
515+ server .kill ()
516+
517+
518+ class EnvMultiFixture (t .NamedTuple ):
519+ """Fixture for multi-var environment propagation errors."""
520+
521+ test_id : str
522+ environment : dict [str , str ]
523+ expected_value : str
524+
525+
526+ ENV_MULTI_CASES = [
527+ pytest .param (
528+ EnvMultiFixture (
529+ test_id = "new_window_multi_vars" ,
530+ environment = {"ENV_VAR_1" : "window_1" , "ENV_VAR_2" : "window_2" },
531+ expected_value = "window_1" ,
532+ ),
533+ id = "env_new_window_multi" ,
534+ marks = pytest .mark .xfail (
535+ reason = "control-mode loses environment with multiple -e flags" ,
536+ strict = True ,
537+ ),
538+ ),
539+ ]
540+
541+
542+ @pytest .mark .parametrize ("case" , ENV_MULTI_CASES )
543+ def test_environment_multi_var_propagation (case : EnvMultiFixture ) -> None :
544+ """Multiple -e flags should all be delivered inside the pane."""
545+ env = shutil .which ("env" )
546+ assert env is not None
547+
548+ socket_name = f"libtmux_test_{ uuid .uuid4 ().hex [:8 ]} "
549+ engine = ControlModeEngine ()
550+ server = Server (socket_name = socket_name , engine = engine )
551+
552+ try :
553+ session = server .new_session (
554+ session_name = "env_multi_repro" ,
555+ attach = True ,
556+ window_name = "window_with_environment" ,
557+ window_shell = f"{ env } PS1='$ ' sh" ,
558+ environment = case .environment ,
559+ kill_session = True ,
560+ )
561+ pane = session .active_window .active_pane
562+ assert pane is not None
563+ pane .send_keys ("echo $ENV_VAR_1" , literal = True , suppress_history = False )
564+ output = pane .capture_pane ()
565+ assert output [- 2 ] == case .expected_value
566+ finally :
567+ with contextlib .suppress (Exception ):
568+ server .kill ()
569+
570+
571+ @pytest .mark .xfail (
572+ reason = "Session.kill still surfaces ControlModeConnectionError on EOF" ,
573+ strict = True ,
574+ )
575+ def test_session_kill_handles_control_eof () -> None :
576+ """Session.kill should swallow control-mode EOF when tmux exits."""
577+ socket_name = f"libtmux_test_{ uuid .uuid4 ().hex [:8 ]} "
578+ engine = ControlModeEngine ()
579+ server = Server (socket_name = socket_name , engine = engine )
580+
581+ try :
582+ session = server .new_session (
583+ session_name = "kill_session_repro" ,
584+ attach = False ,
585+ kill_session = True ,
586+ )
587+ assert session is not None
588+ session .kill ()
589+ finally :
590+ with contextlib .suppress (Exception ):
591+ server .kill ()
0 commit comments