Allow IPC named pipe directory to be overridden on Unix#9846
Allow IPC named pipe directory to be overridden on Unix#9846Evangelink wants to merge 8 commits into
Conversation
On Unix the IPC named pipe is backed by a Unix domain socket file that was always created under /tmp. Sandboxed environments (e.g. Claude Code on macOS) that block /tmp cannot run `dotnet test`. Resolve the directory as: 1. TESTINGPLATFORM_PIPE_DIRECTORY (explicit opt-in override) 2. Path.GetTempPath() (honors TMPDIR on Unix) 3. /tmp (previous default) Add a sun_path length guard (always) and a writability probe (override only), both failing with actionable, localized messages. Keep the logic self-contained in NamedPipeServer.cs so it flows into dotnet/sdk on the next vendor sync, and document the "creator resolves, full path is passed, peers never recompute" invariant so the directory never becomes a shared convention that couples SDK and test-host versions. Fixes #9821 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae434eba-3cad-4c61-a1da-ef252ae87f48
There was a problem hiding this comment.
Pull request overview
Adds configurable Unix IPC socket directories for sandbox compatibility while preserving Windows behavior.
Changes:
- Adds override/temp-directory resolution and safety guards.
- Adds localized diagnostics and environment-variable support.
- Adds unit tests and regenerated localization files.
Show a summary per file
| File | Description |
|---|---|
test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs |
Tests directory resolution and path-length limits. |
src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs |
Implements Unix pipe-directory selection and validation. |
src/Platform/Microsoft.Testing.Platform/Helpers/EnvironmentVariableConstants.cs |
Defines the pipe-directory override variable. |
src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.cs |
Adds resource accessors. |
src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx |
Adds localized diagnostic source strings. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf |
Updates Czech localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf |
Updates German localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf |
Updates Spanish localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf |
Updates French localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf |
Updates Italian localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf |
Updates Japanese localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf |
Updates Korean localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf |
Updates Polish localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf |
Updates Brazilian Portuguese localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf |
Updates Russian localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf |
Updates Turkish localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf |
Updates Simplified Chinese localization. |
src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf |
Updates Traditional Chinese localization. |
Review details
- Files reviewed: 18/18 changed files
- Comments generated: 6
- Review effort level: Medium
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Note
🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.
Review Summary
Overall: Solid, well-motivated change. The design is sound — the single-resolver invariant (creator resolves, peer uses verbatim) is the right architecture and the XML doc captures it well. The guards (path length + writability probe) are practical and the error messages are actionable.
Findings by severity
| Severity | Dimension | Finding |
|---|---|---|
| MAJOR | Test Completeness (#13) | EnsureDirectoryIsWritable has no unit tests — it has meaningful branching worth covering |
| MODERATE | Data-Driven (#14) | Missing " " (whitespace) [DataRow] for tempPath in the /tmp fallback test |
| NIT | Algorithmic (#1) | The /tmp fallback in ResolvePipeDirectory is effectively dead code in production (only reachable via the testable overload) — worth a clarifying comment |
| NIT | Security (#3) | Consider Path.GetFullPath() normalization on the explicit override for path hygiene |
Dimensions checked and clean
✅ Threading & Concurrency — no shared mutable state; all new methods are static/pure or local I/O
✅ Public API — class is internal [Embedded]; constants are public on an internal class (effectively internal); no PublicAPI.Unshipped.txt update needed
✅ IPC Wire Compatibility — no wire format changes; directory resolution is local-only
✅ Performance — GetPipeName is not a hot path; probe I/O only on explicit override
✅ Resource/IDisposable — probe file uses using + File.Delete
✅ Localization — .resx entries added correctly; .xlf regenerated; accessors placed in the !IS_CORE_MTP block (correct — these are needed by extension projects that link the file)
✅ Naming & Conventions — test names describe scenario and outcome
✅ Scope & PR Discipline — focused change, no unrelated refactoring
✅ Build Infrastructure — no dependency changes
No blocking issues found.
🔴 Build Failure AnalysisThe build failed with 14 errors (deduplicated to 7 unique issues) in Root Cause 1: Banned API usage (RS0030) — 3 issuesIn
Fix for line 213: The private Fix for lines 220 & 226: Replace Root Cause 2: Undeclared public API (RS0051) — 4 issuesNew
Fix: Either add these symbols to Summary of required changes
|
There was a problem hiding this comment.
🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 61.6 AIC · ⌖ 4.9 AIC · ⊞ 7.3K · ◷
…ests - Replace banned System.Environment / string.IsNullOrWhiteSpace with SystemEnvironment / RoslynString to fix RS0030. - Declare the four new internal symbols in InternalAPI.Unshipped.txt (RS0016). - Normalize an explicit override with Path.GetFullPath so a relative TESTINGPLATFORM_PIPE_DIRECTORY resolves to a rooted socket path and honors the "full path handed to peers" invariant (also collapses '..'). - Clarify that the '/tmp' fallback is only reachable via the test overload. - Add unit tests for EnsureDirectoryIsWritable (create-missing happy path and non-creatable error path) and a whitespace DataRow for the tempPath fallback. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae434eba-3cad-4c61-a1da-ef252ae87f48
This comment has been minimized.
This comment has been minimized.
…ed-train # Conflicts: # src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt
This comment has been minimized.
This comment has been minimized.
NamedPipeServer.cs is glob-compiled (IPC\*.cs) into HangDump, Retry, TrxReport, and both MSBuild projects, so the earlier fixes must build there: - Read TESTINGPLATFORM_PIPE_DIRECTORY via System.Environment with a scoped RS0030 suppression instead of `new SystemEnvironment()`. Only the MSBuild task project links SystemEnvironment.cs; the other shared-source consumers do not, so the wrapper would not compile there. Reading directly also keeps the file self-contained for the dotnet/sdk vendor copy. - Normalize the selected directory with Path.GetFullPath in every precedence branch (not just the explicit override): Path.GetTempPath() can surface a relative TMPDIR, which NamedPipeServerStream rejects and which would violate the fully-resolved-path invariant. The write probe stays override-only. - Declare the new EnvironmentVariableConstants / NamedPipeServer / PlatformResources members in the HangDump, Retry, TrxReport, and Extensions.MSBuild InternalAPI.Unshipped baselines (they compile these shared files and keep their baselines in sync with core), so RS0016 stays green. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae434eba-3cad-4c61-a1da-ef252ae87f48
…consumers The earlier baseline update missed some projects that link the shared files: - EnvironmentVariableConstants.cs is also linked into HotReload -> add the new TESTINGPLATFORM_PIPE_DIRECTORY const there. - PlatformResources.cs (the !IS_CORE_MTP accessors) is linked into nine API-tracked extensions -> add the two new NamedPipe* accessor signatures to HtmlReport, CtrfReport, JUnitReport, VideoRecorder, and CrashDump (the other four were done previously). Verified each affected project builds clean with -warnaserror:RS0016,RS0017,RS0025,RS0037. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae434eba-3cad-4c61-a1da-ef252ae87f48
This comment has been minimized.
This comment has been minimized.
…cedence The two diagnostics suggested setting TMPDIR, but TESTINGPLATFORM_PIPE_DIRECTORY always wins, so that advice is misleading when the override is the cause: - Writability error is emitted only for the explicit override, so drop the TMPDIR mention and tell users to fix the override or unset it to fall back to the default temp directory. - Path-too-long can come from any source, so clarify that TMPDIR only helps when the override is not set. Regenerated the XLF files for all 14 languages. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae434eba-3cad-4c61-a1da-ef252ae87f48
…unting The existing path-length tests used only ASCII, so a buggy character-count implementation would still pass. Add a test with 40 '好' (3-byte) chars plus a prefix: 45 characters (under the 103 limit) but 125 UTF-8 bytes (over it), proving the guard measures sun_path bytes rather than characters. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae434eba-3cad-4c61-a1da-ef252ae87f48
| // rooted names as socket paths (it rejects separators in non-rooted names). The invariant also requires | ||
| // handing peers a fully-resolved path. This additionally collapses any '..' segments. '/tmp' and an | ||
| // already-absolute temp path are unchanged. | ||
| directory = Path.GetFullPath(directory); |
🧪 Test quality grade — PR #9846
This advisory comment was generated automatically. Grades are heuristic
|
Fixes #9821
Problem
On Unix, the Microsoft.Testing.Platform IPC named pipe is backed by a Unix domain socket file on disk, and
NamedPipeServer.GetPipeNamehardcoded that file to live under/tmp. In sandboxed environments that block/tmp(the reporter hit this with Claude Code on macOS),dotnet testfails and the only workaround is to open up the whole/tmpfolder to the sandbox.Change
On Unix, the pipe directory is now resolved with this precedence:
TESTINGPLATFORM_PIPE_DIRECTORY— new explicit opt-in override (guaranteed escape hatch).Path.GetTempPath()— honorsTMPDIRon Unix, which is the standard POSIX knob and is usually a per-user, sandbox-allowed directory (so many setups are fixed with no extra configuration)./tmp— preserves the previous default when neither is set.Windows is unchanged (it uses the kernel pipe namespace, not the filesystem).
Guards
Because the location is now user-influenced:
sun_path(~104 bytes on macOS, ~108 on Linux). We validate against the smaller 103-byte budget and fail with an actionable, localized message. This matters most for a deepTMPDIRon macOS (/var/folders/…/T/+ a GUID).TMPDIR//tmpare OS-managed and skipped to avoid extra I/O and a behavior change on every pipe creation.Compatibility notes
The design deliberately keeps the existing invariant: the process that creates the pipe resolves the directory locally and hands the fully-resolved path to the peer (via env var, command-line arg, or the dotnet-test handshake); peers use that path verbatim and never recompute it. As a result:
TESTINGPLATFORM_PIPE_DIRECTORY/TMPDIRdifference between two processes is harmless because only the creator's resolution is ever used.NamedPipeServer.cs, whichdotnet/sdkvendors, so thedotnet testentrypoint picks this up on the next vendor sync with no separate SDK design work.An XML-doc note on
GetPipeNamecaptures this invariant so the directory is never turned into a shared convention that both sides derive independently (which would couple SDK and test-host versions).Localization
Two new strings added to
PlatformResources.resxwith hand-maintained accessors in the!IS_CORE_MTPblock;.xlfregenerated for all 14 languages viaUpdateXlf.Tests
Added 8 MSTest cases in
IPCTests.cscovering override precedence,TMPDIRfallback,/tmpfallback, and the length guard (within/exceeds). Resolution and length-check logic was split into internal seams so they are testable on Windows CI, where the Unix branch ofGetPipeNamenever runs. FullIPCTestsclass passes locally (net8.0).