Skip to content

Commit 968727a

Browse files
committed
libtmux(test[logging]): add kill lifecycle, all_except, and stderr tests
why: Cover the new logging behavior for kill operations, all_except dynamic messages, new_session stderr propagation, and exc_info removal. what: - test_server_kill_info_logging: server.kill() emits INFO - test_window_kill_all_except_logging: "other windows killed" message - test_pane_kill_all_except_logging: "other panes killed" message - test_session_kill_all_except_logging: "other sessions killed" message - test_server_new_session_surfaces_kill_session_stderr: stderr raises - test_options_warning_logging_schema: assert no exc_info/traceback
1 parent c5f96c3 commit 968727a

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

tests/test_logging.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import logging
6+
import types
67
import typing as t
78

89
import pytest
@@ -77,6 +78,33 @@ def test_server_new_session_info_logging(
7778
new_session.kill()
7879

7980

81+
def test_server_kill_info_logging(
82+
caplog: pytest.LogCaptureFixture,
83+
) -> None:
84+
"""Test that server.kill() emits a lifecycle INFO record."""
85+
from libtmux.server import Server
86+
from libtmux.test.random import namer
87+
88+
with Server(socket_name=f"libtmux_log_{next(namer)}") as temp_server:
89+
temp_server.new_session(session_name=f"log_session_{next(namer)}")
90+
caplog.clear()
91+
92+
with caplog.at_level(logging.INFO, logger="libtmux.server"):
93+
temp_server.kill()
94+
95+
records = [
96+
r
97+
for r in caplog.records
98+
if getattr(r, "tmux_subcommand", None) == "kill-server"
99+
and r.levelno == logging.INFO
100+
]
101+
assert len(records) >= 1, "expected INFO record for server kill"
102+
103+
rec = t.cast(t.Any, records[0])
104+
assert rec.getMessage() == "server killed"
105+
assert isinstance(rec.tmux_subcommand, str)
106+
107+
80108
def test_window_rename_info_logging(
81109
session: Session,
82110
caplog: pytest.LogCaptureFixture,
@@ -106,6 +134,39 @@ def test_window_rename_info_logging(
106134
)
107135

108136

137+
def test_window_kill_all_except_logging(
138+
session: Session,
139+
caplog: pytest.LogCaptureFixture,
140+
) -> None:
141+
"""Test that window.kill(all_except=True) identifies the surviving window."""
142+
from libtmux.test.random import namer
143+
144+
survivor = session.new_window(window_name=f"log_survivor_{next(namer)}")
145+
other_windows = [
146+
session.new_window(window_name=f"log_other_{next(namer)}"),
147+
session.new_window(window_name=f"log_other_{next(namer)}"),
148+
]
149+
150+
with caplog.at_level(logging.INFO, logger="libtmux.window"):
151+
survivor.kill(all_except=True)
152+
153+
records = [
154+
r
155+
for r in caplog.records
156+
if getattr(r, "tmux_subcommand", None) == "kill-window"
157+
and r.levelno == logging.INFO
158+
]
159+
assert len(records) >= 1, "expected INFO record for all-except window kill"
160+
161+
rec = t.cast(t.Any, records[0])
162+
assert rec.getMessage() == "other windows killed"
163+
assert rec.tmux_window == survivor.window_name
164+
assert rec.tmux_target == survivor.window_id
165+
remaining_window_ids = {window.window_id for window in session.windows}
166+
assert survivor.window_id in remaining_window_ids
167+
assert all(window.window_id not in remaining_window_ids for window in other_windows)
168+
169+
109170
def test_pane_split_info_logging(
110171
session: Session,
111172
caplog: pytest.LogCaptureFixture,
@@ -140,6 +201,96 @@ def test_pane_split_info_logging(
140201
new_pane.kill()
141202

142203

204+
def test_pane_kill_all_except_logging(
205+
session: Session,
206+
caplog: pytest.LogCaptureFixture,
207+
) -> None:
208+
"""Test that pane.kill(all_except=True) identifies the surviving pane."""
209+
window = session.active_window
210+
assert window is not None
211+
window.resize(height=100, width=100)
212+
survivor = window.split()
213+
other_panes = [window.split(), window.split()]
214+
215+
with caplog.at_level(logging.INFO, logger="libtmux.pane"):
216+
survivor.kill(all_except=True)
217+
218+
records = [
219+
r
220+
for r in caplog.records
221+
if getattr(r, "tmux_subcommand", None) == "kill-pane"
222+
and r.levelno == logging.INFO
223+
]
224+
assert len(records) >= 1, "expected INFO record for all-except pane kill"
225+
226+
rec = t.cast(t.Any, records[0])
227+
assert rec.getMessage() == "other panes killed"
228+
assert rec.tmux_pane == survivor.pane_id
229+
assert rec.tmux_target == survivor.pane_id
230+
remaining_pane_ids = {p.pane_id for p in window.panes}
231+
assert survivor.pane_id in remaining_pane_ids
232+
assert all(p.pane_id not in remaining_pane_ids for p in other_panes)
233+
234+
235+
def test_session_kill_all_except_logging(
236+
server: Server,
237+
caplog: pytest.LogCaptureFixture,
238+
) -> None:
239+
"""Test that session.kill(all_except=True) identifies the surviving session."""
240+
from libtmux.test.random import namer
241+
242+
survivor = server.new_session(session_name=f"log_survivor_{next(namer)}")
243+
other_sessions = [
244+
server.new_session(session_name=f"log_other_{next(namer)}"),
245+
server.new_session(session_name=f"log_other_{next(namer)}"),
246+
]
247+
248+
with caplog.at_level(logging.INFO, logger="libtmux.session"):
249+
survivor.kill(all_except=True)
250+
251+
records = [
252+
r
253+
for r in caplog.records
254+
if getattr(r, "tmux_subcommand", None) == "kill-session"
255+
and r.levelno == logging.INFO
256+
]
257+
assert len(records) >= 1, "expected INFO record for all-except session kill"
258+
259+
rec = t.cast(t.Any, records[0])
260+
assert rec.getMessage() == "other sessions killed"
261+
assert rec.tmux_session == survivor.session_name
262+
assert rec.tmux_target == survivor.session_id
263+
remaining_session_ids = {session.session_id for session in server.sessions}
264+
assert survivor.session_id in remaining_session_ids
265+
assert all(
266+
session.session_id not in remaining_session_ids for session in other_sessions
267+
)
268+
269+
270+
def test_server_new_session_surfaces_kill_session_stderr(
271+
monkeypatch: pytest.MonkeyPatch,
272+
) -> None:
273+
"""Test kill-session stderr propagation using monkeypatch for the failure path.
274+
275+
A real tmux fixture is not used here because this path requires forcing a
276+
kill-session command failure before session creation begins.
277+
"""
278+
from libtmux import exc
279+
from libtmux.server import Server
280+
from libtmux.test.random import namer
281+
282+
server = Server(socket_name=f"libtmux_log_{next(namer)}")
283+
monkeypatch.setattr(server, "has_session", lambda session_name: True)
284+
monkeypatch.setattr(
285+
server,
286+
"cmd",
287+
lambda *args, **kwargs: types.SimpleNamespace(stderr=["kill failed"]),
288+
)
289+
290+
with pytest.raises(exc.LibTmuxException, match="kill failed"):
291+
server.new_session(session_name="existing_session", kill_session=True)
292+
293+
143294
def test_options_warning_logging_schema(
144295
caplog: pytest.LogCaptureFixture,
145296
) -> None:
@@ -163,3 +314,5 @@ def test_options_warning_logging_schema(
163314

164315
rec = t.cast(t.Any, records[0])
165316
assert isinstance(rec.tmux_option_key, str)
317+
assert rec.exc_info is None
318+
assert "Traceback" not in caplog.text

0 commit comments

Comments
 (0)