Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e70a23f
chore: Add `__init__.py` for tests/examples
tony Feb 27, 2025
80d3c83
chore: Add `__init__.py` for tests/examples/test
tony Feb 26, 2025
caef9e9
chore: Add `__init__.py` for tests/examples/_internal/waiter
tony Feb 27, 2025
d12cb82
fix(retry): Improve retry_until_extended function with better error m…
tony Feb 26, 2025
6b3cf18
feat(waiter): Enhance terminal content waiting utility with fluent AP…
tony Feb 26, 2025
373d2eb
test(waiter): Fix test cases and improve type safety
tony Feb 26, 2025
edb2bde
docs(waiter): Add comprehensive documentation for terminal content wa…
tony Feb 26, 2025
0857555
pyproject(mypy[exceptions]): examples to ignore `no-untyped-def`
tony Feb 26, 2025
27da8d9
test: add conftest.py to register example marker
tony Feb 26, 2025
ea16c23
refactor(tests[waiter]): Add waiter test examples into individual files
tony Feb 26, 2025
9b2d588
docs(CHANGES) Note `Waiter`
tony Feb 27, 2025
2e48f0d
feat(waiter): Add terminal content waiting utility for testing (#582)
tony Feb 27, 2025
7e97e35
tests(test_waiter[capture_pane]): Add resiliency for CI test grid
tony Feb 28, 2025
ef03e00
tests(test_waiter[exact_match]): Skip flaky exact match test on tmux …
tony Feb 28, 2025
2820203
test(waiter): Replace assertions with warning-based checks in detaile…
tony Feb 28, 2025
f1b3549
pyproject(mypy[test.examples.pytest_plugin]): pytest examples to igno…
tony Feb 26, 2025
d56e051
docs: Improve window.py
tony Feb 1, 2025
92c8e53
docs: Improve test.py
tony Feb 1, 2025
f206709
docs: Improve pytest_plugin.py
tony Feb 1, 2025
b297f7a
docs: Improve conftest.py
tony Feb 1, 2025
afe7536
docs: Add new Topics
tony Feb 26, 2025
daa1b8f
docs(topics): Improve documentation with executable examples
tony Feb 26, 2025
32d57d2
docs,tests(pytest plugin) Examples
tony Feb 26, 2025
f7a7490
!squash docs topics
tony Feb 28, 2025
4893d2b
chore(rebase): Add explicit zip strict= in waiter
tony Jul 18, 2026
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
12 changes: 12 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,18 @@ libtmux 0.46.1 is a maintenance release for the 0.46.x line.
The {meth}`~libtmux.Pane.send_keys` documentation had a typo fixed. Thanks
@subbyte.

### New features

#### Waiting (#582)

Added experimental `waiter.py` module for polling for terminal content in tmux panes:

- Fluent API inspired by Playwright for better readability and chainable options
- Support for multiple pattern types (exact text, contains, regex, custom predicates)
- Composable waiting conditions with `wait_for_any_content` and `wait_for_all_content`
- Enhanced error handling with detailed timeouts and match information
- Robust shell prompt detection

## libtmux 0.46.0 (2025-02-25)

libtmux 0.46.0 finishes the `libtmux.test` helper split by removing root-level
Expand Down
33 changes: 23 additions & 10 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Conftest.py (root-level).
"""Configure root-level pytest fixtures for libtmux.

We keep this in root pytest fixtures in pytest's doctest plugin to be available, as well
as avoiding conftest.py from being included in the wheel, in addition to pytest_plugin
for pytester only being available via the root directory.
We keep this file at the root to make these fixtures available to all
tests, while also preventing unwanted inclusion in the distributed
wheel. Additionally, `pytest_plugins` references ensure that the
`pytester` plugin is accessible for test generation and execution.

See "pytest_plugins in non-top-level conftest files" in
https://docs.pytest.org/en/stable/deprecations.html
See Also
--------
pytest_plugins in non-top-level conftest files
https://docs.pytest.org/en/stable/deprecations.html
"""

from __future__ import annotations
Expand Down Expand Up @@ -36,7 +39,13 @@ def add_doctest_fixtures(
request: pytest.FixtureRequest,
doctest_namespace: dict[str, t.Any],
) -> None:
"""Configure doctest fixtures for pytest-doctest."""
"""Configure doctest fixtures for pytest-doctest.

Automatically sets up tmux-related classes and default fixtures,
making them available in doctest namespaces if `tmux` is found
on the system. This ensures that doctest blocks referencing tmux
structures can execute smoothly in the test environment.
"""
if isinstance(request._pyfuncitem, DoctestItem) and shutil.which("tmux"):
request.getfixturevalue("set_home")
doctest_namespace["Server"] = Server
Expand Down Expand Up @@ -65,22 +74,26 @@ def set_home(
monkeypatch: pytest.MonkeyPatch,
user_path: pathlib.Path,
) -> None:
"""Configure home directory for pytest tests."""
"""Set the HOME environment variable to the temporary user directory."""
monkeypatch.setenv("HOME", str(user_path))


@pytest.fixture(autouse=True)
def setup_fn(
clear_env: None,
) -> None:
"""Function-level test configuration fixtures for pytest."""
"""Apply function-level test fixture configuration (e.g., environment cleanup)."""


@pytest.fixture(autouse=True, scope="session")
def setup_session(
request: pytest.FixtureRequest,
config_file: pathlib.Path,
) -> None:
"""Session-level test configuration for pytest."""
"""Apply session-level test fixture configuration for libtmux testing.

If zsh is in use, applies a suppressing `.zshrc` fix to avoid
default interactive messages that might disrupt tmux sessions.
"""
if USING_ZSH:
request.getfixturevalue("zshrc")
1 change: 1 addition & 0 deletions docs/internals/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ api/libtmux._internal.dataclasses
api/libtmux._internal.query_list
api/libtmux._internal.constants
api/libtmux._internal.sparse_array
waiter
```

## Environmental variables
Expand Down
135 changes: 135 additions & 0 deletions docs/internals/waiter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
(waiter)=

# Waiters - `libtmux._internal.waiter`

The waiter module provides utilities for waiting on specific content to appear in tmux panes, making it easier to write reliable tests that interact with terminal output.

## Key Features

- **Fluent API**: Playwright-inspired chainable API for expressive, readable test code
- **Multiple Match Types**: Wait for exact matches, substring matches, regex patterns, or custom predicate functions
- **Composable Waiting**: Wait for any of multiple conditions or all conditions to be met
- **Flexible Timeout Handling**: Configure timeout behavior and error handling to suit your needs
- **Shell Prompt Detection**: Easily wait for shell readiness with built-in prompt detection
- **Robust Error Handling**: Improved exception handling and result reporting
- **Clean Code**: Well-formatted, linted code with proper type annotations

## Basic Concepts

When writing tests that interact with tmux sessions and panes, it's often necessary to wait for specific content to appear before proceeding with the next step. The waiter module provides a set of functions to help with this.

There are multiple ways to match content:
- **Exact match**: The content exactly matches the specified string
- **Contains**: The content contains the specified string
- **Regex**: The content matches the specified regular expression
- **Predicate**: A custom function that takes the pane content and returns a boolean

## Quick Start Examples

### Simple Waiting

Wait for specific text to appear in a pane:

```{literalinclude} ../../tests/examples/_internal/waiter/test_wait_for_text.py
:language: python
```

### Advanced Matching

Use regex patterns or custom predicates for more complex matching:

```{literalinclude} ../../tests/examples/_internal/waiter/test_wait_for_regex.py
:language: python
```

```{literalinclude} ../../tests/examples/_internal/waiter/test_custom_predicate.py
:language: python
```

### Timeout Handling

Control how long to wait and what happens when a timeout occurs:

```{literalinclude} ../../tests/examples/_internal/waiter/test_timeout_handling.py
:language: python
```

### Waiting for Shell Readiness

A common use case is waiting for a shell prompt to appear, indicating the command has completed. The example below uses a regular expression to match common shell prompt characters (`$`, `%`, `>`, `#`):

```{literalinclude} ../../tests/examples/_internal/waiter/test_wait_until_ready.py
:language: python
```

> Note: This test is skipped in CI environments due to timing issues but works well for local development.

## Fluent API (Playwright-inspired)

For a more expressive and chainable API, you can use the fluent interface provided by the `PaneContentWaiter` class:

```{literalinclude} ../../tests/examples/_internal/waiter/test_fluent_basic.py
:language: python
```

```{literalinclude} ../../tests/examples/_internal/waiter/test_fluent_chaining.py
:language: python
```

## Multiple Conditions

The waiter module also supports waiting for multiple conditions at once:

```{literalinclude} ../../tests/examples/_internal/waiter/test_wait_for_any_content.py
:language: python
```

```{literalinclude} ../../tests/examples/_internal/waiter/test_wait_for_all_content.py
:language: python
```

```{literalinclude} ../../tests/examples/_internal/waiter/test_mixed_pattern_types.py
:language: python
```

## Implementation Notes

### Error Handling

The waiting functions are designed to be robust and handle timing and error conditions gracefully:

- All wait functions properly calculate elapsed time for performance tracking
- Functions handle exceptions consistently and provide clear error messages
- Proper handling of return values ensures consistent behavior whether or not raises=True

### Type Safety

The waiter module is fully type-annotated to ensure compatibility with static type checkers:

- All functions include proper type hints for parameters and return values
- The ContentMatchType enum ensures that only valid match types are used
- Combined with runtime checks, this prevents type-related errors during testing

### Example Usage in Documentation

All examples in this documentation are actual test files from the libtmux test suite. The examples are included using `literalinclude` directives, ensuring that the documentation remains synchronized with the actual code.

## API Reference

```{eval-rst}
.. automodule:: libtmux._internal.waiter
:members:
:undoc-members:
:show-inheritance:
:member-order: bysource
```

## Extended Retry Functionality

```{eval-rst}
.. automodule:: libtmux.test.retry_extended
:members:
:undoc-members:
:show-inheritance:
:member-order: bysource
```
114 changes: 114 additions & 0 deletions docs/pytest-plugin/advanced-techniques.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
myst:
html_meta:
description: "Advanced techniques for testing with the libtmux pytest plugin"
keywords: "tmux, pytest, advanced testing, polling, temporary files"
---

(advanced-techniques)=

# Advanced Techniques

This page covers advanced testing techniques using the libtmux pytest plugin for more sophisticated testing scenarios.

## Testing with Temporary Files

### Creating Temporary Project Directories

```{literalinclude} ../../tests/examples/pytest_plugin/test_temp_files.py
:language: python
:pyobject: temp_project_dir
```

### Working with Files in Temporary Directories

```{literalinclude} ../../tests/examples/pytest_plugin/test_temp_files.py
:language: python
:pyobject: test_project_file_manipulation
```

## Command Polling

### Implementing Robust Wait Functions

```{literalinclude} ../../tests/examples/pytest_plugin/test_command_polling.py
:language: python
:pyobject: wait_for_output
```

### Testing with Command Polling

```{literalinclude} ../../tests/examples/pytest_plugin/test_command_polling.py
:language: python
:pyobject: test_command_with_polling
```

### Error Handling with Polling

```{literalinclude} ../../tests/examples/pytest_plugin/test_command_polling.py
:language: python
:pyobject: test_error_handling
```

## Setting Custom Home Directory

### Temporary Home Directory Setup

```{literalinclude} ../../tests/examples/pytest_plugin/test_home_directory.py
:language: python
:pyobject: set_home
```

## Testing with Complex Layouts

Creating and testing more complex window layouts:

```{literalinclude} ../../tests/examples/pytest_plugin/test_complex_layouts.py
:language: python
:pyobject: test_complex_layouts
```

For an even more advanced layout, you can create a tiled configuration:

```{literalinclude} ../../tests/examples/pytest_plugin/test_complex_layouts.py
:language: python
:pyobject: test_tiled_layout
```

## Testing Across Server Restarts

When you need to test functionality that persists across server restarts:

```{literalinclude} ../../tests/examples/pytest_plugin/test_server_restart.py
:language: python
:pyobject: test_persist_across_restart
```

## Best Practices

### Test Structure

1. **Arrange** - Set up your tmux environment and test data
2. **Act** - Perform the actions you want to test
3. **Assert** - Verify the expected outcome
4. **Clean up** - Reset any state changes (usually handled by fixtures)

### Tips for Reliable Tests

1. **Use appropriate waits**: Terminal operations aren't instantaneous. Add sufficient wait times or use polling techniques.

2. **Capture full pane contents**: Use `pane.capture_pane()` to get all output content for verification.

3. **Isolate tests**: Don't rely on state from other tests. Each test should set up its own environment.

4. **Use descriptive assertions**: When tests fail, the assertion message should clarify what went wrong.

5. **Test error conditions**: Include tests for error handling to ensure your code behaves correctly in failure scenarios.

6. **Keep tests fast**: Minimize wait times while keeping tests reliable.

7. **Use parametrized tests**: For similar tests with different inputs, use pytest's parametrize feature.

8. **Document test requirements**: If tests require specific tmux features, document this in comments.

9. **Mind CI environments**: Tests should work consistently in both local and CI environments, which may have different tmux versions and capabilities.
Loading
Loading