Parallelize Python test suite with pytest-xdist (31s -> 12s) - #51
Merged
Conversation
Add pytest-xdist to the dev dependency group and default the pytest run to '-n auto --dist=worksteal' so the 1526-test unit suite runs across all CPU cores instead of serially on one. The suite was already parallel-safe: every test that binds a real socket (test_mqtt_ws_bridge, test_doctor) uses an OS-assigned ephemeral port via socket.bind((host, 0)); the broker/publisher lifecycle tests fully mock amqtt/aiomqtt so no real ports are opened; and all filesystem and env state flows through per-test tmp_path / monkeypatch. Each xdist worker is its own process with its own tmp base, so 8 workers never collide. The fixed ports in conftest's tmp_config/e2e_config are config-dict values only and are never bound. worksteal rebalances pending tests onto idle workers, which matters because the textual TUI tests are much slower than the rest. CI's Test job runs plain 'pytest ...' with no addopts override, so it inherits -n auto automatically; pytest-xdist ships via the dev extra the job already installs ([all,dev]). Result: ~31s -> ~12s cold wall (2.6x), ~6-9s warm. 1526 passed, verified stable across 5 consecutive parallel runs (all exit 0).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Parallelize the Python unit-test suite with
pytest-xdist. The 1526-test suite was running serially on a single core (~63% CPU on an 8-core box). This defaults the run to-n auto --dist=worksteal, a pure wall-clock win with identical results.Results (8-core box)
-n auto)~2.6x faster cold wall-clock, same 1526 passed, zero skips added.
Changes
pytest-xdist>=3.0to thedevoptional-dependency group.addopts = "-n auto --dist=worksteal"to[tool.pytest.ini_options](existingtestpathsandfilterwarningsuntouched).workstealrebalances pending tests onto idle workers, which matters because the textual TUI tests are much slower than the rest.Parallel safety
No production or test logic needed changing — the suite was already parallel-safe, verified by audit:
test_mqtt_ws_bridge.py,test_doctor.py) already uses OS-assigned ephemeral ports viasocket.bind((host, 0))/getsockname().test_broker.py,test_publisher.py) fully mockamqtt/aiomqtt, so no real ports are opened.tmp_path,monkeypatch.setenv, and patchedPath.home(). Each xdist worker is its own process with its own tmp base, so 8 workers never collide. No test writes to a real~/.claude-commsor a repo-relative path.conftest'stmp_config/e2e_configare config-dict values only and are never bound to a socket.CI
The
Testjob runs plainpytest --tb=short --junitxml=...with noaddoptsoverride and no-p no:xdist, so it inherits-n autoautomatically.pytest-xdistships via the[all,dev]extra the job already installs. Verified the exact CI command works under xdist (junitxml recordstests="1526", exit 0).Flake gate — 5 consecutive parallel runs
All 1526 passed, all exit 0 — no parallel flakiness.
ruff check,ruff format --check, andbasedpyright(src 0/0, tests 0/0) all remain clean.🤖 Generated with Claude Code