Skip to content

Coverage: Test helpers #580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8b78910
test(random): Add comprehensive tests for random test utilities
tony Feb 24, 2025
647c9d1
test(temporary): Add tests for temporary session/window context managers
tony Feb 24, 2025
9385862
test(random): Fix collision tests and improve coverage
tony Feb 24, 2025
1a258fe
test(constants): Add tests for test constants
tony Feb 24, 2025
cc46b5d
test(environment): add comprehensive tests for EnvironmentVarGuard
tony Feb 24, 2025
77cdce7
test(random): enhance RandomStrSequence tests
tony Feb 24, 2025
09d5bdb
test(retry): improve retry_until test reliability
tony Feb 24, 2025
fbefdde
refactor(random): clean up imports and type hints
tony Feb 24, 2025
6675ab2
test(random): enhance test coverage
tony Feb 24, 2025
efaa103
style(test): combine nested with statements
tony Feb 24, 2025
9788b7a
tests: Clear out test/__init__.py
tony Feb 24, 2025
a8b3141
chore(coverage): exclude type checking from coverage
tony Feb 24, 2025
92e0689
pyproject(coverage) Ignore `import typing as t`
tony Feb 24, 2025
55be8e8
pyproject(coverage) Ignore protocol
tony Feb 24, 2025
d661ecf
chore(coverage): exclude doctest examples from coverage
tony Feb 24, 2025
bb24a80
test(environment): improve test coverage to 100%
tony Feb 24, 2025
a1ee582
style: remove unused import
tony Feb 24, 2025
1476875
fix(test): Fix doctest examples in random.py
tony Feb 24, 2025
1e31d9e
test: enhance RandomStrSequence testing coverage
tony Feb 25, 2025
e345a21
test: improve temporary context handling tests
tony Feb 25, 2025
e1a73bd
test: improve coverage for random test utilities
tony Feb 25, 2025
e295aa3
test: improve code coverage with direct tests that don't mock core me…
tony Feb 25, 2025
6210ce6
test: replace multiple mocked collision tests with real tmux objects
tony Feb 25, 2025
a02e05a
test(random): improve test coverage for test utils
tony Feb 25, 2025
39c9a79
docs(CHANGES) Note test coverage updates
tony Feb 25, 2025
a4658c9
docs(MIGRATION) Note `libtmux.test` import fix
tony Feb 25, 2025
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
48 changes: 48 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,54 @@ $ pip install --user --upgrade --pre libtmux

- _Future release notes will be placed here_

### Breaking

#### Imports removed from libtmux.test (#580)

Root-level of imports from `libtmux.test` are no longer possible.

```python
# Before 0.46.0
from libtmux.test import namer
```

```python
# From 0.46.0 onward
from libtmux.test.named import namer
```

Same thing with constants:

```python
# Before 0.46.0
from libtmux.test import (
RETRY_INTERVAL_SECONDS,
RETRY_TIMEOUT_SECONDS,
TEST_SESSION_PREFIX
)
```

```python
# From 0.46.0 onward
from libtmux.test.constants import (
RETRY_INTERVAL_SECONDS,
RETRY_TIMEOUT_SECONDS,
TEST_SESSION_PREFIX
)
```

### Development

#### Test helpers: Increased coverage (#580)

Several improvements to the test helper modules:

- Enhanced `EnvironmentVarGuard` in `libtmux.test.environment` to better handle variable cleanup
- Added comprehensive test suites for test constants and environment utilities
- Improved docstrings and examples in `libtmux.test.random` with test coverage annotations
- Fixed potential issues with environment variable handling during tests
- Added proper coverage markers to exclude type checking blocks from coverage reports

## libtmux 0.45.0 (2025-02-23)

### Breaking Changes
Expand Down
34 changes: 34 additions & 0 deletions MIGRATION
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,40 @@ _Detailed migration steps for the next version will be posted here._

<!-- To the maintainers and contributors: please add migration details for the upcoming release here -->

#### Imports removed from libtmux.test (#580)

Root-level of imports from `libtmux.test` are no longer possible.

```python
# Before 0.46.0
from libtmux.test import namer
```

```python
# From 0.46.0 onward
from libtmux.test.named import namer
```

Same thing with constants:

```python
# Before 0.46.0
from libtmux.test import (
RETRY_INTERVAL_SECONDS,
RETRY_TIMEOUT_SECONDS,
TEST_SESSION_PREFIX
)
```

```python
# From 0.46.0 onward
from libtmux.test.constants import (
RETRY_INTERVAL_SECONDS,
RETRY_TIMEOUT_SECONDS,
TEST_SESSION_PREFIX
)
```

## libtmux 0.45.0 (2025-02-23)

### Test helpers: Module moves
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ exclude_lines = [
"if TYPE_CHECKING:",
"if t.TYPE_CHECKING:",
"@overload( |$)",
'class .*\bProtocol\):',
"from __future__ import annotations",
"import typing as t",
]

[tool.ruff]
Expand Down
35 changes: 0 additions & 35 deletions src/libtmux/test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,36 +1 @@
"""Helper methods for libtmux and downstream libtmux libraries."""

from __future__ import annotations

import contextlib
import logging
import os
import pathlib
import random
import time
import typing as t

from libtmux.exc import WaitTimeout
from libtmux.test.constants import (
RETRY_INTERVAL_SECONDS,
RETRY_TIMEOUT_SECONDS,
TEST_SESSION_PREFIX,
)

from .random import namer

logger = logging.getLogger(__name__)

if t.TYPE_CHECKING:
import sys
import types
from collections.abc import Callable, Generator

