From 61551e2463bef6ac51390714098659d7770c375c Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 06:47:03 -0500 Subject: [PATCH 01/10] chore(git[ignore]): ignore .pytest-optimizer state dir why: The pytest-optimizer tooling writes durable profiling and benchmark JSON under .pytest-optimizer/; it is local state, not source, and should never be committed. what: - Add .pytest-optimizer/ to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9dadb843bd..0fbfc1838b 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,6 @@ docs/_static/css/fonts.css **/CLAUDE.local.md **/CLAUDE.*.md **/.claude/settings.local.json + +# pytest-optimizer durable state (profiling/benchmarks) +.pytest-optimizer/ From 7cf17dc60e154e658f704357301e7c57fa984a0e Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 06:48:38 -0500 Subject: [PATCH 02/10] tests(freezer): poll readiness, drop fixed sleeps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit why: test_freeze_config and test_freeze_logs_debug each slept a fixed time.sleep(0.50) to let tmux settle after build() before freeze() — ~1.0s of dead wait per run. what: - Replace both sleeps with retry_until polling on len(session.windows) >= len(session_config["windows"]) - Import retry_until (libtmux), drop now-unused import time - Suite 39.69s -> 38.53s (-1.16s), above the 0.68s k*MAD noise band --- tests/workspace/test_freezer.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/workspace/test_freezer.py b/tests/workspace/test_freezer.py index d42386ecef..f1f3490dff 100644 --- a/tests/workspace/test_freezer.py +++ b/tests/workspace/test_freezer.py @@ -3,10 +3,10 @@ from __future__ import annotations import logging -import time import typing import pytest +from libtmux.test.retry import retry_until from tests.fixtures import utils as test_utils from tmuxp._internal.config_reader import ConfigReader @@ -31,7 +31,9 @@ def test_freeze_config(session: Session) -> None: builder.build(session=session) assert session == builder.session - time.sleep(0.50) + # Wait for all configured windows to register instead of a fixed sleep. + expected_windows = len(session_config["windows"]) + retry_until(lambda: len(session.windows) >= expected_windows, 2) session = session new_config = freezer.freeze(session) @@ -122,7 +124,9 @@ def test_freeze_logs_debug( builder = WorkspaceBuilder(session_config=session_config, server=session.server) builder.build(session=session) - time.sleep(0.50) + # Wait for all configured windows to register instead of a fixed sleep. + expected_windows = len(session_config["windows"]) + retry_until(lambda: len(session.windows) >= expected_windows, 2) with caplog.at_level(logging.DEBUG, logger="tmuxp.workspace.freezer"): freezer.freeze(session) From 968b583f20a0f48b75593cf91c6047fbb650aee3 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 06:51:05 -0500 Subject: [PATCH 03/10] test(parallel): add pytest-xdist opt-in parallel runs why: the suite is ~88% call-bound across independent, tmux-driven test bodies (each test spawns its own daemon on a unique socket), so it parallelizes cleanly. Measured: serial 39.69s -> ~16-19s under capped xdist (best 13.7s) ~= 2.1-2.9x, far above the 0.68s noise band; fully green under -n 8 in clean CI conditions. what: - Add pytest-xdist to the dev and testing dependency groups - Add a `just test-parallel` recipe with a capped worker count (-n 8); `just test` stays serial - Keep -n out of addopts: -n auto (=cores) thrashes at full core saturation (~40s) and -n breaks --pdb/-x ergonomics --- justfile | 5 +++++ pyproject.toml | 2 ++ uv.lock | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/justfile b/justfile index 3c2fd193da..5b3b09c225 100644 --- a/justfile +++ b/justfile @@ -17,6 +17,11 @@ default: test *args: uv run py.test {{ args }} +# Run tests in parallel with pytest-xdist (capped workers; -n auto can thrash) +[group: 'test'] +test-parallel *args: + uv run py.test -n 8 {{ args }} + # Run tests then start continuous testing with pytest-watcher [group: 'test'] start: diff --git a/pyproject.toml b/pyproject.toml index 2c7ed93b32..7987d04f9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,6 +68,7 @@ dev = [ "pytest-rerunfailures", "pytest-mock", "pytest-watcher", + "pytest-xdist", # Coverage "codecov", "coverage", @@ -95,6 +96,7 @@ testing = [ "pytest-rerunfailures", "pytest-mock", "pytest-watcher", + "pytest-xdist", ] coverage =[ "codecov", diff --git a/uv.lock b/uv.lock index 71bb1214c4..4e4807e037 100644 --- a/uv.lock +++ b/uv.lock @@ -409,6 +409,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] +[[package]] +name = "execnet" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, +] + [[package]] name = "gp-furo-theme" version = "0.0.1a31" @@ -1074,6 +1083,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/3f/172d73600ad2771774cda108efb813fc724fc345e5240a81a1085f1ade5d/pytest_watcher-0.6.3-py3-none-any.whl", hash = "sha256:83e7748c933087e8276edb6078663e6afa9926434b4fd8b85cf6b32b1d5bec89", size = 12431, upload-time = "2026-01-10T23:28:17.64Z" }, ] +[[package]] +name = "pytest-xdist" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -1634,6 +1656,7 @@ dev = [ { name = "pytest-mock" }, { name = "pytest-rerunfailures" }, { name = "pytest-watcher" }, + { name = "pytest-xdist" }, { name = "ruff" }, { name = "sphinx-autobuild", version = "2024.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autobuild", version = "2025.8.25", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -1666,6 +1689,7 @@ testing = [ { name = "pytest-mock" }, { name = "pytest-rerunfailures" }, { name = "pytest-watcher" }, + { name = "pytest-xdist" }, ] [package.metadata] @@ -1693,6 +1717,7 @@ dev = [ { name = "pytest-mock" }, { name = "pytest-rerunfailures" }, { name = "pytest-watcher" }, + { name = "pytest-xdist" }, { name = "ruff" }, { name = "sphinx-autobuild" }, { name = "sphinx-autodoc-api-style", specifier = "==0.0.1a31" }, @@ -1723,6 +1748,7 @@ testing = [ { name = "pytest-mock" }, { name = "pytest-rerunfailures" }, { name = "pytest-watcher" }, + { name = "pytest-xdist" }, ] [[package]] From 0aa5984f1cc7d7cfe4fcc96559b0203864f75d80 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 07:45:33 -0500 Subject: [PATCH 04/10] tests(conftest): pin color env for hermetic CLI tests why: CLI color is decided live from FORCE_COLOR/NO_COLOR (see tmuxp._internal.colors.Colors). A contributor who exports FORCE_COLOR in their shell gets ANSI-wrapped help text, so test_help_examples.py fails (len(examples) == 0). The same leak made the suite look order-dependent under xdist when FORCE_COLOR reached the workers. what: - Add autouse _pin_test_color_env clearing FORCE_COLOR/NO_COLOR for every test; color-specific tests still set them explicitly and monkeypatch restores the contributor's values at teardown - Suite now green with FORCE_COLOR exported and under xdist regardless of ambient color env --- conftest.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/conftest.py b/conftest.py index 1f0583439e..e6cd7b8f2b 100644 --- a/conftest.py +++ b/conftest.py @@ -44,6 +44,23 @@ def _pin_test_shell_env(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("SHELL", "/bin/sh") +@pytest.fixture(autouse=True) +def _pin_test_color_env(monkeypatch: pytest.MonkeyPatch) -> None: + """Neutralize ``FORCE_COLOR`` / ``NO_COLOR`` for every test. + + CLI color is decided live from these env vars (see + :class:`tmuxp._internal.colors.Colors`). A contributor who exports + ``FORCE_COLOR`` in their shell would otherwise get ANSI-wrapped help + text, breaking the plain-text example assertions in + ``tests/cli/test_help_examples.py`` and similar. Clearing both here gives + every test a deterministic, auto-detected baseline; tests that exercise + color set the vars explicitly, and ``monkeypatch`` restores the + contributor's values at teardown. + """ + monkeypatch.delenv("FORCE_COLOR", raising=False) + monkeypatch.delenv("NO_COLOR", raising=False) + + @pytest.fixture(autouse=USING_ZSH, scope="session") def zshrc(user_path: pathlib.Path) -> pathlib.Path | None: """Quiets ZSH default message. From e162c8ec818330ff56f12c2b5e259b2cc4df1f65 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 08:35:20 -0500 Subject: [PATCH 05/10] tests(finders): flatten test classes to functions why: CLAUDE.md mandates functional tests only ("avoid class TestFoo groupings"). test_finders_local.py had two class-based groupings, the only such violation in the suite. what: - Flatten TestFindLocalWorkspaceEdgeCases (7 methods) and TestLocalWorkspaceFilesConstant (2 methods) into module-level def test_* functions; drop the self receiver - Rename the constant tests to test_local_workspace_files_constant_* so they read at module scope without the class for context - No behavior change: same tests collected, assertions verbatim --- tests/workspace/test_finders_local.py | 290 +++++++++++++------------- 1 file changed, 142 insertions(+), 148 deletions(-) diff --git a/tests/workspace/test_finders_local.py b/tests/workspace/test_finders_local.py index 5482df3abc..22b02a239f 100644 --- a/tests/workspace/test_finders_local.py +++ b/tests/workspace/test_finders_local.py @@ -135,151 +135,145 @@ def test_find_local_workspace_files( assert result_relative == expected_paths -class TestFindLocalWorkspaceEdgeCases: - """Edge case tests for local workspace discovery.""" - - def test_at_home_directory( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test behavior when starting at home directory.""" - home = tmp_path / "home" - home.mkdir() - monkeypatch.setattr(pathlib.Path, "home", lambda: home) - - (home / ".tmuxp.yaml").write_text("session_name: home\n") - - result = find_local_workspace_files(home, stop_at_home=True) - - assert len(result) == 1 - assert result[0] == home / ".tmuxp.yaml" - - def test_at_filesystem_root( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test traversal stops at filesystem root.""" - # This test verifies no infinite loop at root - result = find_local_workspace_files(pathlib.Path("/"), stop_at_home=False) - # Should complete without error; result depends on system state - assert isinstance(result, list) - - def test_yaml_precedence_over_json( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test .yaml is preferred when multiple formats exist.""" - home = tmp_path / "home" - project = home / "project" - project.mkdir(parents=True) - monkeypatch.setattr(pathlib.Path, "home", lambda: home) - - # Create both formats - (project / ".tmuxp.yaml").write_text("session_name: yaml\n") - (project / ".tmuxp.json").write_text('{"session_name": "json"}') - - result = find_local_workspace_files(project, stop_at_home=True) - - assert len(result) == 1 - assert result[0].name == ".tmuxp.yaml" - - def test_yml_precedence_over_json( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test .yml is preferred when .yaml not present but .json exists.""" - home = tmp_path / "home" - project = home / "project" - project.mkdir(parents=True) - monkeypatch.setattr(pathlib.Path, "home", lambda: home) - - # Create yml and json (no yaml) - (project / ".tmuxp.yml").write_text("session_name: yml\n") - (project / ".tmuxp.json").write_text('{"session_name": "json"}') - - result = find_local_workspace_files(project, stop_at_home=True) - - assert len(result) == 1 - assert result[0].name == ".tmuxp.yml" - - def test_stop_at_home_false_continues_past_home( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test stop_at_home=False continues traversal past home.""" - # Create structure: /grandparent/home/project - grandparent = tmp_path / "grandparent" - home = grandparent / "home" - project = home / "project" - project.mkdir(parents=True) - - monkeypatch.setattr(pathlib.Path, "home", lambda: home) - - # Put config in grandparent (above home) - (grandparent / ".tmuxp.yaml").write_text("session_name: grandparent\n") - (project / ".tmuxp.yaml").write_text("session_name: project\n") - - # With stop_at_home=True, should only find project config - result_stop = find_local_workspace_files(project, stop_at_home=True) - assert len(result_stop) == 1 - assert "project" in str(result_stop[0]) - - # With stop_at_home=False, should find both - result_continue = find_local_workspace_files(project, stop_at_home=False) - assert len(result_continue) >= 2 - - def test_default_start_dir_uses_cwd( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test that None start_dir uses current working directory.""" - home = tmp_path / "home" - project = home / "project" - project.mkdir(parents=True) - monkeypatch.setattr(pathlib.Path, "home", lambda: home) - monkeypatch.chdir(project) - - (project / ".tmuxp.yaml").write_text("session_name: cwd\n") - - result = find_local_workspace_files(None, stop_at_home=True) - - assert len(result) == 1 - assert result[0] == project / ".tmuxp.yaml" - - def test_symlinked_directory( - self, - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, - ) -> None: - """Test behavior with symlinked directories.""" - home = tmp_path / "home" - real_project = home / "real_project" - real_project.mkdir(parents=True) - symlink_project = home / "symlink_project" - symlink_project.symlink_to(real_project) - monkeypatch.setattr(pathlib.Path, "home", lambda: home) - - (real_project / ".tmuxp.yaml").write_text("session_name: test\n") - - result = find_local_workspace_files(symlink_project, stop_at_home=True) - - assert len(result) == 1 - - -class TestLocalWorkspaceFilesConstant: - """Tests for LOCAL_WORKSPACE_FILES constant.""" - - def test_constant_order(self) -> None: - """Verify LOCAL_WORKSPACE_FILES has correct order (yaml, yml, json).""" - assert LOCAL_WORKSPACE_FILES == [".tmuxp.yaml", ".tmuxp.yml", ".tmuxp.json"] - - def test_constant_is_list(self) -> None: - """Verify LOCAL_WORKSPACE_FILES is a list.""" - assert isinstance(LOCAL_WORKSPACE_FILES, list) - assert len(LOCAL_WORKSPACE_FILES) == 3 +def test_at_home_directory( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test behavior when starting at home directory.""" + home = tmp_path / "home" + home.mkdir() + monkeypatch.setattr(pathlib.Path, "home", lambda: home) + + (home / ".tmuxp.yaml").write_text("session_name: home\n") + + result = find_local_workspace_files(home, stop_at_home=True) + + assert len(result) == 1 + assert result[0] == home / ".tmuxp.yaml" + + +def test_at_filesystem_root( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test traversal stops at filesystem root.""" + # This test verifies no infinite loop at root + result = find_local_workspace_files(pathlib.Path("/"), stop_at_home=False) + # Should complete without error; result depends on system state + assert isinstance(result, list) + + +def test_yaml_precedence_over_json( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test .yaml is preferred when multiple formats exist.""" + home = tmp_path / "home" + project = home / "project" + project.mkdir(parents=True) + monkeypatch.setattr(pathlib.Path, "home", lambda: home) + + # Create both formats + (project / ".tmuxp.yaml").write_text("session_name: yaml\n") + (project / ".tmuxp.json").write_text('{"session_name": "json"}') + + result = find_local_workspace_files(project, stop_at_home=True) + + assert len(result) == 1 + assert result[0].name == ".tmuxp.yaml" + + +def test_yml_precedence_over_json( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test .yml is preferred when .yaml not present but .json exists.""" + home = tmp_path / "home" + project = home / "project" + project.mkdir(parents=True) + monkeypatch.setattr(pathlib.Path, "home", lambda: home) + + # Create yml and json (no yaml) + (project / ".tmuxp.yml").write_text("session_name: yml\n") + (project / ".tmuxp.json").write_text('{"session_name": "json"}') + + result = find_local_workspace_files(project, stop_at_home=True) + + assert len(result) == 1 + assert result[0].name == ".tmuxp.yml" + + +def test_stop_at_home_false_continues_past_home( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test stop_at_home=False continues traversal past home.""" + # Create structure: /grandparent/home/project + grandparent = tmp_path / "grandparent" + home = grandparent / "home" + project = home / "project" + project.mkdir(parents=True) + + monkeypatch.setattr(pathlib.Path, "home", lambda: home) + + # Put config in grandparent (above home) + (grandparent / ".tmuxp.yaml").write_text("session_name: grandparent\n") + (project / ".tmuxp.yaml").write_text("session_name: project\n") + + # With stop_at_home=True, should only find project config + result_stop = find_local_workspace_files(project, stop_at_home=True) + assert len(result_stop) == 1 + assert "project" in str(result_stop[0]) + + # With stop_at_home=False, should find both + result_continue = find_local_workspace_files(project, stop_at_home=False) + assert len(result_continue) >= 2 + + +def test_default_start_dir_uses_cwd( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test that None start_dir uses current working directory.""" + home = tmp_path / "home" + project = home / "project" + project.mkdir(parents=True) + monkeypatch.setattr(pathlib.Path, "home", lambda: home) + monkeypatch.chdir(project) + + (project / ".tmuxp.yaml").write_text("session_name: cwd\n") + + result = find_local_workspace_files(None, stop_at_home=True) + + assert len(result) == 1 + assert result[0] == project / ".tmuxp.yaml" + + +def test_symlinked_directory( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test behavior with symlinked directories.""" + home = tmp_path / "home" + real_project = home / "real_project" + real_project.mkdir(parents=True) + symlink_project = home / "symlink_project" + symlink_project.symlink_to(real_project) + monkeypatch.setattr(pathlib.Path, "home", lambda: home) + + (real_project / ".tmuxp.yaml").write_text("session_name: test\n") + + result = find_local_workspace_files(symlink_project, stop_at_home=True) + + assert len(result) == 1 + + +def test_local_workspace_files_constant_order() -> None: + """Verify LOCAL_WORKSPACE_FILES has correct order (yaml, yml, json).""" + assert LOCAL_WORKSPACE_FILES == [".tmuxp.yaml", ".tmuxp.yml", ".tmuxp.json"] + + +def test_local_workspace_files_constant_is_list() -> None: + """Verify LOCAL_WORKSPACE_FILES is a list.""" + assert isinstance(LOCAL_WORKSPACE_FILES, list) + assert len(LOCAL_WORKSPACE_FILES) == 3 From 79575c2c4860bd0be0fc262da0c2f7c78afadd60 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 08:36:55 -0500 Subject: [PATCH 06/10] tests(pytest): enforce --strict-markers in addopts why: without --strict-markers an unregistered or mistyped marker (e.g. @pytest.mark.flakey) is a silent warning, so a typo'd mark quietly stops applying. Strict mode turns it into a hard collection error. what: - Add --strict-markers to [tool.pytest.ini_options] addopts - No markers ini entry needed: the only custom marker (flaky) is registered by pytest-rerunfailures; there are no project-defined custom markers - Verified: suite green and an unregistered mark now errors at collection --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7987d04f9c..65348327a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -243,7 +243,7 @@ convention = "numpy" "docs/_ext/aafig.py" = ["PTH"] [tool.pytest.ini_options] -addopts = "--reruns=0 --tb=short --no-header --showlocals --doctest-modules" +addopts = "--reruns=0 --tb=short --no-header --showlocals --doctest-modules --strict-markers" doctest_optionflags = "ELLIPSIS NORMALIZE_WHITESPACE" testpaths = [ "src/tmuxp", From bf2230a6add89fd601aef359a693494c1d0d87db Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 09:50:09 -0500 Subject: [PATCH 07/10] tests(freezer): drop redundant post-build wait why: WorkspaceBuilder.build() already calls _wait_for_pane_ready() for each normal pane, so the session is fully materialized when build() returns. The post-build retry_until on window count was a no-op (windows register synchronously) and its comment overstated what it did. what: - Remove the retry_until window-count waits from test_freeze_config and test_freeze_logs_debug, plus the now-unused retry_until import - build() flows straight to freeze(); the existing freezer tests remain the regression coverage --- tests/workspace/test_freezer.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/workspace/test_freezer.py b/tests/workspace/test_freezer.py index f1f3490dff..883a322276 100644 --- a/tests/workspace/test_freezer.py +++ b/tests/workspace/test_freezer.py @@ -6,7 +6,6 @@ import typing import pytest -from libtmux.test.retry import retry_until from tests.fixtures import utils as test_utils from tmuxp._internal.config_reader import ConfigReader @@ -31,11 +30,6 @@ def test_freeze_config(session: Session) -> None: builder.build(session=session) assert session == builder.session - # Wait for all configured windows to register instead of a fixed sleep. - expected_windows = len(session_config["windows"]) - retry_until(lambda: len(session.windows) >= expected_windows, 2) - - session = session new_config = freezer.freeze(session) validation.validate_schema(new_config) @@ -124,10 +118,6 @@ def test_freeze_logs_debug( builder = WorkspaceBuilder(session_config=session_config, server=session.server) builder.build(session=session) - # Wait for all configured windows to register instead of a fixed sleep. - expected_windows = len(session_config["windows"]) - retry_until(lambda: len(session.windows) >= expected_windows, 2) - with caplog.at_level(logging.DEBUG, logger="tmuxp.workspace.freezer"): freezer.freeze(session) From 2fb036f83eea6b182fbb899247fb2fe8ba75c922 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 09:51:38 -0500 Subject: [PATCH 08/10] tests(finders): drop unused fixture params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit why: test_at_filesystem_root requested tmp_path and monkeypatch but used neither — it exercises traversal against the real filesystem root and patches nothing. what: - Remove the unused tmp_path/monkeypatch parameters from the test signature --- tests/workspace/test_finders_local.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/workspace/test_finders_local.py b/tests/workspace/test_finders_local.py index 22b02a239f..cc28002bde 100644 --- a/tests/workspace/test_finders_local.py +++ b/tests/workspace/test_finders_local.py @@ -152,10 +152,7 @@ def test_at_home_directory( assert result[0] == home / ".tmuxp.yaml" -def test_at_filesystem_root( - tmp_path: pathlib.Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: +def test_at_filesystem_root() -> None: """Test traversal stops at filesystem root.""" # This test verifies no infinite loop at root result = find_local_workspace_files(pathlib.Path("/"), stop_at_home=False) From 8a390750ce0d24f93ad7d606f08d80fa72f08da7 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 09:53:13 -0500 Subject: [PATCH 09/10] tests(finders): clarify stop_at_home=False comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit why: the comment said "should find both" above an assertion that tolerates more than two results — when traversal continues past home it can also pick up configs in ancestor directories. what: - Reword the comment to match the >= 2 assertion --- tests/workspace/test_finders_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/workspace/test_finders_local.py b/tests/workspace/test_finders_local.py index cc28002bde..0e1d183d48 100644 --- a/tests/workspace/test_finders_local.py +++ b/tests/workspace/test_finders_local.py @@ -222,7 +222,7 @@ def test_stop_at_home_false_continues_past_home( assert len(result_stop) == 1 assert "project" in str(result_stop[0]) - # With stop_at_home=False, should find both + # With stop_at_home=False, finds both, plus any configs above home result_continue = find_local_workspace_files(project, stop_at_home=False) assert len(result_continue) >= 2 From bd3db6ed1fb8ca80e2ce2b5c21fbb519543dd12a Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 28 Jun 2026 10:45:01 -0500 Subject: [PATCH 10/10] tests(finders): drop redundant constant test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit why: test_local_workspace_files_constant_is_list only asserted the constant is a list of length 3 — both already proven by the sibling test_local_workspace_files_constant_order via its equality assertion against the list literal. what: - Remove the subsumed is_list test --- tests/workspace/test_finders_local.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/workspace/test_finders_local.py b/tests/workspace/test_finders_local.py index 0e1d183d48..c53a651eb4 100644 --- a/tests/workspace/test_finders_local.py +++ b/tests/workspace/test_finders_local.py @@ -268,9 +268,3 @@ def test_symlinked_directory( def test_local_workspace_files_constant_order() -> None: """Verify LOCAL_WORKSPACE_FILES has correct order (yaml, yml, json).""" assert LOCAL_WORKSPACE_FILES == [".tmuxp.yaml", ".tmuxp.yml", ".tmuxp.json"] - - -def test_local_workspace_files_constant_is_list() -> None: - """Verify LOCAL_WORKSPACE_FILES is a list.""" - assert isinstance(LOCAL_WORKSPACE_FILES, list) - assert len(LOCAL_WORKSPACE_FILES) == 3