Skip to content

Conversation

@wvanderen
Copy link

Pull Request Draft


Title: fix(test): prevent memory and resource leaks in test infrastructure


Summary

Fixes critical resource leaks in test infrastructure that were causing:

  • Temporary directories accumulating indefinitely
  • Spawned processes leaking on test failures
  • OOM crashes during test runs (vitest workers consuming ~900MB each)
  • Terminals closing unexpectedly (appears as "freezes" to developers)

Changes

1. Re-enable Temp Directory Cleanup

File: packages/opencode/test/fixture/fixture.ts

  const result = {
    [Symbol.asyncDispose]: async () => {
      await options?.dispose?.(dirpath)
-     // await fs.rm(dirpath, { recursive: true, force: true })
+     await fs.rm(dirpath, { recursive: true, force: true })
    },
    path: realpath,
    extra: extra as T,
  }

Impact: Temp directories created by tmpdir() fixture are now properly deleted when tests complete. Previously commented out, causing directories to accumulate in /tmp/ forever.


2. Add Process Cleanup Guarantees to LSP Tests

File: packages/opencode/test/lsp/client.test.ts

Wrapped all LSP client tests with try/finally blocks:

  test("handles workspace/workspaceFolders request", async () => {
    const handle = spawnFakeServer() as any

-   const client = await Instance.provide({...})
-   // ... test code ...
-   await client.shutdown()
+   try {
+     const client = await Instance.provide({...})
+     // ... test code ...
+     await client.shutdown()
+   } finally {
+     handle.process.kill()
+   }
  })

Impact: Fake LSP server processes are now always killed, even when tests throw exceptions before reaching shutdown(). Previously, failing tests would leave orphan Node.js processes consuming memory.


Motivation

While developing locally, terminals running OpenCode tests would "freeze" then close unexpectedly. Investigation revealed:

Evidence from System Logs

Jan 17 13:14 - vitest worker memory usage:
• vitest 1: 897 MB
• vitest 2: 900 MB
• vitest 3: 910 MB
• vitest 4: 905 MB
• vitest 5: 898 MB
• vitest 7: 912 MB

Total: ~5.3 GB for test workers alone!

OOM Killer Activity

Jan 17 13:14:28 lemarchy kernel: Out of memory: Killed process 1750015 (node (vitest 7))
  total-vm:4985788kB, anon-rss:3646756kB, file-rss:620kB

V8 Stack Traces

V8::FatalProcessOutOfMemory
→ Node aborts (signal 6/ABRT)
→ Terminal closes

System State

  • Swap: 3.9 GB / 4 GB used (95% full)
  • Workers: 6-11 vitest workers spawned per run
  • Impact: System becomes sluggish, eventually unresponsive

Testing

Verify Temp Directory Cleanup

# Run tests
cd packages/opencode
bun test --run

# Check temp directories (should be clean or minimal)
ls -la /tmp/ | grep opencode-test

Expected: Minimal or no opencode-test-* directories remaining.


Verify Process Cleanup

# Temporarily force a test to fail
# Edit packages/opencode/test/lsp/client.test.ts:
test("handles workspace/workspaceFolders request", async () => {
  const handle = spawnFakeServer() as any
  throw new Error("Force failure")  // Temporary: test cleanup
})

# Run tests
bun test

# Check for orphan processes (should be none)
ps aux | grep fake-lsp-server

Expected: No fake-lsp-server processes running after test failure.


Verify Memory Stability in Watch Mode

# Monitor memory usage during watch mode
watch -n 5 'ps aux | grep "vitest\|bun test" | awk "{sum += \$6} END {print "Total RSS:", sum/1024, "MB"}'

# In another terminal, run tests in watch mode
cd packages/opencode
bun test  # Let run for 10+ minutes

Expected: Memory usage remains stable (doesn't grow indefinitely).


Impact

  • Developers: Test runs no longer consume increasing memory/disk
  • CI/CD: Reduced OOM failures in test pipelines
  • System: Stable swap usage, no system-wide slowdowns
  • Experience: Tests run reliably without unexpected terminal closures

Related Issues

Fixes #9136


Checklist

  • Code follows project style guidelines
  • Tests pass locally: bun test
  • No new warnings or errors
  • Documentation updated (if applicable)
  • Commit messages are clear and descriptive

Notes

  • This is a first-time open source contribution
  • Process leaks are a common pattern — any test spawning subprocesses should use try/finally or afterEach hooks

Files Changed

packages/opencode/test/fixture/fixture.ts     | 2 +-
packages/opencode/test/lsp/client.test.ts      | 69 ++++++++++++++--------
2 files changed, 71 insertions(+), 58 deletions(-)

OpenCode Version

v1.1.24 (dev branch)


Environment

  • OS: Arch Linux 6.18.3-arch1-1
  • Terminal: Alacritty
  • Node: v25.2.1
  • RAM: 30 GB

Fix critical issues causing test infrastructure to accumulate resources:

1. **Temp directory leak**: Re-enabled cleanup in tmpdir() fixture
   - Cleanup was accidentally commented out, causing temp directories to persist
   - Each test run creates permanent directories, leading to disk/memory bloat

2. **Process leak**: Added try/finally blocks to LSP client tests
   - Fake LSP server processes could leak if tests threw before shutdown()
   - Ensures processes are killed even when tests fail

## Evidence
- journalctl showed vitest workers consuming ~900MB each (6-11 workers = 5-10GB)
- OOM killer repeatedly killing node processes
- Swap at 95% capacity (3.9GB / 4GB)
- V8 stack traces showing FatalProcessOutOfMemory

Closes #XXX
@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

No duplicate PRs found

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.

fix(test): prevent memory and resource leaks in test infrastructure

1 participant