Skip to content

test(resolution): close graphs and connections, and always remove the temp project - #1786

Open
bompus wants to merge 1 commit into
colbymchenry:mainfrom
bompus:fix/resolution-test-temp-leak
Open

test(resolution): close graphs and connections, and always remove the temp project#1786
bompus wants to merge 1 commit into
colbymchenry:mainfrom
bompus:fix/resolution-test-temp-leak

Conversation

@bompus

@bompus bompus commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #1777.

On Windows, __tests__/resolution.test.ts leaks 171 temp directories per run and fails 4 of its 200 tests. Both come from the same thing: something still holds the SQLite database open when the code that removes its directory runs.

Measured

Windows 11 Pro 26200, Node 24 (the bundled runtime), vitest 2.1.9, TEMP redirected to a private directory for the vitest process so concurrent work on the machine could not contaminate the count.

directories left in TEMP tests
main (7be699c) 171 4 failed, 196 passed
this branch 0 200 passed

The four failures on main are pre-existing, not introduced here:

FAIL  C/C++ Import Resolution > connects #include to the real header file via include-dir scan (end-to-end)
FAIL  PHP Include Resolution > resolves require_once to a file→file imports edge (#660)
FAIL  PHP Include Resolution > resolves a subdirectory include path to the correct file (#660)
FAIL  PHP Include Resolution > does not mis-connect an unresolvable include to a same-named file elsewhere (#660)
Error: EPERM, Permission denied: ...\codegraph-php-subdir-mdiGnx

Three changes

1. afterEach removes the project on every test. It removed tempDir only in the else branch of if (cg), and cg is describe-scoped and never reset — so from the first test that assigns it (the CodeGraph.init around line 1105) onward, every later test kept its directory.

The reason the else looked reasonable is worth stating: destroy() is a deprecated alias for close(). It releases the database and leaves the project on disk; uninitialize() is the method that removes it. So the if branch was never cleaning up anything, and making removal its alternative meant removal almost never ran.

2. Five DatabaseConnection.open calls are closed. None of them were. Making the removal unconditional immediately converts the silent leak into EPERM, so these have to be fixed in the same change — a second connection to the same database keeps a handle on the directory. Each is now closed once its rows are materialised, before the assertions that use them.

3. Four nested tests close their graph before removing their own project. These create their own tempProject, assign the outer cg, and remove the directory in a finally that runs before the outer afterEach closes cg. The graph is now closed inside that finally.

Note on the removal no longer being silent

afterEach now lets a failed removal throw rather than swallowing it. That is deliberate: the swallowed failure is what let this run unnoticed, and it was hiding a real second defect (the unclosed connections), not just noise. force: true, maxRetries: 5 covers Windows releasing handles slightly after close() returns.

Related: #1722 was closed as "tests leak file handles into their temp projects: 24 fail with EPERM on Windows". This is the same underlying behaviour in this file, where it manifested as a silent leak instead of a failure.

… temp project

On Windows this file leaked 171 temp directories per run and failed 4 tests, both from the same cause: things that hold the SQLite database open outlive the code that removes their directory.

afterEach removed tempDir only in the else branch of \if (cg)\, and cg is describe-scoped and never reset, so from the first test that assigns it every later test kept its directory. destroy() is a deprecated alias for close(): it releases the database but does not remove the project, so removal cannot be its alternative -- uninitialize() is the method that removes. Removal is now unconditional.

That alone turns the silent leak into EPERM, because five DatabaseConnection.open calls were never closed and four nested tests removed their own tempProject while the outer cg still held it open. Each connection is now closed once its rows are materialised, and each nested finally closes the graph before removing its directory.

Measured on Windows 11 with TEMP redirected to a private directory: before, 171 leaked directories and 4 failed / 196 passed; after, 0 leaked directories and 200 passed. Fixes colbymchenry#1777.
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.

resolution.test.ts leaks ~170 temp directories per run (49,646 accumulated here)

1 participant