test(windev): buffer the test named pipes so the full-suite testhost exits
The windev full-suite wedge — every test reported, then the x64 testhost sitting at ~0 CPU forever while `dotnet test` never returns — was one test blocked on a pipe write, not a leaked thread or an undisposed fixture. `dotnet-stack report` on the wedged host showed no thread running test code: xUnit's RunTestsInAssembly was parked on WaitHandle.WaitOne() waiting for the assembly-finished event, so the wait lived in a suspended async state machine. `dotnet-dump analyze -c dumpasync` named the frame — WorkerClientTests .StagingChannelOverflowFaultsWorkerWithoutWaitingForFullModeTimeout awaiting WorkerFrameWriter.WriteAsync on a 63-byte frame, with <extra>5__15 = 6, i.e. the seventh of the twelve events the test pushes past the client's staging bound. That test faults the worker client on purpose, and a faulted client stops its read loop by design. The test-side pipe came from the NamedPipeServerStream overload without buffer arguments, which passes inBufferSize: 0 / outBufferSize: 0 to CreateNamedPipe; on Windows that reserves no buffer at all, so a write completes only once the peer reads it. Measured on windev, that pipe absorbed 0 bytes against a non-reading peer where the same pipe declared with 64 KiB buffers absorbed 65 520. On macOS and Linux .NET backs named pipes with Unix domain sockets whose socket buffer swallows the writes regardless, which is why the identical test never hung there and the bug read as environmental. Test-owned server pipes now go through TestSupport/TestNamedPipe.CreateServer in both test projects, declaring explicit 64 KiB buffers so those tests exercise the gateway's own staging/queue backpressure rather than the OS pipe's flow control. Separately, every fake-worker write in WorkerClientTests now goes through PipePair.WriteAsync, bounded by the class's five-second TestTimeout. That is where the severity came from: a test method that never returns keeps xUnit from raising ITestAssemblyFinished, so one unbounded await cost the whole suite its result and reported 855 of a real 856 with the hung test simply uncounted. A blocked write is now a named test failure instead of a silent wedge. Product code is unaffected. SessionWorkerClientFactory.CreatePipe keeps the unbuffered declaration deliberately: both ends run continuous read loops and every gateway write is bounded by the worker client's _stopCts, so a stalled peer cancels the write rather than blocking on it. Docs: GatewayTesting.md replaces the --blame-hang workaround section with the root cause and the 856 baseline, CLAUDE.md's Source Update Workflow no longer tells readers the windev suite wedges, and ToolchainLinks.md records dotnet-stack and dotnet-dump as installed on windev.
This commit is contained in:
@@ -124,7 +124,7 @@ powershell -ExecutionPolicy Bypass -File scripts/run-client-e2e-tests.ps1
|
||||
|
||||
When source code changes, build and test the affected component before reporting work done. If the change crosses component boundaries, build each affected component — don't rely on a single top-level build:
|
||||
|
||||
**Run targeted tests per task, never the full suite each time.** When executing a plan task-by-task, run only the tests that exercise the code that task touched (`dotnet test --filter "FullyQualifiedName~<TestClass>"`, or the per-task test named in the plan). The full gateway suite is slow — run it at most once per phase (after a related batch of tasks lands), not after every task. This is a speed guideline on macOS, where the suite exits cleanly (0 surviving `testhost`/worker processes after a full run) — filtered runs there are about turnaround, not about avoiding a process leak. **On windev a full-suite run wedges**: every test completes, then the x64 `testhost` never exits and `dotnet test` never returns (filtered runs exit normally). Run the full suite there with `--blame-hang --blame-hang-timeout 5m --blame-hang-dump-type none` and read the summary line, not the exit code — see "Running the Gateway Suite on windev" in `docs/GatewayTesting.md`.
|
||||
**Run targeted tests per task, never the full suite each time.** When executing a plan task-by-task, run only the tests that exercise the code that task touched (`dotnet test --filter "FullyQualifiedName~<TestClass>"`, or the per-task test named in the plan). The full gateway suite is slow — run it at most once per phase (after a related batch of tasks lands), not after every task. This is a speed guideline, not a correctness one: the suite exits cleanly on both macOS and windev (0 surviving `testhost`/worker processes after a full run), so filtered runs are about turnaround. The long-standing windev full-suite wedge — every test reported, then `testhost` never exiting — was a zero-buffer named pipe blocking one test's write forever; it is fixed and the `--blame-hang` workaround is no longer needed. See "Running the Gateway Suite on windev" in `docs/GatewayTesting.md`, which also documents the load-sensitivity caveat that still applies there.
|
||||
|
||||
| Changed area | Required verification |
|
||||
|---|---|
|
||||
|
||||
Reference in New Issue
Block a user