Skip to content

Conversation

@ThomasK33
Copy link
Member

This PR speeds up the local edit→test loop by making coverage opt-in and reducing the largest luacov hot spots.

Key changes

  • make test is now fast (no coverage); make test-cov runs coverage + luacov.
  • Added .luacov config to focus coverage on plugin code (exclude tests/mocks).
  • Optimized TCP port selection and WebSocket masking hot paths.
  • Removed expensive shell-spawn sleep in the vim.wait test mock.
  • Reduced integration test overhead by entering the nix shell once.

Verification

  • make check
  • make test
  • make test-cov

📋 Implementation Plan

Speed up test runs (busted / luacov)

Context / Why

Running the repo’s tests via make test is currently painfully slow (~4m+), which breaks the local edit→test loop.

Primary goal: make the default developer test command fast again, while keeping a coverage-producing path for CI/release quality.

Targets

  • Default local run (make test): <15s (current suite can run in ~9s without coverage)
  • Coverage run (make test-cov): materially faster than today (~4m17s), ideally <90s

Evidence (what we verified)

  • Makefile:test runs busted --coverage -v over all tests/**/*_{spec,test}.lua. (Makefile:25–35)
  • Timing profile (same suite, same machine/environment):
    • make test with coverage: ~4m17s (423 tests)
    • same tests without coverage: ~8.6s
    • ~96% of runtime is coverage overhead (luacov debug hook)
    • worst individual file under coverage: tests/unit/terminal_spec.lua (~1m50s vs ~8s without)
  • Other contributors that become especially expensive under coverage:
    • lua/claudecode/server/tcp.lua builds + shuffles an entire port range array before binding. (lua/claudecode/server/tcp.lua:26–35)
    • lua/claudecode/server/utils.lua precomputes a 256×256 XOR lookup table at module load. (lua/claudecode/server/utils.lua:367–391)
    • tests/mocks/vim.lua mock vim.wait uses os.execute("sleep 0.001"), spawning a shell repeatedly. (tests/mocks/vim.lua:712–724)
    • several specs clear package.loaded[...] in before_each, forcing repeated module reloads (amplifies module-load hot spots), e.g. tests/unit/terminal_spec.lua:225+.
  • Integration runner spawns a fresh nvim --headless per file and wraps each in nix develop, with a 30s timeout. (scripts/run_integration_tests_individually.sh:30–33)

Plan

Phase 0 — Lock in a baseline (so improvements are measurable)

  1. Add a short note to DEVELOPMENT.md documenting the expected ballpark timings and the two modes:
    • make test (fast, no coverage)
    • make test-cov (coverage)
  2. (Optional) Add a CI step that prints test wall time (informational only at first).

Phase 1 — Fast-by-default local tests (biggest win)

  1. Make coverage opt-in in Makefile:
    • Change make test to run busted -v … without --coverage.
    • Add make test-cov (or make coverage) that runs busted --coverage -v ….
    • Optionally support COVERAGE=1 make test as an alias.
  2. Update docs that reference make test:
    • DEVELOPMENT.md (currently recommends make test as the primary command; see DEVELOPMENT.md:79–95).
    • README.md (mentions make test; see around README.md:779).
  3. Ensure CI continues to produce coverage by switching workflows/scripts to call make test-cov.

Phase 2 — Reduce coverage-enabled runtime (so CI stays reasonable)

  1. Eliminate the O(N) “shuffle the whole port range” in lua/claudecode/server/tcp.lua:
    • Replace “build array + shuffle + try all” with a cheaper strategy:
      • random sampling with capped retries, or
      • sequential scan with random start offset.
    • Add assertions and deterministic behavior for tests (inject RNG or seed once).
  2. Remove the XOR lookup table precompute in lua/claudecode/server/utils.lua:
    • Prefer LuaJIT bit.bxor (available in Neovim) or compute XOR directly per byte.
    • Add correctness tests (e.g., property: apply_mask(apply_mask(data, mask), mask) == data).
  3. Stop reloading heavy modules per test unless required:
    • Introduce a helper (e.g., tests/helpers/reload.lua) to reset stateful globals without nuking package.loaded broadly.
    • Move package.loaded[...] = nil from before_eachsetup (per-file) where isolation allows.
    • Start with tests/unit/terminal_spec.lua since it dominates coverage time.
  4. Fix the expensive vim.wait mock in tests/mocks/vim.lua:
    • Replace os.execute("sleep …") with vim.loop.sleep/uv.sleep (no shell) or a pure-Lua approach suitable for unit tests.
    • Where possible, refactor tests to avoid time-based polling.
  5. Add a .luacov config to focus coverage on plugin code:
    • Exclude tests/, tests/mocks/, and third-party deps; include lua/claudecode/.
    • Verify CI still gets the coverage signal it needs.

Phase 3 — Integration test speed / reliability (separate, but related)

  1. Avoid nix develop per integration file in scripts/run_integration_tests_individually.sh:
    • Run the script inside a single nix shell session, or detect IN_NIX_SHELL like the Makefile does.
  2. If plenary.test_harness.test_directory hangs, either:
    • isolate and fix/disable the hanging spec(s), or
    • run integration tests in fewer Neovim invocations (per-folder) to cut startup cost.

Validation / Success Criteria

  • make test (no coverage): consistently <15s.
  • make test-cov (coverage): improves materially from ~4m17s to <90s (or at least 2× faster), with tests/unit/terminal_spec.lua no longer dominating.
  • Coverage artifacts still generated for CI (e.g., luacov.report.out, luacov.stats.out).
  • No new flakiness (run full suite 3× locally + CI green).

Generated with mux • Model: openai:gpt-5.2 • Thinking: xhigh

Change-Id: I2c6e1d74affcc868ffea51a39e2a37548f5402d7
Signed-off-by: Thomas Kosiewski <[email protected]>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc16ba73ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +6 to +8
# Detect if the required dev tools are already available; otherwise run via Nix.
ifeq (,$(shell command -v busted >/dev/null 2>&1 && command -v luacheck >/dev/null 2>&1 && echo ok))
NIX_PREFIX := nix develop .\#ci -c

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate Nix fallback on luacov too

The new NIX_PREFIX detection only checks for busted and luacheck, but make test-cov also requires luacov. In an environment where a developer has busted/luacheck on PATH but not luacov (common when coverage isn’t installed), NIX_PREFIX is empty and make test-cov will now fail at the luacov step instead of falling back to nix develop like it did before. Consider including luacov in the tool check or making test-cov explicitly use the Nix wrapper when luacov is missing.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant