Skip to content

Commit 9853289

Browse files
committed
feat(pytest-plugin): Add executable test examples
- Create a new pytest_examples directory with test files for all examples - Update the examples.md file to use sphinx literalinclude for including test code - Enhance pytest-plugin index.md with better explanations and references - Ensure all examples are properly linted and match project code standards - Add sections for advanced testing techniques like multi-pane, custom configs, and command polling - Make examples directly runnable and testable via pytest
1 parent 5d648f3 commit 9853289

File tree

10 files changed

+709
-495
lines changed

10 files changed

+709
-495
lines changed

docs/pytest-plugin/examples.md

Lines changed: 145 additions & 489 deletions
Large diffs are not rendered by default.

docs/pytest-plugin/index.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# tmux `pytest` plugin
44

55
libtmux provides pytest fixtures for tmux. The plugin automatically manages setup and teardown of an
6-
independent tmux server.
6+
independent tmux server, making it easy to test tmux-related functionality with complete isolation.
77

88
```{seealso} Using the pytest plugin?
99
@@ -26,14 +26,24 @@ Install `libtmux` via the python package manager of your choosing, e.g.
2626
$ pip install libtmux
2727
```
2828

29-
The pytest plugin will be automatically detected via pytest, and the fixtures will be added.
29+
The pytest plugin will be automatically detected via pytest, and the fixtures will be added to your test environment.
30+
31+
### Key Benefits
32+
33+
- **Isolated Testing Environment**: Each test gets a fresh tmux server that won't interfere with other tests
34+
- **Automatic Cleanup**: Servers, sessions, and other resources are automatically cleaned up after tests
35+
- **Simplified Setup**: Common fixtures for server, session, window, and pane management
36+
- **Reliable Testing**: Consistent environment for reproducible test results
37+
- **Custom Configuration**: Easily test with different tmux configurations and settings
3038

3139
### Real world usage
3240

3341
View libtmux's own [tests/](https://github.com/tmux-python/libtmux/tree/master/tests) as well as
34-
tmuxp's [tests/](https://github.com/tmux-python/tmuxp/tree/master/tests).
42+
tmuxp's [tests/](https://github.com/tmux-python/tmuxp/tree/master/tests) for real-world examples.
3543

36-
libtmux's tests `autouse` the {ref}`recommended-fixtures` above to ensure stable test execution, assertions and
44+
For more detailed examples with code snippets, see the {ref}`pytest_plugin_examples` page.
45+
46+
libtmux's tests `autouse` the {ref}`recommended-fixtures` below to ensure stable test execution, assertions and
3747
object lookups in the test grid.
3848

3949
## pytest-tmux
@@ -137,6 +147,18 @@ def set_home(
137147
monkeypatch.setenv("HOME", str(user_path))
138148
```
139149

150+
## Advanced Testing Techniques
151+
152+
For more sophisticated testing scenarios, see our detailed examples:
153+
154+
1. **Testing with Multiple Panes**: Learn how to test applications that require interaction between multiple panes
155+
2. **Testing with Custom Configurations**: Set up tests with specific tmux configuration settings
156+
3. **Managing Temporary Files**: Work with temporary files and directories in your tests
157+
4. **Advanced Command Polling**: Implement robust waiting for command completion
158+
5. **Parametrized Testing**: Run the same test with different inputs
159+
160+
See the {ref}`pytest_plugin_examples` page for detailed code examples of these techniques.
161+
140162
## Fixtures
141163

142164
```{eval-rst}
@@ -152,5 +174,4 @@ def set_home(
152174

153175
```{toctree}
154176
155-
examples
156-
```
177+
examples

tests/pytest_examples/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Example tests for the libtmux pytest plugin.
2+
3+
These examples are used in the documentation and tests.
4+
"""
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""Basic usage examples for the libtmux pytest plugin."""
2+
3+
from __future__ import annotations
4+
5+
import time
6+
7+
8+
def test_basic_server(server, session) -> None:
9+
"""Test basic server connection.
10+
11+
Note: We need a session fixture to ensure there's an active session.
12+
"""
13+
# Verify the server has sessions by checking for the session provided by the fixture
14+
sessions = server.list_sessions()
15+
assert sessions, "Server should have at least one session"
16+
17+
# Check if the server is responding to commands
18+
assert server.cmd("list-sessions").stdout is not None
19+
20+
21+
def test_basic_session(session) -> None:
22+
"""Test basic session functionality."""
23+
# Session should be created by the fixture
24+
assert session is not None
25+
26+
# Session should have a name
27+
assert session.session_name
28+
29+
# Get session info
30+
session_info = session.cmd("display-message", "-p", "#{session_name}").stdout
31+
assert len(session_info) > 0
32+
33+
34+
def test_basic_window(session) -> None:
35+
"""Test basic window creation."""
36+
# Create a new window
37+
window = session.new_window(window_name="test-window")
38+
39+
# Verify window was created with the correct name
40+
assert window.window_name == "test-window"
41+
42+
# Get the number of panes in the window
43+
assert len(window.panes) == 1
44+
45+
# Rename the window
46+
window.rename_window("renamed-window")
47+
assert window.window_name == "renamed-window"
48+
49+
50+
def test_basic_pane(session) -> None:
51+
"""Test basic pane functionality."""
52+
window = session.new_window(window_name="pane-test")
53+
pane = window.active_pane
54+
55+
# Send a command to the pane
56+
pane.send_keys("echo 'Hello, tmux!'", enter=True)
57+
58+
# Give the command time to execute
59+
time.sleep(0.5)
60+
61+
# Capture the pane output
62+
output = pane.capture_pane()
63+
64+
# Verify the output contains our message
65+
assert any("Hello, tmux!" in line for line in output)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""Examples for command polling in tmux tests."""
2+
3+
from __future__ import annotations
4+
5+
import time
6+
7+
8+
def test_command_with_polling(session) -> None:
9+
"""Test running a command and polling for completion."""
10+
# Create a window for testing
11+
window = session.new_window(window_name="polling-test")
12+
pane = window.active_pane
13+
14+
# Clear the pane
15+
pane.send_keys("clear", enter=True)
16+
time.sleep(0.3)
17+
18+
# Run a command that takes some time (using sleep)
19+
pane.send_keys("echo 'Starting task'; sleep 2; echo 'Task complete'", enter=True)
20+
21+
# Poll for completion by checking for the completion message
22+
max_polls = 10
23+
poll_interval = 0.5
24+
completion_found = False
25+
26+
for _ in range(max_polls):
27+
output = pane.capture_pane()
28+
if any("Task complete" in line for line in output):
29+
completion_found = True
30+
break
31+
time.sleep(poll_interval)
32+
33+
# Verify the task completed
34+
assert completion_found, "Task did not complete within the expected time"
35+
36+
# Additional verification that both messages are in the output
37+
final_output = pane.capture_pane()
38+
assert any("Starting task" in line for line in final_output)
39+
assert any("Task complete" in line for line in final_output)
40+
41+
42+
def test_error_handling(session) -> None:
43+
"""Test error handling during command execution."""
44+
# Create a window for testing
45+
window = session.new_window(window_name="error-test")
46+
pane = window.active_pane
47+
48+
# Clear the pane
49+
pane.send_keys("clear", enter=True)
50+
time.sleep(0.3)
51+
52+
# Run a command that will produce an error
53+
pane.send_keys(
54+
"echo 'Running command with error'; "
55+
"ls /nonexistent_directory; "
56+
"echo 'Command finished'",
57+
enter=True,
58+
)
59+
time.sleep(1) # Wait for command to complete
60+
61+
# Capture the output
62+
output = pane.capture_pane()
63+
64+
# Verify error message and completion message
65+
assert any("Running command with error" in line for line in output), (
66+
"Start message not found"
67+
)
68+
assert any("Command finished" in line for line in output), (
69+
"Completion message not found"
70+
)
71+
72+
# Verify error message is in the output
73+
has_error = any("No such file or directory" in line for line in output) or any(
74+
"cannot access" in line for line in output
75+
)
76+
assert has_error, "Error message not found in output"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Examples for working with custom tmux configurations."""
2+
3+
from __future__ import annotations
4+
5+
import time
6+
7+
8+
def test_with_custom_config(TestServer, tmp_path) -> None:
9+
"""Test using a custom tmux configuration."""
10+
# Create a custom tmux configuration file
11+
config_file = tmp_path / "tmux.conf"
12+
# Simply test with a history-limit change which is more reliable
13+
config_file.write_text("set -g history-limit 5000")
14+
15+
# Create a server with the custom configuration
16+
server = TestServer(config_file=str(config_file))
17+
18+
# Create a session to ensure the server is active
19+
session = server.new_session("custom-config-test")
20+
21+
# Verify server has our session
22+
assert server.has_session("custom-config-test")
23+
24+
# Test that we can run commands in the session, which proves the config is working
25+
window = session.active_window
26+
pane = window.active_pane
27+
28+
# Send a command
29+
pane.send_keys("echo 'Testing custom config'", enter=True)
30+
time.sleep(0.5)
31+
32+
# Verify the command was executed
33+
output = pane.capture_pane()
34+
assert any("Testing custom config" in line for line in output)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""Examples for working with custom environment in tmux tests."""
2+
3+
from __future__ import annotations
4+
5+
import os
6+
import time
7+
8+
9+
def test_environment_variables(session) -> None:
10+
"""Test setting and using environment variables."""
11+
# Create a window for testing
12+
window = session.new_window(window_name="env-test")
13+
pane = window.active_pane
14+
15+
# Set environment variables for the pane
16+
test_env = {
17+
"TEST_VAR1": "value1",
18+
"TEST_VAR2": "value2",
19+
"TEST_PATH": "/custom/path:/another/path",
20+
}
21+
22+
# Clear the pane first
23+
pane.send_keys("clear", enter=True)
24+
time.sleep(0.3)
25+
26+
# Set environment variables
27+
for key, value in test_env.items():
28+
pane.send_keys(f"export {key}='{value}'", enter=True)
29+
30+
time.sleep(0.5)
31+
32+
# Verify environment variables were set
33+
pane.send_keys("echo $TEST_VAR1", enter=True)
34+
time.sleep(0.5)
35+
output = pane.capture_pane()
36+
assert any("value1" in line for line in output)
37+
38+
# Test with a script that uses the variables
39+
pane.send_keys('echo "Combined: $TEST_VAR1 and $TEST_VAR2"', enter=True)
40+
time.sleep(0.5)
41+
output = pane.capture_pane()
42+
assert any("Combined: value1 and value2" in line for line in output)
43+
44+
45+
def test_directory_navigation(session, tmp_path) -> None:
46+
"""Test navigating directories in tmux."""
47+
# Create a window for testing
48+
window = session.new_window(window_name="dir-test")
49+
pane = window.active_pane
50+
51+
# Clear the pane
52+
pane.send_keys("clear", enter=True)
53+
time.sleep(0.3)
54+
55+
# Get and save the initial directory to tmp_path
56+
initial_dir_file = tmp_path / "initial_dir.txt"
57+
pane.send_keys(f"pwd > {initial_dir_file}", enter=True)
58+
time.sleep(0.5)
59+
60+
# Navigate to /tmp directory
61+
pane.send_keys("cd /tmp", enter=True)
62+
time.sleep(0.5)
63+
64+
# Verify we changed directory
65+
pane.send_keys("pwd", enter=True)
66+
time.sleep(0.5)
67+
output = pane.capture_pane()
68+
assert any("/tmp" in line for line in output)
69+
70+
# Create a test directory and navigate to it
71+
test_dir = f"tmux_test_{os.getpid()}"
72+
pane.send_keys(f"mkdir -p {test_dir}", enter=True)
73+
pane.send_keys(f"cd {test_dir}", enter=True)
74+
time.sleep(0.5)
75+
76+
# Verify we're in the test directory
77+
pane.send_keys("pwd", enter=True)
78+
time.sleep(0.5)
79+
output = pane.capture_pane()
80+
assert any(f"/tmp/{test_dir}" in line for line in output)
81+
82+
# Clean up
83+
pane.send_keys("cd ..", enter=True)
84+
pane.send_keys(f"rm -r {test_dir}", enter=True)
85+
86+
87+
def test_custom_session(TestServer) -> None:
88+
"""Test creating a session with custom parameters."""
89+
# Create a new server instance
90+
server = TestServer()
91+
92+
# Create a session with custom parameters
93+
session_params = {
94+
"session_name": "custom-session",
95+
"x": 800,
96+
"y": 600,
97+
"start_directory": "/tmp",
98+
}
99+
100+
session = server.new_session(**session_params)
101+
102+
# Verify session was created with the right name
103+
assert session.session_name == "custom-session"
104+
105+
# Verify working directory was set correctly
106+
window = session.active_window
107+
pane = window.active_pane
108+
109+
pane.send_keys("pwd", enter=True)
110+
time.sleep(0.5)
111+
output = pane.capture_pane()
112+
assert any("/tmp" in line for line in output)

0 commit comments

Comments
 (0)