Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/libtmux/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ def new_session(
if proc.stderr:
raise exc.LibTmuxException(proc.stderr)

if not proc.stdout:
msg = "new-session produced no output"
raise exc.LibTmuxException(msg)

session_stdout = proc.stdout[0]

finally:
Expand Down
89 changes: 89 additions & 0 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import pytest

from libtmux import exc
from libtmux.server import Server

if t.TYPE_CHECKING:
Expand Down Expand Up @@ -105,6 +106,94 @@ def test_new_session(server: Server) -> None:
assert server.has_session("test_new_session")


def test_new_session_empty_stdout(
server: Server,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Server.new_session raises LibTmuxException when tmux returns no output.

monkeypatch is used to simulate empty stdout, which cannot be triggered
through normal tmux operations on a healthy server.
"""
original_cmd = server.cmd

def mock_cmd(cmd: str, *args: t.Any, **kwargs: t.Any) -> t.Any:
result = original_cmd(cmd, *args, **kwargs)
if cmd == "new-session":
result.stdout = []
return result

monkeypatch.setattr(server, "cmd", mock_cmd)

with pytest.raises(exc.LibTmuxException, match="new-session produced no output"):
server.new_session(session_name="test_empty_stdout")

monkeypatch.undo()
if server.has_session("test_empty_stdout"):
server.kill_session("test_empty_stdout")


def test_new_session_restores_tmux_env_on_error(
server: Server,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Server.new_session restores TMUX env var when an exception is raised.

monkeypatch is used to simulate empty stdout (forcing the exception path),
which cannot be triggered through normal tmux operations.
"""
original_cmd = server.cmd
sentinel = "/tmp/libtmux-test-fake-socket,12345,0"

monkeypatch.setenv("TMUX", sentinel)

def mock_cmd(cmd: str, *args: t.Any, **kwargs: t.Any) -> t.Any:
result = original_cmd(cmd, *args, **kwargs)
if cmd == "new-session":
result.stdout = []
return result

monkeypatch.setattr(server, "cmd", mock_cmd)

with pytest.raises(exc.LibTmuxException, match="new-session produced no output"):
server.new_session(session_name="test_env_restore")

assert os.environ.get("TMUX") == sentinel

monkeypatch.undo()
if server.has_session("test_env_restore"):
server.kill_session("test_env_restore")


def test_new_session_restores_tmux_env_on_setup_error(
server: Server,
monkeypatch: pytest.MonkeyPatch,
tmp_path: pathlib.Path,
) -> None:
"""Server.new_session restores TMUX env when setup code before cmd() raises.

monkeypatch makes pathlib.Path.expanduser raise to verify the try/finally
covers the arg-building phase, not just the cmd() call.
"""
sentinel = "/tmp/libtmux-test-fake-socket,99999,0"

monkeypatch.setenv("TMUX", sentinel)

def broken_expanduser(self: pathlib.Path) -> t.Any:
msg = "injected setup error"
raise RuntimeError(msg)

monkeypatch.setattr(pathlib.Path, "expanduser", broken_expanduser)

with pytest.raises(RuntimeError, match="injected setup error"):
server.new_session(
session_name="test_setup_error",
start_directory=tmp_path,
)

assert os.environ.get("TMUX") == sentinel


def test_new_session_returns_populated_session(server: Server) -> None:
"""Server.new_session returns Session populated from -P output."""
session = server.new_session(session_name="test_populated")
Expand Down
Loading