diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/ApplicationDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/ApplicationDeployer.cs index a9212b0d54..999b9c788c 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/ApplicationDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/ApplicationDeployer.cs @@ -127,11 +127,27 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting hostProcess.StartAndCaptureOutAndErrToLogger("dotnet-publish", Logger); - hostProcess.WaitForExit(); - - if (hostProcess.ExitCode != 0) + // A timeout is passed to Process.WaitForExit() for two reasons: + // + // 1. When process output is read asynchronously, WaitForExit() without a timeout blocks until child processes + // are killed, which can cause hangs due to MSBuild NodeReuse child processes started by dotnet.exe. + // With a timeout, WaitForExit() returns when the parent process is killed and ignores child processes. + // https://stackoverflow.com/a/37983587/102052 + // + // 2. If "dotnet publish" does hang indefinitely for some reason, tests should fail fast with an error message. + const int timeoutMinutes = 5; + if (hostProcess.WaitForExit(milliseconds: timeoutMinutes * 60 * 1000)) { - var message = $"{DotnetCommandName} publish exited with exit code : {hostProcess.ExitCode}"; + if (hostProcess.ExitCode != 0) + { + var message = $"{DotnetCommandName} publish exited with exit code : {hostProcess.ExitCode}"; + Logger.LogError(message); + throw new Exception(message); + } + } + else + { + var message = $"{DotnetCommandName} publish failed to exit after {timeoutMinutes} minutes"; Logger.LogError(message); throw new Exception(message); }