Skip to content

Commit 6a42a33

Browse files
committed
feat(pytest): Add TempServer fixture for test server management
Adds a new pytest fixture TempServer that returns a factory for creating tmux servers with unique socket names. Each server is automatically cleaned up when the test completes. The fixture provides: - Factory function returning partial'd Server instances - Unique socket names for each server instance - Automatic cleanup through pytest's addfinalizer - Support for custom tmux configs in tests Example usage: def test_example(TempServer): Server = TempServer() # Get partial'd Server server = Server() # Create server instance server.new_session()
1 parent 9052045 commit 6a42a33

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

src/libtmux/pytest_plugin.py

+56
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import contextlib
6+
import functools
67
import getpass
78
import logging
89
import os
@@ -256,3 +257,58 @@ def session(
256257
assert TEST_SESSION_NAME != "tmuxp"
257258

258259
return session
260+
261+
262+
@pytest.fixture
263+
def TempServer(
264+
request: pytest.FixtureRequest,
265+
) -> Server:
266+
"""Create a temporary tmux server that cleans up after itself.
267+
268+
This is similar to the server pytest fixture, but can be used outside of pytest.
269+
The server will be killed when the test completes.
270+
271+
Returns
272+
-------
273+
Server
274+
A factory function that returns a Server with a unique socket_name
275+
276+
Examples
277+
--------
278+
>>> server = Server() # Create server instance
279+
>>> server.new_session()
280+
Session($... ...)
281+
>>> server.is_alive()
282+
True
283+
>>> # Each call creates a new server with unique socket
284+
>>> server2 = Server()
285+
>>> server2.socket_name != server.socket_name
286+
True
287+
"""
288+
created_sockets: list[str] = []
289+
290+
def on_init(server: Server) -> None:
291+
"""Track created servers for cleanup."""
292+
created_sockets.append(server.socket_name or "default")
293+
294+
def socket_name_factory() -> str:
295+
"""Generate unique socket names."""
296+
return f"libtmux_test{next(namer)}"
297+
298+
def fin() -> None:
299+
"""Kill all servers created with these sockets."""
300+
for socket_name in created_sockets:
301+
server = Server(socket_name=socket_name)
302+
if server.is_alive():
303+
server.kill()
304+
305+
request.addfinalizer(fin)
306+
307+
return t.cast(
308+
"Server",
309+
functools.partial(
310+
Server,
311+
on_init=on_init,
312+
socket_name_factory=socket_name_factory,
313+
),
314+
)

tests/test_pytest_plugin.py

+85
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
from __future__ import annotations
44

55
import textwrap
6+
import time
67
import typing as t
78

89
if t.TYPE_CHECKING:
10+
import pathlib
11+
912
import pytest
1013

14+
from libtmux.server import Server
15+
1116

1217
def test_plugin(
1318
pytester: pytest.Pytester,
@@ -71,3 +76,83 @@ def test_repo_git_remote_checkout(
7176
# Test
7277
result = pytester.runpytest(str(first_test_filename))
7378
result.assert_outcomes(passed=1)
79+
80+
81+
def test_temp_server(TempServer: t.Callable[..., Server]) -> None:
82+
"""Test TempServer creates and cleans up server."""
83+
server = TempServer()
84+
assert server.is_alive() is False # Server not started yet
85+
86+
session = server.new_session()
87+
assert server.is_alive() is True
88+
assert len(server.sessions) == 1
89+
assert session.session_name is not None
90+
91+
# Test socket name is unique
92+
assert server.socket_name is not None
93+
assert server.socket_name.startswith("libtmux_test")
94+
95+
# Each call creates a new server with unique socket
96+
server2 = TempServer()
97+
assert server2.socket_name is not None
98+
assert server2.socket_name.startswith("libtmux_test")
99+
assert server2.socket_name != server.socket_name
100+
101+
102+
def test_temp_server_with_config(
103+
TempServer: t.Callable[..., Server],
104+
tmp_path: pathlib.Path,
105+
) -> None:
106+
"""Test TempServer with config file."""
107+
config_file = tmp_path / "tmux.conf"
108+
config_file.write_text("set -g status off", encoding="utf-8")
109+
110+
server = TempServer(config_file=str(config_file))
111+
session = server.new_session()
112+
113+
# Verify config was loaded
114+
assert session.cmd("show-options", "-g", "status").stdout[0] == "status off"
115+
116+
117+
def test_temp_server_cleanup(TempServer: t.Callable[..., Server]) -> None:
118+
"""Test TempServer properly cleans up after itself."""
119+
server = TempServer()
120+
socket_name = server.socket_name
121+
assert socket_name is not None
122+
123+
# Create multiple sessions
124+
server.new_session(session_name="test1")
125+
server.new_session(session_name="test2")
126+
assert len(server.sessions) == 2
127+
128+
# Verify server is alive
129+
assert server.is_alive() is True
130+
131+
# Delete server and verify cleanup
132+
server.kill()
133+
time.sleep(0.1) # Give time for cleanup
134+
135+
# Create new server to verify old one was cleaned up
136+
new_server = TempServer()
137+
assert new_server.is_alive() is False # Server not started yet
138+
new_server.new_session() # This should work if old server was cleaned up
139+
assert new_server.is_alive() is True
140+
141+
142+
def test_temp_server_multiple(TempServer: t.Callable[..., Server]) -> None:
143+
"""Test multiple TempServer instances can coexist."""
144+
server1 = TempServer()
145+
server2 = TempServer()
146+
147+
# Each server should have a unique socket
148+
assert server1.socket_name != server2.socket_name
149+
150+
# Create sessions in each server
151+
server1.new_session(session_name="test1")
152+
server2.new_session(session_name="test2")
153+
154+
# Verify sessions are in correct servers
155+
assert any(s.session_name == "test1" for s in server1.sessions)
156+
assert any(s.session_name == "test2" for s in server2.sessions)
157+
assert not any(s.session_name == "test1" for s in server2.sessions)
158+
assert not any(s.session_name == "test2" for s in server1.sessions)

0 commit comments

Comments
 (0)