from libtmux.server import Server
from libtmux.session import Session
from libtmux.window import Window

if sys.version_info >= (3, 11):
from typing import Self
else:
from typing_extensions import Self
10 changes: 8 additions & 2 deletions src/libtmux/test/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ def set(self, envvar: str, value: str) -> None:
def unset(self, envvar: str) -> None:
"""Unset environment variable."""
if envvar in self._environ:
self._reset[envvar] = self._environ[envvar]
# If we previously set this variable in this context, remove it from _unset
if envvar in self._unset:
self._unset.remove(envvar)
# If we haven't saved the original value yet, save it
if envvar not in self._reset:
self._reset[envvar] = self._environ[envvar]
del self._environ[envvar]

def __enter__(self) -> Self:
Expand All @@ -69,4 +74,5 @@ def __exit__(
for envvar, value in self._reset.items():
self._environ[envvar] = value
for unset in self._unset:
del self._environ[unset]
if unset not in self._reset: # Don't delete variables that were reset
del self._environ[unset]
47 changes: 22 additions & 25 deletions src/libtmux/test/random.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
"""Random helpers for libtmux and downstream libtmux libraries."""

from __future__ import annotations
from __future__ import annotations # pragma: no cover

import logging
import random
import typing as t
import typing as t # pragma: no cover

from libtmux.test.constants import (
TEST_SESSION_PREFIX,
)

logger = logging.getLogger(__name__)

if t.TYPE_CHECKING:
import sys
if t.TYPE_CHECKING: # pragma: no cover
import sys # pragma: no cover

from libtmux.server import Server
from libtmux.session import Session
from libtmux.server import Server # pragma: no cover
from libtmux.session import Session # pragma: no cover

if sys.version_info >= (3, 11):
pass
if sys.version_info >= (3, 11): # pragma: no cover
pass # pragma: no cover
else: # pragma: no cover
pass # pragma: no cover


logger = logging.getLogger(__name__)

if t.TYPE_CHECKING:
import sys

if sys.version_info >= (3, 11):
pass


class RandomStrSequence:
"""Factory to generate random string."""
Expand All @@ -40,12 +34,12 @@ def __init__(
) -> None:
"""Create a random letter / number generator. 8 chars in length.

>>> rng = RandomStrSequence()
>>> next(rng)
>>> rng = RandomStrSequence() # pragma: no cover
>>> next(rng) # pragma: no cover
'...'
>>> len(next(rng))
>>> len(next(rng)) # pragma: no cover
8
>>> type(next(rng))
>>> type(next(rng)) # pragma: no cover
<class 'str'>
"""
self.characters: str = characters
Expand Down Expand Up @@ -81,11 +75,13 @@ def get_test_session_name(server: Server, prefix: str = TEST_SESSION_PREFIX) ->

Examples
--------
>>> get_test_session_name(server=server)
>>> get_test_session_name(server=server) # pragma: no cover
'libtmux_...'

Never the same twice:
>>> get_test_session_name(server=server) != get_test_session_name(server=server)
>>> name1 = get_test_session_name(server=server) # pragma: no cover
>>> name2 = get_test_session_name(server=server) # pragma: no cover
>>> name1 != name2 # pragma: no cover
True
"""
while True:
Expand Down Expand Up @@ -119,12 +115,13 @@ def get_test_window_name(

Examples
--------
>>> get_test_window_name(session=session)
>>> get_test_window_name(session=session) # pragma: no cover
'libtmux_...'

Never the same twice:

>>> get_test_window_name(session=session) != get_test_window_name(session=session)
>>> name1 = get_test_window_name(session=session) # pragma: no cover
>>> name2 = get_test_window_name(session=session) # pragma: no cover
>>> name1 != name2 # pragma: no cover
True
"""
assert prefix is not None
Expand Down
51 changes: 51 additions & 0 deletions tests/test/test_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Tests for libtmux's test constants."""

from __future__ import annotations

from typing import TYPE_CHECKING

from libtmux.test.constants import (
RETRY_INTERVAL_SECONDS,
RETRY_TIMEOUT_SECONDS,
TEST_SESSION_PREFIX,
)

if TYPE_CHECKING:
import pytest


def test_test_session_prefix() -> None:
"""Test TEST_SESSION_PREFIX is correctly defined."""
assert TEST_SESSION_PREFIX == "libtmux_"


def test_retry_timeout_seconds_default() -> None:
"""Test RETRY_TIMEOUT_SECONDS default value."""
assert RETRY_TIMEOUT_SECONDS == 8


def test_retry_timeout_seconds_env(monkeypatch: pytest.MonkeyPatch) -> None:
"""Test RETRY_TIMEOUT_SECONDS can be configured via environment variable."""
monkeypatch.setenv("RETRY_TIMEOUT_SECONDS", "10")
from importlib import reload

import libtmux.test.constants

reload(libtmux.test.constants)
assert libtmux.test.constants.RETRY_TIMEOUT_SECONDS == 10


def test_retry_interval_seconds_default() -> None:
"""Test RETRY_INTERVAL_SECONDS default value."""
assert RETRY_INTERVAL_SECONDS == 0.05


def test_retry_interval_seconds_env(monkeypatch: pytest.MonkeyPatch) -> None:
"""Test RETRY_INTERVAL_SECONDS can be configured via environment variable."""
monkeypatch.setenv("RETRY_INTERVAL_SECONDS", "0.1")
from importlib import reload

import libtmux.test.constants

reload(libtmux.test.constants)
assert libtmux.test.constants.RETRY_INTERVAL_SECONDS == 0.1
Loading