Skip to content

Clear TestDrive with .NET IO instead of per-item Remove-Item#2917

Open
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-perf-testdrive-cleanup
Open

Clear TestDrive with .NET IO instead of per-item Remove-Item#2917
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-perf-testdrive-cleanup

Conversation

@nohwnd

@nohwnd nohwnd commented Jul 19, 2026

Copy link
Copy Markdown
Member

Clear-TestDrive ran Remove-Item -Path $item -Force -Recurse -ErrorAction Ignore for every new root item after each block. Two problems with that:

  • Remove-Item costs ~470us per item where [IO.Directory]::Delete / [IO.File]::Delete cost ~85us. On a TestDrive-heavy suite the cleanup was the single hottest line in the profile.
  • -Path treats the path as a wildcard pattern. A file named like file[1].txt never matched its own name, was never deleted, and leaked across block teardowns. The reverse is also possible, a name with * in it could over-delete. The .NET calls treat the path literally, so this is also a small bug fix.

Symlink semantics stay the same: recursive Directory.Delete removes reparse points without following them into their target, which is what Remove-TestDrive has already been doing for the whole drive with the exact same call since 2021. I verified this empirically on macOS with dir symlinks, file symlinks, dangling links, the PowerShell/PowerShell#621 problematic link set, and a symlink cycle, the linked target content survives in all cases. Anything that throws (read-only files on Windows, locked files) falls back to Remove-Item -LiteralPath -Force -Recurse -ErrorAction Ignore, keeping the old behavior for those, just slower on that rare path.

A ride-along in New-RandomTempRegistry: Join-Path replaced with plain string interpolation, registry provider paths always use backslash and the base key has no trailing separator, so the result is identical and it skips a cmdlet invocation per new registry key.

One heads-up: the symlink tests in TestDrive.Tests.ps1 are entirely commented out (they needed admin on Windows), so nothing in CI exercises symlink cleanup today. Might be worth re-enabling them at least on Linux/macOS runners in a separate PR.

Verification: full suite green on macOS (inline and dot-sourced build), TestDrive.Tests.ps1 including the Clear-TestDrive #2657 regression suite and Pester.TestDrive.ts.ps1 pass.

Related perf PRs: #2914, #2915, #2916, #2918. Measured together they cut ~21% from four representative workloads (inline build, macOS).

🤖

Clear-TestDrive ran Remove-Item -Path -Force -Recurse for every new root
item after each block. Remove-Item costs ~470us per item where
IO.Directory/IO.File Delete costs ~85us, and -Path treats the path as a
wildcard pattern, so a file named like 'file[1].txt' was never deleted and
leaked across block teardowns. The .NET calls treat the path literally,
delete reparse points without following them into their target (same as
Remove-TestDrive already does for the whole drive since 2021), and anything
that throws (read-only files on Windows, locked files) falls back to
Remove-Item -LiteralPath -Force -ErrorAction Ignore, keeping the old
semantics for those.

Also replaced a Join-Path with plain string interpolation in
New-RandomTempRegistry, registry provider paths always use backslash.

🤖
Comment on lines +98 to +99
# registry provider paths always use backslash; plain interpolation avoids a per-call cmdlet invocation
$Path = "$tempPath\$([IO.Path]::GetRandomFileName().Substring(0, 4))"

@fflaten fflaten Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not really a hot path, but sure 👍

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

got some free fable tokens, told it to optimize (especially the hot path), looks like most of the optimizations are microscopic.

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.

2 participants