diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 92c2456cc..1d97fad1c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -71,7 +71,7 @@ jobs: export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH ls $HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin tmux -V - poetry run py.test --cov=./ --cov-append --cov-report=xml + poetry run py.test --cov=./ --cov-append --cov-report=xml -n auto env: COV_CORE_SOURCE: . COV_CORE_CONFIG: .coveragerc diff --git a/CHANGES b/CHANGES index 063347e87..53ba0bdd1 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,34 @@ $ pip install --user --upgrade --pre libtmux +### Testing + +- Add `pytest-xdist` ([PyPI](https://pypi.org/project/pytest-xdist/), [GitHub](https://github.com/pytest-dev/pytest-xdist)) for parallel testing (#522). + + pytest: + + ```console + py.test -n auto + ``` + + pytest-watcher: + + ```console + env PYTEST_ADDOPTS='-n auto' make start + ``` + + entr(1): + + ```console + make watch_test test="-n auto" + ``` + +- Improve flakey tests: + + - `retry_until()` tests: Relax clock in `assert` (#522). + - `tests/test_pane.py::test_capture_pane_start`: Use `retry_until()` to poll, + improve correctness of test (#522). + ### Documentation - Automatically linkify links that were previously only text. diff --git a/poetry.lock b/poetry.lock index 0453f5d59..b7d3c378d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -274,6 +274,20 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "execnet" +version = "2.1.1" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, + {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + [[package]] name = "furo" version = "2024.1.29" @@ -745,6 +759,26 @@ files = [ tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} watchdog = ">=2.0.0" +[[package]] +name = "pytest-xdist" +version = "3.5.0" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, + {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + [[package]] name = "pytz" version = "2024.1" @@ -1271,4 +1305,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "0baf7a516f4d472d14265a5fe813c7fbd106a51eb833914db67cedb950d8d032" +content-hash = "cc8619b0d3bedf67f5116b03fdd3a5a77695ce7cc233e8df2c6c5cebf4d45d43" diff --git a/pyproject.toml b/pyproject.toml index f2f3e8724..e317a2457 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ pytest = "*" pytest-rerunfailures = "*" pytest-mock = "*" pytest-watcher = "*" +pytest-xdist = "*" gp-libs = "~0.0.4" [tool.poetry.group.coverage.dependencies] @@ -154,8 +155,18 @@ convention = "numpy" "*/__init__.py" = ["F401"] [tool.pytest.ini_options] -addopts = "--tb=short --no-header --showlocals --doctest-docutils-modules --reruns 2 -p no:doctest" -doctest_optionflags = "ELLIPSIS NORMALIZE_WHITESPACE" +addopts = [ + "--tb=short", + "--no-header", + "--showlocals", + "--doctest-docutils-modules", + "-p no:doctest", + "--reruns=2" +] +doctest_optionflags = [ + "ELLIPSIS", + "NORMALIZE_WHITESPACE" +] testpaths = [ "src/libtmux", "tests", diff --git a/tests/legacy_api/test_test.py b/tests/legacy_api/test_test.py deleted file mode 100644 index 8356524f1..000000000 --- a/tests/legacy_api/test_test.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Tests for libtmux's testing utilities.""" - -from time import time - -import pytest - -from libtmux.exc import WaitTimeout -from libtmux.test import retry_until - - -def test_retry_three_times() -> None: - """Test retry_until().""" - ini = time() - value = 0 - - def call_me_three_times() -> bool: - nonlocal value - - if value == 2: - return True - - value += 1 - - return False - - retry_until(call_me_three_times, 1) - - end = time() - - assert abs((end - ini) - 0.1) < 0.01 - - -def test_function_times_out() -> None: - """Test time outs with retry_until().""" - ini = time() - - def never_true() -> bool: - return False - - with pytest.raises(WaitTimeout): - retry_until(never_true, 1) - - end = time() - - assert abs((end - ini) - 1.0) < 0.01 - - -def test_function_times_out_no_raise() -> None: - """Tests retry_until() with exception raising disabled.""" - ini = time() - - def never_true() -> bool: - return False - - retry_until(never_true, 1, raises=False) - - end = time() - - assert abs((end - ini) - 1.0) < 0.01 - - -def test_function_times_out_no_raise_assert() -> None: - """Tests retry_until() with exception raising disabled, returning False.""" - ini = time() - - def never_true() -> bool: - return False - - assert not retry_until(never_true, 1, raises=False) - - end = time() - - assert abs((end - ini) - 1.0) < 0.01 - - -def test_retry_three_times_no_raise_assert() -> None: - """Tests retry_until() with exception raising disabled, with closure variable.""" - ini = time() - value = 0 - - def call_me_three_times() -> bool: - nonlocal value - - if value == 2: - return True - - value += 1 - - return False - - assert retry_until(call_me_three_times, 1, raises=False) - - end = time() - - assert abs((end - ini) - 0.1) < 0.01 diff --git a/tests/test_pane.py b/tests/test_pane.py index 428fda2e3..ac59ec7fa 100644 --- a/tests/test_pane.py +++ b/tests/test_pane.py @@ -8,6 +8,7 @@ from libtmux.common import has_gte_version, has_lt_version, has_lte_version from libtmux.constants import PaneDirection, ResizeAdjustmentDirection from libtmux.session import Session +from libtmux.test import retry_until logger = logging.getLogger(__name__) @@ -100,14 +101,31 @@ def test_capture_pane_start(session: Session) -> None: pane_contents = "\n".join(pane.capture_pane()) assert pane_contents == '$ printf "%s"\n$' pane.send_keys("clear -x", literal=True, suppress_history=False) - pane_contents = "\n".join(pane.capture_pane()) - assert pane_contents == "$" - pane_contents_start = pane.capture_pane(start=-2) - assert pane_contents_start[0] == '$ printf "%s"' - assert pane_contents_start[1] == "$ clear -x" - assert pane_contents_start[-1] == "$" - pane_contents_start = pane.capture_pane(start="-") - assert pane_contents == "$" + + def wait_until_pane_cleared() -> bool: + pane_contents = "\n".join(pane.capture_pane()) + return "clear -x" not in pane_contents + + retry_until(wait_until_pane_cleared, 1, raises=True) + + def pane_contents_shell_prompt() -> bool: + pane_contents = "\n".join(pane.capture_pane()) + return pane_contents == "$" + + retry_until(pane_contents_shell_prompt, 1, raises=True) + + pane_contents_history_start = pane.capture_pane(start=-2) + assert pane_contents_history_start[0] == '$ printf "%s"' + assert pane_contents_history_start[1] == "$ clear -x" + assert pane_contents_history_start[-1] == "$" + + pane.send_keys("") + + def pane_contents_capture_visible_only_shows_prompt() -> bool: + pane_contents = "\n".join(pane.capture_pane(start=1)) + return pane_contents == "$" + + assert retry_until(pane_contents_capture_visible_only_shows_prompt, 1, raises=True) def test_capture_pane_end(session: Session) -> None: diff --git a/tests/test_test.py b/tests/test_test.py index 8356524f1..f2313cdd4 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -27,7 +27,7 @@ def call_me_three_times() -> bool: end = time() - assert abs((end - ini) - 0.1) < 0.01 + assert abs((end - ini) - 1.0) > 0 < 0.1 def test_function_times_out() -> None: @@ -42,7 +42,7 @@ def never_true() -> bool: end = time() - assert abs((end - ini) - 1.0) < 0.01 + assert abs((end - ini) - 1.0) > 0 < 0.1 def test_function_times_out_no_raise() -> None: @@ -56,7 +56,7 @@ def never_true() -> bool: end = time() - assert abs((end - ini) - 1.0) < 0.01 + assert abs((end - ini) - 1.0) > 0 < 0.1 def test_function_times_out_no_raise_assert() -> None: @@ -70,7 +70,7 @@ def never_true() -> bool: end = time() - assert abs((end - ini) - 1.0) < 0.01 + assert abs((end - ini) - 1.0) > 0 < 0.1 def test_retry_three_times_no_raise_assert() -> None: @@ -92,4 +92,4 @@ def call_me_three_times() -> bool: end = time() - assert abs((end - ini) - 0.1) < 0.01 + assert abs((end - ini) - 1.0) > 0 < 0.1