Skip to content

Commit b67ee0d

Browse files
committed
tests(cli/load): Add regression test for session killed during attach
Adds a regression test documenting the fix for TmuxObjectDoesNotExist being raised when a session is killed while the user is attached. The issue was caused by libtmux's Session.attach() calling refresh() after attach-session returned. This was fixed in libtmux by removing the semantically incorrect refresh() call. Test uses NamedTuple + parametrize + test_id pattern per project style. Related: libtmux 9a5147aa (introduced bug), tmuxp fdafdd2 (triggered it) Fix: libtmux PR #616
1 parent 06f5769 commit b67ee0d

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

tests/cli/test_load.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,3 +751,83 @@ def test_load_append_windows_to_current_session(
751751

752752
assert len(server.sessions) == 1
753753
assert len(server.windows) == 6
754+
755+
756+
class LoadAttachExceptionFixture(t.NamedTuple):
757+
"""Test fixture for _load_attached() exception handling regression.
758+
759+
This tests the scenario where Session.attach() raises TmuxObjectDoesNotExist
760+
because the session was killed while the user was attached (e.g., user killed
761+
all windows from within tmux before detaching).
762+
763+
See: https://github.com/tmux-python/tmuxp/issues/1002
764+
"""
765+
766+
test_id: str
767+
session_killed_during_attach: bool
768+
should_not_raise: bool
769+
770+
771+
LOAD_ATTACH_EXCEPTION_FIXTURES: list[LoadAttachExceptionFixture] = [
772+
LoadAttachExceptionFixture(
773+
test_id="session_killed_during_attach_should_not_propagate",
774+
session_killed_during_attach=True,
775+
should_not_raise=True, # _load_attached should NOT propagate exception
776+
),
777+
]
778+
779+
780+
@pytest.mark.parametrize(
781+
list(LoadAttachExceptionFixture._fields),
782+
LOAD_ATTACH_EXCEPTION_FIXTURES,
783+
ids=[test.test_id for test in LOAD_ATTACH_EXCEPTION_FIXTURES],
784+
)
785+
def test_load_attached_handles_session_killed_during_attach(
786+
server: Server,
787+
monkeypatch: pytest.MonkeyPatch,
788+
mocker: MockerFixture,
789+
test_id: str,
790+
session_killed_during_attach: bool,
791+
should_not_raise: bool,
792+
) -> None:
793+
"""Regression test: _load_attached() handles session killed during attach.
794+
795+
When a user is attached to a tmux session via `tmuxp load`, then kills the
796+
session from within tmux (e.g., kills all windows), and then detaches,
797+
the _load_attached() function should complete without raising an exception.
798+
799+
This was fixed by removing the refresh() call from Session.attach() in libtmux
800+
since attach-session is a blocking interactive command and session state can
801+
change arbitrarily while the user is attached.
802+
803+
See: https://github.com/tmux-python/tmuxp/issues/1002
804+
"""
805+
# Load outside of tmux
806+
monkeypatch.delenv("TMUX", raising=False)
807+
808+
yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml")
809+
session_config = ConfigReader._load(fmt="yaml", content=yaml_config)
810+
811+
builder = WorkspaceBuilder(session_config=session_config, server=server)
812+
813+
if session_killed_during_attach:
814+
# Simulate attach returning successfully but session being killed during
815+
# the attachment period. With the fix in libtmux, attach() doesn't call
816+
# refresh() anymore, so this doesn't raise an exception.
817+
#
818+
# We patch at class level since builder.session doesn't exist until build()
819+
def mock_attach(self: Session, *args: t.Any, **kwargs: t.Any) -> Session:
820+
"""Simulate attach() completing after session was killed during attach."""
821+
# Kill the session to simulate user action during attachment
822+
self.kill()
823+
# Return the session object (even though it's now dead)
824+
# This is what attach() does now - it doesn't try to refresh
825+
return self
826+
827+
mocker.patch("libtmux.session.Session.attach", mock_attach)
828+
829+
if should_not_raise:
830+
# With the libtmux fix, attach() doesn't call refresh(), so even if the
831+
# session was killed during attachment, no exception is raised
832+
_load_attached(builder, detached=False)
833+
# If we get here without exception, the test passes

0 commit comments

Comments
 (0)