From 5769fd67da6cab48f3e01534dca169b1e0b138ff Mon Sep 17 00:00:00 2001 From: Amaury Leveugle Date: Wed, 22 Jul 2026 15:13:25 +0200 Subject: [PATCH] [dotnet-watch tests] Cap output-wait timeout below the MTP hang-dump AwaitableProcess derived its per-wait output timeout directly from HELIX_WORK_ITEM_TIMEOUT (work-item timeout minus 10s = 59:50 for the 60-min work items in test/UnitTests.proj). Microsoft.Testing.Platform's hang-dump fires at ~80% of the work-item timeout (48 min), so it always triggered before the graceful per-wait Assert.Fail. A single wedged output wait (e.g. the intermittent WebSocket hot-reload connection hang) therefore hung the entire work item until the 48-min hang-dump, killing the run and losing all results instead of failing that one test. Cap the per-wait timeout at 10 minutes (still honoring a shorter work-item timeout when configured) so a wedged wait fails fast with a useful 'Output not found' message, well below the hang-dump. No single output wait legitimately needs more than a few minutes. Follow-up to the CI analysis on #55297. Related: #55258, #55044. --- .../AwaitableProcess.cs | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.DotNet.HotReload.Test.Utilities/AwaitableProcess.cs b/test/Microsoft.DotNet.HotReload.Test.Utilities/AwaitableProcess.cs index 6da20abd21a8..a58c26b679f2 100644 --- a/test/Microsoft.DotNet.HotReload.Test.Utilities/AwaitableProcess.cs +++ b/test/Microsoft.DotNet.HotReload.Test.Utilities/AwaitableProcess.cs @@ -10,9 +10,30 @@ namespace Microsoft.DotNet.Watch.UnitTests { internal sealed class AwaitableProcess : IAsyncDisposable { - // cancel just before we hit timeout used on CI (TestWorkItemTimeout value in test/UnitTests.proj) - private static readonly TimeSpan s_timeout = Environment.GetEnvironmentVariable("HELIX_WORK_ITEM_TIMEOUT") is { } value - ? TimeSpan.Parse(value).Subtract(TimeSpan.FromSeconds(10)) : TimeSpan.FromMinutes(10); + // Per-wait timeout for expected process output. A single wait should never take anywhere near the + // CI work-item timeout, so this is capped well below Microsoft.Testing.Platform's hang-dump timeout + // (which fires at ~80% of the work-item timeout, e.g. 48 min for the 60-min work items configured by + // TestWorkItemTimeout in test/UnitTests.proj). Without the cap, deriving the value directly from + // HELIX_WORK_ITEM_TIMEOUT (60 min - 10 s = 59:50) means the hang-dump always fires first, converting a + // single wedged test into a work-item-wide hang that kills the whole run and loses all results. With + // the cap, a genuinely wedged wait fails *this* test fast with a useful "Output not found" message. + // See https://github.com/dotnet/sdk/issues/55258 and https://github.com/dotnet/sdk/issues/55044. + private static readonly TimeSpan s_timeout = GetOutputWaitTimeout(); + + private static TimeSpan GetOutputWaitTimeout() + { + var cap = TimeSpan.FromMinutes(10); + + // Honor a shorter work-item timeout (fire just before it), but never exceed the cap. + if (Environment.GetEnvironmentVariable("HELIX_WORK_ITEM_TIMEOUT") is { } value && + TimeSpan.TryParse(value, out var workItemTimeout)) + { + var derived = workItemTimeout.Subtract(TimeSpan.FromSeconds(10)); + return derived < cap ? derived : cap; + } + + return cap; + } private readonly List _lines = [];